Best Practices: Ensuring Reliable Payment Integration
This section provides key recommendations to secure, optimize, and ensure the reliability of your payment integration using the Payment API.
Idempotency: Preventing Duplicate Transactions
Why is Idempotency Important?
Requests to the Payment API may be accidentally duplicated due to:
- Network failures, causing a retry.
- Timeouts, leading the merchant system to resend the same request.
- User actions, such as clicking multiple times on a "Pay" button.
Without idempotency, these scenarios could result in duplicate payments or refunds.
Send no key at all, and a retry issued after a lost response is indistinguishable from a new operation: it either executes a second time, or is rejected with a 400 once the requested amount is no longer available on the payment. Idempotency keys are strongly recommended for batch and retry-driven integrations.
Best Practices
- Send
x-idempotency-keyon every state-changing call: payment creation (POST /v2/payments), capture, refund, and void. - One key per operation, never one key per payment. Keys are scoped to the payment, and a payment can legitimately be captured, voided, or refunded several times — two partial captures on the same authorization, for example. If two different operations on the same payment reuse the same key, the second is treated as a replay and refused with a
409instead of being executed. - Derive the key from the operation itself — your own capture or refund identifier, or the payment ID plus a sequence number. Never derive it from the payment or the order alone.
- Keep the key stable across every retry, including a retry sent hours or days later. A key regenerated per HTTP attempt provides no protection at all.
- Retry with the same key when a response is lost (client timeout, network error,
5xx). The operation is then executed at most once. - Store keys for at least as long as your retry window, so a late retry can reuse the original key.
Example: Using Idempotency in a Capture Request
curl -X POST "https://api.purse-sandbox.com/payment/v2/payments/${PAYMENT_ID}/captures" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "x-idempotency-key: CAP#1223445" \
--data '{
"amount": 5000,
"merchant_reference": "CAP#1223445"
}'
The key is derived from the merchant's own capture reference, so every retry of this capture carries the same value, while a second capture on the same payment gets a key of its own.
A request carrying a key already processed on the same payment is answered with a 409 Conflict — whether it is a genuine retry or a different operation that reused the key by mistake:
{
"type": "urn:eu.purse:write-conflict",
"title": "Idempotency violation",
"status": 409,
"instance": "/v2/payments/558f47c6-fd88-4c28-aa2f-5d3d7a44709f/captures",
"resource_id": "432ccde8-13e8-4c31-a900-a9c40546c12a"
}
resource_id points at the resource the first call created: the payment ID for POST /v2/payments, or the capture, void, or refund operation ID for the operation endpoints. Read it back to recover the outcome of that first call.
A replay arriving while the first call is still in flight is held for a bounded number of attempts, then answered like any other replay. If the first call never completes at all, the replay errors instead of resolving — so a retry that keeps failing with a 5xx should be settled by reading the payment back with GET /v2/payments/{id}, not by replaying the key again.
Security: Protecting Payment Data
Best Practices
- Always use HTTPS for all API requests to encrypt sensitive data.
- Use OAuth2 authentication to secure API access and prevent unauthorized requests.
- Restrict API keys and tokens to specific IPs, users, or actions to limit exposure.
- Monitor API access logs to detect suspicious behavior and prevent fraud.
Webhook Security
All webhook events sent by the Payment API are digitally signed to ensure their authenticity.
- Always verify the signature using the public keys provided by
/payment/v2/signing-jwks. - Reject any webhook that fails signature validation to prevent spoofed requests.
curl -X GET "https://api.purse-sandbox.com/payment/v2/signing-jwks" \\
-H "Authorization: Bearer <access-token>"
The returned public keys can be used to validate webhook payloads before processing them.
Optimizing API Calls: Performance & Reliability
Best Practices
- Efficient Token Usage → Store and reuse the access token until it expires instead of requesting a new one for each API call.
- Avoid unnecessary polling → Use webhooks instead to receive real-time updates.
- Use connection timeouts and retries with exponential backoff for network failures.
Handling Webhooks Efficiently
Best Practices
- Respond to webhook requests within 5 seconds to avoid timeouts.
- Perform minimal processing inside the webhook handler → Offload heavy tasks to a background queue.
- Log all received webhooks to debug failures and reprocess missed events if necessary.
- Validate webhook signatures to prevent unauthorized events from being processed.
Example: Webhook Payload
{
"id": "acb149dc-cbc7-4be5-b5c7-4d3f4d40b595",
"status": "AUTHORIZED",
"amount": 12000,
"currency": "EUR",
"customer": {
},
"order": {
"reference": "123456789"
}
}
Error Handling & Retry Strategies
Best Practices
- Check all API responses and handle errors appropriately.
- Implement retries for network failures, but do not retry on client errors (
4xx). - Log all failed API calls to investigate recurring issues.
- Use unique request IDs to trace failed transactions and reprocess them if necessary.
Final Recommendations
Following these best practices will help you:
- Ensure safe and secure payment processing.
- Improve API efficiency and reduce unnecessary calls.
- Prevent duplicate operations and avoid transaction inconsistencies.
- Handle webhook events effectively without missing critical updates.
Now you're ready to build a robust and reliable payment integration!