Skip to content

Complete payment flow ​

Checkout sequence ​

StepEndpointNext action
1. MethodsGET /v2/payment-methodsSend customer ID; display available and saved payment methods.
1a. Method optionsGET /v2/payment-methods/{payment_method_code}/optionsCall only when the selected method's payment_details_schema contains option_code; let the customer pick an option (currently FPX banks).
2. CalculatePOST /v2/orders/calculateSend items, method, and customer fee percent; confirm the returned total.
3. OrderPOST /v2/ordersCreate once; save order_id. No charge starts.
4. PaymentPOST /v2/paymentsSend order ID, payer, return URL, method, and confirmed amount. For FPX or new CARD, add payment_method_details; for saved CARD, send top-level saved_payment_method_id instead. New CARD can request saving with top-level save_payment_method. Follow the returned action.
5. ResultsWebhooksTrack collection, per-unit fulfillment, and order-level refunds.

With hosted checkout, skip steps 1, 1a and 4: after step 3, call POST /v2/checkout-sessions and open the returned hosted_payment_url. The hosted page calculates and confirms the total. Step 2 is optional, to show a total before redirecting. Step 3 still needs a payment_method_code, such as CARD; the customer can pick another method on the hosted page.

Send requests from your backend. Confirm payment using webhooks or a status request, not the browser redirect.

Other order and payment operations ​

PurposeEndpoint
Calculate before retrying paymentPOST /v2/orders/calculate with order_id instead of items
Cancel an eligible pending paymentPOST /v2/payments/{payment_id}/cancel
Read collection statusGET /v2/payments/{payment_id}
Read fulfillment and refundsGET /v2/orders/{order_id}
Retrieve order historyGET /v2/orders

See Calculate Order, cancellation, and result reads. There is no order cancellation or public refund-submission endpoint.

Retries and idempotency ​

Send a unique Idempotency-Key (16–128 letters, digits, ., _, ~ or -; a UUID works) when creating an order, payment or checkout session. Reuse the same key and request for network retries; never reuse a key for a different operation. Keep the returned order and payment IDs.

Retry responses are retained for at least seven days. Payment-create responses remain available for at least seven days after the payment's final update; unresolved attempts defer removal. Replay is not guaranteed forever. For older requests, check the existing order/payment rather than resubmitting creation. Your order's external_reference must remain unique within your account.

While the idempotency record is retained, retrying with the same key and request returns the original creation response without submitting another payment, including a committed 400 provider_validation_error rejection. If creation has not yet recorded a response, 409 payment_creation_in_progress includes error.order_id and error.payment_id so you can track that same attempt. Do not create a new key to poll; use GET /v2/payments/{payment_id} or payment webhooks for current status.

SituationAction
A creation response is lostRetry with the same key and request, using fresh authentication headers. See the card-specific retry guidance below.
idempotency_conflictThe key was used with different data. Check the existing order/payment.
total_mismatchConfirm the newly calculated total, then submit the corrected request.
Payment is pending, processing, or cancellation is pendingCheck its status; do not create another payment.
Payment is failed, cancelled, or expiredIf the order is still unpaid and unexpired, recalculate and create a new payment with a new key.

A new key does not bypass payment safety: an unresolved attempt returns 409 active_payment_exists; an order with a successful collection returns 409 payment_already_successful. An eligible new attempt keeps the same order_id but receives a new payment_id.

For card retries, do not store card details.

Payment expiry ​

Create payments within 24 hours of order creation. Each payment has a 30-minute customer-action window; use its expires_at value.

Always check the payment's status before retrying. An elapsed deadline alone does not mean the payment failed.

Shared errors ​

Error responses use this envelope; endpoint-specific tables describe additional codes.

Status codes follow one rule: 400 means fix the request, 403 means payments are not enabled for your account, 404 means not found, 409 means the request conflicts with the current order, payment, or Idempotency-Key state, 422 means a refund account was refused, 429 means a limit was reached, and 500/503 mean retry the same request later, after Retry-After when it is present. Branch on error.code for the specific case.

FieldTypeDescription
error.codestringMachine-readable error code.
error.messagestringSafe explanation.
error.request_idstringReference for support. Omitted on 503 feature_unavailable.
error.order_idstringRelated order ID on payment_creation_in_progress and on payment-cancellation conflicts.
error.payment_idstringExisting attempt ID when creation is in progress or an attempt-specific rejection supplies it. Use it to read payment status.
error.detailsobject or arrayReturned only where documented: validation, item failures, duplicate reference, or total mismatch.
HTTPCodeAction
400validation_errorFix the fields named in error.details; see validation details.
401Authentication errorCheck the signing headers. Returned in this envelope, for example error.code: "invalid_signature".
403feature_not_enabledPayments are not enabled for your account; new orders and payments are refused, existing ones stay readable. Contact IIMMPACT.
500internal_errorRetry with bounded backoff; preserve creation keys and follow the retry rules above.
503feature_unavailablePayments are temporarily switched off; nothing was processed. Retry later.
503service_unavailableAuthentication is temporarily unavailable and the request was not processed. Retry with bounded backoff, signing each retry with a new X-Nonce and current X-Timestamp; keep the same creation keys.

Validation details ​

FieldTypeDescription
error.details[].fieldstringSnake_case name of the invalid field. Item fields include their position, such as items[1].amount. Fields inside customer and payment_method_details use their own name, such as external_id or card_number. A missing or malformed Idempotency-Key header is reported as idempotency_key, and unsupported fields as undeclared_fields (or items[1].undeclared_fields).
error.details[].codestringWhat is wrong with the field, such as required, max_length, invalid_amount, invalid_quantity, invalid_order_id, invalid_customer, invalid_return_url, invalid_idempotency_key, items_required, unsupported_field, invalid_payment_source, incompatible_fields, or invalid_json.
error.details[].messagestringHow to correct the field, for example order_id must be a valid 26-character ULID. Never echoes submitted values.
json
{
  "error": {
    "code": "validation_error",
    "message": "The request is invalid.",
    "request_id": "req_01M3KC2PHYKJ1FMVA0MZGXECRN",
    "details": [
      {
        "field": "items",
        "code": "items_required",
        "message": "items must contain at least one item."
      }
    ]
  }
}

If an item fails product validation rather than field-shape validation, the error is 400 invalid_items and error.details.items lists each failing item with its index, code (invalid_amount, invalid_extras, invalid_product, invalid_account, invalid_topup, or invalid_item) and message. Correct the listed items and calculate again.

IIMMPACT API Documentation