Request & Response
There are some basic rules for PTS request and response. Please check these rules before starting your implementation.
- All request and data are using
UTF-8
format. - The payment request is based on
HTTPS
protocol, onlyPOST
form is supported. - All parameters & participate in signature must be sorted in ASCII. Null values still need to be added to signature.
- All requests have specified header. Please follow the setting for the request header.
- Please ask our customer service team for API Host URL
- Please let us know your
Host IP
, so we can add your IP into whitelist
Request Format
Please following guide to build your request:
- Prepare your request data and generate the signature.
- Convert request body into JSON string.
- Encrypt request JSON string by
AES-ECB-PKCS7
with your merchant safecode. - Encode your encrypted string via
base64_encode
and get the final request string. - Content-Type of HTTPS requests should be
text/plain
// Prepare your request data and generate the signature
{
"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…."
}
// Convert request body into JSON string
{"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..."}
// Encrypt JSON string by AES-ECB-PKCS7 with your merchant safecode, and convert it into base64_encode
wbTkX4OdkK8xqvrnqqKalTp/XiC+svRLvgu6UGQ5gDPx9iTRSS3ng8cRkLwfrxnN3Ba4YZAtMtb2PahMj0KNz56ovbuctKsMWMjztpIn2eLCHWNzVHRrU8eJ/aG0OgDztdceON2xBGYEtzpyf1Lc9jycfnd35tANhZgWFlNvCPrTNsbTjrVA3fH1gOKzn35CfHsuyWertBQjp/FqMkDWa7G1gRxXa2L1s...
// Then, now you can send request via HTTPS Post in text/plain
// Or you can send request via HTTPS Post in application/json with data parameter
{
"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)
Response format
The responses and callbacks from PTS will follow this format.
Header | Content |
---|---|
Content-Type | application/json |
Param | Type | Required | Description |
---|---|---|---|
code | string | ✅ | Success return 1000 and else are failure indexes |
message | string | ✅ | Result description |
data | array | ❌ | Provides when code is 1000 |
// Example
{
"code": "1000",
"message": "Accepted",
"data": {
// available while code=1000
}
}