Idempotency

Network failures happen. If your request to create a payment times out you cannot know whether the payment was created. Retrying blindly could create two payments for one order. The Idempotency-Key header makes retries safe.

POST /api/v1/payments
Idempotency-Key: order-CLOUD-12345

Behaviour

Situation Result
First request with a key Payment created, 201, Idempotency-Replayed: false
Same key, same body Existing payment returned, 200, Idempotency-Replayed: true — same payment_id and payment_url
Same key, different body 409 IDEMPOTENCY_CONFLICT with error.details.payment_id; nothing created
Same key, different application Independent — keys are scoped per application
Concurrent duplicate requests Exactly one payment is created; the other request returns it
Creation failed at the gateway (GATEWAY_ERROR / GATEWAY_UNAVAILABLE) The key is released; a retry with the same key creates a fresh attempt

"Same body" means the same amount, currency, order_id, callback_url, description and metadata (key order does not matter).

Choosing a key

Client pattern

$key = "order-{$order->id}-attempt-{$order->payment_attempts}";

try {
    $payment = CartinoPay::createPayment($payload, idempotencyKey: $key);
} catch (ConnectionException) {
    // Safe to retry with the same key.
    $payment = CartinoPay::createPayment($payload, idempotencyKey: $key);
}

Verification is idempotent too

POST /payments/{id}/verify needs no key: repeated calls always converge on the same result and never double-process a paid payment.