API Paiement
Wallet Partenaire
Documentation complète de l'API CHC dédiée au paiement via wallet partenaire. Intégration sécurisée avec RIA Money Transfer pour la réception et le crédit de transferts internationaux sur le wallet - Wallet Partenaire.
La plateforme CHC expose une API REST sécurisée permettant au Wallet Partenaire de recevoir des paiements de transferts internationaux RIA Money Transfer. Chaque crédit wallet est conditionné à une double validation : RIA (payabilité) puis CHC (contrôle AML).
Toutes les requêtes nécessitent un token JWT Bearer. Le wallet Partenaire dispose en plus d'une clé partenaire dédiée à inclure dans chaque requête.
Authorization: Bearer <jwt_token> Content-Type: application/json
X-Partner-Key: <clé_api_wallet-partenaire> X-Partner-ID: WP-IDENTIFIER X-Idempotency-Key: <uuid_v4>
POST /auth/refresh. Le header X-Idempotency-Key (UUID v4 unique par tentative) est obligatoire pour prévenir les doublons de transactions.Le flux suit les 10 étapes définies dans le processus officiel CHC. Chaque étape est obligatoire et séquentielle.
Cycle de vie d'une transaction wallet. Les transitions sont strictement contrôlées par le moteur d'état CHC — aucune transition non autorisée n'est possible.
Étapes ① ② — Le wallet Partenaire soumet une demande de paiement en transmettant la référence RIA et les données KYC complètes du bénéficiaire.
| Header | Type | Requis | Description |
|---|---|---|---|
| X-Partner-Key | string | Requis | Clé API unique attribuée à Wallet Partenaire par CHC |
| X-Partner-ID: WP-IDENTIFIER | |||
| X-Idempotency-Key | string (UUID v4) | Requis | Clé d'idempotence — même clé = même résultat, pas de doublon |
{ "ria_reference": "RIA-124xxxxxxxxx", "wallet_account_id": "WP-ACC-00934521", "beneficiary_kyc": { "first_name": "Mohamed Ahmed", "last_name": "Ould Salem", "date_of_birth": "1985-03-22", "nationality": "MR", "phone_number": "+22222345678", "document": { "type": "NNI", // NNI | PASSPORT | RESIDENCE_PERMIT "number": "MR123456789", "issue_date": "2020-01-15", "expiry_date": "2030-01-14", "issuing_country": "MR", "photo_doc_url": "https://wallet-partenaire.mr/kyc/docs/abc123.jpg", } }, "callback_url": "https://api.wallet-partenaire.mr/webhooks/chc" }
| Champ | Type | Requis | Description |
|---|---|---|---|
| ria_reference | string | Requis | Référence unique de la transaction RIA fournie par le client |
| wallet_account_id | string | Requis | Identifiant du compte Wallet Partenaire du bénéficiaire |
| beneficiary_kyc | object | Requis | Données KYC complètes du bénéficiaire (voir détail) |
| beneficiary_kyc.document | object | Requis | Document d'identité avec photos du document et selfie |
| callback_url | string (URL) | Requis | URL Wallet Partenaire pour recevoir les webhooks CHC (HTTPS requis) |
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "INITIÉE", "ria_reference": "RIA-125xxxxxxxxx", "amount": { "value": 25000, "currency": "MRU", "original_value": 650, "original_currency": "EUR" }, "estimated_processing_time": "PT2M", // ISO 8601 duration "poll_url": "/v1/wallet/status/CHC-TXN-20240615-00456" }
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "INITIÉE", "ria_reference": "RIA-124xxxxxxxxx", "amount": { "value": 25000, "currency": "MRU", "original_value": 650, "original_currency": "EUR" }, "estimated_processing_time": "PT2M", "poll_url": "/v1/wallet/status/CHC-TXN-20240615-00456" }
{ "error": { "code": "INVALID_KYC_DATA", "message": "Données KYC incomplètes ou malformées.", "details": { "missing_fields": ["beneficiary_kyc.document.photo_selfie_url", "beneficiary_kyc.date_of_birth"] }, "request_id": "req_1a2b3c4d..." } }
{ "error": { "code": "UNAUTHORIZED_PARTNER", "message": "Clé partenaire invalide ou absente.", "details": { "partner_id": "WALLET_PARTNER", "reason": "X-Partner-Key manquante ou révoquée" }, "request_id": "req_2b3c4d5e..." } }
{ "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction introuvable ou non accessible.", "details": { "ria_reference": "RIA-2024-XXXXX" }, "request_id": "req_3c4d5e6f..." } }
{ "error": { "code": "TRANSACTION_NOT_PAYABLE", "message": "La transaction n'est pas dans un état payable.", "details": { "ria_reference": "RIA-124xxxxxxxxx", "ria_status": "ALREADY_PAID", // ALREADY_PAID | BLOCKED | EXPIRED | ON_HOLD "reason": "Cette transaction a déjà été payée." }, "request_id": "req_4d5e6f7g..." } }
{ "error": { "code": "TRANSACTION_NOT_FOUND", // intentionnellement identique au cas "introuvable" "message": "Transaction introuvable ou non accessible.", "details": { "ria_reference": "RIA-124xxxxxxxxx" // Aucune indication sur la raison réelle pour des raisons de sécurité }, "request_id": "req_5e6f7g8h..." } }
{ "transaction_id": "CHC-TXN-20240615-00456", // transaction originale "status": "INITIÉE", "ria_reference": "RIA-124xxxxxxxxx", "idempotency_key": "550e8400-e29b-41d4-a716-446655440000", "duplicate": true, "original_created_at": "2024-06-15T14:30:00Z" }
Étapes ③ ④ ⑤ ⑥ — Interroger l'état d'avancement d'une transaction en cours. Retourne le credit_token dès que la transaction est prête pour crédit.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
| transaction_id | string | Requis | Identifiant CHC retourné par POST /wallet/request |
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "VALIDÉE_PAR_RIA", "ria_reference": "RIA-124xxxxxxxxx", "aml_status": "CLEARED", // CLEARED | PENDING_REVIEW | BLOCKED "amount": { "value": 25000, "currency": "MRU" }, "beneficiary_name": "Mohamed Ahmed Ould Salem", "wallet_account_id": "WP-ACC-00934521", "ready_for_credit": true, "credit_token": "ctk_9f2a1b3c4d5e...", // présent UNIQUEMENT si ready_for_credit=true "credit_token_expires_at": "2024-06-15T15:36:45Z", "updated_at": "2024-06-15T14:34:02Z" }
ready_for_credit: true et le credit_token. Alternativement, écouter le webhook transaction.ready_for_credit pour éviter le polling.aml_status retourne PENDING_REVIEW, la transaction est en attente de validation superviseur CHC. Le credit_token ne sera émis qu'après validation manuelle.{ "transaction_id": "CHC-TXN-20240615-00456", "status": "VALIDÉE_PAR_RIA", "ria_reference": "RIA-124xxxxxxxxx", "aml_status": "CLEARED", "amount": { "value": 25000, "currency": "MRU" }, "beneficiary_name": "Mohamed Ahmed Ould Salem", "wallet_account_id": "WP-ACC-00934521", "ready_for_credit": true, "credit_token": "ctk_9f2a1b3c4d5e...", "credit_token_expires_at": "2024-06-15T14:49:10Z", "updated_at": "2024-06-15T14:34:02Z" }
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "EN_COURS_DE_VÉRIFICATION", "ria_reference": "RIA-124xxxxxxxxx", "aml_status": "PENDING_REVIEW", "aml_alert_reason": "HIGH_AMOUNT", // HIGH_AMOUNT | HIGH_FREQUENCY | RISKY_COUNTRY "amount": { "value": 50000, "currency": "MRU" }, "ready_for_credit": false, "credit_token": null, // absent tant que non validée "estimated_review_time": "PT30M", "updated_at": "2024-06-15T14:34:02Z" }
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "BLOQUÉE_AML", "ria_reference": "RIA-124xxxxxxxxx", "aml_status": "BLOCKED", "aml_block_reason": "SANCTION_LIST_MATCH", "ready_for_credit": false, "credit_token": null, "updated_at": "2024-06-15T14:35:00Z" }
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "REJETÉE_PAR_RIA", "ria_reference": "RIA-124xxxxxxxxx", "aml_status": "CLEARED", "ria_rejection_reason": "COMPLIANCE_REVIEW_FAILED", "ready_for_credit": false, "credit_token": null, "updated_at": "2024-06-15T14:50:00Z" }
{ "error": { "code": "TRANSACTION_NOT_FOUND", // intentionnellement identique au 404 "message": "Transaction introuvable ou non accessible.", "details": { "transaction_id": "CHC-TXN-20240615-00456" }, "request_id": "req_6f7g8h9i..." } }
{ "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction introuvable ou non accessible.", "details": { "transaction_id": "CHC-TXN-20240615-XXXXX" }, "request_id": "req_7g8h9i0j..." } }
Étapes ⑦ ⑧ ⑨ ⑩ — Après réception du credit_token, Wallet Partenaire soumet la confirmation de crédit. CHC notifie RIA de la finalisation.
GET /wallet/status) est obligatoire et à usage unique. Il expire 15 minutes après émission. Le crédit du compte client ne doit être effectué par Wallet Partenaire qu'après réception du status: CRÉDITÉE_WALLET en réponse. Toute tentative sans token valide sera rejetée (403) et journalisée.{ "transaction_id": "CHC-TXN-20240615-00456", "credit_token": "ctk_9f2a1b3c4d5e...", "wallet_account_id": "WP-ACC-00934521", "amount_to_credit": 25000, "currency": "MRU" }
| Champ | Type | Requis | Description |
|---|---|---|---|
| transaction_id | string | Requis | Identifiant CHC de la transaction |
| credit_token | string | Requis | Token de crédit à usage unique, obtenu via GET /wallet/status |
| wallet_account_id | string | Requis | Doit correspondre au wallet_account_id de la demande initiale |
| amount_to_credit | integer | Requis | Montant en unités entières (MRU) — doit correspondre au montant validé |
| currency | string (ISO 4217) | Requis | Devise — toujours MRU pour les paiements locaux |
{ "transaction_id": "CHC-TXN-20240615-00456", "payment_status": "CRÉDITÉE_WALLET", "credited_at": "2024-06-15T14:36:45Z", "wallet_account_id": "WP-ACC-00934521", "amount_credited": 25000, "currency": "MRU", "ria_reference": "RIA-124xxxxxxxxx", "ria_confirmation_code": "RIA-CONF-9876543", "chc_receipt_id": "RCP-W-20240615-00456" }
{ "transaction_id": "CHC-TXN-20240615-00456", "payment_status": "CRÉDITÉE_WALLET", "credited_at": "2024-06-15T14:36:45Z", "wallet_account_id": "WP-ACC-00934521", "amount_credited": 25000, "currency": "MRU", "ria_reference": "RIA-124xxxxxxxxx", "ria_confirmation_code": "RIA-CONF-9876543", "chc_receipt_id": "RCP-W-20240615-00456" }
{ "error": { "code": "INVALID_CREDIT_TOKEN", "message": "Le credit_token a expiré.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "reason": "TOKEN_EXPIRED", "expired_at": "2024-06-15T14:49:10Z", "action": "Appeler GET /v1/wallet/status/{id} pour un nouveau token" }, "request_id": "req_8h9i0j1k..." } }
{ "error": { "code": "INVALID_CREDIT_TOKEN", "message": "Le credit_token a déjà été utilisé.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "reason": "TOKEN_ALREADY_USED", "used_at": "2024-06-15T14:36:45Z", "current_status": "CRÉDITÉE_WALLET" }, "request_id": "req_9i0j1k2l..." } }
{ "error": { "code": "WALLET_ACCOUNT_MISMATCH", "message": "Le wallet_account_id ne correspond pas à la transaction.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "reason": "ACCOUNT_ID_MISMATCH" // Le wallet_account_id attendu n'est pas divulgué pour des raisons de sécurité }, "request_id": "req_0j1k2l3m..." } }
{ "error": { "code": "AMOUNT_MISMATCH", "message": "Le montant soumis ne correspond pas au montant validé.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "amount_submitted": 200000, "amount_expected": 25000, "currency": "MRU" }, "request_id": "req_1k2l3m4n..." } }
{ "error": { "code": "RIA_UNAVAILABLE", "message": "L'API RIA est temporairement indisponible. Réessayez.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "current_status": "VALIDÉE_PAR_RIA", // statut inchangé "retry_after": 30, // secondes avant de réessayer "credit_token_still_valid": true }, "request_id": "req_2l3m4n5o..." } }
Annulation possible uniquement si la transaction est au statut INITIÉE ou EN_COURS_DE_VÉRIFICATION. Toute transaction déjà confirmée ou créditée ne peut être annulée.
{ "reason": "CLIENT_REQUEST", // CLIENT_REQUEST | TIMEOUT | TECHNICAL_ERROR "notes": "Le client a demandé l'annulation" }
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "ANNULÉE", "cancelled_at": "2024-06-15T14:40:00Z", "reason": "CLIENT_REQUEST" }
{ "transaction_id": "CHC-TXN-20240615-00456", "status": "ANNULÉE", "cancelled_at": "2024-06-15T14:40:00Z", "reason": "CLIENT_REQUEST" }
{ "error": { "code": "CANNOT_CANCEL", "message": "La transaction ne peut pas être annulée dans son état actuel.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "current_status": "CRÉDITÉE_WALLET", "reason": "La transaction a déjà été créditée sur le wallet client." }, "request_id": "req_3m4n5o6p..." } }
{ "error": { "code": "CANNOT_CANCEL", "message": "La transaction ne peut pas être annulée dans son état actuel.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "current_status": "VALIDÉE_PAR_RIA", "reason": "Transaction validée par RIA — contacter RIA pour toute demande de remboursement." }, "request_id": "req_4n5o6p7q..." } }
{ "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction introuvable ou non accessible.", "details": { "transaction_id": "CHC-TXN-20240615-XXXXX" }, "request_id": "req_5o6p7q8r..." } }
CHC envoie des notifications en temps réel vers l'URL de callback Wallet Partenaire définie dans POST /wallet/request. Chaque payload est signé HMAC-SHA256.
X-CHC-Signature: sha256=<hmac>. Wallet Partenaire doit vérifier cette signature avant tout traitement. La clé secrète HMAC est fournie lors de l'onboarding partenaire.// POST https://api.wallet-partenaire.mr/webhooks/chc // Headers: X-CHC-Signature: sha256=abc123..., Content-Type: application/json { "event": "transaction.ready_for_credit", "event_id": "evt_7f3a2b1c9d8e...", "occurred_at": "2024-06-15T14:34:10Z", "data": { "transaction_id": "CHC-TXN-20240615-00456", "status": "VALIDÉE_PAR_RIA", "credit_token": "ctk_9f2a1b3c4d5e...", "credit_token_expires_at": "2024-06-15T14:49:10Z", "amount": { "value": 25000, "currency": "MRU" }, "ria_reference": "RIA-124xxxxxxxxx" } }
Le double niveau de contrôle (Wallet Partenaire + CHC) garantit une conformité renforcée. Wallet Partenaire applique le KYC à l'entrée ; CHC applique les règles AML à chaque transaction.
| Montant élevé | > 50 000 MRU → mise en attente superviseur |
| Fréquence | > 3 transactions / 24h / bénéficiaire |
| Pays à risque | Origine sur liste FATF → vérification renforcée |
| Document expiré | Blocage automatique immédiat |
| Incohérence KYC | Données ≠ registre RIA → rejet |
| Nom complet | Requis |
| Date de naissance | Requis |
| Nationalité | Requis |
| Numéro de téléphone | Requis |
| Document ID + photo | Requis |
| Selfie bénéficiaire | Requis |
Toutes les erreurs suivent la même structure JSON. Le champ request_id permet de tracer la requête dans les journaux CHC.
{ "error": { "code": "AML_BLOCKED", "message": "La transaction a été bloquée par le moteur AML.", "details": { "transaction_id": "CHC-TXN-20240615-00456", "triggered_rule": "HIGH_FREQUENCY" }, "request_id": "req_9a8b7c6d5e4f..." } }
| Code erreur | HTTP | Description |
|---|---|---|
| TRANSACTION_NOT_FOUND | 404 | Référence RIA introuvable |
| TRANSACTION_NOT_PAYABLE | 403 | Statut RIA non payable au moment de la requête |
| AML_BLOCKED | 403 | Transaction bloquée définitivement par le moteur AML CHC |
| IDENTITY_MISMATCH | 422 | Données KYC incohérentes avec les données RIA |
| INVALID_CREDIT_TOKEN | 403 | credit_token absent, expiré ou déjà utilisé |
| AMOUNT_MISMATCH | 422 | Montant soumis différent du montant validé |
| DUPLICATE_TRANSACTION | 409 | X-Idempotency-Key déjà utilisée — la transaction existante est retournée |
| INVALID_KYC_DATA | 400 | Données KYC manquantes ou malformées dans le corps |
| UNAUTHORIZED_PARTNER | 401 | X-Partner-Key invalide ou wallet non autorisé |
| CANNOT_CANCEL | 403 | Transaction dans un statut non annulable |
| RIA_UNAVAILABLE | 503 | API RIA temporairement indisponible — réessayer après 30 secondes |