Accepting Card Payments
Card payments add reach for customers with Visa and Mastercard. This guide covers the charge flow, 3-D Secure authentication, idempotency to prevent double charges, and refunds.
đ Quick Startâ
curl -X POST https://api.afriroute.ai/api/v1/payments/cards/charge \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: charge-ORDER-A1029" \
-d '{
"token": "tok_visa_xxx",
"amount": 4500,
"currency": "NGN",
"reference": "ORDER-A1029",
"callback_url": "https://example.com/webhooks/payments"
}'
Never send raw card numbers to your server â collect them with the client-side SDK, which returns a single-use token.
đ 3-D Secureâ
Many African issuers require 3DS. When a charge needs authentication, the API returns a redirect.
const res = await fetch('https://api.afriroute.ai/api/v1/payments/cards/charge', {
method: 'POST',
headers: {
'Authorization': 'Bearer $AFRIROUTE_API_KEY',
'Content-Type': 'application/json',
'Idempotency-Key': `charge-${orderId}`
},
body: JSON.stringify({ token, amount: 4500, currency: 'NGN', reference: orderId })
});
const data = await res.json();
if (data.status === 'requires_action') {
window.location = data.redirect_url; // bank's 3DS challenge
}
After the customer completes the challenge, the final result arrives via your callback_url.
đ Idempotencyâ
Always send an Idempotency-Key. Network retries with the same key return the original charge instead of charging twice.
import requests
requests.post(
'https://api.afriroute.ai/api/v1/payments/cards/charge',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY', 'Idempotency-Key': f'charge-{order_id}'},
json={'token': token, 'amount': 4500, 'currency': 'NGN', 'reference': order_id}
)
âŠī¸ Refundsâ
Refund full or partial amounts by transaction ID.
curl -X POST https://api.afriroute.ai/api/v1/payments/cards/refund \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Idempotency-Key: refund-A1029" \
-d '{ "transaction_id": "txn_abc123", "amount": 4500 }'
| Field | Required | Notes |
|---|---|---|
transaction_id | Yes | The original charge |
amount | No | Omit for full refund |
reason | No | Stored for records |
đĄ Best Practicesâ
- Tokenize on the client â never touch raw PANs (PCI scope).
- Send an idempotency key on every charge and refund.
- Handle
requires_actionfor 3DS rather than treating it as failure. - Confirm via webhook before fulfilling â don't trust the redirect alone.
- Verify webhook signatures to prevent spoofed success events.
- Refund, don't reverse, after settlement.
đ Related Resourcesâ
Last Updated: May 2026