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.
1 · Create the PaymentIntent on your server
Server-side only — never put sk_ in the browser.
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"
}'| Name | Type | Description |
|---|---|---|
amountrequired | integer | Minor units. DJF is zero-decimal — pass an integer DJF amount. |
currencyrequired | string | ISO 4217. For Waafi: "DJF". |
providerrequired | string | Always "waafi" for this rail. |
customer_msisdnrequired | string | E.164 — "+25377xxxxxxx". |
description | string | Free-form, ≤140 chars. Appears on the customer's receipt. |
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
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.
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
+25377xxxxxxxshape - OTP
123456always succeeds in sandbox - OTP
000000always 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.