接入说明与开发指南
本文档介绍商户接入孟加拉支付系统的通用规范、请求头标准、RSA 签名机制与密钥生成方法。
一、通用请求规范
| 交互类型 | 请求方法 | 请求头 Content-Type | 报文格式 |
|---|---|---|---|
| API 请求(订单创建、查询、余额等) | POST | application/json; charset=utf-8 | JSON |
| 回调通知(平台推送至商户系统) | POST | application/json; charset=utf-8 | JSON |
二、通用请求头(HTTP Headers)
所有发送至平台的 API 请求以及平台发送的回调通知,均必须包含以下请求头:
| 请求头参数 | 是否必填 | 类型 | 说明 |
|---|---|---|---|
merchantNo | 是 | String | 商户号,由平台统一下发分配 |
sign | 是 | String | 请求 Body 内容的 RSA 签名(Base64 编码) |
三、RSA 签名与验签算法
1. 签名流程
- 根据具体的接口文档构造请求参数对象;
- 将请求参数对象序列化为标准 JSON 字符串(作为请求 Body);
- 使用**商户自身的私钥(Private Key)**对该 JSON 字符串进行
SHA256withRSA签名; - 将签名后的字节数组通过 Base64 编码后放入 HTTP Header 的
sign字段中; - 将商户号放入 HTTP Header 的
merchantNo字段中,发起 HTTP POST 请求。
2. 验签流程
- 商户端请求平台:平台使用商户提供的公钥对
sign与请求 Body 进行验签; - 平台回调商户端:平台推送异步回调时,商户端需使用平台提供的公钥对
sign与回调 Body 进行验签(注:验签时请注意忽略大小写差异)。
四、平台公钥
测试环境平台公钥
NOTE
以下为测试环境的平台公钥,用于验证平台回调的签名(正式环境公钥,在商户后台-账号信息查看):
text
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx370DGx1EXhyRVF242HkEtGAYuGbG51bE8Ne/lMgRJXnCZLvL4jYsb+3X8vRxLhitzp/iU6Dtdo0f5dMWW8i2ofnZPkg0bD2jeXXsK5KvGYh89Y0SrNROspQcMR+e2R7XdXVfjwiPcTwnpAviYPgFkgIBB0ZpssO4BVMeYeh51dc496emQadB4KXaN7KJGld1nimzYVUigA6SQfMV5jHiD4BgVRSWof3f6I2H4hdFpLniQ+rY/zAC3mwMdKP6/UIFp83DIzRSszjsLVdZ/BA5re9Ifur9Rlps/CY0+N28om/e9U5wXxuGuGWtiJ/Y4lMm8DzxzNTVq+SBShWFCCdnHxfn4Y3TwgXYg1s4uIue3XMcSfLa9nwq31UvMq7+YH0JeVduWsdtvyzJ+ZD8oQ30CwPNkPFpT3lCH78Quq269PRFvxtWOFS2t4gS4yEQgwIwN9jzT0WLhLK9HFs9/4+6kQEpHe0BrFoGKyAhtUvejj8elLJnb+3vX+zkQVFbKwWMyJsGKH1dcdpR8k6Q4XBYe9zDo+jJw34LFo5h+oX5ImwDoLc5VxvbwT7lDpBcAXygP96rTIvCh9HGQkAZSgLyI5UrjAmv1nV6PpWc8DooMxyMzFpjIzkKYd+juC6GYIbh1Vnb45lgJns42irOzYoyVGL+dxZ27m0/0lmxSW5t7UCAwEAAQ==五、Java 代码示例
1. 签名与验签工具类 (SignUtil.java)
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class SignUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(SignUtil.class);
/**
* 验签方法
* @param param 请求或回调的 JSON 报文体
* @param sign 请求头中的签名 sign
* @param publicKey 公钥字符串 (Base64)
* @return 验签是否通过
*/
public static boolean checkSign(String param, String sign, String publicKey) {
try {
Signature signature = Signature.getInstance("SHA256withRSA");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
signature.initVerify(keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))));
signature.update(param.getBytes());
return signature.verify(Base64.getDecoder().decode(sign));
} catch (Exception e) {
LOGGER.error("Sign 校验错误: ", e);
}
return false;
}
/**
* 加签方法
* @param param 待签名的 JSON 报文体
* @param privateKey 私钥字符串 (Base64)
* @return Base64 签名串
*/
public static String sign(String param, String privateKey) {
try {
Signature signature = Signature.getInstance("SHA256withRSA");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
signature.initSign(keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))));
signature.update(param.getBytes());
byte[] bytes = signature.sign();
return Base64.getEncoder().encodeToString(bytes);
} catch (Exception e) {
LOGGER.error("Sign 签名错误: ", e);
}
return null;
}
}2. 发起请求示例
java
import com.google.gson.Gson;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import java.util.HashMap;
import java.util.Map;
public class RequestDemo {
public static void main(String[] args) {
String url = "https://api.example.com/payin/create";
String merchantNo = "M1639466186292";
String privateKey = "YOUR_PRIVATE_KEY_BASE64";
Map<String, Object> map = new HashMap<>();
map.put("timestamp", System.currentTimeMillis());
map.put("merchantOrderNo", "ORDER_" + System.currentTimeMillis());
map.put("amount", "200.00");
map.put("payType", "BKASH"); // 支付通道类型:BKASH 或 NAGAD
map.put("notifyUrl", "https://yourdomain.com/payin/notify");
Gson gson = new Gson();
String body = gson.toJson(map);
String sign = SignUtil.sign(body, privateKey);
HttpResponse<String> response = Unirest.post(url)
.header("Content-Type", "application/json; charset=utf-8")
.header("merchantNo", merchantNo)
.header("sign", sign)
.body(body)
.asString();
System.out.println("HTTP Status: " + response.getStatus());
System.out.println("Response Body: " + response.getBody());
}
}3. RSA 密钥对生成示例
商户接入前需生成一对 RSA-4096(或 RSA-2048)密钥,并将生成的公钥提供给平台配置:
java
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class KeyPairGenDemo {
public static void main(String[] args) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PublicKey publicKey = keyPair.getPublic();
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
System.out.println("--- 商户公钥 (提供给平台) ---");
System.out.println(publicKeyBase64);
PrivateKey privateKey = keyPair.getPrivate();
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
System.out.println("--- 商户私钥 (商户本地妥善保管) ---");
System.out.println(privateKeyBase64);
}
}六、商户 RSA 密钥生成示例
6.1 在线生成工具
相关工具链接:
- a. 公私钥生成工具链接(选择 1024 位、2048 位、4096 位 / 格式
PKCS#8):https://www.bejson.com/enc/rsa/#18 - b. 去换行工具链接(用这个链接去除一下换行再上传):http://www.wuqianling.top/software/notepad/compress.html
生成与配置说明:
- 生成密钥对:使用密钥生成工具生成密钥对,长度必须是
1024bit、2048bit或4096bit,格式必须是PKCS#8。 - 去除换行并上传公钥:将生成密钥对中的公钥,去除换行后复制,上传到商户后台。不要包含
-----BEGIN *** KEY-----与-----END *** KEY-----。 - 保管私钥:公钥上传完成后,请妥善保管私钥,切勿外泄。
- 技术支持:如对接过程遇到加解密、签名异常等问题,可以发送请求参数至对接群里,并艾特我们客服或者技术人员。
- 回调验签说明:对于平台回调签名,请使用平台公钥进行解密验证,切勿使用商户公钥(您自行生成的公钥)。
WARNING
回调通知是由平台发送给商户的,因此商户验证回调签名时必须使用平台公钥。商户自身公钥仅用于平台验证商户主动发起的 API 请求。
