Payments example
Errors, idempotency, and webhooks
Serious payments integrations are judged on failure handling and asynchronous delivery, so the example exposes those mechanics directly instead of hiding them in repository files.
Error handling
Structured JSON errors
{
"error": {
"code": "duplicate_payment",
"message": "A payment with this idempotency key already exists.",
"trace_id": "trace_9f0e7c",
"docs_url": "https://docs.startup-payments.example/errors#duplicate_payment"
}
}
- 400: invalid JSON, missing fields, unsupported currency
- 401: missing or invalid bearer token
- 409: duplicate create or conflicting refund request
- 429: client exceeded rate limit
Idempotency
Retry-safe write operations
Clients should send an Idempotency-Key header on payment creation and refund requests. The first valid request creates the resource, repeated requests return the original result, and key reuse with a different payload should return a conflict.
curl -X POST "https://api.startup-payments.example/v1/payments" -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -H "Idempotency-Key: 3d8f7d89-a0c5-4f54-97f2-3a4ef83da3b4" -d '{"amount":125.5,"currency":"USD","customer_id":"cus_123"}'
Webhooks
Asynchronous payment lifecycle delivery
- Events are delivered with POST requests to a customer HTTPS endpoint.
- Deliveries should be signed and timestamped.
- Non-2xx responses trigger retries with backoff.
- Core example events include payment.created, payment.completed, payment.failed, and payment.refunded.
{
"id": "evt_123",
"type": "payment.completed",
"created": "2026-03-12T11:42:00Z",
"data": {
"payment_id": "pay_123",
"status": "completed",
"amount": 125.5,
"currency": "USD"
}
}