Skip to content

Contrat REST ERP ​

Adaptateur générique rest : Epayment appelle les endpoints HTTP configurés par la régie. Montants en unités mineures (centimes). Devise ISO 4217 (XAF). Timestamps ISO 8601 UTC.

Authentification ​

Configurée dans les credentials chiffrés côté Epayment. Détail : Auth et secrets.

ModeChamps
noneAucun en-tête
api_key_headerapi_key, header_name (défaut X-Api-Key)
bearerbearer_token
basicusername, password
custom_headersMap d'en-têtes

En-têtes non secrets optionnels : map d'en-têtes additionnels (max 10 entrées).

Correspondance des chemins ​

Base URL : URL configurée dans l'intégration ERP de la régie

OpérationMéthodeClé mappingChemin par défautParamètres
HealthGEThealth_path/healthAucun
DuesGETdues_list_path/dues?{dues_subject_query_param}={subject_ref} (défaut param : subject_ref)
DisbursementGETdisbursement_batch_path/disbursements?batch_ref={batch_ref}
Confirmation paiementPOSTpayment_confirmation_path/payment-confirmationsCorps JSON

Clés d'identité ​

CléSensUsage
subject_refContribuable / assujettiQuery param pull dues
erp_due_refLigne de créanceCorrélation paiement ; metadata checkout
batch_refLot disbursementQuery param pull lignes sortantes
revenue_line_codeCode recette ERPMapping vers service catalogue

Health ​

Requête : GET {base_url}{health_path}

Réponse 200 :

json
{
  "ok": true,
  "message": "ERP operational",
  "latency_ms": 42
}
ChampTypeObligatoireDescription
okbooleanOuitrue si le SI est opérationnel
messagestringNonDétail lisible (maintenance, version)
latency_msinteger ≥ 0NonLatence interne ERP (informatif)

Epayment considère la connexion valide lorsque ok est true et le code HTTP est 2xx.

Signature inbound (ordres ERP) ​

Lorsque la régie active inbound_signing_required, les réponses dues et disbursement ne sont plus { "items": [...] } brut. Elles doivent être une enveloppe JWS compact (RFC 7515) signée EdDSA (Ed25519) :

json
{
  "signed": "<header.payload.signature>"
}
ÉlémentValeur
Header JWS{ "alg": "EdDSA", "kid": "<key id>" } uniquement. Tout autre alg est refusé.
PayloadJSON UTF-8 avec typ, iss, aud, iat, exp, jti, ref, items
typerp.dues.v1 ou erp.disbursement.v1
audepayment
issIdentifiant ERP configuré hors bande sur la régie (doit matcher)
refsubject_ref (dues) ou batch_ref (disbursement), égal au paramètre de requête
iat / expUnix seconds ; exp - iat ≤ 300 ; skew ±60 s à la réception
itemsMême schéma que les réponses non signées ci-dessous

La clé privée reste côté ERP. La régie enregistre uniquement la clé publique PEM (kid + SPKI) dans Epayment. Health et confirmations de paiement ne sont pas signés inbound.

Si inbound_signing_required est false, le format { "items": [...] } actuel reste valide (cutover).

Fixtures JWS EdDSA (clé sandbox documentaire erp-fake-1) :

Dues (pull créances) ​

Requête : GET {base_url}{dues_list_path}?subject_ref={subject_ref}

Réponse 200 (unsigned, cutover) :

json
{
  "items": [
    {
      "erp_due_ref": "DUE-2026-001",
      "label": "Taxe foncière 2026",
      "amount_minor": 15000,
      "currency": "XAF",
      "due_date": "2026-06-30T23:59:59.000Z",
      "revenue_line_code": "REC-TF-01",
      "service_id": null,
      "service_code": null,
      "service_label": null,
      "outstanding_minor": 15000,
      "original_minor": 20000,
      "allow_partial": true,
      "minimum_amount_minor": 5000
    }
  ]
}

Objet due (chaque élément de items) ​

ChampTypeObligatoireDescription
erp_due_refstring 1-120OuiIdentifiant unique de la créance côté ERP
labelstring 1-500OuiLibellé affiché au citoyen
amount_minorinteger ≥ 0OuiMontant proposé au paiement (centimes)
currencystring len 3OuiCode ISO 4217 (XAF)
due_datedatetime ISO 8601NonÉchéance
revenue_line_codestring max 50NonCode recette pour mapping catalogue
service_iduuid ou nullNonFourni par ERP ; écrasé si mapping Epayment réussi
service_codestring max 50 ou nullNonSlug service ; enrichi par Epayment
service_labelstring max 200 ou nullNonLibellé catalogue ; enrichi par Epayment
outstanding_minorinteger ≥ 0NonSolde restant dû ; défaut = amount_minor
original_minorinteger ≥ 0NonMontant d'origine avant remises ou paiements partiels
allow_partialbooleanNon (défaut false)Autorise un paiement inférieur au solde
minimum_amount_minorinteger ≥ 0ConditionnelObligatoire si allow_partial est true ; ne peut excéder outstanding_minor

Règles de validation :

  • Si allow_partial est true, minimum_amount_minor doit être présent.
  • minimum_amount_minor ≤ outstanding_minor (ou amount_minor si outstanding_minor absent).

Disbursement (pull lignes de lot) ​

Requête : GET {base_url}{disbursement_batch_path}?batch_ref={batch_ref}

Réponse 200 :

