跳到主要内容

配置与签名

配置指南

配置与签名

本文档介绍商户后台的账号获取、密钥配置及签名流程,帮助您快速完成接入准备。

1

获取商户后台账号

接入方完成入驻后,PayCools 将通过邮件下发商户后台登录凭据,请根据邮件指引登录管理员账号。

⚠️
注意:首次登录需强制修改密码,请妥善保管账号信息,避免泄露。
2

获取 AppID 与密钥

调试 API 接口所需的商户 AppID,需通过商户后台获取。接口签名采用 SHA256WithRSA 算法,商户需自行生成 RSA 密钥对,并将公钥上传至商户后台。

⚠️
注意:请妥善保管私钥,若发生泄露请立即更新密钥对。

配置入口:开发者 → 应用详情

应用配置
3

密钥配置

密钥用途

私钥由商户自行保存,公钥请上传至商户后台。上传时需去除前后的 -----BEGIN XXX KEY----------END XXX KEY----- 标识,以及换行和空格。

方式一:在线生成密钥对

通过 PayCools 提供的 在线工具 生成密钥对。该工具基于浏览器内置 Web Crypto API 实现,密钥生成过程完全在本地完成,不会与 PayCools 服务器产生任何交互。

方式二:通过代码生成

商户也可通过代码自行生成密钥对。生成的商户私钥用于对请求报文进行签名,商户公钥需上传至平台用于验签,以防止报文在网络传输中被篡改。同时,请在对接群中获取 PayCools 回调公钥,用于验证平台回传报文的签名。

4

签名

统一请求参数

所有 API 请求均采用以下统一结构:

参数名是否必填类型说明
appIdstring商户 AppID,由 PayCools 分配
signstringparam 的 RSA 签名值
paramstring业务请求参数 JSON 字符串

签名步骤:

  1. appId 为 PayCools 分配的固定值,无需额外处理;
  2. 根据具体接口文档构造业务参数,将其序列化为 JSON 字符串,作为 param 的值;
  3. 使用商户 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:组装最终请求体

appIdsignparam 三者组合为完整的请求体:

{
"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);
}
}