Moneybag
Status: stable · Driver: moneybag · Refunds: no public API · Sync: no public API (see below)
Moneybag is a Bangladeshi hosted checkout (cards, mobile wallets, EMI). Its public merchant API is small: create a checkout, and verify a transaction.
Setup
MONEYBAG_API_KEY=your-merchant-api-key
MONEYBAG_SANDBOX=true
// config/bdpaygate.php → gateways
'moneybag' => [
'driver' => 'moneybag',
'api_key' => env('MONEYBAG_API_KEY'),
'sandbox' => env('MONEYBAG_SANDBOX', true),
// 'base_url' => env('MONEYBAG_BASE_URL'), // override the API origin
],
| API origin | |
|---|---|
| Sandbox | https://sandbox.api.moneybag.com.bd |
| Live | https://api.moneybag.com.bd |
Sandbox accounts are self-serve: create one at https://developers.moneybag.com.bd/sandbox (or https://sandbox.moneybag.com.bd). The merchant API key is shown once, so store it straight away. Production keys need a separate review and approval by Moneybag. The public contract lists only the sandbox origin; the live origin above comes from Moneybag's own SDK and plugin releases. Confirm it with Moneybag at go-live, and set base_url if they give you another. Docs: https://developers.moneybag.com.bd/docs.
How a payment is verified
charge()creates a checkout (POST /api/v2/payments/checkout, headerX-Merchant-API-Key). This attempt'sgateway_referenceis sent asorder_id, and your own reference goes inmetadata.reference. The returnedsession_idis stored ingateway_payment_id, and the customer is sent to the returnedcheckout_url.- Moneybag returns the customer to
/bdpaygate/moneybag/{success|fail|cancel}?ref=…, addingtransaction_idandstatus(SUCCESS,FAILEDorCANCELLED). - Neither parameter is trusted. The driver calls
GET /api/v2/payments/verify/{transaction_id}and checks:- that the transaction's
order_idis this attempt'sgateway_reference; - for a payment, that
statusisSUCCESSandverifiedistrue, and thatamountandcurrencymatch.
- that the transaction's
- A verified
SUCCESScompletes the payment, even on the fail or cancel URL. A verifiedFAILEDorCANCELLEDfails or cancels it. Anything else stays pending: an unknown transaction, a non-success answer, orverified: false. - The Moneybag
transaction_idis stored ingateway_transaction_id.
No sync, no refunds
The public API can only verify by transaction_id. There is no lookup by order or session, and no refund API: Moneybag lists refunds, payouts and transaction history as merchant-portal features outside the public API. So:
-
The driver does not implement
SupportsSyncorSupportsRefunds, andbdpaygate:syncskips it. Refund from the Moneybag merchant portal. -
If a customer pays but never comes back, find the transaction id in the merchant portal and settle the payment yourself:
BdPayGate::for($payment)->verifyTransaction($payment, 'txn…'); -
If the customer cancels before a transaction exists, the return has no
transaction_id. The payment stays pending, and the customer lands on yourpendingpage.
Limits and quirks
- Amount must be 10.00 to 1000000.00 BDT. Checked before the API call.
order_idmust be 10 to 36 characters; attempt ids are 20.customer.emailis required by Moneybag; the driver falls back tonoemail@example.com. Pass$meta['postcode']to send a postcode.- Validation errors are HTTP 422 with
{"detail":[{"loc","msg","type"}]}; they are included in the exception message. - Webhooks are not handled yet. Moneybag signs webhooks (
X-Webhook-Signature: sha256=HMAC(secret, "{timestamp}.{raw body}")), but the payment event body is not in the public contract, and webhook endpoints are configured in the merchant portal. The driver sends noipn_url. - Some outcomes are unverified until checked against a sandbox:
- The non-terminal
statusvalues returned by verify. OnlySUCCESS,FAILEDandCANCELLEDare documented. - Whether
amountorcharge_amountcarries the order amount for payments where the customer pays a fee. The driver checksamount.
- The non-terminal
Hosts (checked 2026-10-15)
Both https://sandbox.api.moneybag.com.bd (this driver's sandbox default) and https://staging.api.moneybag.com.bd (the official SDK's default) are live and require authentication. https://api.moneybag.com.bd is production. Override any of them with base_url. A merchant API key is issued from the Moneybag panel; there is no public demo key.
Sandbox run (2026-10-15)
Verified against the real Moneybag sandbox through a Laravel 13 app with a self-serve sandbox account (developer portal → sandbox onboarding issues a merchant ID and a one-time-display API key). A 120 BDT payment paid with bKash completed after GET /api/v2/payments/verify/{transaction_id}; a simulated failure was confirmed as FAILED and marked failed. The verify response reports the order amount in amount and Moneybag's service charge separately in charge_amount (the customer saw 121.20), so the amount check correctly compares amount.