bKash集成完整指南
bKash集成完整指南
📋 目录
🚀 bKash平台概述
市场地位与影响力
bKash是孟加拉国最大的移动金融服务提供商,成立于2011年,由BRAC银行与蚂蚁金服合资成立。截至2024年:
- 用户规模:超过6000万注册用户,约占孟加拉人口的35%
- 市场份额:移动支付市场占有率超过70%
- 交易量:日均处理交易超过2000万笔
- 覆盖范围:全国64个区县,40万+代理点
业务模式分析
bKash采用代理商模式,通过遍布全国的代理网点提供现金存取服务,形成了完整的数字金融生态:
个人用户服务:
- Send Money(转账)
- Cash Out(取现)
- Mobile Recharge(话费充值)
- Bill Payment(账单缴费)
- Merchant Payment(商户支付)
商户解决方案:
- Point of Sale(POS支付)
- Online Payment(在线支付)
- Bulk Payment(批量支付)
- Salary Disbursement(工资发放)
📝 商户申请流程
1. 申请前准备
个人商户所需材料:
- 有效身份证件(NID/护照)
- 银行账户证明
- 业务证明(网站/APP截图、业务说明书)
- 预计月交易量说明
企业商户所需材料:
- 营业执照(Trade License)
- 税务登记证(TIN Certificate)
- 银行开户许可证
- 法人身份证明
- 公司章程
- 业务许可证(如适用)
2. 申请流程详解
mermaid
graph TD
A[提交申请材料] --> B[bKash初审]
B --> C[补充材料]
C --> D[实地核查]
D --> E[风控评估]
E --> F[签署协议]
F --> G[获得API凭证]
G --> H[技术集成测试]
H --> I[正式上线]
时间周期:
- 个人商户:5-10个工作日
- 企业商户:10-15个工作日
- 大型企业:15-30个工作日
3. 费率结构
bKash商户费率采用阶梯定价模式:
月交易量 | 个人商户费率 | 企业商户费率 | 备注 |
---|---|---|---|
< 50万塔卡 | 1.85% | 1.75% | 基础费率 |
50万-200万 | 1.70% | 1.60% | 中等规模优惠 |
200万-500万 | 1.55% | 1.45% | 大客户优惠 |
> 500万塔卡 | 1.40% | 1.30% | 最优费率 |
🏗️ 技术架构解析
系统架构图
┌─────────────────────────────────────────────────────────────┐
│ bKash Payment Gateway │
├─────────────────────────────────────────────────────────────┤
│ Web API Layer │ Mobile API │ USSD Gateway │ Agent API │
├─────────────────────────────────────────────────────────────┤
│ Core Payment Processing Engine │
├─────────────────────────────────────────────────────────────┤
│ Risk Engine │ KYC System │ Notification Service │
├─────────────────────────────────────────────────────────────┤
│ Database Cluster (MySQL/Redis) │
├─────────────────────────────────────────────────────────────┤
│ BRAC Bank Core Banking System Integration │
└─────────────────────────────────────────────────────────────┘
API环境配置
沙盒环境:
- Base URL:
https://tokenized.sandbox.bka.sh/v1.2.0-beta
- 用途: 开发测试
- 限制: 无真实资金流动
生产环境:
- Base URL:
https://tokenized.pay.bka.sh/v1.2.0-beta
- 用途: 正式交易
- 要求: 必须通过商户审核
🔌 API接口详解
1. 认证机制 (Grant Token)
javascript
// 获取访问令牌
POST /tokenized/checkout/token/grant
Headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"username": "YOUR_USERNAME",
"password": "YOUR_PASSWORD"
}
Body: {
"app_key": "YOUR_APP_KEY",
"app_secret": "YOUR_APP_SECRET"
}
// 响应示例
{
"statusCode": "0000",
"statusMessage": "Successful",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}
2. 创建支付 (Create Payment)
javascript
POST /tokenized/checkout/create
Headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"authorization": "Bearer " + id_token,
"x-app-key": "YOUR_APP_KEY"
}
Body: {
"mode": "0011", // Payment mode
"payerReference": "CUSTOMER_ID_123",
"callbackURL": "https://yoursite.com/callback",
"amount": "100.50",
"currency": "BDT",
"intent": "sale",
"merchantInvoiceNumber": "INV_001"
}
// 响应示例
{
"statusCode": "0000",
"statusMessage": "Successful",
"paymentID": "TR0011234567890",
"bkashURL": "https://tokenized.pay.bka.sh/checkout?paymentID=TR0011234567890",
"callbackURL": "https://yoursite.com/callback",
"successCallbackURL": "https://yoursite.com/success",
"failureCallbackURL": "https://yoursite.com/failure",
"cancelledCallbackURL": "https://yoursite.com/cancel"
}
3. 执行支付 (Execute Payment)
javascript
POST /tokenized/checkout/execute
Headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"authorization": "Bearer " + id_token,
"x-app-key": "YOUR_APP_KEY"
}
Body: {
"paymentID": "TR0011234567890"
}
// 成功响应
{
"statusCode": "0000",
"statusMessage": "Successful",
"paymentID": "TR0011234567890",
"trxID": "8M2A59MID7",
"transactionStatus": "Completed",
"amount": "100.50",
"currency": "BDT",
"paymentExecuteTime": "2024-01-15T10:30:45",
"merchantInvoiceNumber": "INV_001"
}
4. 查询支付 (Query Payment)
javascript
POST /tokenized/checkout/payment/status
Headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"authorization": "Bearer " + id_token,
"x-app-key": "YOUR_APP_KEY"
}
Body: {
"paymentID": "TR0011234567890"
}
💻 代码实战示例
PHP集成示例
php
<?php
class BkashPayment {
private $app_key;
private $app_secret;
private $username;
private $password;
private $base_url;
public function __construct($config) {
$this->app_key = $config['app_key'];
$this->app_secret = $config['app_secret'];
$this->username = $config['username'];
$this->password = $config['password'];
$this->base_url = $config['sandbox'] ?
'https://tokenized.sandbox.bka.sh/v1.2.0-beta' :
'https://tokenized.pay.bka.sh/v1.2.0-beta';
}
// 获取Token
public function getToken() {
$url = $this->base_url . '/tokenized/checkout/token/grant';
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'username: ' . $this->username,
'password: ' . $this->password
];
$data = [
'app_key' => $this->app_key,
'app_secret' => $this->app_secret
];
$response = $this->makeRequest($url, $headers, $data);
return json_decode($response, true);
}
// 创建支付
public function createPayment($amount, $invoice_number, $customer_id = '') {
$token_response = $this->getToken();
if ($token_response['statusCode'] !== '0000') {
throw new Exception('Token获取失败: ' . $token_response['statusMessage']);
}
$url = $this->base_url . '/tokenized/checkout/create';
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'authorization: Bearer ' . $token_response['id_token'],
'x-app-key: ' . $this->app_key
];
$data = [
'mode' => '0011',
'payerReference' => $customer_id,
'callbackURL' => 'https://yoursite.com/bkash/callback',
'amount' => strval($amount),
'currency' => 'BDT',
'intent' => 'sale',
'merchantInvoiceNumber' => $invoice_number
];
$response = $this->makeRequest($url, $headers, $data);
return json_decode($response, true);
}
// 执行支付
public function executePayment($payment_id) {
$token_response = $this->getToken();
$url = $this->base_url . '/tokenized/checkout/execute';
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'authorization: Bearer ' . $token_response['id_token'],
'x-app-key: ' . $this->app_key
];
$data = ['paymentID' => $payment_id];
$response = $this->makeRequest($url, $headers, $data);
return json_decode($response, true);
}
// HTTP请求方法
private function makeRequest($url, $headers, $data) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new Exception('cURL错误: ' . curl_error($ch));
}
curl_close($ch);
if ($http_code !== 200) {
throw new Exception('HTTP错误: ' . $http_code);
}
return $response;
}
}
// 使用示例
$config = [
'app_key' => 'your_app_key',
'app_secret' => 'your_app_secret',
'username' => 'your_username',
'password' => 'your_password',
'sandbox' => true // 生产环境设为false
];
$bkash = new BkashPayment($config);
try {
// 创建支付
$payment = $bkash->createPayment(100.00, 'INV_001', 'CUSTOMER_123');
if ($payment['statusCode'] === '0000') {
// 重定向到bKash支付页面
header('Location: ' . $payment['bkashURL']);
exit;
} else {
echo '支付创建失败: ' . $payment['statusMessage'];
}
} catch (Exception $e) {
echo '错误: ' . $e->getMessage();
}
?>
Node.js集成示例
javascript
const axios = require('axios');
class BkashPayment {
constructor(config) {
this.appKey = config.appKey;
this.appSecret = config.appSecret;
this.username = config.username;
this.password = config.password;
this.baseUrl = config.sandbox ?
'https://tokenized.sandbox.bka.sh/v1.2.0-beta' :
'https://tokenized.pay.bka.sh/v1.2.0-beta';
}
async getToken() {
try {
const response = await axios.post(
`${this.baseUrl}/tokenized/checkout/token/grant`,
{
app_key: this.appKey,
app_secret: this.appSecret
},
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'username': this.username,
'password': this.password
}
}
);
return response.data;
} catch (error) {
throw new Error(`Token获取失败: ${error.response?.data?.statusMessage || error.message}`);
}
}
async createPayment(amount, invoiceNumber, customerId = '') {
const tokenResponse = await this.getToken();
if (tokenResponse.statusCode !== '0000') {
throw new Error(`Token获取失败: ${tokenResponse.statusMessage}`);
}
try {
const response = await axios.post(
`${this.baseUrl}/tokenized/checkout/create`,
{
mode: '0011',
payerReference: customerId,
callbackURL: 'https://yoursite.com/bkash/callback',
amount: amount.toString(),
currency: 'BDT',
intent: 'sale',
merchantInvoiceNumber: invoiceNumber
},
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'authorization': `Bearer ${tokenResponse.id_token}`,
'x-app-key': this.appKey
}
}
);
return response.data;
} catch (error) {
throw new Error(`支付创建失败: ${error.response?.data?.statusMessage || error.message}`);
}
}
async executePayment(paymentId) {
const tokenResponse = await this.getToken();
try {
const response = await axios.post(
`${this.baseUrl}/tokenized/checkout/execute`,
{ paymentID: paymentId },
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'authorization': `Bearer ${tokenResponse.id_token}`,
'x-app-key': this.appKey
}
}
);
return response.data;
} catch (error) {
throw new Error(`支付执行失败: ${error.response?.data?.statusMessage || error.message}`);
}
}
}
// 使用示例
const config = {
appKey: 'your_app_key',
appSecret: 'your_app_secret',
username: 'your_username',
password: 'your_password',
sandbox: true
};
const bkash = new BkashPayment(config);
// Express.js路由示例
app.post('/create-payment', async (req, res) => {
try {
const { amount, invoiceNumber, customerId } = req.body;
const payment = await bkash.createPayment(amount, invoiceNumber, customerId);
if (payment.statusCode === '0000') {
res.json({
success: true,
paymentUrl: payment.bkashURL,
paymentId: payment.paymentID
});
} else {
res.status(400).json({
success: false,
message: payment.statusMessage
});
}
} catch (error) {
res.status(500).json({
success: false,
message: error.message
});
}
});
🔒 安全机制详解
1. API签名验证
bKash使用Bearer Token认证机制,所有API请求都需要在Header中包含有效的访问令牌:
javascript
// Token有效期管理
class TokenManager {
constructor() {
this.token = null;
this.tokenExpiry = null;
this.refreshToken = null;
}
isTokenValid() {
return this.token &&
this.tokenExpiry &&
new Date() < new Date(this.tokenExpiry);
}
async getValidToken() {
if (!this.isTokenValid()) {
await this.refreshAccessToken();
}
return this.token;
}
async refreshAccessToken() {
// 刷新token的逻辑
if (this.refreshToken) {
// 使用refresh token获取新的access token
} else {
// 重新获取token
const tokenResponse = await this.getToken();
this.token = tokenResponse.id_token;
this.tokenExpiry = new Date(Date.now() + tokenResponse.expires_in * 1000);
this.refreshToken = tokenResponse.refresh_token;
}
}
}
2. 数据加密传输
所有API通信必须使用HTTPS协议,确保数据在传输过程中的安全性:
javascript
// HTTPS配置示例(Node.js)
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem'),
// 强制使用TLS 1.2或更高版本
secureProtocol: 'TLSv1_2_method',
// 禁用不安全的密码套件
ciphers: [
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES128-SHA256',
'ECDHE-RSA-AES256-SHA384'
].join(':'),
honorCipherOrder: true
};
https.createServer(options, app).listen(443, () => {
console.log('HTTPS服务器运行在443端口');
});
3. 回调验证机制
为了确保回调请求的真实性,建议实现以下验证机制:
php
<?php
// 回调验证示例
function verifyBkashCallback($postData, $secretKey) {
// 1. 验证IP白名单
$allowedIPs = ['103.106.118.10', '103.106.118.11']; // bKash服务器IP
$clientIP = $_SERVER['REMOTE_ADDR'];
if (!in_array($clientIP, $allowedIPs)) {
return false;
}
// 2. 验证时间戳(防重放攻击)
$timestamp = $postData['timestamp'] ?? 0;
$currentTime = time();
if (abs($currentTime - $timestamp) > 300) { // 5分钟有效期
return false;
}
// 3. 验证签名
$expectedSignature = hash_hmac('sha256', json_encode($postData), $secretKey);
$receivedSignature = $_SERVER['HTTP_X_BKASH_SIGNATURE'] ?? '';
return hash_equals($expectedSignature, $receivedSignature);
}
// 处理回调
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$postData = json_decode(file_get_contents('php://input'), true);
if (verifyBkashCallback($postData, 'your_secret_key')) {
// 回调验证成功,处理业务逻辑
$paymentId = $postData['paymentID'];
$trxId = $postData['trxID'];
$status = $postData['transactionStatus'];
// 更新订单状态
updateOrderStatus($paymentId, $status, $trxId);
// 返回成功响应
http_response_code(200);
echo json_encode(['status' => 'success']);
} else {
// 回调验证失败
http_response_code(400);
echo json_encode(['status' => 'invalid']);
}
}
?>
4. 防重放攻击
javascript
// 防重放攻击实现
class ReplayProtection {
constructor() {
this.usedNonces = new Set();
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, 300000); // 每5分钟清理一次
}
isValidRequest(nonce, timestamp) {
// 检查时间戳
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - timestamp) > 300) {
return false; // 超过5分钟的请求拒绝
}
// 检查nonce是否已使用
const nonceKey = `${nonce}_${timestamp}`;
if (this.usedNonces.has(nonceKey)) {
return false; // 重复请求
}
// 记录nonce
this.usedNonces.add(nonceKey);
return true;
}
cleanup() {
// 清理过期的nonce记录
const currentTime = Math.floor(Date.now() / 1000);
const expiredTime = currentTime - 300;
for (const nonceKey of this.usedNonces) {
const timestamp = parseInt(nonceKey.split('_')[1]);
if (timestamp < expiredTime) {
this.usedNonces.delete(nonceKey);
}
}
}
}
🐛 常见问题解决
1. Token过期问题
问题现象:API返回401 Unauthorized错误
解决方案:
javascript
// 自动重试机制
async function makeRequestWithRetry(apiCall, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await apiCall();
} catch (error) {
if (error.response?.status === 401 && i < maxRetries - 1) {
// Token过期,刷新后重试
await tokenManager.refreshAccessToken();
continue;
}
throw error;
}
}
}
2. 网络超时处理
问题现象:请求超时或连接失败
解决方案:
javascript
// 超时和重试配置
const axiosConfig = {
timeout: 30000, // 30秒超时
retry: 3,
retryDelay: 1000,
retryCondition: (error) => {
return error.code === 'ECONNABORTED' ||
(error.response && error.response.status >= 500);
}
};
// 指数退避重试
async function exponentialBackoffRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
3. 支付状态不一致
问题现象:本地订单状态与bKash状态不匹配
解决方案:
php
<?php
// 定时对账任务
function reconcilePayments() {
$pendingPayments = getPendingPayments(); // 获取待确认支付
foreach ($pendingPayments as $payment) {
$paymentId = $payment['payment_id'];
// 查询bKash支付状态
$bkashStatus = queryBkashPaymentStatus($paymentId);
if ($bkashStatus['statusCode'] === '0000') {
$transactionStatus = $bkashStatus['transactionStatus'];
// 更新本地状态
updateLocalPaymentStatus($paymentId, $transactionStatus);
// 记录对账日志
logReconciliation($paymentId, $transactionStatus);
}
// 避免API频率限制
sleep(1);
}
}
// 每30分钟执行一次对账
// 在crontab中添加: */30 * * * * php /path/to/reconcile.php
?>
4. 金额精度问题
问题现象:小数点金额处理错误
解决方案:
javascript
// 金额处理工具类
class AmountUtils {
static formatAmount(amount) {
// 确保金额为两位小数
return parseFloat(amount).toFixed(2);
}
static validateAmount(amount) {
const numAmount = parseFloat(amount);
// 检查是否为有效数字
if (isNaN(numAmount) || numAmount <= 0) {
throw new Error('无效金额');
}
// 检查最小金额限制(bKash最小支付1塔卡)
if (numAmount < 1) {
throw new Error('金额不能少于1塔卡');
}
// 检查最大金额限制
if (numAmount > 25000) {
throw new Error('单笔支付不能超过25000塔卡');
}
return this.formatAmount(numAmount);
}
static convertToPaisa(amount) {
// 将塔卡转换为派沙(1塔卡=100派沙)
return Math.round(parseFloat(amount) * 100);
}
}
🎯 最佳实践建议
1. 错误处理策略
javascript
// 统一错误处理
class BkashErrorHandler {
static handleError(error, context = '') {
const errorInfo = {
timestamp: new Date().toISOString(),
context: context,
error: {
message: error.message,
code: error.code || 'UNKNOWN',
statusCode: error.response?.status,
responseData: error.response?.data
}
};
// 记录错误日志
console.error('bKash API Error:', JSON.stringify(errorInfo, null, 2));
// 根据错误类型返回用户友好的错误信息
switch (error.response?.status) {
case 400:
return '请求参数错误,请检查支付信息';
case 401:
return '认证失败,请稍后重试';
case 403:
return '权限不足,请联系客服';
case 429:
return '请求过于频繁,请稍后重试';
case 500:
return 'bKash服务暂时不可用,请稍后重试';
default:
return '支付处理失败,请稍后重试或联系客服';
}
}
static isRetryableError(error) {
const retryableStatuses = [408, 429, 500, 502, 503, 504];
return retryableStatuses.includes(error.response?.status) ||
error.code === 'ECONNABORTED' ||
error.code === 'ENOTFOUND';
}
}
2. 日志记录规范
javascript
// 支付日志记录
class PaymentLogger {
static logPaymentStart(invoiceNumber, amount, customerId) {
const logData = {
level: 'INFO',
action: 'PAYMENT_START',
timestamp: new Date().toISOString(),
data: {
invoiceNumber,
amount,
customerId,
sessionId: this.generateSessionId()
}
};
this.writeLog(logData);
return logData.data.sessionId;
}
static logPaymentCreated(sessionId, paymentId, bkashUrl) {
const logData = {
level: 'INFO',
action: 'PAYMENT_CREATED',
timestamp: new Date().toISOString(),
sessionId,
data: {
paymentId,
bkashUrl
}
};
this.writeLog(logData);
}
static logPaymentCompleted(sessionId, paymentId, trxId, amount) {
const logData = {
level: 'INFO',
action: 'PAYMENT_COMPLETED',
timestamp: new Date().toISOString(),
sessionId,
data: {
paymentId,
trxId,
amount,
completedAt: new Date().toISOString()
}
};
this.writeLog(logData);
}
static logPaymentError(sessionId, error, context) {
const logData = {
level: 'ERROR',
action: 'PAYMENT_ERROR',
timestamp: new Date().toISOString(),
sessionId,
data: {
error: error.message,
context,
stack: error.stack
}
};
this.writeLog(logData);
}
static writeLog(logData) {
// 写入日志文件或发送到日志服务
console.log(JSON.stringify(logData));
// 生产环境中建议使用专业的日志服务
// 如 Winston, Bunyan 或云端日志服务
}
static generateSessionId() {
return 'PAY_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
}
3. 性能优化建议
javascript
// 连接池管理
const https = require('https');
const agent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 30000
});
// API请求优化
class OptimizedBkashClient {
constructor(config) {
this.config = config;
this.tokenCache = null;
this.tokenExpiry = null;
// 创建优化的axios实例
this.httpClient = axios.create({
httpsAgent: agent,
timeout: 30000,
headers: {
'Connection': 'keep-alive',
'Accept-Encoding': 'gzip, deflate'
}
});
// 添加请求拦截器
this.httpClient.interceptors.request.use(this.requestInterceptor.bind(this));
this.httpClient.interceptors.response.use(
this.responseInterceptor.bind(this),
this.errorInterceptor.bind(this)
);
}
requestInterceptor(config) {
// 添加请求追踪ID
config.headers['X-Request-ID'] = this.generateRequestId();
// 记录请求开始时间
config.metadata = { startTime: Date.now() };
return config;
}
responseInterceptor(response) {
// 计算响应时间
const duration = Date.now() - response.config.metadata.startTime;
console.log(`API请求耗时: ${duration}ms`);
return response;
}
errorInterceptor(error) {
if (error.config) {
const duration = Date.now() - error.config.metadata.startTime;
console.error(`API请求失败,耗时: ${duration}ms`);
}
return Promise.reject(error);
}
generateRequestId() {
return 'req_' + Date.now() + '_' + Math.random().toString(36).substr(2, 8);
}
}
4. 缓存策略
javascript
// Redis缓存实现
const redis = require('redis');
const client = redis.createClient({
host: 'localhost',
port: 6379,
db: 0
});
class BkashCache {
static async cacheToken(token, expiresIn) {
const key = 'bkash:token';
const ttl = expiresIn - 60; // 提前1分钟过期,确保安全
await client.setex(key, ttl, JSON.stringify(token));
}
static async getToken() {
const key = 'bkash:token';
const cached = await client.get(key);
return cached ? JSON.parse(cached) : null;
}
static async cachePaymentStatus(paymentId, status, ttl = 300) {
const key = `bkash:payment:${paymentId}`;
await client.setex(key, ttl, JSON.stringify(status));
}
static async getPaymentStatus(paymentId) {
const key = `bkash:payment:${paymentId}`;
const cached = await client.get(key);
return cached ? JSON.parse(cached) : null;
}
static async invalidatePaymentCache(paymentId) {
const key = `bkash:payment:${paymentId}`;
await client.del(key);
}
}
5. 监控和告警
javascript
// 监控指标收集
class PaymentMetrics {
constructor() {
this.metrics = {
totalPayments: 0,
successfulPayments: 0,
failedPayments: 0,
averageResponseTime: 0,
errorRates: {},
lastReset: Date.now()
};
}
recordPaymentAttempt() {
this.metrics.totalPayments++;
}
recordPaymentSuccess(responseTime) {
this.metrics.successfulPayments++;
this.updateAverageResponseTime(responseTime);
}
recordPaymentFailure(errorCode) {
this.metrics.failedPayments++;
this.metrics.errorRates[errorCode] = (this.metrics.errorRates[errorCode] || 0) + 1;
// 检查是否需要发送告警
this.checkAlerts();
}
updateAverageResponseTime(responseTime) {
const total = this.metrics.successfulPayments;
const current = this.metrics.averageResponseTime;
this.metrics.averageResponseTime = ((current * (total - 1)) + responseTime) / total;
}
checkAlerts() {
const failureRate = this.getFailureRate();
// 失败率超过10%时发送告警
if (failureRate > 0.1) {
this.sendAlert({
type: 'HIGH_FAILURE_RATE',
rate: failureRate,
message: `bKash支付失败率过高: ${(failureRate * 100).toFixed(2)}%`
});
}
// 响应时间超过5秒时发送告警
if (this.metrics.averageResponseTime > 5000) {
this.sendAlert({
type: 'SLOW_RESPONSE',
responseTime: this.metrics.averageResponseTime,
message: `bKash API响应时间过慢: ${this.metrics.averageResponseTime}ms`
});
}
}
getFailureRate() {
const total = this.metrics.totalPayments;
return total > 0 ? this.metrics.failedPayments / total : 0;
}
getSuccessRate() {
const total = this.metrics.totalPayments;
return total > 0 ? this.metrics.successfulPayments / total : 0;
}
sendAlert(alert) {
// 发送告警到监控系统或通知渠道
console.error('🚨 bKash支付告警:', alert);
// 可以集成钉钉、微信、邮件等通知方式
// this.sendDingTalkAlert(alert);
// this.sendEmailAlert(alert);
}
getMetrics() {
return {
...this.metrics,
successRate: this.getSuccessRate(),
failureRate: this.getFailureRate(),
uptime: Date.now() - this.metrics.lastReset
};
}
reset() {
this.metrics = {
totalPayments: 0,
successfulPayments: 0,
failedPayments: 0,
averageResponseTime: 0,
errorRates: {},
lastReset: Date.now()
};
}
}
// 全局监控实例
const paymentMetrics = new PaymentMetrics();
// 定期重置统计(每小时)
setInterval(() => {
console.log('当前支付指标:', paymentMetrics.getMetrics());
paymentMetrics.reset();
}, 3600000);
6. 数据库设计建议
sql
-- 支付记录表
CREATE TABLE bkash_payments (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
invoice_number VARCHAR(50) NOT NULL UNIQUE,
payment_id VARCHAR(50) UNIQUE,
trx_id VARCHAR(20),
customer_id VARCHAR(50),
amount DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'BDT',
status ENUM('PENDING', 'CREATED', 'COMPLETED', 'FAILED', 'CANCELLED') DEFAULT 'PENDING',
bkash_url TEXT,
callback_data JSON,
error_message TEXT,
session_id VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
INDEX idx_invoice_number (invoice_number),
INDEX idx_payment_id (payment_id),
INDEX idx_trx_id (trx_id),
INDEX idx_customer_id (customer_id),
INDEX idx_status (status),
INDEX idx_created_at (created_at)
);
-- 支付日志表
CREATE TABLE bkash_payment_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
payment_id VARCHAR(50),
session_id VARCHAR(50),
action VARCHAR(50) NOT NULL,
level ENUM('INFO', 'WARN', 'ERROR') DEFAULT 'INFO',
message TEXT,
request_data JSON,
response_data JSON,
error_data JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_payment_id (payment_id),
INDEX idx_session_id (session_id),
INDEX idx_action (action),
INDEX idx_level (level),
INDEX idx_created_at (created_at)
);
-- 对账记录表
CREATE TABLE bkash_reconciliation (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
payment_id VARCHAR(50),
local_status VARCHAR(20),
bkash_status VARCHAR(20),
is_matched BOOLEAN DEFAULT FALSE,
discrepancy_reason TEXT,
reconciled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_payment_id (payment_id),
INDEX idx_is_matched (is_matched),
INDEX idx_reconciled_at (reconciled_at)
);
7. 部署和运维建议
Docker部署示例
dockerfile
# Dockerfile
FROM node:16-alpine
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源码
COPY . .
# 创建非root用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# 设置权限
RUN chown -R nextjs:nodejs /app
USER nextjs
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK \
CMD curl -f http://localhost:3000/health || exit 1
# 启动应用
CMD ["npm", "start"]
Docker Compose配置
yaml
version: '3.8'
services:
bkash-payment-api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- BKASH_APP_KEY=${BKASH_APP_KEY}
- BKASH_APP_SECRET=${BKASH_APP_SECRET}
- BKASH_USERNAME=${BKASH_USERNAME}
- BKASH_PASSWORD=${BKASH_PASSWORD}
- REDIS_URL=redis://redis:6379
- DB_HOST=mysql
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME}
depends_on:
- redis
- mysql
restart: unless-stopped
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
restart: unless-stopped
volumes:
redis_data:
mysql_data:
Nginx反向代理配置
nginx
# /etc/nginx/sites-available/bkash-payment
server {
listen 80;
server_name your-domain.com;
# 重定向到HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL配置
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# 安全头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
# API路由
location /api/ {
proxy_pass http://localhost:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# 超时设置
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# 健康检查
location /health {
proxy_pass http://localhost:3000/health;
access_log off;
}
# 限流配置
location /api/payment/ {
limit_req zone=payment burst=10 nodelay;
proxy_pass http://localhost:3000/api/payment/;
}
}
# 限流配置
http {
limit_req_zone $binary_remote_addr zone=payment:10m rate=30r/m;
}
📚 相关资源链接
官方文档
技术社区
测试工具
📞 技术支持
如果您在集成过程中遇到任何问题,可以通过以下方式获得支持:
- Telegram客服: @zfxt01 (推荐,响应最快)
- 邮件支持: support@bangladeshpaymentsystem.com
- 在线客服: 工作时间 9:00-21:00 GMT+8
本指南由孟加拉支付系统专家团队编写,定期更新以确保信息的准确性和时效性。最后更新时间:2024年12月
Share This Story, Choose Your Platform!
最近支付文章
支付标签
API开发
bKash vs Nagad
Nagad集成
商户服务
国际支付
国际汇款
在线支付
在线支付处理
外汇支付
外汇管制
孟加拉央行
孟加拉支付
孟加拉支付系统
孟加拉支付网关
孟加拉支付选择
孟加拉数字支付
孟加拉跨境支付
孟加拉金融科技
市场份额分析
支付SDK
支付处理器
支付技术
支付方式选择
支付网关集成
支付路由
支付集成
政府支付钱包
数字支付
汇率管理
海外汇款
用户体验对比
电商支付
电子支付
移动支付
移动支付对比
移动金融
移动钱包对比
系统集成
网关API
跨境合规
跨境电商支付
金融机构
金融科技
钱包功能对比
钱包市场分析
支付文章分类
官方联系方式
Telegram客服:@zfxt01
7天×24小时在线服务
平均响应时间:30分钟内
节假日无休,随时咨询