Appearance
Complete payment flow
Checkout sequence
| Step | Endpoint | Next action |
|---|---|---|
| 1. Methods | GET /v2/payment-methods | Send customer ID; display available and saved payment methods. |
| 2. Calculate | POST /v2/orders/calculate | Send items, method, and customer fee percent; confirm the returned total. |
| 3. Order | POST /v2/orders | Create once; save order_id. No charge starts. |
| 4. Payment | POST /v2/payments | Send order ID, payer, return URL, method, and confirmed amount. Follow the returned action. |
| 5. Results | Webhooks | Track collection, per-unit fulfillment, and order-level refunds. |
For direct card entry, the client form sends input through your backend. A returned redirect may be for 3DS rather than card entry. Browser return or successful authentication is not proof of collection; use verified status from webhooks or API reads.
Other operations
| Purpose | Endpoint |
|---|---|
| Calculate before retrying payment | POST /v2/orders/calculate with order_id instead of items |
| Cancel an eligible pending payment | POST /v2/payments/{payment_id}/cancel |
| Read collection status | GET /v2/payments/{payment_id} |
| Read fulfillment and refunds | GET /v2/orders/{order_id} |
| Retrieve order history | GET /v2/orders |
See Calculate Order, cancellation, and result reads. There is no order cancellation or public refund-submission endpoint.
Retries and idempotency
Order and payment creation require a unique Idempotency-Key. Reuse it when retrying the same request. Order external_reference must also remain unique within your account.
Keys are scoped per endpoint: an order-creation key and a payment-creation key do not conflict with each other. A key is bound once the API accepts the request (2xx or 202); rejected requests (4xx) do not consume it. A bound key replays indefinitely for the lifetime of the resource.
| Situation | Action |
|---|---|
| Order creation or hosted/saved-method payment creation loses its response | Retry with the same key and body, using fresh HMAC headers. Never create a new key merely because the response was lost. |
| An admitted request is replayed | The original response is returned, not current state. Read the resource before using a replayed redirect. Changed bound contents return idempotency_conflict. |
Request was rejected (4xx) | The key is not consumed. Correct the fields and retry with the same key, or use a new key. Either is safe. |
Payment returns total_mismatch | No payment was created. Confirm error.details.calculation.totals.payment_total, then submit the corrected request with a new key. |
| Payment is pending, processing, or cancellation is unresolved | Do not create another attempt. Read latest_payment_id from the order, then check that payment's status. |
| Payment is confirmed failed, cancelled, or expired | Recalculate, confirm, then use the same unpaid order and a new payment key. The new attempt supplies its own payer, method, share, and return URL. |
Direct full-PAN recovery has additional restrictions: never persist sensitive card input for retries.
Payment expiry
Each payment link is usable for 30 minutes. After that, check the payment status before retrying:
- Still processing: wait. The payment may already have been collected.
- Failed, cancelled, or expired: calculate again, confirm the total, and create a new payment for the same unpaid order.
The order stays available for payment. Its item prices stay the same; payment fees may change. If an extra payment is collected, it is refunded rather than fulfilling the order twice.
Shared errors
Error responses use this envelope; endpoint-specific tables describe additional codes.
| Field | Type | Description |
|---|---|---|
error.code | string | Machine-readable error code. |
error.message | string | Safe explanation. |
error.request_id | string | Reference for support. |
error.details | object or array | Returned only where documented: validation, duplicate reference, or total mismatch. |
| HTTP | Code | Action |
|---|---|---|
| 400 | invalid_request | Correct malformed JSON or request format. |
| 401 | Authentication error | Check the signing headers. |
| 429 | rate_limited | Honor Retry-After or use bounded backoff. |
| 503 | service_unavailable | Back off; preserve creation keys and follow the retry rules above. |
Validation details
| Field | Type | Description |
|---|---|---|
error.details[].field | string | Field path, including array position when relevant. |
error.details[].code | string | required_field, invalid_field, or a product validation code. |
error.details[].message | string | How to correct the field; never echoes card input. |
json
{
"error": {
"code": "validation_error",
"message": "The request contains invalid fields.",
"request_id": "req_example",
"details": [
{
"field": "items[0].account",
"code": "invalid_account",
"message": "Check the account number."
}
]
}
}If any item is invalid, calculation returns the field errors instead of a total. Correct the listed fields and calculate again.
