Quickstart Sandbox

Le mode Sandbox est un environnement de test gratuit intégré à MonCashConnect. Construisez votre intégration, exécutez des flux de paiement de bout en bout, voyez vos webhooks se déclencher — tout cela sans déplacer d'argent réel et sans toucher au vrai MonCash. Quand vous êtes prêt, vous échangez deux valeurs de secret et vous êtes en live. Même API, mêmes payloads, mêmes formes de réponse.

Ce guide vous emmène de l'inscription à votre premier paiement réel en moins de 30 minutes. Pas de changement de code entre sandbox et live — uniquement deux variables d'environnement.

Avant de commencer

Il vous faut :

  • Un compte MonCashConnect sur moncashconnect.com — pour tester en Sandbox, aucune vérification d'identité n'est requise. La vérification (KYC) n'est demandée que pour activer les paiements Live.
  • Un backend capable d'émettre des requêtes HTTPS sortantes et d'héberger un endpoint webhook.
  • Un domaine vers lequel rediriger vos clients après paiement.

Implémenter avec une IA

La plupart des marchands ont déjà un assistant IA dans leur workflow — Lovable, Cursor, Claude, ChatGPT, Copilot. Le prompt ci-dessous encapsule l'intégration sandbox MCC complète en un seul copier-coller : votre assistant pose trois questions, puis écrit le backend, le webhook, le bouton checkout et la page de retour.

Avant de coller : ayez sous la main votre MCC test secret + votre test webhook secret (depuis « Régénérer les clés de test » sur l'onglet Projets de la page Développeur) et le domaine de votre site.

You are implementing MonCashConnect (MCC) sandbox payments on this project.

MCC is a Stripe-style payment gateway for MonCash (Haiti's mobile money). Sandbox mode lets us test the full payment flow without moving any real money. When we're ready, the same code goes live by swapping two env values — no code change required.

The user will provide three pieces of info. Ask for them before writing code. Do not invent placeholders.

- DOMAIN: the merchant's domain (e.g. mamasoil.shop). It becomes the Origin header on the call to MCC, and must already be in MCC's "Allowed domains" list (the user adds it in the MCC dashboard).
- MCC_SECRET: starts with `sk_test_proj_`. From MCC dashboard → Developer → Projets → "Régénérer les clés de test" modal.
- MCC_WEBHOOK_SECRET: the test webhook secret from the same modal.

== MCC API contract ==

1) Create a payment — your backend → MCC
   POST https://hvlmeoqyxaguzcujpmit.supabase.co/functions/v1/pay-create
   Headers:
     Authorization: Bearer <MCC_SECRET>
     Origin: https://<DOMAIN>      ← MUST match Allowed domains exactly. Server-side fetch does not set Origin automatically; set it manually.
     Content-Type: application/json
   Body:
     {
       "amount": <integer HTG, 1 to 1_000_000>,
       "referenceId": "<unique id you generate, UUID is fine>",
       "returnUrl": "https://<DOMAIN>/checkout/return",
       "customerEmail": "optional@example.com",   ← OMIT (not empty string) if absent
       "customerName": "optional"                  ← OMIT if absent
     }
   Response 200:
     { paymentUrl, reference, expiresAt, livemode: false }
   Redirect the browser to paymentUrl. In sandbox it's the MCC simulator (3-button page); in live it's the real MonCash URL.

2) Receive webhook — MCC → your backend
   POST <your webhook endpoint, the URL the user will set in MCC project settings>
   Headers:
     X-MCC-Signature: sha256=<hex>
     X-MCC-Timestamp: <unix seconds>
     Content-Type: application/json
   Body:
     {
       event: "payment.completed" | "payment.failed" | "payment.cancelled",
       reference: "<the referenceId you sent earlier>",
       amount: <integer HTG>,
       status: "completed" | "failed" | "cancelled",
       completedAt: <ISO8601 or null>,
       livemode: false              ← present on sandbox only; ABSENT on live (do NOT gate with === true)
     }

   Verification (CRITICAL):
     a. Read the body as RAW BYTES first (e.g. req.text() in Deno, raw req in Express). Do NOT parse JSON before verifying — re-serializing reorders keys and breaks HMAC.
     b. Compute expected = "sha256=" + hex(HMAC-SHA256(MCC_WEBHOOK_SECRET, rawBody))
     c. Compare to X-MCC-Signature header. Reject with 401 if mismatch.
     d. Only AFTER verifying, JSON.parse(rawBody) and dispatch on event.
   Handle ALL three event types. Returning non-200 on any of them causes MCC to retry forever.
   Return 200 with any short body on success.

