bKash回调接口配置实战指南
准备工作
- 确保已拥有bKash商户账户并开通API权限
- 获取以下关键信息:
- Merchant ID
- Merchant Wallet Number (用户名)
- API Key
- API Secret
服务器端配置步骤
1. 创建接收回调的URL端点
// PHP示例代码(可根据实际语言调整)
<?php
header('Content-Type: application/json');
// bKash发送的数据是JSON格式的POST请求体
$json = file_get_contents('php://input');
$data = json_decode($json, true);
// TODO: 验证签名和业务逻辑处理...
http_response_code(200);
echo json_encode(['status' => 'success']);
?>
2. SSL证书配置(必须HTTPS)
- bKash只支持HTTPS回调,确保你的服务器有有效的SSL证书
3. IP白名单设置(可选但推荐)
将bKash官方IP加入防火墙白名单:
103.102.43.0/24
103.76.52.0/22
45\.249\.212\.[0-9]{1,3}
bKash商家后台配置步骤
- 登录 bKash商户门户
- 导航至 "API Settings" > "Callback URL"
3.填写字段:成功支付回调URL: https://yourdomain.com/bkash/success_callback
失败支付回调URL: https://yourdomain.com/bkash/fail_callback
退款通知URL: https://yourdomain.com/bkash/refund_callback
定期付款通知URL(如适用): https://yourdomain.com/bkash/recurring_callback
4.保存并测试
5.等待审核(通常需要24小时)
Java Spring Boot示例实现
@RestController @RequestMapping("/api/payment")
public class BkashCallbackController {
private static final Logger logger = LoggerFactory.getLogger(BkathCallbackController.class);
@PostMapping("/callback")
public ResponseEntity<Map<String,String>> handleCallback(
@RequestBody Map<String,Object> payload,
HttpServletRequest request) {
// Step1:验证IP来源是否来自bkath官方IP范围
// Step2:验证签名(Signature或X-BKA-SIGNATURE头)
// Step3:处理业务逻辑(订单状态更新等)
logger.info("Received bkath callback:"+payload.toString());
return ResponseEntity.ok(Map.of("status","success"));
}
}
Python Flask示例实现
from flask import Flask,request,jsonify
app=Flask(__name__)
@app.route('/bkath/callbacks',methods=['POST'])
def payment_notification():
data=request.get_json()
print(f"Received callback:{data}")
#TODO添加验签和业务处理
return jsonify({"status":"received"}),200
if __name__=='__main__':
app.run(ssl_context='adhoc') #生产环境使用真实证书替代adhoc模式 ```
常见问题排查(Q&A)
Q:为什么我的回调没有被触发?
A:-检查网络可达性(bKa sh能否访问你的服务)-确认没有防火墙拦截-确认使用了HTTPS而非HTTP
Q:如何处理重复通知?
A:-通过paymentID去重记录每个交易的状态变更历史
Q:如何测试沙箱环境的回调解调?
A:-使用[开发者沙盒](https:/developer.bka.sh/)模拟各种场景
bKash回调接口配置进阶指南(续)
4. 签名验证实现细节
bKash使用SHA256算法生成签名,以下是Java验证示例:
public boolean verifySignature(String payload, String receivedSignature, String secretKey) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] signatureBytes = sha256_HMAC.doFinal(payload.getBytes());
String computedSignature = Base64.getEncoder().encodeToString(signatureBytes);
return computedSignature.equals(receivedSignature);
} catch (Exception e) {
logger.error("签名验证失败", e);
return false;
}
}
5. 回调数据解析与处理
典型成功支付回调数据结构:
{
"paymentID": "TR0011X8",
"createTime": "2023-05-20T12:00:00 GMT+6",
"updateTime": "2023-05-20T12:02:30 GMT+6",
"trxID": "7X9A2B3C4D",
transactionStatus":"Completed",
amount":"500.50",
currency":"BDT" ,
intent:"sale" ,
merchantInvoiceNumber:"INV-10001"
}
建议数据库存储字段至少包含:
payment_id | trx_id | status | amount | currency | invoice_no
callback_time | processed_flag | verification_count
Node.js完整示例实现
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Middleware验证bkath签名
function verifyBkashSig(req, res, next){
const bkashSig=req.headers['x-bkash-signature'];
const payload=JSON.stringify(req.body);
const hmac=crypto.createHmac('sha256', process.env.BKASH_SECRET_KEY)
.update(payload)
.digest('base64');
if(hmac===bkashSig){
next(); //验签通过继续处理流程
}else{
res.status(403).send({error:"Invalid Signature"});
}
}
app.post('/webhook/bkath',verifyBkashSig,(req,res)=>{
console.log("Valid callback received:",req.body);
//业务逻辑示例:更新订单状态为已支付
res.status(200).json({status:"processed"});
});
app.listen(4430,()=>console.log("Callback server running"));
生产环境关键注意事项
-
幂等性设计:
- bKa sh可能重发相同通知(网络超时等情况)
trxID应作为唯一键避免重复处理
-
异步处理机制:
# Python Celery示例(Django/Flask通用)
@app.task(bind=True,max_retries=3)
def process_bkath_callback(self,callback_data):
try:
order=Order.objects.get(invoice_no=callback_data['merchantInvoiceNumber'])
if not order.is_paid:
order.mark_as_paid()
except Exception as exc:
self.retry(exc=exc) #自动重试机制
@shared_task(queue='high_priority')
def log_failed_callback(raw_data):... #专门记录失败的回调信息 ```
3.监控告警系统集成:建议添加以下监控点:
监控项|阈值|响应措施|
---|---|---
未处理的回调积压数|>10条|立即人工检查|
平均响应时间|>800ms|扩容服务器|
5xx错误率|>0.1%|触发告警|
4.合规性要求:根据孟加拉国央行规定必须保留原始通知数据至少5年
沙箱测试全流程
1)获取测试凭证:[Sandbox Credentials Generator](https://developer.bka.sh/test-creds)
2)使用Postman模拟流程:
步骤①:获取授权Token
POST /tokenized/checkout/token/grant HTTP/1.1
Headers:{username:,password:,} Body:{app_key:,app_secret:}
步骤②:发起模拟交易请求
步骤③:在商家后台配置`ngrok`临时HTTPS地址进行接收测试
推荐测试用例场景包括但不限于以下情况:
Test Case ID Description Expected Result
TC-BKH-001 正常支付完成 收到success_callback且验签通过
TC-BKH-002 部分退款操作 refund_callback触发且金额匹配
TC-BKH-003 恶意伪造请求 返回403并记录安全日志
需要更详细的哪个部分的实现?我可以提供特定语言或框架的深度代码示范。
bKash回调接口深度优化与安全实践
6. 高级安全防护方案
6.1 多层防御机制
# Python Django中间件示例
class BkashSecurityMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 第一层:IP白名单验证
client_ip = request.META.get('REMOTE_ADDR')
if not self._is_valid_ip(client_ip):
return HttpResponseForbidden()
# 第二层:请求频率限制(10次/分钟)
rate_limit_key = f"bkash_ratelimit_{client_ip}"
count = cache.get(rate_limit_key, default=0)
if count > 10:
return HttpResponseTooManyRequests()
response = self.get_response(request)
第三层:响应数据脱敏处理(如需返回敏感信息)
if isinstance(response.data,dict):
response.data=self._sanitize_data(response.data)
return response
def _is_valid_ip(self, ip):
allowed_subnets=['103.102.43.0/24','45.249.212.0/23']
for subnet in allowed_subnets:
if ipaddress.ip_address(ip) in ipaddress.ip_network(subnet):
return True
return False
sanitize_fields=['accountNo','nid']
def _sanitize_data(self,data:dict)->dict:
for field in self.sanitize_fields:
data[field]=data[field][-4:].rjust(len(data[field]),'*')
return data
6.2 JWT增强验证(适用于企业版API)
// Java JWT验签增强示例 (Spring Security)
public class BkashJwtFilter extends OncePerRequestFilter {
@Override protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) {
String authHeader=req.getHeader("Authorization");
if(authHeader==null||!authHeader.startsWith("Bearer ")){
sendError(res,"Missing JWT token");return;
}
try{
DecodedJWT jwt=JWT.decode(authHeader.substring(7));
//额外校验发行者是否为bKash官方
Algorithm algorithm=Algorithm.HMAC256(config.getBkashApiSecret());
algorithm verify(jwt);
检查自定义声明是否符合预期,例如:
assert jwt claim merchantId equals config merchantId;
}catch(JWTVerificationException e){
sendError(res,"Invalid token:"+e.getMessage());return;
}
chain.doFilter(req res);
}
}
7. 高可用架构设计
7.1 集群部署方案
[Load Balancer]
/ | \
/ | \
[Nginx Pod]——[Redis Cluster]——[App Pods]——[DB Replica Set]
↑ ↓
[Rate Limiting] [Circuit Breaker]
关键配置项:
| 组件 | 配置要点 |
|---|---|
| Nginx | limit_req_zone $binary_remote_addr zone=bkath_callback:10m rate=100r/m; |
| Redis | 设置持久化AOF模式并启用TLS传输加密 |
| 应用节点 | 实现优雅停机机制确保正在处理的回调不中断 |
7.2 灾难恢复流程
1)当主数据中心不可用时自动切换DNS至备用区域
2)使用WAL日志进行数据重建的步骤:
bash pg wal replay --recovery-target-time="2023-06-01T12:00:00Z"
3)事后验证清单:
✓比较切换前后最后一个成功处理的trxID是否连续
✓抽样检查金额一致性
✓确认监控系统无异常告警
8. 合规审计准备
需要准备的文档材料包括:
| 文档类型 内容要求 更新频率 | ||
|---|---|---|
| PCI DSS SAQ AEP证明 第三方支付处理器合规证明 年审 | ||
| 交易流水存档 包含原始请求/响应报文、时间戳、IP等 永久保存 | ||
| 渗透测试报告 由认证机构出具的Web应用安全扫描结果每季度 |
审计重点关注的5个领域:
①签名算法实现是否正确(必须使用HMAC-SHA256而非MD5等弱算法)
②敏感信息是否在日志中正确脱敏
③密钥轮换策略是否符合规范(建议每90天更换API Secret)
④错误率监控是否覆盖所有5xx状态码
⑤员工访问权限是否遵循最小特权原则
Q&A补充解答
Q:如何处理时区差异问题?
A: bKa sh所有时间字段均使用孟加拉标准时区(GMT+6),推荐解决方案:
数据库存储统一UTC时间并在应用层转换:
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
bkath_time VARCHAR(25) --原始带时区的时间字符串如"2023-06-20T15:30:45 GMT+6"
); ```
前端显示根据用户位置动态转换的JavaScript代码示例:
```javascript function formatBkathTime(bdTimeStr){
const options={timeZone:'Asia/Dhaka',year:'numeric',month:'short'...};
new Date(bdTimeStr).toLocaleString('en-US',options);} ```
是否需要针对您的具体技术栈提供更多实现细节?例如.NET Core或Go语言的特定实现方案。