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.

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 when creating an order or payment; a UUID is recommended. 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, 404 means not found, 409 means the request conflicts with the current order, payment, or Idempotency-Key state, 429 means slow down, and 500/503 mean retry the same request later. Branch on error.code for the specific case.

FieldTypeDescription
error.codestringMachine-readable error code.
error.messagestringSafe explanation.
error.request_idstringReference for support.
error.order_idstringRelated order ID on payment-creation and cancellation conflicts, such as payment_creation_in_progress or payment_already_successful.
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.
429rate_limit_exceededToo many requests from your account. Retry with bounded backoff.
500internal_errorRetry with bounded backoff; preserve creation keys and follow the retry rules above.

Validation details ​

FieldTypeDescription
error.details[].fieldstringField path, including array position when relevant.
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, 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.

Pine Labs API Documentation