Authentication

PIN

Set, verify, change, and reset the transaction PIN.

Flow Summary

The current PIN module has two groups:

Signed-in PIN Management

Step 1. Set PIN
POST /api/v1/auth/set-pin

Step 2. Verify PIN
POST /api/v1/auth/verify-pin

Step 3. Change PIN
POST /api/v1/auth/change-pin

PIN Reset Flow

Step 1. Request reset OTP
POST /api/v1/auth/forgot-pin

Step 2. Verify OTP
POST /api/v1/auth/verify-otp

Step 3. Reset PIN
POST /api/v1/auth/reset-pin

In development environments, OTP is currently hardcoded to 123456.

1. Set PIN

POST /api/v1/auth/set-pin

Treat this as a protected route and send:

Authorization: Bearer <access_token>

Request

{
  "pin": "123456"
}

Success response

{
  "status_code": 200,
  "message": "PIN set successfully",
  "data": null
}

Rules

  • pin must be exactly 6 digits
  • this endpoint only works if the user does not already have a PIN
  • if a PIN already exists, the service rejects the request and expects change-pin instead

2. Verify PIN

POST /api/v1/auth/verify-pin

Treat this as a protected route and send:

Authorization: Bearer <access_token>

Request

{
  "pin": "123456"
}

Success response

{
  "status_code": 200,
  "message": "OTP verified successfully",
  "data": null
}

Notes

  • The success message reuses the OTP verification locale string in the current handler.
  • Wrong PIN returns 422, not 401.
  • The service tracks failed attempts in Redis. After every 5 wrong attempts, the user is blocked for 1 minute and receives 429.
  • A successful verification clears the wrong-attempt counter.

3. Change PIN

POST /api/v1/auth/change-pin

Treat this as a protected route and send:

Authorization: Bearer <access_token>

Request

{
  "current_pin": "123456",
  "new_pin": "654321"
}

Success response

{
  "status_code": 200,
  "message": "PIN changed successfully",
  "data": null
}

Rules

  • both current_pin and new_pin must be exactly 6 digits
  • the user must already have a PIN
  • current_pin must match the stored PIN
  • new_pin must not be the same as current_pin

4. Request PIN Reset OTP

POST /api/v1/auth/forgot-pin

This flow is designed for authenticated users and the handler checks the current mobile user context before it sends an OTP.

Request

{
  "phone_code": "855",
  "country_code": "KH",
  "phone_number": "012345678"
}

You can also use:

{
  "email": "user@example.com"
}

Success response

{
  "status_code": 200,
  "message": "OTP sent successfully",
  "data": {
    "session_id": "9b7f1b4d-7c75-4d14-bec8-0d03b0f809d6",
    "expires_at": 600
  }
}

Notes

  • phone_number or email is required.
  • The phone or email must belong to the currently authenticated user.
  • The chosen phone/email must already be verified.
  • The OTP session is stored in Redis for 10 minutes.
  • Unlike forgot-password, this endpoint does populate expires_at with 600.

5. Verify OTP For PIN Reset

POST /api/v1/auth/verify-otp

Use the session_id returned by forgot-pin.

Request

{
  "phone_code": "855",
  "country_code": "KH",
  "phone_number": "012345678",
  "otp_code": "123456",
  "session_id": "9b7f1b4d-7c75-4d14-bec8-0d03b0f809d6"
}

Email-based reset can send email instead of the phone fields.

Success response

{
  "status_code": 200,
  "message": "OTP verified successfully",
  "data": {
    "success": true,
    "message": "OTP verified successfully",
    "session_id": "2f264af8-98c4-4a3d-89bc-5958c5ba6931"
  }
}

Use the returned session_id in reset-pin.

6. Reset PIN

POST /api/v1/auth/reset-pin

The Swagger annotation marks this as BearerAuth, but the current handler only validates the verified OTP session. The practical gate is possession of the session_id returned by verify-otp.

Request

{
  "session_id": "2f264af8-98c4-4a3d-89bc-5958c5ba6931",
  "new_pin": "654321"
}

Success response

{
  "status_code": 200,
  "message": "PIN reset successfully",
  "data": {
    "success": true,
    "message": "PIN reset successfully"
  }
}

Notes

  • new_pin must be exactly 6 digits.
  • The service loads the verified OTP session from Redis, hashes the new PIN, updates the user record, and deletes the verification session.
  • If the verification session is missing or expired, the service returns 400.

Integration Notes

  1. Use set-pin only once during first-time setup.
  2. Use verify-pin for transaction confirmation or other sensitive confirmations that rely on the stored PIN.
  3. For recovery, use the shared OTP step the same way as Password, but finish with reset-pin instead of reset-password.
Copyright © 2026