json
{
  "items": [
    {
      "line_ref": "LINE-0001",
      "beneficiary_ref": "BEN-8842",
      "beneficiary_name": "Marie Nguema",
      "amount_minor": 75000,
      "currency": "XAF",
      "label": "Subvention agricole T1",
      "payment_method": "MTN_MOMO",
      "payout": {
        "phone_e164": "+242061234567",
        "operator_hint": "mtn"
      }
    },
    {
      "line_ref": "LINE-0002",
      "beneficiary_ref": "BEN-8843",
      "beneficiary_name": "Jean Okemba",
      "amount_minor": 120000,
      "currency": "XAF",
      "payment_method": "BANK_TRANSFER",
      "payout": {
        "account_number": "00123456789",
        "bank_code": "BCH",
        "account_holder_name": "Jean Okemba",
        "iban": "CG39..."
      }
    }
  ]
}

Objet ligne disbursement (chaque élément de items) ​

ChampTypeObligatoireDescription
line_refstring 1-120OuiIdentifiant ligne dans le lot ERP
beneficiary_refstring 1-120OuiIdentifiant bénéficiaire ERP
beneficiary_namestring 1-200OuiNom affiché
amount_minorinteger ≥ 0OuiMontant à verser (centimes)
currencystring len 3OuiXAF
labelstring max 500NonMotif du versement
payment_methodenumNonMode de paiement (voir ci-dessous)
payoutobjetNonCoordonnées selon le mode (voir variantes)
phone_e164string max 20NonLegacy : téléphone plat
accountstring max 120NonLegacy : compte plat
channelstring max 50NonLegacy : canal libre

Valeurs payment_method ​

ValeurStatutUsage
AIRTEL_MONEYRecommandé v2Mobile money Airtel
MTN_MOMORecommandé v2Mobile money MTN
BANK_TRANSFERRecommandé v2Virement bancaire
MOBILE_MONEYDépréciéRésolu vers AIRTEL_MONEY ou MTN_MOMO via operator_hint
CASHDéprécié v2Non supporté en exécution disbursement v2
CHECKDéprécié v2Non supporté en exécution disbursement v2

Résolution legacy : si payment_method vaut MOBILE_MONEY, Epayment déduit l'opérateur depuis payout.operator_hint (mtn, momo → MTN_MOMO ; airtel → AIRTEL_MONEY).

Variantes payout ​

Mobile money (AIRTEL_MONEY, MTN_MOMO, ou MOBILE_MONEY) :

json
{
  "phone_e164": "+242061234567",
  "operator_hint": "mtn"
}
ChampTypeObligatoire
phone_e164string 1-20Oui
operator_hintstring max 32Non

Virement bancaire (BANK_TRANSFER) :

json
{
  "account_number": "00123456789",
  "bank_code": "BCH",
  "account_holder_name": "Jean Okemba",
  "iban": "CG39..."
}
ChampTypeObligatoire
account_numberstring 1-120Oui
bank_codestring 1-50Oui
account_holder_namestring 1-200Oui
ibanstring max 64Non

Espèces (CASH, déprécié) :

json
{
  "agency_code": "AG-BZV-01",
  "pickup_reference": "REF-123"
}

Chèque (CHECK, déprécié) :

json
{
  "check_payable_to": "Marie Nguema",
  "mailing_address": "BP 123, Brazzaville"
}

Payment confirmation (Epayment → ERP) ​

Requête : POST {base_url}{payment_confirmation_path}

Corps :

json
{
  "erp_due_ref": "DUE-2026-001",
  "notice_ref": "ABCD1234",
  "transaction_id": "550e8400-e29b-41d4-a716-446655440001",
  "amount_minor": 15000,
  "currency": "XAF",
  "paid_at": "2026-06-15T10:30:00.000Z",
  "payment": {
    "channel": "MOBILE_MONEY",
    "payment_ref": "ABCD1234",
    "connector_code": "momo",
    "payer_phone_e164": "+242069876543",
    "payment_method_label": "MTN Mobile Money",
    "subject_ref": "CONTRIB-8842"
  }
}

Corps de confirmation (champs racine) ​

ChampTypeObligatoireDescription
erp_due_refstring 1-120OuiCréance soldée ou partiellement soldée
notice_refstring 1-120OuiRéférence avis / quittance Epayment
transaction_iduuidOuiIdentifiant transaction plateforme (idempotence)
amount_minorinteger ≥ 0OuiMontant effectivement encaissé
currencystring len 3OuiXAF
paid_atdatetime ISO 8601OuiHorodatage encaissement
paymentobjetNonDétail canal (recommandé)

Objet payment (détail canal) ​

ChampTypeObligatoireDescription
channelMOBILE_MONEY | CASH | CARDOuiCanal de collecte v1
payment_refstring 1-120OuiRéférence citoyen (agence ou MM)
connector_codestring max 64NonCode connecteur (momo, airtel, paddle, …)
payer_phone_e164string max 20NonTéléphone payeur (mobile money)
payment_method_labelstring max 200NonLibellé affiché
subject_refstring max 200NonContribuable si connu

Réponse attendue : HTTP 2xx. Corps libre ou { "ok": true } pour les probes.

L'ERP doit traiter transaction_id de manière idempotente : un second POST identique ne doit pas créer un second encaissement.

Probes (régie / admin) ​

POST .../probe/payment-confirmation avec corps optionnel :

json
{
  "erp_due_ref": "DEMO-DUE",
  "subject_ref": "DEMO-SUBJECT"
}

Sandbox : écriture journal uniquement. REST : appel HTTP réel avec payload de démonstration.

Codes HTTP attendus ​

SituationCode
Succès200-299
Corps invalide côté ERP (si validation)400
Auth invalide401 / 403
Ressource inconnue (erp_due_ref, batch_ref)404
Indisponibilité temporaire503

Epayment journalise le code et le corps ; les erreurs 5xx déclenchent des alertes opérationnelles côté régie.

Plateforme d'orchestration de paiement DPI. Les fonds ne transitent pas par Epayment.