Payment Flow

Understand the complete payment lifecycle

Overview#

A payment goes through several states from creation to completion. The gateway supports two capture methods: direct capture (immediate) and reservation (capture later).

Payment States#

CREATED->PROCESSING->REQUIRES_ACTION->PRE_AUTHORIZED->SUCCEEDED
StateCodeDescription
CREATED-Payment intent has been created, awaiting payment method
PROCESSING-Payment is being processed with the provider
REQUIRES_ACTION12OTP verification required — redirect user to payUrl
PRE_AUTHORIZED13Funds reserved, awaiting capture or release
SUCCEEDED2Payment completed successfully
FAILED3Payment was rejected (terminal state)
CANCELED10Pre-authorization was released (terminal state)
REFUNDED4Payment was fully refunded (terminal state)
PARTIALLY_REFUNDED16Payment was partially refunded

Direct Capture Flow#

This is the default flow. The payment is captured immediately when processed.

Loading diagram...
  1. Register Customer — Ensure the customer exists by calling POST /customers with their phone number. This is idempotent — if the customer already exists, handle the 409 and continue.
  2. Create Payment Intent — Your server creates a payment intent with the amount and currency. The userId must be the registered customer's phone number. Leave captureMethod as default (DIRECT) or omit it.
  3. Proceed with Wallet — Call proceed with paymentMethodCode: "WALLET_V2".
  4. Handle OTP — The response includes require3DS: true and a payUrl. Redirect the user to the payUrl for OTP verification. Append your returnUrl so the user is redirected back after verification.
  5. Check Result — After the user returns from OTP, call the Check endpoint to confirm the payment status. A statusCode of 2 means success.
  6. Webhook — Your server receives a webhook with remoteStatusCode: 2 (succeeded) or 3 (rejected).

OTP Verification#

When you call the Proceed endpoint with Yassir Cash, the response includes require3DS: true and a payUrl in the metadata. The user needs to verify the payment with a one-time password (OTP), as shown in steps 4-5 of the diagram above.

How it works#

You don't need to implement any OTP logic. Yassir handles the entire OTP flow — your integration only needs to:

  1. Redirect the user to the payUrl from the Proceed response. Append a returnUrl query parameter so the user is sent back to your app after verification.
  2. Wait for the redirect back — After the user enters their OTP on the Yassir-hosted page, they are automatically redirected to your returnUrl.
  3. Check the result — Call the Check endpoint or rely on the webhook to confirm whether the payment succeeded.
OTP Redirect Flow
// Proceed response with OTP required:
{
  "data": {
    "statusCode": 12,
    "require3DS": true,
    "metadata": {
      "payUrl": "https://otp.payment.yassir.io/otp/verify?requestId=...&phone=...&amount=1500&currency=DZD"
    }
  }
}

// Your code: redirect the user
const { payUrl } = response.data.metadata;
const returnUrl = `${window.location.origin}/payment-result?paymentId=${paymentId}`;

const url = new URL(payUrl);
url.searchParams.set('returnUrl', returnUrl);
window.location.href = url.toString();

// That's it! Yassir shows the OTP page, verifies the code,
// and redirects the user back to your returnUrl.
// Then check the payment status:
GET /payments/intents/:id/check
// → statusCode: 2 (succeeded) or 3 (rejected)

OTP is fully managed

The OTP page is hosted by Yassir. It handles sending the SMS, displaying the input form, validating the code, and retries. You only need to redirect the user there and handle them coming back.

Redirect URL (Return URL)#

After OTP verification or 3DS authentication, Yassir redirects the user back to your application. To receive these redirects, you must configure a redirect URL for your service during onboarding (or request it to be updated via your Yassir contact).

Redirect URL format#

When Yassir redirects the user back, the following query parameters are appended to your configured redirect URL:

ParameterTypeDescription
paymentIdstringThe payment intent ID
statusCodenumberThe payment status code (e.g., 2 = succeeded, 3 = rejected, 13 = pre-authorized)
Handling the redirect
// Example redirect URL after successful payment:
https://yourapp.com/payment-result?paymentId=123e4567-e89b-12d3-a456-426614174000&statusCode=2

// Example redirect URL after failed payment:
https://yourapp.com/payment-result?paymentId=123e4567-e89b-12d3-a456-426614174000&statusCode=3

// Your handler:
const params = new URLSearchParams(window.location.search);
const paymentId = params.get('paymentId');
const statusCode = Number(params.get('statusCode'));

if (statusCode === 2) {
  // Payment succeeded — show success page
} else if (statusCode === 13) {
  // Pre-authorized — show capture/release UI
} else {
  // Payment failed — show error and allow retry
}

Always verify server-side

The redirect URL parameters are for UX purposes only (e.g., showing the right page to the user). Always call the Check endpoint or rely on webhooks to confirm the payment status server-side before fulfilling an order.

Reservation Flow (Manual Capture)#

For scenarios where you need to reserve funds first and capture later (e.g., hotels, car rentals, service bookings), use the reservation flow.

Loading diagram...
  1. Register Customer — Same as direct flow, ensure the customer exists.
  2. Create Payment Intent — Set captureMethod: "RESERVATION".
  3. Proceed with Wallet — Same as direct flow. The user will be redirected to the OTP page for verification.
  4. Pre-Authorized — After successful OTP verification, the payment enters the PRE_AUTHORIZED state (statusCode 13). Funds are reserved in the user's wallet but not yet captured.
  5. Capture or Release — When ready:
    • Capture — Call the Capture endpoint. You can capture the full amount or a partial amount (the remainder is automatically released).
    • Release — Call the Release endpoint to release all reserved funds back to the user.
  6. Webhook — Your server receives a webhook after capture (remoteStatusCode: 2) or release (remoteStatusCode: 10).

Refund Flow#

After a payment has been successfully captured (statusCode 2), you can refund it fully or partially.

Loading diagram...
  • Full refund — Omit the amount field. The entire payment amount is refunded to the user's wallet.
  • Partial refund — Specify an amount less than the original payment. The specified amount is refunded.
  • Multiple partial refunds — You can issue multiple partial refunds on the same payment. Each refund reduces the remaining refundable balance until it reaches zero.

Webhook Notifications

A webhook is sent to your registered URL after every state change (proceed, capture, release, refund). Use the remoteStatusCode field to determine the event type. See the Webhooks guide for the full payload format.