请求与响应
在使用 PTS 的请求和响应时,有一些基本规则。在开始实施之前务必确认这些规则。
- 所有请求和数据都使用
UTF-8
格式。 - 交易请求基于
HTTPS
协议,仅支持POST
表单。 - 所有参数和参与签名的数据必须按 ASCII 排序。空值仍需包含在签名中。
- 所有请求都有指定的标头。请遵循请求标头的设置。
- 请向我们的客户服务团队索要 API 主机 URL。
- 请告诉我们您的
Host IP
,以便我们将您的 IP 添加到白名单中。
请求格式
请按照以下指南构建您的请求:
- 准备请求数据并生成签名。
- 将请求正文转换为 JSON 字符串。
- 使用您的商户安全码通过
AES-ECB-PKCS7
加密请求 JSON 字符串。 - 通过
base64_encode
编码您的加密字符串,获取最终的请求字符串。 - HTTPS 请求的 Content-Type 应为
text/plain
准备您的请求数据并生成签名
{
"order_id": "20210514183849",
"amount": "40000",
"currency": "CNY",
"timestamp": "1612245402",
"callback_url": "http://my.service/callback",
"redirect_url": "https://my.service/redirect",
"channel": "alipay",
"bank_code": "",
"remark": "test",
"user_id": "1",
"sign": "lOp6SoczkquxzYTDufsVTIjjTdKuCGZnGEa7…."
}
// 将请求正文转换为 JSON 字符串
{"order_id":"20210514184046","amount":"40000","currency":"CNY","timestamp":"1612245402","callback_url":"http://my.service/callback","redirect_url":"https://my.service/redirect","channel":"alipay","bank_code":"","remark":"test","user_id":"1","sign":"lOp6SoczkquxzYTDufsVTIjjTdKuCGZnGEa7..."}
// 使用商户安全码通过 AES-ECB-PKCS7 加密 JSON 字符串,并通过 base64_encode 编码您的加密字符串
wbTkX4OdkK8xqvrnqqKalTp/XiC+svRLvgu6UGQ5gDPx9iTRSS3ng8cRkLwfrxnN3Ba4YZAtMtb2PahMj0KNz56ovbuctKsMWMjztpIn2eLCHWNzVHRrU8eJ/aG0OgDztdceON2xBGYEtzpyf1Lc9jycfnd35tANhZgWFlNvCPrTNsbTjrVA3fH1gOKzn35CfHsuyWertBQjp/FqMkDWa7G1gRxXa2L1s...
// 现在,您可以通过 HTTPS POST 发送请求,Content-Type 应为 text/plain
// 或者,您可以通过HTTPS POST 发送请求,Content-Type 应为 application/json,並使用data参数。
{
"data": "wbTkX4OdkK8xqvrnqqKalTp/XiC+svRLvgu6UGQ5gDPx9iTRSS3ng8cRkLwfrxnN3Ba4YZAtMtb2PahMj0KNz56ovbuctKsMWMjztpIn2eLCHWNzVHRrU8eJ/aG0OgDztdceON2xBGYEtzpyf1Lc9jycfnd35tANhZgWFlNvCPrTNsbTjrVA3fH1gOKzn35CfHsuyWertBQjp/FqMkDWa7G1gRxXa2L1s..."
}
请求示例代码
- PHP
- JAVA
- PYTHON
$user_id = '1';
$request_type = 'payment_v2'; // payment_v2, withdraw_v2, payment_query_v2, withdraw_query_v2, balance_v2
$base_url = 'http://service.local.test';
$safe_code = '8b998abba49ae33a194cb9677b13ecbe';
$guid = 'smajhv5r2etr';
$transaction_signature = 'f55c19747fde060a4af40dc6ed187ae3';
$params = [
'user_id' => $user_id,
'order_id' => 'TEST'.$user_id.time(),
'amount' => rand(10, 1000),
'currency' => 'PHP',
'channel' => 'QRPH',
'callback_url' => 'http://payment.local.test/callback',
'redirect_url' => 'http://payment.local.test/redirect',
'remark' => 'test',
'timestamp' => time(),
'email' => '[email protected]',
'phone' => '09175123213',
];
try {
$response = send_request($request_type, $params);
echo json_encode($response);
if ($response['code'] !== '1000') {
throw new Exception($response['message']);
}
} catch (Exception $e) {
echo $e->getMessage();
}
function send_request(string $request_type, array $params): array
{
global $safe_code, $base_url, $routes, $transaction_signature;
$params['sign'] = generate_signature($params, $safe_code, $request_type);
$json_string = json_encode($params);
$encrypt = openssl_encrypt($json_string, 'AES-256-ECB', $safe_code, OPENSSL_RAW_DATA);
$params = base64_encode($encrypt);
$transaction_signature = transaction_signature($params, $transaction_signature);
$header = [
'Content-Type: text/plain',
'X-Transaction-Signature: '.$transaction_signature
];
$response = send_curl_request($base_url.$routes[$request_type], $params, $header);
return json_decode($response, true);
}
function send_curl_request($url, $body, $header): string
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => $header,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0',
]);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.json.JSONObject;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.sql.Timestamp;
public class Demo {
static String safecode = "8b998abba49ae33a194cb9677b13ecbe";
static String requestUrl = "http://service.local.test";
static String userId = "1";
static String requestType = "payment_v2"; // payment_v2, withdraw_v2, payment_query_v2, withdraw_query_v2, balance_v2
static String transcationSerect = "f55c19747fde060a4af40dc6ed187ae3";
static String guid = "smajhv5r2etr";
public static void main(String[] args) throws UnirestException {
Map<String, String> params = new HashMap<>();
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
params.put("user_id", userId);
params.put("order_id", "TEST" + userId + timestamp.getTime());
params.put("amount", String.valueOf(Math.floor(Math.random() * (1000 - 100 + 1) + 100)));
params.put("currency", "PHP");
params.put("channel", "QRPH");
params.put("callback_url", "http://my.service/callback");
params.put("redirect_url", "https://my.service/redirect");
params.put("timestamp", String.valueOf((int) Math.floor(timestamp.getTime() / 1000)));
params.put("remark", "test");
params.put("email", "[email protected]");
try {
JSONObject jsonBody = sendRequest(requestType, params);
int code = jsonBody.getInt("code");
if (code != 1000) {
throw new Exception(jsonBody.getString("message"));
}
} catch (Exception e) {
System.out.println("Error:: " + e.getMessage());
}
}
public static JSONObject sendRequest(String requestType, Map<String, String> params)
throws UnirestException {
JSONObject jsonObject = new JSONObject();
SortedSet<String> parameter = new TreeSet<>(params.keySet());
for (String key : parameter) {
jsonObject.put(key, params.get(key));
}
jsonObject.put("sign", generateSignature(params, requestType));
System.out.println("json:: " + jsonObject.toString());
String aesString = aes256Encode(jsonObject.toString(), safecode);
Unirest.setTimeouts(0, 0);
System.out.println("AES:: " + aesString);
System.out.println("transactionSignature:: " + transactionSignature(aesString, transcationSerect));
var jsonResponse = Unirest.post(requestUrl + getRoute(requestType))
.header("Content-Type", "text/plain")
.header("X-Transaction-Signature", transactionSignature(aesString, transcationSerect))
.body(aesString).asJson();
System.out.println("sendRequest response:: " + jsonResponse.getBody());
return jsonResponse.getBody().getObject();
}
import base64
import json
import hashlib
import random
import time
import calendar
import requests
import hmac
from aes import AESCrypt
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
user_id = '1' # '1'
url = 'http://service.local.test' # 'http://service.local.test'
safecode = '8b998abba49ae33a194cb9677b13ecbe' # '164ddce3d0b13d21fbc4cd02e14d9f98'
request_type = 'payment_v2' # payment_v2, withdraw_v2, payment_query_v2, withdraw_query_v2, balance_v2
transaction_secret = 'f55c19747fde060a4af40dc6ed187ae3'
guid = 'smajhv5r2etr'
def send_request(request_type, params):
# Get Before Sign string
serialize_sign_string = serialize_sign(params, safecode, request_type)
print('serialize sign string:::' + serialize_sign_string)
# Assign sign to params
#
sign = generate_signature(serialize_sign_string)
print('sign base64 encode string:::' + sign)
params['sign'] = sign
json_string = json.dumps(params, indent=4, ensure_ascii=False)
# AES encrypt
aes = AESCrypt(safecode)
aes_string = aes.encrypt(json_string).decode('utf-8')
print('AES string::' + aes_string)
# Request
try:
headers = {'content-type': 'text/plain', 'X-Transaction-Signature': transaction_signature(aes_string, transaction_secret)}
response = requests.post(url=(url + routes[request_type]), data=aes_string, headers=headers)
print('response result::' + response.text)
except Exception as e:
print('Error:', e)
if response.status_code == 200:
return response.text
else:
return None
post_params = {
'user_id': user_id,
'order_id': f'TEST{user_id}{calendar.timegm(time.gmtime())}',
'amount': random.randint(10, 1000),
'currency': 'PHP',
'channel': 'QRPH',
'callback_url': 'http://my.service/callback',
'redirect_url': 'https://my.service/redirect',
'timestamp': calendar.timegm(time.gmtime()),
'email': '[email protected]',
'remark': 'test'
}
send_request('payment_v2', post_params)
响应格式
来自 PTS 的响应和回调将遵循以下格式。
Note
✅ 必填 ⭕ 可選 ❌ 不適用
标头 | 内容 |
---|---|
Content-Type | application/json |
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
code | 字符串 | ✅ | 成功返回 1000,其他为 错误代码 |
message | 字符串 | ✅ | 结果描述 |
data | 对象 | ⭕ | 当 code 为 1000 时提供 |
// Example
{
"code": "1000",
"message": "Accepted",
"data": {
// 仅在 code=1000 时可用
}
}