1. Overview
AWDPay Checkout API V2 allows a merchant to initiate a payment from their platform, redirect the customer to AWDPay for OTP/payment confirmation, and receive payment status updates.
/api/v2/checkout/....
Older routes using /api/checkout/v2/... may remain available for existing integrations.
2. Base URL
https://www.awdpay.com
3. Authentication
All protected requests must include the merchant private key in the Authorization header.
Authorization: Bearer YOUR_PRIVATE_KEY
Content-Type: application/json
4. Initiate Checkout
Create a checkout transaction and receive a hosted payment URL.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payment amount. |
currency | string | Yes | Currency code, for example XOF, USD, EUR. |
customIdentifier | string | Yes | Your internal order/payment reference. |
callbackUrl | string | Recommended | Webhook URL to receive payment updates. |
successUrl | string | Yes | Customer redirect URL after successful payment. |
failedUrl | string | Yes | Customer redirect URL if payment fails or is cancelled. |
logo | string | No | Merchant logo URL displayed on checkout page. |
test | boolean | No | Use true for test mode and false for production. |
cURL example
curl -X POST https://www.awdpay.com/api/v2/checkout/initiate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_PRIVATE_KEY" \
-d '{
"amount": 1000,
"currency": "XOF",
"customIdentifier": "ORDER-2026-0001",
"callbackUrl": "https://merchant.com/api/awdpay/v2/callback",
"successUrl": "https://merchant.com/payment/success",
"failedUrl": "https://merchant.com/payment/failed",
"logo": "https://merchant.com/logo.png",
"test": false
}'
Success response
{
"success": true,
"message": "Checkout initiated successfully",
"trxId": "TRX_ID",
"redirectUrl": "https://www.awdpay.com/checkout/..."
}
5. Send Customer OTP
Send or validate the customer OTP step depending on the checkout flow.
Example
curl -X POST https://www.awdpay.com/api/v2/checkout/customer/otp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_PRIVATE_KEY" \
-d '{
"trxId": "TRX_ID",
"otp": "123456"
}'
6. Confirm Customer Payment
Confirm the payment after the OTP or customer authorization step.
Example
curl -X POST https://www.awdpay.com/api/v2/checkout/customer/pay \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_PRIVATE_KEY" \
-d '{
"trxId": "TRX_ID",
"otp": "123456"
}'
success status should be considered as confirmed payment.
7. Verify Payment
Verify a payment by transaction ID or merchant custom identifier.
Examples
curl -X GET "https://www.awdpay.com/api/v2/checkout/verify?trxId=TRX_ID" \
-H "Authorization: Bearer YOUR_PRIVATE_KEY"
curl -X GET "https://www.awdpay.com/api/v2/checkout/verify?customIdentifier=ORDER-2026-0001" \
-H "Authorization: Bearer YOUR_PRIVATE_KEY"
Response example
{
"success": true,
"trxId": "TRX_ID",
"status": "success",
"amount": 1000,
"currency": "XOF",
"customIdentifier": "ORDER-2026-0001",
"paidBy": "customer@example.com",
"test": false,
"updatedAt": "2026-05-09T10:00:00.000Z"
}
8. Find Pending Payment
Find a checkout payment if the merchant needs to recover a pending transaction.
Example
curl -X GET "https://www.awdpay.com/api/v2/checkout/find?trxId=TRX_ID" \
-H "Authorization: Bearer YOUR_PRIVATE_KEY"
9. Merchant Callback
AWDPay sends an HTTP POST callback to the merchant’s callbackUrl
when the payment status changes.
Callback payload example
{
"event": "payment.success",
"status": "success",
"trxId": "TRX_ID",
"amount": 1000,
"currency": "XOF",
"customIdentifier": "ORDER-2026-0001",
"paidBy": "customer@example.com",
"timestamp": "2026-05-09T10:00:00.000Z"
}
Callback headers
Content-Type: application/json
X-AWDPAY-Event: payment.success
X-AWDPAY-Timestamp: <TIMESTAMP>
X-AWDPAY-Signature: <RECEIVED_SIGNATURE>
sha256= prefix. Do not prepend one when comparing.
HTTP 200 OK after successful processing.
10. Verify Callback Signature
Use the raw request body, the X-AWDPAY-Timestamp header and your webhook
secret to verify X-AWDPAY-Signature.
Signature formula
message = <TIMESTAMP> + "." + <RAW_BODY>
signature = HMAC_SHA256_HEX(<WEBHOOK_SECRET>, message)
<TIMESTAMP> is the exact value of the X-AWDPAY-Timestamp
header — a Unix epoch in seconds. <RAW_BODY> is the
exact byte sequence of the request body, as received.
- the timestamp and the separating dot are part of the signed message — signing the body alone will never match;
- Checkout API V2 sends the signature without any
sha256=prefix — compare against the bare hexadecimal value.
import crypto from "crypto";
function verifyAwdpaySignature(rawBody, signatureHeader, timestampHeader, webhookSecret) {
// Checkout API V2: no "sha256=" prefix.
const expected = crypto
.createHmac("sha256", webhookSecret)
.update(`${timestampHeader}.${rawBody.toString("utf8")}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(String(signatureHeader || ""));
// timingSafeEqual throws on length mismatch, so compare lengths first.
// This only reveals the length, never the content.
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
- Capture the raw body before any JSON parsing middleware
(for example
express.raw({ type: "application/json" })). - Never rebuild the JSON after parsing:
JSON.parsefollowed byJSON.stringifycan reorder keys or change spacing, and the signature will no longer match. - Compare in constant time, never with
==or!==. - Reject stale timestamps — a window of 5 minutes is recommended.
- Process each
trxIdonly once.
Important note for webhook/callback verification
This page documents the Checkout API V2. AWDPay also operates a Gateway V3, which uses a different signature scheme. If your integration receives callbacks from both, verify each one according to the API version that sent it.
If your integration receives AWDPay Gateway V3 webhook headers such as
X-AWDPAY-Signature, X-AWDPAY-Timestamp,
X-AWDPAY-Event and X-AWDPAY-Signature-Key, you must follow the
API V3 webhook signature verification documentation.
The presence of X-AWDPAY-Signature-Key identifies a Gateway V3 callback:
this header does not exist in Checkout API V2.
| Checkout API V2 (this page) | Gateway V3 | |
|---|---|---|
| Signed message | timestamp + "." + rawBody | timestamp + "." + rawBody |
| Header prefix | none — bare hexadecimal | sha256= |
X-AWDPAY-Signature-Key | not sent | sent |
The two APIs do not share the same signature convention. The algorithm is
identical, but the header format is not: reusing a Gateway V3 verifier as-is on Checkout
API V2 callbacks will fail on the missing sha256= prefix, and the reverse
will fail too. Select the convention from the presence of
X-AWDPAY-Signature-Key, which only Gateway V3 sends.
For Gateway V3 callbacks the signature is calculated using:
message = X-AWDPAY-Timestamp + "." + rawBody
signature = "sha256=" + HMAC_SHA256_HEX(secret, message)
- Use the exact raw HTTP body.
- Do not rebuild the JSON after parsing.
- Do not modify spaces, field order, or encoding.
- Compare signatures in constant time.
- Reject invalid signatures.
- Process each
trxIdonly once.
Important. successUrl is only a browser redirect and must
not be treated as proof of payment. Only a correctly signed server-to-server callback,
or a server-side payment verification, should be trusted.
Sandbox payments must not trigger delivery, credit, confirmation, or activation.
Full Gateway V3 reference: https://docs.awdpay.com/v3-api
11. Payment Statuses
| Status | Description |
|---|---|
pending | Payment initiated but not completed. |
success | Payment completed successfully. |
failed | Payment failed or was cancelled. |
12. Best Practices
- Do not expose your private key in frontend code.
- Create checkout requests from your backend only.
- Store
trxIdandcustomIdentifierwith your order. - Do not mark an order as paid only because the customer reaches
successUrl. - Always verify the final status using callback or
/api/v2/checkout/verify. - Only
successis a final successful payment. - Always verify
X-AWDPAY-Signaturebefore confirming an order.
Forfaits prépayés et Formules d’abonnement
AWDPay Checkout API V2 supporte maintenant trois types d’intégration :
payment, prepaid et subscription.
Cette évolution permet aux marchands de vendre des paiements simples, des forfaits prépayés
et des formules d’abonnement avec un seul endpoint.
POST https://www.awdpay.com/checkout/v2/initiate
Types disponibles
| Type | Description | Webhook succès |
|---|---|---|
payment |
Paiement simple et ponctuel. | payment.success |
prepaid |
Forfait payé une fois avec validité, unités ou accès. | prepaid.payment.success |
subscription |
Formule d’abonnement liée à un plan. | subscription.activated |
Exemple — Paiement simple
curl -X POST https://www.awdpay.com/checkout/v2/initiate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "payment",
"amount": 5000,
"currency": "XOF",
"country": "CI",
"customIdentifier": "ORDER-2026-0001",
"paymentName": "Commande Boutique",
"callbackUrl": "https://merchant.com/api/awdpay/callback",
"successUrl": "https://merchant.com/payment/success",
"failedUrl": "https://merchant.com/payment/failed",
"logo": "https://merchant.com/logo.png"
}'
Exemple — Forfait prépayé
curl -X POST https://www.awdpay.com/checkout/v2/initiate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "prepaid",
"amount": 10000,
"currency": "XOF",
"country": "CI",
"customIdentifier": "PACK-2026-0001",
"paymentName": "Forfait SMS",
"customer": {
"name": "Client Test",
"email": "client@example.com",
"phone": "+2250700000000",
"external_id": "CUSTOMER-001"
},
"prepaid": {
"plan_id": "PACK_SMS_5000",
"name": "Pack SMS 5000",
"validity_days": 30,
"units": 5000,
"unit_type": "sms"
},
"metadata": {
"order_id": "ORDER-2026-0001",
"service": "sms_pack"
},
"callbackUrl": "https://merchant.com/api/awdpay/callback",
"successUrl": "https://merchant.com/payment/success",
"failedUrl": "https://merchant.com/payment/failed",
"logo": "https://merchant.com/logo.png"
}'
Exemple — Formule d’abonnement
curl -X POST https://www.awdpay.com/checkout/v2/initiate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "subscription",
"amount": 15000,
"currency": "XOF",
"country": "CI",
"customIdentifier": "SUB-ORDER-2026-0001",
"paymentName": "Premium Mensuel",
"customer": {
"name": "Client Abonnement",
"email": "client@example.com",
"phone": "+2250500000000",
"external_id": "CUSTOMER-SUB-001"
},
"subscription": {
"plan_id": "PREMIUM_MONTHLY",
"name": "Premium Mensuel",
"interval": "monthly",
"interval_count": 1,
"auto_renew": true,
"renewal_mode": "wallet_auto_debit",
"grace_period_days": 3
},
"metadata": {
"merchant_user_id": "USER-001",
"package": "premium"
},
"callbackUrl": "https://merchant.com/api/awdpay/callback",
"successUrl": "https://merchant.com/subscription/success",
"failedUrl": "https://merchant.com/subscription/failed",
"logo": "https://merchant.com/logo.png"
}'
Réponse checkout
{
"success": true,
"message": "Generated Checkout Link",
"trxId": "NOQTFCL3U7V9",
"type": "subscription",
"planId": "PREMIUM_MONTHLY",
"subscriptionId": "SUB-NOQTFCL3U7V9",
"redirectUrl": "https://www.awdpay.com/checkoutv2?trxId=NOQTFCL3U7V9"
}
Headers webhook
X-AWD-Event: subscription.activated
X-AWD-Timestamp: 1778426580
Content-Type: application/json
Webhook — Forfait prépayé payé
{
"event": "prepaid.payment.success",
"status": "success",
"trxId": "R1Q4SWHHVHV4",
"type": "prepaid",
"amount": 10000,
"currency": "XOF",
"customIdentifier": "PACK-2026-0001",
"planId": "PACK_SMS_5000",
"subscriptionId": null,
"country": "CI",
"paymentName": "Forfait SMS",
"paidBy": "client@example.com",
"customer": {
"name": "Client Test",
"email": "client@example.com",
"phone": "+2250700000000",
"external_id": "CUSTOMER-001"
},
"prepaid": {
"plan_id": "PACK_SMS_5000",
"name": "Pack SMS 5000",
"validity_days": 30,
"units": 5000,
"unit_type": "sms"
},
"subscription": null,
"metadata": {
"order_id": "ORDER-2026-0001",
"service": "sms_pack"
},
"timestamp": "2026-05-10T15:23:00.000Z"
}
Webhook — Abonnement activé
{
"event": "subscription.activated",
"status": "success",
"trxId": "NOQTFCL3U7V9",
"type": "subscription",
"amount": 15000,
"currency": "XOF",
"customIdentifier": "SUB-ORDER-2026-0001",
"planId": "PREMIUM_MONTHLY",
"subscriptionId": "SUB-NOQTFCL3U7V9",
"country": "CI",
"paymentName": "Premium Mensuel",
"paidBy": "client@example.com",
"customer": {
"name": "Client Abonnement",
"email": "client@example.com",
"phone": "+2250500000000",
"external_id": "CUSTOMER-SUB-001"
},
"prepaid": null,
"subscription": {
"plan_id": "PREMIUM_MONTHLY",
"name": "Premium Mensuel",
"interval": "monthly",
"interval_count": 1,
"auto_renew": true,
"renewal_mode": "wallet_auto_debit",
"grace_period_days": 3
},
"metadata": {
"merchant_user_id": "USER-001",
"package": "premium"
},
"timestamp": "2026-05-10T15:23:00.000Z"
}
Codes d’erreur
{
"success": false,
"code": "INVALID_PAYMENT_TYPE",
"message": "Invalid payment type. Allowed values: payment, prepaid, subscription."
}
{
"success": false,
"code": "SUBSCRIPTION_INTERVAL_REQUIRED",
"message": "subscription.interval is required."
}
Renouvellement automatique des abonnements
Lorsqu’une transaction de type subscription est payée avec succès,
AWDPay crée un abonnement actif. Le système peut ensuite renouveler automatiquement
l’abonnement selon son intervalle, créer une facture de renouvellement et envoyer un webhook au marchand.
Cycle de vie d’un abonnement
| Statut | Description | Action AWDPay |
|---|---|---|
active |
L’abonnement est actif et peut être renouvelé automatiquement. | AWDPay surveille nextBillingAt. |
past_due |
Le renouvellement a échoué, par exemple solde client insuffisant. | AWDPay envoie subscription.payment.failed. |
cancelled |
L’abonnement est désactivé ou annulé. | Aucun renouvellement automatique n’est exécuté. |
Champs importants
{
"subscriptionId": "SUB-HC8HBMYWJJZN",
"subscriptionStatus": "active",
"currentPeriodStart": "2026-05-10T16:38:02.000Z",
"currentPeriodEnd": "2026-06-10T16:38:02.000Z",
"nextBillingAt": "2026-06-10T16:38:02.000Z",
"lastBillingAt": "2026-05-10T16:46:08.000Z"
}
Webhook — Abonnement renouvelé
{
"event": "subscription.renewed",
"status": "success",
"trxId": "RNWVYQQTISKG138",
"type": "subscription",
"amount": 15000,
"currency": "XOF",
"customIdentifier": "RENEWAL-SUB-HC8HBMYWJJZN-1778426768",
"transactionNumber": "subscription-renewal-rnwvyqqtiskg138",
"planId": "PREMIUM_MONTHLY",
"subscriptionId": "SUB-HC8HBMYWJJZN",
"invoiceId": "INV-RNWVYQQTISKG138",
"billingReason": "renewal",
"subscriptionStatus": "active",
"currentPeriodStart": "2026-05-10T16:38:02.000Z",
"currentPeriodEnd": "2026-06-10T16:38:02.000Z",
"nextBillingAt": "2026-06-10T16:38:02.000Z",
"paidBy": "client@example.com",
"customer": {
"name": "Client Abonnement",
"email": "client@example.com",
"phone": "+2250500000000",
"external_id": "CUSTOMER-SUB-001"
},
"subscription": {
"plan_id": "PREMIUM_MONTHLY",
"name": "Premium Mensuel",
"interval": "monthly",
"interval_count": 1,
"auto_renew": true,
"renewal_mode": "wallet_auto_debit",
"subscription_status": "active"
},
"metadata": {},
"timestamp": "2026-05-10T16:46:08.000Z"
}
Webhook — Échec de renouvellement
{
"event": "subscription.payment.failed",
"status": "failed",
"trxId": null,
"type": "subscription",
"amount": 15000,
"currency": "XOF",
"planId": "PREMIUM_MONTHLY",
"subscriptionId": "SUB-HC8HBMYWJJZN",
"invoiceId": "INV-FAILED-SUB-HC8HBMYWJJZN-1778426768",
"billingReason": "renewal",
"subscriptionStatus": "past_due",
"currentPeriodStart": "2026-05-10T16:38:02.000Z",
"currentPeriodEnd": "2026-06-10T16:38:02.000Z",
"nextBillingAt": "2026-06-10T16:38:02.000Z",
"paidBy": "client@example.com",
"customer": {
"name": "Client Abonnement",
"email": "client@example.com"
},
"subscription": {
"plan_id": "PREMIUM_MONTHLY",
"interval": "monthly",
"auto_renew": true
},
"metadata": {},
"timestamp": "2026-05-10T16:46:08.000Z"
}
Headers webhook
X-AWD-Event: subscription.renewed
X-AWD-Timestamp: 1778426768
Content-Type: application/json
past_due et le marchand reçoit
subscription.payment.failed.
API V2 Wallet — Transferts entre comptes AWDPay
L’API V2 Wallet permet à un marchand d’effectuer des transferts depuis son compte AWDPay vers le compte AWDPay d’un client.
Le destinataire peut être identifié avec son email ou son wallet.
Cette API est destinée aux intégrations backend des marchands.
Base URL
https://www.awdpay.com/api
Authentification
Toutes les requêtes doivent inclure le token d’accès du marchand dans l’en-tête
Authorization.
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Consulter le solde marchand
Cet endpoint permet de consulter le solde disponible du compte AWDPay marchand par devise. Il est recommandé de vérifier le solde avant de créer un transfert.
Exemple cURL
curl -X GET "https://www.awdpay.com/api/v2/balance" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Créer un transfert vers un client
Cet endpoint permet au marchand de transférer un montant vers un client AWDPay. Le compte marchand est débité et le compte client est crédité.
Corps de la requête
| Champ | Type | Requis | Description |
|---|---|---|---|
type |
string | Oui | Doit valoir send. |
amount |
number | Oui | Montant à transférer. Le montant doit être positif. |
currency |
string | Oui | Code devise, par exemple XOF ou XAF. |
email |
string | Conditionnel | Email du compte AWDPay du client destinataire. Requis si wallet est absent. |
wallet |
string | Conditionnel | Identifiant wallet AWDPay du client destinataire. Requis si email est absent. |
Le destinataire doit être identifié soit par email, soit par wallet.
Au moins l’un des deux champs est obligatoire.
Exemple cURL par email
curl -X POST "https://www.awdpay.com/api/v2/transfers" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"type": "send",
"amount": 5000,
"currency": "XOF",
"email": "client@example.com"
}'
Exemple cURL par wallet
curl -X POST "https://www.awdpay.com/api/v2/transfers" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"type": "send",
"amount": 5000,
"currency": "XOF",
"wallet": "W150XXXX"
}'
Identifier le destinataire
Le compte client peut être identifié de deux façons.
| Méthode | Description |
|---|---|
email |
Adresse email du compte AWDPay du client. |
wallet |
Identifiant wallet AWDPay du client. |
AWDPay vérifie que le compte et le wallet existent avant d’effectuer le transfert. Le marchand ne peut pas se transférer de l’argent à lui-même.
Réponse transfert
Réponse de succès
{
"api": "V2",
"type": "transfer",
"amount": 5000,
"currency": "XOF",
"email": "client@example.com",
"trxId": "TRX_ID",
"userId": 12345,
"status": "success"
}
Réponse d’erreur
{
"message": "Insufficient balance"
}
| Message | Cause possible |
|---|---|
Insufficient balance |
Le solde marchand est insuffisant pour couvrir le montant. |
user email not exists. |
Aucun compte AWDPay n’existe avec cet email. |
user wallet not exists. |
Le wallet destinataire est introuvable. |
The wallet is not supporting X |
Le wallet ne prend pas en charge la devise demandée. |
Callback API V2
Après le transfert, AWDPay peut envoyer une requête HTTP POST
à l’URL de webhook configurée par le marchand.
Payload exemple
{
"api": "V2",
"type": "transfer",
"amount": 5000,
"currency": "XOF",
"wallet": "W150XXXX",
"email": "client@example.com",
"trxId": "TRX_ID",
"status": "success"
}
HTTP 200 OK après traitement.
Idempotence
L’idempotence protège contre les doublons lorsqu’une même requête est renvoyée à cause d’un timeout réseau ou d’un retry.
Pour une même opération, le marchand doit réutiliser exactement les mêmes paramètres. AWDPay retourne alors le résultat de la requête initiale au lieu de créer un second transfert.
type, amount, currency,
wallet et email.
Statuts transfert
| Statut | Description |
|---|---|
success |
Transfert effectué : le marchand est débité et le client est crédité. |
failed |
Transfert non effectué, par exemple en cas de solde insuffisant. |
Bonnes pratiques API V2
| # | Recommandation |
|---|---|
| 1 | Ne jamais exposer le token d’accès ou la clé API dans le frontend. |
| 2 | Effectuer les transferts uniquement depuis le backend marchand. |
| 3 | Consulter GET /v2/balance avant un transfert. |
| 4 | Vérifier que le client destinataire est correctement identifié par email ou wallet. |
| 5 | Conserver le trxId avec votre référence interne. |
| 6 | Réutiliser les mêmes paramètres lors d’un retry pour bénéficier de l’idempotence. |
| 7 | Traiter le callback type: transfer pour confirmer le statut côté marchand. |