lomi.

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.

RailEndpointCustomer step
WavePOST /charge/waveOpen wave_launch_url or checkout_url from the response
MTN MoMoPOST /charge/mtnApprove on their phone; live status starts PENDING
CardPOST /charge/cardNot available yet: use hosted checkout
Switch (server-side card)POST /charge/switchNot 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 dev

Card 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.

  1. POST /charge/card with amount, currency, and customer_id or (customer_email + customer_name).
  2. Mount Payment Elements with lomi_pk_... and the returned client_secret.
  3. Listen for PAYMENT_SUCCEEDED webhooks for fulfillment.

Mobile money flow (summary)

  1. POST /charge/wave or POST /charge/mtn with amount, currency, and customer phone in E.164 format.
  2. Redirect or instruct the customer per the response.
  3. 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:

ValueResult
pendingCharge stays PENDING (no auto-complete)
failed400 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_1234567890

See lomi. Network.

Troubleshooting

Use GET /providers to confirm which rails are connected for the organization before calling direct charge endpoints.

SymptomLikely causeFix
Card charge returns 503 service_unavailableCard direct charges are not configured on the API deploymentOps: 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 IDWave is not fully connected in the dashboardConnect Wave in the dashboard and save the Aggregated Merchant ID for the organization.
Wave charge returns 400 with a Wave edge errorInvalid payload or Wave-side rejectionSend currency: "XOF", nested customer.phoneNumber in E.164, and optional successUrl / errorUrl (camelCase). See Wave.
Card charge returns 400 about customer fieldsMissing reconciliation fieldsInclude customer_id, or both customer_email and customer_name.
Hosted checkout works but direct charge failsDifferent prerequisites per railHosted 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.

On this page