Home / Node.js / DGePay Experimental
DGePay
Also for Laravel: https://github.com/byteminus/laravel-bdpaygate/blob/main/docs/gateways/dgepay.md
Status: experimental · Driver: dgepay · Refunds: no (use the DGePay merchant panel) · Sync: yes
DGePay is a Bangladesh Bank-licensed payment service operator. Its hosted checkout covers bKash, Nagad and other mobile wallets.
Experimental. DGePay's API guide is only given to merchants. This driver is built from the provider's published SDK and has not been run against DGePay's UAT, in either the Laravel or the Node package. Request the integration guide and UAT credentials from DGePay, and verify the driver against UAT before going live.
Setup
DGEPAY_CLIENT_ID=
DGEPAY_CLIENT_SECRET=
DGEPAY_API_KEY=
DGEPAY_SANDBOX=true
These are read by gatewaysFromEnv(process.env). Or configure the gateway in code:
gateways: {
dgepay: {
clientId: process.env.DGEPAY_CLIENT_ID,
clientSecret: process.env.DGEPAY_CLIENT_SECRET,
apiKey: process.env.DGEPAY_API_KEY,
sandbox: true, // true = UAT
// Optional:
// baseUrl: 'https://api-dev.dgepay.net/dipon/v3', // DGePay's DEV environment
// failedStatusCodes: ['4'], // status codes treated as a definite failure
// tokenTtl: 300, // seconds, if the JWT has no exp claim
},
}
The optional settings have no environment variables; set them in code. sandbox defaults to true. Set it to false for live payments.
| Environment | API base |
|---|---|
UAT (sandbox: true) |
https://api-uat.dgepay.net/dipon/v3 |
| Live | https://apiv2.dgepay.net/dipon/v3 |
DEV (set as baseUrl) |
https://api-dev.dgepay.net/dipon/v3 |
Capabilities
| Charge (hosted checkout) | yes |
| Callback verification | status API, every time |
Sync (pay.sync, pay.syncPending) |
yes |
| Refunds | no. No refund endpoint has been published. pay.refund() throws UnsupportedOperationError. |
| Server-to-server notification | none known. Run pay.syncPending() on a schedule. |
| Currency | BDT only |
How a payment is verified
-
Authenticate.
POST /payment_gateway/authenticatewithAuthorization: Basic base64(clientId:clientSecret), plus the same two values in the JSON body. The JWT that comes back is cached until itsexpclaim, less a minute, in thecacheyou pass tocreateBdPayGate. If DGePay answers 401, the driver fetches a new token and retries once. -
Initiate.
POST /payment_gateway/initiate_paymentsendsamount,unique_txn_id,redirect_urlandnote(your description, or your reference). It also sendscustomer_tokenandpayment_method(null unless you passmeta.paymentMethod), andpayee_informationwhencustomer.phoneis a Bangladeshi mobile number.unique_txn_idis this attempt'sgatewayReference, new for every attempt.- The body is encrypted: JSON with sorted keys → AES-128-ECB (PKCS#7) → base64. The key is the client secret, padded with
0or cut to 16 bytes. - The
Signatureheader is base64(HMAC-SHA256(api key, canonical)). canonical is the same sorted JSON with{ } " : ,and spaces removed, so{"amount":150.0,"note":"Order 1"}becomesamount150.0noteOrder1. Nulls appear asnull, and amounts as150.0or150.5. - The customer is redirected to
payment.checkoutUrl(data.webview_url).
-
Return. DGePay returns the customer to
/bdpaygate/dgepay/callback?data=<encrypted JSON>.- Query-string parsing turns
+into spaces, so the driver restores the+signs, then decrypts. Adatavalue that does not decrypt with your client secret is rejected. - Only
unique_txn_idis used from the decrypted payload, to find the payment. Itsstatus_codeis ignored.
- Query-string parsing turns
-
Confirm.
POST /payment_gateway/check_transaction_status(encrypted and signed in the same way) gives the result:data.status_codeResult 3Completed, if data.amountmatches8Cancelled 4(failedStatusCodes)Failed anything else, or no datastays pending On completion,
txn_idgoes intopayment.gatewayPaymentIdandtxn_numberintopayment.gatewayTransactionId. An HTTP 5xx answer is treated as "DGePay unavailable": the payment stays pending.
Limits and quirks
- Errors come back with HTTP 200. The body has
"success": 0and anerrorlist. The driver reads the body. - Plain JSON is rejected. DGePay answers it with
REQUESTED_PARAMETERS_ARE_MISMATCHED. Every call after authentication must be encrypted and signed. - AES-ECB is DGePay's choice. It is a weak mode, used here only because the API requires it. Keep the client secret and API key out of version control.
- Signature formatting is strict. Numeric-looking strings (phone numbers,
+88) are signed as given. Only real numbers are rendered as JSON numbers. node:crypto. The encryption and signature usenode:crypto, loaded lazily. It runs on Node, Bun, Deno and Cloudflare Workers withnodejs_compat.
Unverified
These points come from DGePay's published SDK, not from the request-only API guide:
- The full status-code table.
3(success) and8(cancelled) are documented.4comes from an SDK sample whose message is "TRANSACTION FAILED". Every other code is treated as pending. - How amounts with two decimal places (
150.25) appear in the signature. The driver uses the JSON form,150.25. One unofficial client rounds to one decimal place instead. - Whether
authenticatealso needs the body credentials. The SDK sends Basic auth only. This driver sends both. - Whether
payee_informationandpayment_methodare validated. - How long a token lasts, beyond the JWT
expclaim. - Whether DGePay has a webhook/IPN or a refund API. None is known.
- How to get UAT or DEV credentials.