配置与签名
配置与签名
本文档介绍商户后台的账号获取、密钥配置及签名流程,帮助您快速完成接入准备。
获取商户后台账号
接入方完成入驻后,PayCools 将通过邮件下发商户后台登录凭据,请根据邮件指引登录管理员账号。
获取 AppID 与密钥
调试 API 接口所需的商户 AppID,需通过商户后台获取。接口签名采用 SHA256WithRSA 算法,商户需自行生成 RSA 密钥对,并将公钥上传至商户后台。
配置入口:开发者 → 应用详情

密钥配置
密钥用途
私钥由商户自行保存,公钥请上传至商户后台。上传时需去除前后的 -----BEGIN XXX KEY----- 和 -----END XXX KEY----- 标识,以及换行和空格。
方式一:在线生成密钥对
通过 PayCools 提供的 在线工具 生成密钥对。该工具基于浏览器内置 Web Crypto API 实现,密钥生成过程完全在本地完成,不会与 PayCools 服务器产生任何交互。
方式二:通过代码生成
商户也可通过代码自行生成密钥对。生成的商户私钥用于对请求报文进行签名,商户公钥需上传至平台用于验签,以防止报文在网络传输中被篡改。同时,请在对接群中获取 PayCools 回调公钥,用于验证平台回传报文的签名。
签名
统一请求参数
所有 API 请求均采用以下统一结构:
| 参数名 | 是否必填 | 类型 | 说明 |
|---|---|---|---|
| appId | 是 | string | 商户 AppID,由 PayCools 分配 |
| sign | 是 | string | 对 param 的 RSA 签名值 |
| param | 是 | string | 业务请求参数 JSON 字符串 |
签名步骤:
appId为 PayCools 分配的固定值,无需额外处理;- 根据具体接口文档构造业务参数,将其序列化为 JSON 字符串,作为
param的值; - 使用商户 RSA 私钥(PKCS#8 格式)以
SHA256WithRSA算法对param进行签名,将签名结果进行 Base64 编码后作为sign的值。
完整请求示例
以下以收银台模式创建支付订单为例,演示从构造参数到发送请求的完整流程。
环境地址:
| 环境 | 地址 |
|---|---|
| 测试环境 | https://globalapi-dev.paycools.com:8902 |
| 生产环境 | https://openapi.paycools.com |
接口: POST /open-api/payment/checkout/generate
Step 1:构造业务参数(param)
根据接口文档,将业务参数序列化为 JSON 字符串:
{
"timestamp": 1715595802,
"mchOrderId": "10e5595801938341100",
"merchantLogo": "https://www.example.com/logo.png",
"currency": "PH",
"settlementCurrency": "PHP",
"countryCode": "PH",
"customerName": "John Doe",
"email": "john@example.com",
"mobile": "09123456789",
"amount": 10000,
"expireSeconds": 3600,
"remark": "order remark",
"notifyUrl": "https://www.example.com/notify",
"redirectUrl": "https://www.example.com/return"
}
注意:
amount的单位为分(cents),10000代表100.00。
将上述 JSON 整体作为字符串,得到 param 的值:
{"timestamp":1715595802,"mchOrderId":"10e5595801938341100","merchantLogo":"https://www.example.com/logo.png","currency":"PH","settlementCurrency":"PHP","countryCode":"PH","customerName":"John Doe","email":"john@example.com","mobile":"09123456789","amount":10000,"expireSeconds":3600,"remark":"order remark","notifyUrl":"https://www.example.com/notify","redirectUrl":"https://www.example.com/return"}
Step 2:使用 RSA 私钥对 param 签名
使用商户私钥以 SHA256WithRSA 算法对上述 param 字符串进行签名,得到 sign 的值:
A5Vd8NcQvU3QT41Yee2jCIK58jDAKZ6kP5gEE4q7Yu92hUCY3k00FKTSlCNU+CcZm0LSrGbEMFMID3p7uvXaqy5khNv3kPndrgp7MIRHUmQnMgRK+g1XG7PzWdnrqlXc3g+L+kqVja+qrFRz+uVS6GLKLR1P4AtgTa9dok6NU7YTWOnG9r/FwIVx/At4czfEpI10pvg2TptVpiANmseGmz4G30hkaYTTNahkcOMQJn6PDFjivHvjNLZNJVOqHQzVUa+kca1yZZMPHtgxR647KjoY2oAjjl0Y45GL6zP9qHD/eVwcPPAPrRZ4K2o05OJnPf67fAcWNVqpnu6ZGQIXhQ==
Step 3:组装最终请求体
将 appId、sign、param 三者组合为完整的请求体:
{
"appId": "733b887a4a784708bb369524db5b6ded",
"sign": "A5Vd8NcQvU3QT41Yee2jCIK58jDAKZ6kP5gEE4q7Yu92hUCY3k00FKTSlCNU+CcZm0LSrGbEMFMID3p7uvXaqy5khNv3kPndrgp7MIRHUmQnMgRK+g1XG7PzWdnrqlXc3g+L+kqVja+qrFRz+uVS6GLKLR1P4AtgTa9dok6NU7YTWOnG9r/FwIVx/At4czfEpI10pvg2TptVpiANmseGmz4G30hkaYTTNahkcOMQJn6PDFjivHvjNLZNJVOqHQzVUa+kca1yZZMPHtgxR647KjoY2oAjjl0Y45GL6zP9qHD/eVwcPPAPrRZ4K2o05OJnPf67fAcWNVqpnu6ZGQIXhQ==",
"param": "{\"timestamp\":1715595802,\"mchOrderId\":\"10e5595801938341100,\"merchantLogo\":\"https://www.example.com/logo.png\",\"currency\":\"PH\",\"settlementCurrency\":\"PHP\",\"countryCode\":\"PH\",\"customerName\":\"John Doe\",\"email\":\"john@example.com\",\"mobile\":\"09123456789\",\"amount\":10000,\"expireSeconds\":3600,\"remark\":\"order remark\",\"notifyUrl\":\"https://www.example.com/notify\",\"redirectUrl\":\"https://www.example.com/return\"}"
}
Step 4:发送 HTTP 请求
curl --location --request POST \
'https://globalapi-dev.paycools.com:8902/open-api/payment/checkout/generate' \
--header 'Content-Type: application/json' \
--data-raw '{
"appId": "733b887a4a784708bb369524db5b6ded",
"sign": "A5Vd8NcQvU3QT41Yee2jCIK58jDAKZ6kP5gEE4q7Yu92hUCY3k00FKTSlCNU+CcZm0LSrGbEMFMID3p7uvXaqy5khNv3kPndrgp7MIRHUmQnMgRK+g1XG7PzWdnrqlXc3g+L+kqVja+qrFRz+uVS6GLKLR1P4AtgTa9dok6NU7YTWOnG9r/FwIVx/At4czfEpI10pvg2TptVpiANmseGmz4G30hkaYTTNahkcOMQJn6PDFjivHvjNLZNJVOqHQzVUa+kca1yZZMPHtgxR647KjoY2oAjjl0Y45GL6zP9qHD/eVwcPPAPrRZ4K2o05OJnPf67fAcWNVqpnu6ZGQIXhQ==",
"param": "{\"timestamp\":1715595802,\"mchOrderId\":\"10e5595801938341100\",\"merchantLogo\":\"https://www.example.com/logo.png\",\"currency\":\"PH\",\"settlementCurrency\":\"PHP\",\"countryCode\":\"PH\",\"customerName\":\"John Doe\",\"email\":\"john@example.com\",\"mobile\":\"09123456789\",\"amount\":10000,\"expireSeconds\":3600,\"remark\":\"order remark\",\"notifyUrl\":\"https://www.example.com/notify\",\"redirectUrl\":\"https://www.example.com/return\"}"
}'
Step 5:平台响应
请求成功后,平台返回如下响应:
{
"code": 10000,
"message": "Success",
"data": {
"checkoutId": "CH1789964654026559488",
"checkoutUrl": "https://cashier.paycools.com/checkout/NzQxPl1wE-cdtloi1h0pTgc23ZcdGdXiGGZ2YvywJ-o=",
"status": "PENDING",
"expiresTime": "2024-05-14 15:27:00"
}
}
商户获取到 checkoutUrl 后,将其返回给前端进行跳转,用户即可进入 PayCools 收银台完成支付。
附录:RSA 代码示例(Java)
生成 RSA 密钥对
import java.security.*;
import java.util.Base64;
public class RsaGenerateKeyPair {
public static void main(String[] args) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PublicKey publicKey = keyPair.getPublic();
byte[] publicKeyBytes = publicKey.getEncoded();
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKeyBytes);
System.out.println(publicKeyBase64);
PrivateKey privateKey = keyPair.getPrivate();
byte[] privateKeyBytes = privateKey.getEncoded();
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKeyBytes);
System.out.println(privateKeyBase64);
}
}
公钥需提交至 PayCools 平台,请勿向任何第三方披露。
私钥由商户自行保管,请勿向任何人透露。
商户使用私钥对请求参数签名后,平台将使用对应公钥进行验签。验签失败的请求将被平台拒绝处理。
RSA 签名
使用商户私钥对 param 字符串进行签名:
import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class RsaSign {
public static final String PRIVATE_KEY = "<your_private_key>";
public static void main(String[] args) throws Exception {
String param = "{\"timestamp\":1715595802,\"mchOrderId\":\"10e5595801938341100\","
+ "\"merchantLogo\":\"https://www.example.com/logo.png\","
+ "\"currency\":\"PH\",\"settlementCurrency\":\"PHP\","
+ "\"countryCode\":\"PH\",\"customerName\":\"John Doe\","
+ "\"email\":\"john@example.com\",\"mobile\":\"09123456789\","
+ "\"amount\":10000,\"expireSeconds\":3600,"
+ "\"remark\":\"order remark\","
+ "\"notifyUrl\":\"https://www.example.com/notify\","
+ "\"redirectUrl\":\"https://www.example.com/return\"}";
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(PRIVATE_KEY))));
signature.update(param.getBytes());
String sign = Base64.getEncoder().encodeToString(signature.sign());
System.out.println(sign);
}
}
RSA 签名验证
使用平台公钥对回传数据进行验签:
import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RsaVerifySign {
public static final String PUBLIC_KEY = "<platform_public_key>";
public static void main(String[] args) throws Exception {
String param = "<callback_param>";
String sign = "<callback_sign>";
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(
Base64.getDecoder().decode(PUBLIC_KEY))));
signature.update(param.getBytes());
boolean result = signature.verify(Base64.getDecoder().decode(sign));
System.out.println(result);
}
}