接入说明与开发指南
本文档介绍商户接入孟加拉支付系统的通用规范、请求头标准、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
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0kr9IpgDckceWl1g8IRAMV6hALHspEb+SzIsrcc2+Q9WSot/c9YbRZzd+yuzkazMv4HAHeW7oLkG1lcxFYxgPM2x0GbhWqy1Yk5iiARS0h3E2mbuDy7neaL9DzZ411Xwq/pcoEAaklJtwwkXwlIcJGVHUFAebX6Yc6BitcfSvjZe7ilyVX0LA2aTxRGoF490gWo3R7TEXK/Lmdh/rC6FldyexdlAoc1DYbCBFZJQXxJReBbvNj+jJi1BsrsrbrKXBHd9M7/vzBwrHGDa09Y4Yw1wVPm+ZRFFs8hC3bVlZWrpOrR9o9S9kSH47YmnHDPve8fK9/ySEHp6JZ0QxJOnd6DQ0uGdAWswWlIZ4Jjg2qJxC6PHmoRYeWjyc9BuekUtDv9Yiyi9SBfGJQJStjC9PksKsMtUCGaoszPMLhMDGCX9mvYKuxK+vHDK3csWu2iRtX9TIulk/ojNsoZkSKV9tnsmiPFKb9vD79k2RiRyKYVlwpLe9Gyq/joN32i6qIyuN7h5iyms+mxya3DiGNNGdrYwaRGK+nwyJtuZJ9LI8GvWjcq5hVDeAgYnUs16N/ujCbWzHpvzDpmZxgJHXxHPk9TAFzrbTUJQ4p6TBpY87NHb2ozJcOFkFzoYEmqMhivM7vn4gqmd/r1z7PNfjNpJK2dv7pjUQ28S9FEZnsnnJecCAwEAAQ==五、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");
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);
}
}