法币(泰铢) 代付下单
# 代码示例
- 代码示例仅供参考,具体参数说明请参考请求参数说明
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.Map;
import java.util.TreeMap;
public class TopCashDemo {
//MCH_ID: 商户ID
//请登录商户后台,点击 个人中心 > 个人信息 在基本信息中获取商户ID
private static final String MCH_ID = "S820211021094748000001";
private static final String PLAT_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2JoMfFqLsSJjAiCahEnlP3aRj8yCT+WHzR+VvPBTw9S1i7iYWb+MY09CG/HYuHF4+IxshXDJygmndxKf/esuwPybS8mAd//yubHpmZsmBqg1FffT8VH1APa6ZRWASUp4U01ZrbCCp35QA8FuWrJGMJxGx4xk7KUtV2yujxC8noQIDAQAB"; // 平台公钥
private static final String MCH_PRIVATE_KEY = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAJU8gKFKD0luIYx7X8+JRdCIE0UDBctS6LjXxWLEv/EO7jDBTid6zYP1KmNgpd2DAWWtBFBSQ+gcNwVZZSBHJiSDqVvvJVs2FEbeBvfdv4X93+IYRAXksBasSW5Tpdshbo82pVL4V7wuKCuFLk9UxBHbpQjWAbfyF66RmwIbZD71AgMBAAECgYBjPe7UU2nDDSfmQg0++CyjNjqKRC5QPfxhH6w1uF1kMueXKJWOj42n2RutJpJmsj31nY8m0u4xpsG4HvCu/GGSFhhKZCHLvzp41oY2ubYj9nuFNU//81LycQjulWo2y0UUBY0k2piEt+SwPaiUNbT6nMxNMjlnjRe2okp/3rw+KQJBANG3YlZWoVbCEqzy64bJJLxiPsCA5ErGB0NzRGitq44xkhqGtR8ZZQyVz40pruNa58d73O2xyJSy5+fmZGn4E+sCQQC2LBnguj0CSCKub0mPDcunTTz9V79VXBBZdlB1/YGmRUx2s4sQrJNZS7rL4EqBQ3maIRnG+s+AXCSTfsYrV6CfAkEAxugnVfpelhoGepEAgNuggyivmgfl/2Gpm/jk5l/qOjib+ZrQiQmeBPzGWX4yiSM8eMDrP2sC8r5pJFMp5DRONwJBAJ4n4XuSFJ9jgwCPy3vvzSv9SYLk6E6yM9uHdUlKgoGYzk6Lh6M9QFuY/J49plFdBDiEnj16yCU3WeXXfTJpzB8CQQDMNMR/rIOTE9xGybS3mlQbt22AUnO6XhupWcckEKW4nPGxATwYBQzCY3i/9FTGN0vA+9ZPC2cwHtNxI2kXf3Vp"; // 商户私钥
private static final String cashUrl = "https://tl-openapi.toppay.asia/gateway/cash";
private static final String cashNotify = "http://host:port/notify";
public static void main(String[] args) throws Exception {
cash();
}
private static void cash() throws Exception {
Map<String, String> requestParams = new TreeMap<>();
requestParams.put("merchantCode", MCH_ID);
requestParams.put("orderNum", "186888188666"); // 商户订单号
requestParams.put("method", "Disbursement"); // 收款方式(默认传Disbursement)
requestParams.put("orderType", "0"); // 订单类型(0-法币(泰铢)交易)
requestParams.put("money", "125.12"); // 订单金额,支持两位小数位
requestParams.put("feeType", "0"); // 手续费类型(0-帐内扣除,1-帐外扣除)
requestParams.put("dateTime", "20200101235959");// 时间戳 格式 yyyyMMddHHmmss
requestParams.put("number", "2021071209403321313122"); // 客户银行卡号
requestParams.put("bankCode", "001"); // 银行编码:参考附录I 代付
//name原文参与签名,但是在调用API时需要进行Unicode编码
requestParams.put("name", "จางซาน"); // 客户名称
requestParams.put("mobile", "082122965511"); // 客户手机号,可不传
requestParams.put("email", "[email protected]"); // 客户邮箱,可不传
//description原文参与签名,但是在调用API时需要进行Unicode编码
requestParams.put("description", "คำอธิบาย"); // 描述
requestParams.put("notifyUrl", cashNotify); // 回调地址
List<String> paramNameList = new ArrayList<>();
for (String key : maps.keySet()) {
paramNameList.add(key);
}
Collections.sort(paramNameList);
StringBuilder stringBuilder = new StringBuilder();
for (String key : paramNameList) {
stringBuilder.append(requestParams.get(key)); // 拼接参数
}
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
System.out.println("keyStr:" + keyStr);
String signedStr = TopPayRequestUtil.privateEncrypt(keyStr, TopPayRequestUtil.getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
requestParams.put("sign", signedStr);
//签名完成后,对name和description进行编码 UnicodeUtil示例代码参考 本节第9项 泰文Unicode
requestParams.put("name", UnicodeUtil.toUnicode("จางซาน", true)); // 客户名称
requestParams.put("description", UnicodeUtil.toUnicode("คำอธิบาย", true)); // 描述
String postJson = new Gson().toJson(requestParams);
System.out.println("Post Json Params:" + postJson);
String responseJson = TopPayRequestUtil.doPost(cashUrl, postJson); // 发送 post json请求
System.out.println("Response Msg:" + responseJson);
boolean pass = TopPayRequestUtil.verifySign(new Gson().fromJson(responseJson, JsonObject.class), PLAT_PUBLIC_KEY); // 签名验证
if (pass) {
// ... 签名验证通过,执行正常的业务逻辑
} else {
// ... 签名验证错误
}
}
}
# 请求地址
- 请求方式 : POST
- 请求地址 : https://tl-openapi.toppay.asia/gateway/cash
# 请求参数
注:所有的参数中,不要填写中文!!!
参数 | 类型 | 必填 | 描述 | 示例 |
---|---|---|---|---|
merchantCode | String | Y | 商户ID,在商户平台获取 | S820211021094748000001 |
orderType | string(10) | Y | 订单类型 | 0-法币(泰铢)交易 |
method | string(10) | Y | 收款方式 | 默认传Disbursement |
orderNum | String | Y | 商户订单号 | 10000001 |
money | String | Y | 代付金额 | 150.12(支持两位小数点位) |
feeType | String | Y | 手续费类型 | 0 : 代付金额内扣除(实际到账金额=下单金额-手续费) 1 : 手续费另计(实际到账金额=下单金额) |
bankCode | String | Y | 银行编号 | 001 点击查看支持的银行编码 |
number | String | Y | 客户银行卡号 | 12312431241 |
name | String | Y | 客户名称 | Jack |
mobile | String | N | 用户手机号码 | 1234567890 |
String | N | 用户邮箱 | [email protected] | |
notifyUrl | String | Y | 回调地址 | https://123123.com |
dateTime | String | Y | 时间戳格式:yyyyMMddHHmmss | 20200101235959 |
description | String | Y | 按请求参数返回 | 代付下单 |
sign | String | Y | 签名 | Yg+ePvTFhiRrARcZKBcRG0l8 ... |
# 请求报文示例
{
"merchantCode": "S820211021094748000001",
"orderType": "0",
"method": "Disbursement",
"orderNum": "186888188666",
"money": "100.00",
"feeType": "1",
"bankCode": "001",
"number": "2021071209403321313122",
"name": "test cash name",
"mobile": "082122965511",
"email": "[email protected]",
"notifyUrl": "your notify url",
"dateTime": "20220101235959",
"description": "test cash",
"sign": "Yg+ePvTFhiRrARcZKBcRG0l89rqisPIuZQStYqBIwSMPaqwH77qRXI1J+jElOBpa"
}
# 响应参数
参数 | 类型 | 必填 | 描述 | 示例 |
---|---|---|---|---|
platRespCode | String | Y | 请求业务是否成功 | FAIL:失败\SUCCESS:成功 |
platRespMessage | String | Y | 接口响应信息提示 | 请求成功 |
platOrderNum | String | Y | 平台订单号 | PI1453242857400963072 |
orderNum | String | Y | 商户订单号 | 23645782 |
status | Int | Y | 订单状态 | 查看订单状态描述 |
statusMsg | String | Y | 订单状态信息 | Apply |
money | String | Y | 代付金额 | 100.00 |
fee | String | Y | 手续费金额 | 10.00 |
feeType | String | Y | 手续费类型 | 0:订单内扣除, 1:手续费另计 |
bankCode | String | N | 银行编号 | 001 点击查看支持的银行编码 |
number | String | Y | 客户银行卡号 | 12312431241 |
name | String | Y | 客户名称 | Jack |
description | String | Y | 按请求参数返回 | 代付下单 |
platSign | String | Y | 签名 | PI1453242857400963072 |
# 代付响应报文示例
{
"platRespCode": "SUCCESS",
"platRespMessage": "Request success",
"platOrderNum": "W0620220119174331000001",
"orderNum": "186888188666",
"status": 0,
"statusMsg": "Apply",
"money": "100.00",
"fee": "10.00",
"feeType": "0",
"bankCode": "001",
"number": "2021071209403321313122",
"name": "test cash name",
"description": "test cash",
"platSign": "E5uNF7B9NXyhtlRo2I7/KVHN4Sbz0c3VbwCLpH3vjUpv6Cai+bmJA/Q8dVE2RJRe1+dsbzg=="
}
# 代付异步通知
- 进行验签时,要用 商户后台-收付款配置-API配置中提供的平台公钥进行解密!!!
- 进行验签时,以实际回调的参数为准,不要验签固定的参数
- 订单的最终状态以通知的status为准!!!
- TopPay结果异步通知后,需响应
SUCCESS
字符串,否则TopPay将继续向下游发起5次通知。
# 通知参数
参数 | 描述 | 示例 |
---|---|---|
platOrderNum | 平台订单号 | BK_1563278763273 |
orderNum | 商户订单号 | T1231511321515 |
money | 代付金额 | 150.12(支持小数点) |
feeType | 手续费类型 | 手续费类型 0:订单内扣除, 1:手续费另计 |
fee | 手续费 | 10.00 |
name | 客户名称 | Neo |
number | 客户银行卡号 | 45649849659456 |
bankCode | 到账银行 | 001 点击查看支持的银行编码 |
status | 订单状态 | 查看订单状态描述 |
statusMsg | 订单状态描述 | Payout Success |
description | 订单描述 | 商户上传原样返回 |
platSign | 平台签名 | ja6R8eukQY9jc8... |
# 异步通知报文示例
{
"platOrderNum": "W0620220119174331000001",
"orderNum": "186888188666",
"money": "100.00",
"fee": "10.00",
"feeType": "0",
"name": "test cash name",
"number": "2021071209403321313122",
"bankCode": "001",
"status": "2",
"statusMsg": "SUCCESS",
"description": "test cash",
"platSign": "LGEpz2LjbZ6Iyvn+zLc/+t26AaH0aEhHVD62lSCdo6XIkAg86AUncCvmym62wVoE3r2+dHnv27qi/01UQDcqFK8DYioRCcydYSjB4QRVezG3fcZlhWrACmWGacnXkE7p5zChL7pK5h0HuBhbo1zKt4FunQR6QMmcBVfv7YfB3W0"
}
# 泰文Unicode
法币(泰铢)代付下单,name
和 description
如果使用泰文,则在下单时需要对其进行Unicode编码转换
需要注意!参数签名时,请使用泰文原文参与签名,否则会验签失败!!!
import org.apache.commons.lang3.StringUtils;
/**
* Unicode工具类
*
*/
public class UnicodeUtil {
public static void main(String[] args) {
String str = "ข้อความต้นฉบับ";
System.out.println("原始字符串:" + str);
String unicodeStr = toUnicode(str, true);
System.out.println("转换后的Unicode字符串:" + unicodeStr);
String originalStr = toString(unicodeStr);
System.out.println("转换回原始字符串:" + originalStr);
}
/**
* 将字符串转换为Unicode编码的字符串
*
* @param originalText 待转换的原文
* @param isSkipAscii 是否跳过ASCII字符(只跳过可见字符)
* @return 转换后的unicode
*/
public static String toUnicode(String originalText, boolean isSkipAscii) {
if (StringUtils.isEmpty(originalText)) {
return originalText;
}
final int len = originalText.length();
final StringBuilder unicode = new StringBuilder(originalText.length() * 6);
char c;
for (int i = 0; i < len; i++) {
c = originalText.charAt(i);
if (isSkipAscii && isAsciiPrintable(c)) {
unicode.append(c);
} else {
unicode.append(toUnicodeHex(c));
}
}
return unicode.toString();
}
/**
* unicode 转字符串
*
* @param unicode 待转换原文
* @return 转换后的字符串
*/
public static String toString(String unicode) {
if (StringUtils.isBlank(unicode)) {
return unicode;
}
final int len = unicode.length();
StringBuilder sb = new StringBuilder(len);
int i;
int pos = 0;
while ((i = StringUtils.indexOfIgnoreCase(unicode, "\\u", pos)) != -1) {
sb.append(unicode, pos, i);//写入Unicode符之前的部分
pos = i;
if (i + 5 < len) {
char c;
try {
c = (char) Integer.parseInt(unicode.substring(i + 2, i + 6), 16);
sb.append(c);
pos = i + 6;//跳过整个Unicode符
} catch (NumberFormatException e) {
//非法Unicode符,跳过
sb.append(unicode, pos, i + 2);//写入"\\u"
pos = i + 2;
}
} else {
//非Unicode符,结束
break;
}
}
if (pos < len) {
sb.append(unicode, pos, len);
}
return sb.toString();
}
/**
* 是否为可见ASCII字符,可见字符位于32~126之间
*
* @param ch 待校验字符
* @return 是否为可见ASCII字符
*/
public static boolean isAsciiPrintable(char ch) {
return ch >= 32 && ch < 127;
}
/**
* 转换char为unicode
*
* @param ch 待转换char
* @return 转换后的unicode
*/
public static String toUnicodeHex(char ch) {
String hex = Integer.toHexString(ch);
if (hex.length() < 4) {
hex = "0" + hex;
}
return "\\u" + hex;
}
}