3) Poll status (webhook fallback) — your return page → MCC, no auth
   GET https://hvlmeoqyxaguzcujpmit.supabase.co/functions/v1/pay-status?reference=<referenceId>
   Response: { reference, status, amount, ... }
   Use this from the customer's /checkout/return page, polling every 3 seconds for up to 90 seconds, in case the webhook is delayed.

== Files to create ==

1. Backend endpoint `create-mcc-payment` (or your stack's equivalent)
   Calls MCC pay-create with MCC_SECRET. Must handle CORS preflight (OPTIONS → 204 with CORS headers).

2. Backend endpoint `mcc-webhook`
   Receives, verifies HMAC, dispatches on event. Uses raw body for HMAC, parses JSON only after verifying.

3. Frontend "Pay with MonCash" button on the checkout page
   Calls create-mcc-payment with the amount, then window.location = paymentUrl.

4. Frontend `/checkout/return` page
   Reads ?reference, ?error, ?cancelled from the query string. Polls pay-status every 3s up to 30 attempts (90s total) as the webhook fallback. Shows success / failed / cancelled / timeout state to the customer.

5. Two environment variables on the backend
   MCC_SECRET           = sk_test_proj_<...>      (sandbox value for now)
   MCC_WEBHOOK_SECRET   = <test webhook secret>
   Use GENERIC names (no _TEST_) so the live swap is a value change, not a code change.

6. The MCC project's "Webhook URL" field (the user sets this in MCC dashboard) must point at your deployed mcc-webhook endpoint.

== Critical gotchas ==

- Origin header MUST be set on the server-side fetch to MCC. Browsers add it automatically; servers do not.
- Raw body for HMAC verify, never the parsed-then-restringified payload.
- All three webhook event types (completed / failed / cancelled) must be handled — non-200 = infinite retry.
- `livemode` is `false` on sandbox payloads and ABSENT on live. Never gate logic with `livemode === true`.
- CORS preflight on create-mcc-payment — return CORS headers on OPTIONS, not just on POST.
- Omit customerEmail / customerName if absent; do not send empty string.
- amount is integer HTG between 1 and 1,000,000. Recommended test value: 100.

== Out of scope (do NOT do these) ==

- Live mode. Implement sandbox only. Live is one env swap later, not a code change.
- The merchant's own order persistence / inventory / fulfillment logic. Hand them the payment.completed event and let them wire it.
- Translating "Sandbox" to French if generating French copy. It's the product feature name and stays English (like Stripe).

== When done ==

Give the user:
1. The two env vars they need to set + the webhook URL they need to paste into the MCC project.
2. A 4-step test plan: open the simulator URL returned from pay-create, click Pay success on the MCC simulator, verify webhook arrives in your logs + your /checkout/return resolves to "completed".
3. A one-line note: going live = swap MCC_SECRET to sk_proj_<...> + MCC_WEBHOOK_SECRET to the live webhook secret, no code change.

Lovable — collez tel quel dans le chat.

Claude / ChatGPT — même copier-coller ; l'assistant vous demandera les trois valeurs.

Cursor / Copilot — ouvrez un panneau de chat dans votre repo, collez, répondez aux trois questions.

Les trois valeurs que l'IA vous demandera

  • Votre domaine (ex. mamasoil.shop)
  • Votre MCC_SECRET (commence par sk_test_proj_)
  • Votre MCC_WEBHOOK_SECRET (le test webhook secret)

Une fois que l'IA a implémenté et que vous avez déployé, revenez sur la section suivante pour vérifier que le contrat d'API correspond bien à ce qui a été construit.

1. Créer un projet

Connectez-vous → DéveloppeurProjets Nouveau projet.

À remplir

Nom / CodeType / LimiteDescription
NominternePour votre référence — n'apparaît nulle part côté client.
URL WebhookHTTPS requiseURL où MCC POSTera les événements de paiement.
Domaines autorisésexactDomaines exacts que votre backend déclarera dans l'en-tête Origin lors des appels à l'API MCC. Mismatch = 403.

À la soumission, une fenêtre modale s'affiche une seule fois avec QUATRE valeurs. Sauvegardez-les toutes :

Nom / CodeType / LimiteDescription
Live publishablepk_proj_<32 hex>Optionnelle — rendu côté client.
Live secretsk_proj_<48 hex>Bearer token pour les paiements live.
Live webhook secretwhsec_<48 hex>Clé HMAC pour vérifier les webhooks live.
Test secretsk_test_proj_<48 hex>Bearer token pour le Sandbox.
Sauvegardez ces valeurs immédiatement. Les secrets complets ne sont affichés qu'une seule fois. Perdus = « Régénérer les clés » invalide la version précédente.

La clé publishable de test et le webhook secret de test sont également accessibles depuis l'onglet Projets (le webhook secret de test via « Régénérer les clés de test »).

2. L'API MCC

Trois endpoints. Identiques en sandbox et en live — seul le bearer token change.

2.1 Créer un paiement

POST
https://hvlmeoqyxaguzcujpmit.supabase.co/functions/v1/pay-create

En-têtes :

  • Authorization: Bearer <VOTRE_SECRET>
  • Origin: https://VOTRE-DOMAINE-AUTORISE
  • Content-Type: application/json

Corps :

{
  "amount": 100,
  "referenceId": "<votre-id-unique>",
  "returnUrl": "https://VOTRE-DOMAINE/checkout/return",
  "customerEmail": "optionnel@example.com",
  "customerName": "optionnel"
}

Réponse (200) :

{
  "paymentUrl": "https://moncashconnect.com/sim/<reference>?p=<short>",
  "reference": "<votre-id-unique>",
  "expiresAt": "2026-01-01T00:00:00Z",
  "livemode": false
}
Contraintes
  • amount : entier HTG, de 1 à 1 000 000.
  • referenceId : votre identifiant unique (UUID recommandé), 100 chars max, [a-zA-Z0-9\-_].
  • returnUrl : doit être en HTTPS.
  • L'en-tête Origin est REQUIS et doit correspondre à un domaine autorisé.
  • Omettez customerEmail / customerName s'ils sont absents — les chaînes vides font échouer la validation.

Redirigez ensuite le navigateur du client vers la paymentUrl retournée. En sandbox : le simulateur MCC. En live : la vraie page MonCash.

2.2 Recevoir les webhooks

MCC POSTe votre URL webhook à chaque changement de statut. Vérifiez la signature HMAC-SHA256 avant de faire confiance au payload.

En-têtes :

  • Content-Type: application/json
  • X-MCC-Signature: sha256=<hex>
  • X-MCC-Timestamp: <unix seconds>

Corps :

{
  "event": "payment.completed" | "payment.failed" | "payment.cancelled",
  "reference": "<votre-id-unique>",
  "amount": 100,
  "status": "completed" | "failed" | "cancelled",
  "completedAt": "2026-01-01T00:00:00Z" | null,
  "livemode": false
}
Vérification
  1. Lisez le corps de la requête en BYTES BRUTS (ne parsez pas le JSON d'abord — re-sérialiser casse le HMAC).
  2. Calculez sha256=<hex> de HMAC-SHA256(votre-webhook-secret, raw-body).
  3. Comparez à l'en-tête X-MCC-Signature.
  4. Si ça matche, parsez le JSON et dispatchez sur event.
Traitez les TROIS événements. Une réponse autre que 200 sur l'un d'entre eux entraîne des retries indéfinis de la part de MCC.
livemode vaut false sur les payloads sandbox et est ABSENT sur les payloads live. Ne gardez pas votre logique derrière livemode === true — ce ne sera jamais vrai.

2.3 Sonder le statut (fallback)

GET
https://hvlmeoqyxaguzcujpmit.supabase.co/functions/v1/pay-status?reference=<votre-id-unique>

Aucune authentification requise. Renvoie la même fiche de paiement. Utile sur votre page de retour quand le webhook n'est pas encore arrivé.

3. Tester en sandbox

3.1 Préparer votre environnement

Deux secrets dans le .env de votre backend (ou équivalent) :

MCC_SECRET = sk_test_proj_<votre-test-secret>
MCC_WEBHOOK_SECRET = <votre-test-webhook-secret>
Utilisez des noms de variables génériques (MCC_SECRET, pas MCC_TEST_SECRET). En passant en live, vous échangez uniquement les valeurs — pas de changement de code.

3.2 Déclencher un paiement sandbox

Montant de test recommandé : 100 HTG (~0,75 USD). Le même montant que vous utiliserez pour la vérification live ensuite — assez petit pour garder les deux tests bon marché, assez gros pour se comporter comme un vrai paiement.

Depuis votre checkout, appelez votre endpoint pay-create. La réponse vous donne une paymentUrl. Redirigez le navigateur.

3.3 Ce que vous verrez sur le simulateur

Le navigateur ouvre https://moncashconnect.com/sim/<reference>?p=<short>. Une carte centrée avec :

  • Un badge ambre SANDBOX
  • « Transaction de test — pas d'argent réel »
  • Votre identifiant de référence
  • Trois boutons :
    • Pay success (vert) — bascule le statut à completed, crédite le ledger sandbox.
    • Pay fail (rouge) — bascule le statut à failed, aucun crédit.
    • Cancel / timeout (outline) — bascule le statut à cancelled, aucun crédit.
  • Un footer explicatif

Cliquez sur l'un d'eux.

3.4 Ce qui se passe ensuite

  1. Le statut de la transaction bascule dans la base de données MCC.
  2. Le webhook part vers votre endpoint avec livemode: false et l'événement correspondant.
  3. Le navigateur redirige vers <votre returnUrl>?reference=<id> (+ &error=true&cancelled=true pour cancel, &error=true pour fail).

3.5 Où vérifier dans MCC

Après avoir cliqué sur un bouton du simulateur :

1

MCC Dashboard → /transactions

Toggle en pilule en haut : En direct | Test. Cliquez Test. Vos paiements sandbox apparaissent avec statut, montant, coloration entrée/sortie, et référence. Les transactions sandbox ne sont JAMAIS mélangées avec les live ; elles sont isolées.

2

Logs de votre handler webhook

Un POST doit être visible avec une signature HMAC valide et le type d'événement correspondant.

3

Votre page de retour

Doit refléter le résultat.

Les trois en phase → intégration validée.

3.6 Tester les trois issues

Nom / CodeType / LimiteDescription
Pay successpayment.completed?reference=<id>
Pay failpayment.failed?reference=<id>&error=true
Cancelpayment.cancelled?reference=<id>&error=true&cancelled=true

Déclenchez un nouveau paiement par issue (chaque transaction ne peut être transitionnée qu'une seule fois).

4. Passer en live

Une fois les trois issues sandbox vérifiées proprement :

4.1 Échanger deux valeurs de secret

Nom / CodeType / LimiteDescription
MCC_SECRETsk_test_proj_…Live : sk_proj_…
MCC_WEBHOOK_SECRETtest webhook secretLive : live webhook secret
Aucun changement de code. Le contrat d'API est identique.

4.2 Ce qui change en live

Nom / CodeType / LimiteDescription
paymentUrlSandbox : simulateur MCCLive : vraie URL MonCash (hébergée par Digicel)
Flux clientSandbox : 3 boutonsLive : vraie page MonCash : téléphone, PIN, confirmation
RèglementSandbox : instantanéLive : ~5-30 s après PIN ; SMS de confirmation
livemode (webhook)Sandbox : falseLive : absent
ArgentSandbox : aucunLive : HTG réels, ~3 % commission
Onglet DashboardSandbox : TestLive : En direct (par défaut)

4.3 Premier test en live

Recommandé : le même montant de 100 HTG que vous avez testé en sandbox.

  1. Cliquez votre bouton checkout. Le navigateur ouvre une vraie URL MonCash.
  2. Entrez votre vrai numéro MonCash + PIN. Confirmez.
  3. Le navigateur redirige vers votre returnUrl avec ?reference=…
  4. Dans les 30-60 secondes, le réconciliateur MCC met à jour la transaction à completed et envoie le webhook.
  5. Vérifiez dans MCC /transactions (onglet En direct) — votre ligne 100 HTG, completed, ~3 HTG de commission, ~97 HTG net.

4.4 Limitation connue de livraison webhook

Un problème connu : les webhooks live peuvent arriver en retard ou avec des signatures qui ne vérifient pas du premier coup. Mitigation : implémentez un sondage pay-status sur votre page de retour pendant jusqu'à 90 secondes. Votre client voit la confirmation immédiatement, quelle que soit la latence du webhook.

5. Erreurs courantes

Nom / CodeType / LimiteDescription
403 Origin not allowedEn-tête Origin ≠ Domaines autorisésAjoutez le domaine au projet MCC.
project_lookup_failed 502Mauvais bearer tokenRecopiez depuis le tableau de bord MCC.
tx_not_found_or_not_pendingTx déjà traitéeDéclenchez un nouveau paiement.
bad signature (webhook)JSON parsé utilisé pour le HMACUtilisez les bytes bruts du corps.
Webhook sandbox OK, live KOLimitation connue, cf. §4.4Utilisez le fallback pay-status en sondage.
Console navigateur : Failed to fetchPreflight CORS sur VOTRE backendRenvoyez les en-têtes CORS sur OPTIONS.

FAQ

Le Sandbox est-il payant ?

Non.

Puis-je sauter le KYC pour le sandbox ?

Oui. Le Sandbox est ouvert à tout compte signé — créez un projet, récupérez vos clés sk_test_proj_… et commencez tout de suite. La vérification d'identité (KYC) n'est demandée que pour activer le mode Live (sk_proj_…).

Les transactions sandbox sont-elles visibles aux admins MCC ?

Non. Uniquement sur votre /transactions (onglet Test). N'affectent jamais les résumés du dashboard, la finance, ou les vues admin.

Plusieurs projets ?

Oui. Chacun obtient sa propre paire de clés live + test + URL webhook.

Montant min / max ?

1 à 1 000 000 HTG.

Le sandbox simule-t-il l'UI MonCash ?

Non — trois boutons. Le sandbox teste votre intégration, pas l'UI Digicel.

Schéma de signature webhook ?

HMAC-SHA256, X-MCC-Signature: sha256=<hex>. Identique en sandbox et en live.

Passer en live = redéployer ?

Non. Seules les valeurs des variables d'environnement changent.

Remboursements ?

Pas encore exposés via l'API publique. Contactez le support MCC.

Statut

Ce guide est publié comme référence publique. Si quelque chose n'est pas clair, contactez le support MCC avec le nom de votre projet, l'extrait de log de la function, et l'UUID de référence.