Node.js example
A complete Node.js integration: MD5 sorted signing, create deposit/payout, verify webhooks.
Install
Only Node's built-in crypto, fetch, and express are used — no extra SDK.
Configuration
# .env
API_BASE=https://api.168tonpay.xyz/api/v1/compat/godapay
APPLY_BASE=https://apply.168tonpay.xyz/api/v1/compat/godapay
MERCHANT_ID=aef6025b-f435-443c-bb18-f10e65d9314c
API_SECRET=...
Signing helper
// gateway/auth.js
const crypto = require('crypto');
// GodaPay-compat MD5 sorted signature.
// Exclude signature + null values, sort keys, append &key=SECRET, MD5 lowercase hex.
function sign(params, apiSecret) {
const signStr = Object.keys(params)
.filter((k) => k !== 'signature' && params[k] != null)
.sort()
.map((k) => `${k}=${params[k]}`)
.join('&') + `&key=${apiSecret}`;
return crypto.createHash('md5').update(signStr).digest('hex').toLowerCase();
}
module.exports = { sign };
API client
// gateway/client.js
const { sign } = require('./auth');
class GatewayError extends Error {
constructor(code, message) { super(message); this.code = code; }
}
async function post(base, path, params) {
const body = { ...params };
body.signature = sign(body, process.env.API_SECRET);
const res = await fetch(`${base}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const json = await res.json();
if (json.code !== 0) {
throw new GatewayError(json.code, json.message);
}
return json.data;
}
const API = process.env.API_BASE;
const APPLY = process.env.APPLY_BASE;
module.exports = {
GatewayError,
createCollectOrder: (d) => post(API, '/pay/order/create', d),
queryOrder: (d) => post(API, '/pay/order/query', d),
getBalance: () => post(API, '/pay/balance', { merchantId: process.env.MERCHANT_ID }),
createPayoutOrder: (d) => post(APPLY, '/apply/create', d),
};
Create a deposit
const gateway = require('./gateway/client');
async function startDeposit(player, orderNo) {
const { payUrl, platformOrderNo } = await gateway.createCollectOrder({
merchantId: process.env.MERCHANT_ID,
orderNo,
returnUrl: 'https://yourgame.com/api/webhooks/payment',
playerPhone: player.phone, // registered number; load-bearing fraud signal
playerRef: player.id,
});
// Open the hosted payUrl for the player; wait for the webhook to credit
return { payUrl, platformOrderNo };
}
Create a payout
async function startWithdrawal(player, outOrderNo, amount) {
return gateway.createPayoutOrder({
merchantId: process.env.MERCHANT_ID,
outOrderNo,
amount: amount.toFixed(2),
bankType: 'BKASH',
userName: player.name,
account: player.phone,
returnUrl: 'https://yourgame.com/api/webhooks/payout',
});
}
Webhook handling
const express = require('express');
const { sign } = require('./gateway/auth');
const app = express();
// the webhook is form-urlencoded
app.use(express.urlencoded({ extended: false }));
app.post('/api/webhooks/payment', async (req, res) => {
const { signature, ...fields } = req.body;
if (sign(fields, process.env.API_SECRET) !== signature) {
return res.status(401).send('invalid signature');
}
// status: '1' = paid
if (req.body.status === '1') {
// Dedupe on platformOrderNo; credit with amount
await creditPlayer(req.body.orderNo, req.body.amount).catch(console.error);
}
res.send('SUCCESS'); // must reply with the literal string SUCCESS
});
Error handling
const { GatewayError } = require('./gateway/client');
try {
await gateway.createCollectOrder(data);
} catch (err) {
if (err instanceof GatewayError) {
// non-zero code = business error; handle by code / message
throw new Error(`[${err.code}] ${err.message}`);
}
throw err;
}
Testing
Ask the platform to flag your merchant as a demo merchant, then create orders with isTest: true; a synthetic SMS is injected and a real-format webhook fires. See Test credentials.