Skip to main content
Version: 1.0.x

Request & Response

There are some basic rules for PTS request and response. Please check these rules before starting your implementation.

  1. All request and data are using UTF-8 format.
  2. The payment request is based on HTTPS protocol, only POST form is supported.
  3. All parameters & participate in signature must be sorted in ASCII. Null values still need to be added to signature.
  4. All requests have specified header. Please follow the setting for the request header.
  5. Please ask our customer service team for API Host URL
  6. Please let us know your Host IP, so we can add your IP into whitelist

Request Format

Please following guide to build your request:

  1. Prepare your request data and generate the signature.
  2. Convert request body into JSON string.
  3. Encrypt request JSON string by AES-ECB-PKCS7 with your merchant safecode.
  4. Encode your encrypted string via base64_encode and get the final request string.
  5. 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..."
}
import base64
import json
import hashlib
import random
import time
import calendar
import requests

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'
url = '' # 'http://service.local.pts.test'
safecode = '' # '164ddce3d0b13d21fbc4cd02e14d9f98'

def send_request(url_path, params, sign_type):
token = ''
# Get Before Sign string
serialize_sign_string = serialize_sign(params, safecode, sign_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', 'Authorization': token}
response = requests.post(url=(url + url_path), data=aes_string, headers=headers)
dict_obj = json.loads(response.text)
print('response result::' + response.text)
if dict_obj.get('code') != 1000:
raise Exception(dict_obj.get('message'))
except Exception as e:
# Get token
token = get_token()
headers = {'content-type': 'text/plain', 'Authorization': token}
response = requests.post(url=(url + url_path), data=aes_string, headers=headers)
dict_obj = json.loads(response.text)
print('response result::' + response.text)

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('/merchant/payment', post_params, 'payment')

# aes.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

class AESCrypt:
def __init__(self, key):
self.key = key.encode('utf-8')
self.block_size = 16 # AES block size is 16 bytes

def encrypt(self, data):
cipher = AES.new(self.key, AES.MODE_ECB)
padded_data = pad(data.encode('utf-8'), self.block_size)
ciphertext = cipher.encrypt(padded_data)
return base64.b64encode(ciphertext)

def decrypt(self, encrypted_data):
encrypted_data = base64.b64decode(encrypted_data)
cipher = AES.new(self.key, AES.MODE_ECB)
decrypted_data = unpad(cipher.decrypt(encrypted_data), self.block_size)
return decrypted_data.decode('utf-8')

Response format

The responses and callbacks from PTS will follow this format.

HeaderContent
Content-Typeapplication/json
ParamTypeRequiredDescription
codestringSuccess return 1000 and else are failure indexes
messagestringResult description
dataarrayProvides when code is 1000
// Example
{
"code": "1000",
"message": "Accepted",
"data": {
// available while code=1000
}
}