How do I use direct charges?
Server-initiated Wave, MTN MoMo, and card charges when you need full control over the payment UI.
Direct charges let you collect payment from your server using POST /charge/*. Unlike hosted checkout sessions, you build the customer-facing flow yourself.
Card and Switch direct charges are not available yet. POST /charge/card and POST /charge/switch are turned off while the card rails are being finalized, and calling them today returns 503 service_unavailable. Use hosted checkout or payment links for cards. Only Wave and MTN mobile money direct charges are live.
Prefer hosted checkout or payment links unless you need a custom mobile-money flow. Direct charges require you to handle pending states and webhooks.
When to use direct charges
Use direct charges when you want to charge quickly and will not reuse the same payment method object for repeat charges, similar to a one-shot orchestrated payment.
| Rail | Endpoint | Customer step |
|---|---|---|
| Wave | POST /charge/wave | Open wave_launch_url or checkout_url from the response |
| MTN MoMo | POST /charge/mtn | Approve on their phone; live status starts PENDING |
| Card | POST /charge/card | Not available yet: use hosted checkout |
| Switch (server-side card) | POST /charge/switch | Not available yet: use hosted checkout |
Reference implementation
The monorepo includes a runnable example:
- Path:
apps/plugins/references/direct-charge-integration-reference - Routes: Wave, MTN, card, and webhook verification
- cURL scripts:
curl/create-wave-charge.sh,curl/create-mtn-charge.sh
cd apps/plugins/references/direct-charge-integration-reference
pnpm install && cp .env.example .env
pnpm run devCard flow (summary)
Not available yet. The steps below describe the planned card flow; POST /charge/card and POST /charge/switch currently return 503 service_unavailable. Use hosted checkout for cards.
POST /charge/cardwith amount, currency, andcustomer_idor (customer_email+customer_name).- Mount Payment Elements with
lomi_pk_...and the returnedclient_secret. - Listen for
PAYMENT_SUCCEEDEDwebhooks for fulfillment.
Mobile money flow (summary)
POST /charge/waveorPOST /charge/mtnwith amount, currency, and customer phone in E.164 format.- Redirect or instruct the customer per the response.
- Before you provide value, confirm the transaction's final status and amount via webhooks or
GET /transactions/{id}.
See Mobile money for the full guide.
Sandbox scenarios (test key only)
With a test API key, direct Wave and MTN charges auto-complete by default. To test async or failure paths in CI, send X-Scenario-Key:
| Value | Result |
|---|---|
pending | Charge stays PENDING (no auto-complete) |
failed | 400 error |
curl -sS -X POST "https://sandbox.api.lomi.africa/charge/mtn" \
-H "X-API-KEY: $LOMI_TEST_KEY" \
-H "X-Scenario-Key: pending" \
-H "Content-Type: application/json" \
-d '{"amount":1000,"currency":"XOF","customer_phone":"+2250700000000"}'Hosted checkout and card direct charges do not support this header yet. See Simulate errors and Sandbox payments.
lomi. Network
Operators can create direct charges on behalf of connected member accounts:
POST /charge/wave HTTP/1.1
X-API-KEY: lomi_sk_live_operator_...
Lomi-Account: acct_1234567890See lomi. Network.
Troubleshooting
Use GET /providers to confirm which rails are connected for the organization before calling direct charge endpoints.
| Symptom | Likely cause | Fix |
|---|---|---|
Card charge returns 503 service_unavailable | Card direct charges are not configured on the API deployment | Ops: ensure platform card-payment credentials are set on the API service for the target environment. Merchants do not configure card-processor account keys: card direct charges run on lomi.'s infrastructure. |
Wave charge returns 400 about missing Aggregated Merchant ID | Wave is not fully connected in the dashboard | Connect Wave in the dashboard and save the Aggregated Merchant ID for the organization. |
Wave charge returns 400 with a Wave edge error | Invalid payload or Wave-side rejection | Send currency: "XOF", nested customer.phoneNumber in E.164, and optional successUrl / errorUrl (camelCase). See Wave. |
Card charge returns 400 about customer fields | Missing reconciliation fields | Include customer_id, or both customer_email and customer_name. |
| Hosted checkout works but direct charge fails | Different prerequisites per rail | Hosted checkout uses lomi.'s checkout app; direct charges hit /charge/* and require correct payload shape plus provider connection. |
Merchants only need lomi_sk_... (server) and lomi_pk_... (Payment Elements). They never configure platform card-payment secrets themselves.