Skip to content

Errors & Reference

Errors & Reference

HTTP Status Codes

CodeMeaningDescriptionAction
200OKRequest succeededContinue
201CreatedResource createdStore returned ID
400Bad RequestValidation failedFix request parameters
401UnauthorizedMissing/invalid API keyCheck API key
403ForbiddenAPI key revoked/expiredRegenerate API key
404Not FoundResource not foundCheck ID
409ConflictDuplicate ID/referenceUse unique value
429Too Many RequestsRate limit exceededImplement backoff
500Internal Server ErrorServer errorRetry with backoff
503Service UnavailableTemporary outageRetry with backoff

Error Response Format

All errors follow this format:

{
"status": "error",
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message"
},
"metadata": {
"timestamp": "2025-01-07T12:00:00Z",
"api_version": "v1"
}
}

Error Codes

General Errors

CodeHTTPDescription
INVALID_INPUT400Request validation failed
VALIDATION_FAILED400Field validation failed
UNAUTHORIZED401Authentication failed
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
INTERNAL_ERROR500Internal server error

Payment Errors

CodeHTTPDescription
PAYMENT_NOT_FOUND404Payment not found
INVALID_AMOUNT400Amount out of range (R55-R3000)
INVALID_CURRENCY400Currency not supported
DUPLICATE_ORDER_ID409merchant_order_id already used

Payout Errors

CodeHTTPDescription
PAYOUT_NOT_FOUND404Payout not found
INSUFFICIENT_BALANCE400Not enough available balance
INVALID_PAYOUT_METHOD400Unsupported payout method
INVALID_PAYMENT_DETAILS400Bank details validation failed
INVALID_STATUS_TRANSITION400Invalid status change

Idempotency

Payment Initialization

Use Idempotency-Key header for safe retries:

Terminal window
curl -X POST "https://api.qrpay2.com/api/v1/payments/init" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: ORDER-123-1704636000-abc123" \
-H "Content-Type: application/json" \
-d '{"amount": 10000, ...}'

Behavior:

  • Same key + identical payload → Same payment_id (200 OK)
  • Same key + different payload → Conflict (409)

Recommended format: {merchant_order_id}-{timestamp}-{random}

Payout Idempotency

Use merchant_reference for idempotent payout creation:

  • Same reference + identical params → Return existing payout
  • Same reference + different params → Conflict (409)

Webhook Idempotency

Use X-QRPay-Event-Id for webhook processing:

  • Store event ID before processing
  • Return 200 OK for duplicate events

Best Practices

Security

  1. Store API keys securely - Use environment variables or secrets manager
  2. Never log secrets - Mask API keys and webhook secrets
  3. Use HTTPS - Required for all production requests
  4. Rotate keys regularly - Coordinate with account manager
  5. Validate timestamps - Prevent replay attacks on webhooks
  6. Don’t expose details - Keep payment_details out of logs

Error Handling

  1. Implement exponential backoff for 5xx and 429 errors
  2. Log all errors with request context
  3. Don’t retry 4xx (except 429) without fixing the issue
  4. Handle insufficient balance by checking before retries

Monitoring

  1. Track success rates - Detect issues early
  2. Monitor webhook delivery - Alert on failures
  3. Set balance alerts - Prevent payout failures
  4. Log state transitions - Audit trail for debugging

Rate Limits

EndpointLimit
Payment init100/min per merchant
Payment status300/min per merchant
Payout create50/min per merchant
Payout status300/min per merchant
Balance check60/min per merchant

When rate limited (429), implement exponential backoff starting at 1 second.