Provider · Mobile money

Waafipay direct integration

The dominant mobile-money rail in Djibouti. Two-step flow: customer provides MSISDN, receives an OTP on their phone, enters it in your UI, payment settles.

WalletCardC2B

1 · Create the PaymentIntent on your server

Server-side only — never put sk_ in the browser.

POST /v1/payment_intentsbash
curl -X POST https://api.merashub.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order_42" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "currency": "DJF",
    "provider": "waafi",
    "customer_msisdn": "+25377xxxxxxx",
    "description": "Order #42"
  }'
NameTypeDescription
amount
required
integerMinor units. DJF is zero-decimal — pass an integer DJF amount.
currency
required
stringISO 4217. For Waafi: "DJF".
provider
required
stringAlways "waafi" for this rail.
customer_msisdn
required
stringE.164 — "+25377xxxxxxx".
descriptionstringFree-form, ≤140 chars. Appears on the customer's receipt.
Response will include client_secret and status: "requires_action" with next_action.type = "collect_otp". Pass client_secret (only) to the browser.

2 · Browser confirms with the OTP

meras.confirmPaymentts
import { MerasPay } from "@meraspay/sdk-js";

const meras = MerasPay("pk_test_...");
const elements = meras.elements({ clientSecret });
const otp = elements.create("otp");
otp.mount({ selector: "#otp" });

document.getElementById("pay").onclick = async () => {
  const result = await meras.confirmPayment(paymentIntentId, {
    client_secret: clientSecret,
    otp: otp.getToken(),
  });
  if (result.status === "succeeded") {
    window.location.href = "/thank-you";
  }
};

3 · Card acceptance (Visa / Mastercard)

Waafi also acquires cards through its hosted page. Create the intent with provider: "waafi" and metadata.waafi_mode: "card" — you get a next_action.type = "redirect"and send the customer to Waafi's PCI-compliant page to enter their card. No PAN touches your servers (PCI SAQ-A), and 3-D Secure is handled there. The mobile-wallet flow uses the same redirect with waafi_mode: "hpp". See the Card reference and the Direct guide for the full flow.

Card via Waafi hosted pagebash
curl -X POST https://api.merashub.com/v1/payment_intents \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "currency": "DJF",
    "provider": "waafi",
    "metadata": { "waafi_mode": "card" },
    "return_url": "https://yoursite.example/orders/42/return"
  }'

Sandbox test data

  • Any MSISDN in +25377xxxxxxx shape
  • OTP 123456 always succeeds in sandbox
  • OTP 000000 always fails (test the error path)
  • Any other 6-digit OTP fails with "expired"

Webhooks

Subscribe to payment_intent.succeeded and payment_intent.payment_failed. The server-side webhook is the source of truth — never finalise the order on the client alone.