Introduction
Welcome to the official QF Pay open API documentation. To get started, please review the Developer Instructions below.
There are language bindings available in Python, Java, Node.js and PHP! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right corner.
If you would like to quickly test the payment function in Postman we provide a collection that includes a pre-request script to generate the signature, download the file from here.
Developer Instructions
In order to use the QF Pay open API, you must have active API credentials, including an app_code and client_key. In case of technical issues please contact technical.support@qfpay.global.
There are seperate environments available for application testing and development as well as production.
Please note that transactions conducted in the sandbox environment will not have settlement. Therefore, make sure to test with small amounts and process refunds using the API refund endpoint or Merchant APP on the same day as the original transaction day.
Each merchant will be provided with a set of app code and key with or without mchid
. Merchants with multiple branches will usually be supplied with app code, key and mchid
. The hashed mchid
is used to identify shops and outlets. Otherwise, only app code and key will be given.
Encoding Description
All return parameters from API are in UTF-8
code unless otherwise noted.
Environments
API Environments
The table below depicts base URLs for each country/ region. There is a general sandbox available to everybody, and country/ region specifiy test environments.
Environment Name | Test URL | Prod. URL |
---|---|---|
Sandbox (Only for credit card simulations) | https://openapi-int.qfapi.com | |
Live Test | https://openapi-test.qfpay.com | |
China Mainland | https://openapi-test.qfpay.com | https://openapi.qfpay.com |
Hong Kong | https://test-openapi-hk.qfapi.com | https://openapi-hk.qfapi.com |
Japan | https://openapi-jp.qfapi.com | |
Thailand | https://test-openapi-th.qfapi.com | https://openapi-th.qfapi.com |
Dubai | https://test-openapi-db.qfapi.com | https://openapi-db.qfapi.com |
Singapore, Malaysia, Canada, Philippines | https://test-openapi-sg.qfapi.com | https://openapi-sg.qfapi.com |
Indonesia | https://test-openapi-id.qfapi.com | https://openapi-id.qfapi.com |
Europe | https://test-openapi-eur.qfapi.com | https://openapi-eur.qfapi.com |
Signature Generation
For code instructions select Python, Java, Node.js or PHP with the tabs above.
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' # In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '800101' # Alipay CPM = 800108 , MPM = 800101
auth_code='283854702356157409' #CPM only
out_trade_no = random_string
txdtm = current_time
goods_name = 'test1'
auth_code = '280438849930815813'
key = client_key
mchid = 'ZaMVg*****' # ID is provided during merchant onboarding
#data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'udid': udid, 'auth_code': auth_code, 'mchid': mchid}
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(make_req_sign(data, key))
/*
This class is the utility for QFPay Payment API.
Note:This is just an example.
getMd5Value:
After do the string manipulation, like:abc=value&bad=value&bcd=valueKey
This method generates MD5 signature using hexadecimal format.
getDataString:
This method pass in with the map, and generate the string like:abc=value&bad=value&bcd=value.
*/
public class QFPayUtils {
public static String getMd5Value(String input) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(input.getBytes( "UTF-8" ));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; i++) {
sb.append( String.format( "%02x", array[i]));
}
return sb.toString().toUpperCase();
} catch ( NoSuchAlgorithmException | UnsupportedEncodingException e) {
return null;
}
}
public static <T> String getDataString(Map resultMap) {
Map<String, String> map = new TreeMap<String, String>(
new Comparator<String>() {
public int compare(String obj1, String obj2) {
return obj1.compareTo(obj2);
}
});
Iterator<Map.Entry<String, String>> it = resultMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
map.put(entry.getKey(), entry.getValue());
}
StringBuilder sb = new StringBuilder();
it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
sb.append(entry.getKey()+"="+entry.getValue()+"&");
}
return sb.deleteCharAt(sb.length() - 1).toString();
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37**********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '800101', // Alipay CPM = 800108 , MPM = 800101
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '800101';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get the current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //Sort parameters in ascending order from A to Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"B3B251B202801388BE4AC8E5537B81B1"
}
Step 1: Sort all parameters in ascending order according to parameter names
Parameter list: abc=value1 bcd=value2 bad=value3 Sort result: abc=value1 bad=value3 bcd=value2
Step 2: Connect all parameters with ‘&’,and get the string to be signed
abc=value1&bad=value3&bcd=value2
Step 3: Combine the string with client_key
from QFPay.
abc=value1&bad=value3&bcd=value2Key
Step 4: Sign the string from step 3 with MD5 or SHA256. We recommend to use SHA256.
MD5(abc=value1&bad=value3&bcd=value2Key) HASH(“SHA256”, abc=value1&bad=value3&bcd=value2Key)
Step 5: Request API with the signature
Save the signature in the http header field X-QF-SIGN
unless otherwise specified in this document.
Request Description
Field | Description |
---|---|
Character | UTF-8 |
Method | POST/ GET (Depends on individual API function) |
Content-type | application/x-www-form-urlencoded |
Required Parameter Settings in HTTP Header to Request the API
Field | Mandatory | Description |
---|---|---|
X-QF-APPCODE |
Yes | App code assigned to the merchant |
X-QF-SIGN |
Yes | Signature generated according to the signature formulation method described above |
X-QF-SIGNTYPE |
No | Signature algorithm used to generate the signature. If SHA256 is used, the developer must pass the value as SHA256 . The default value is MD5 in case this field is not passed to the API. |
Payments
Payment Codes
PayType Table
Code | Description |
---|---|
800008 | Consumer Present QR Code Mode (CPM) for WeChat, Alipay, UNIONPAY Quick Pass |
800101 | Alipay Merchant Presented QR Code Payment in store (MPM) (Overseas Merchants) |
800108 | Alipay Consumer Presented QR Code Payment (CPM) (Overseas & HK Merchants) |
801101 | Alipay Online WEB (in browser Chrome etc.) Payment (Overseas Merchants) ** |
801107 | Alipay Online WAP (in mobile browser Chrome etc.) Payment (Overseas Merchants) |
801110 | Alipay in-APP Payments (Overseas Merchants) |
800107 | Alipay Service Window H5 Payment (in Alipay APP H5 payments) |
801501 | Alipay Merchant Presented QR Code (MPM) Payment (HK Merchants) |
801510 | Alipay In-App Payment (HK Merchants) |
801512 | Alipay Online WAP Payment (HK Merchants) |
801514 | Alipay Online WEB Payment (HK Merchants) |
800201 | WeChat Merchant Presented QR Code Payment (MPM) (Overseas & HK Merchants) |
800208 | WeChat Consumer Presented QR Code Payment (CPM) (Overseas & HK Merchants) |
800207 | WeChat JSAPI Payment - WeChat Official Account Payment (in Wechat App)(Overseas & HK Merchants) |
800212 | WeChat H5 Payment (In mobile browser) |
800210 | WeChat in-APP Payment (Overseas & HK Merchants) |
800213 | WeChat Mini-Program Payment (Overseas & HK Merchants) |
801008 | WeChat Pay HK Consumer Presented QR Code Payment (CPM) (Direct Settlement, HK Merchants) |
801010 | WeChat Pay HK In-App Payment (Direct Settlement, HK Merchants) |
805801 | PayMe Merchant Presented QR Code Payment in store (MPM) (HK Merchants) |
805808 | PayMe Consumer Presented QR Code Payment (CPM) (HK Merchants) |
805814 | PayMe Online WEB (in browser Chrome etc.) Payment (HK Merchants) |
805812 | PayMe Online WAP (in mobile browser Chrome etc.) Payment (HK Merchants) |
800701 | UNIONPAY Quick Pass Merchant Presented QR Code Payment (MPM) |
800708 | UNIONPAY Quick Pass Consumer Presented QR Code Payment (CPM) |
800712 | UNIONPAY WAP Payment (HK Merchants) |
800714 | UNIONPAY PC-Web Payment (HK Merchants) |
800710 | UNIONPAY In-App Payment (HK Merchants) |
801208 | LINEPAY dynamic QRC Payment - Consumer Present Mode (CPM) (TH Merchants) |
801301 | LINEPAY Online Payment (TH Merchants) |
801408 | AIRPAY dynamic QRC Payment - Consumer Present Mode (CPM) (TH Merchants) |
801701 | NETSPAY Merchant Presented QR Code Payment (MPM) |
801801 | Alipay Pre-Authorization dynamic QRC Payment - Consumer Present Mode (CPM) |
801808 | Alipay Pre-Authorization dynamic QRC Payment - Merchant Present Mode (MPM) |
801810 | Alipay Pre-Authorization in-APP Payment |
801814 | Alipay Pre-Authorization Online Payment |
801908 | Origami Consumer Presented QR Code Payment (CPM) |
802001 | FPS Merchant Presented QR Code Payment (MPM) (HK Merchants)*** |
802201 | AIRPAY Online Payment (TH Merchants) |
802301 | PayNow Merchant Presented QR Code Payment (MPM) (SG Merchants)*** |
802901 | PromptPay dynamic QRC Payment - Merchant Present Mode (MPM) (TH Merchants)*** |
803001 | eWallet dynamic QRC Payment - Merchant Present Mode (MPM) |
803008 | eWallet dynamic QRC Payment - Consumer Present Mode (CPM) |
803101 | VIA dynamic QRC Payment - Merchant Present Mode (MPM) (JP and HK Merchants) |
803108 | VIA dynamic QRC Payment - Consumer Present Mode (CPM) (JP and HK Merchants) |
803208 | Touch 'n Go (TNG) dynamic QRC Payment - Consumer Present Mode (CPM) (MY Merchants) |
803214 | Touch 'n Go (TNG) Online Payment (MY Merchants)** |
803301 | Razer dynamic QRC Payment - Merchant Present Mode (MPM) (MY Merchants) |
803308 | Razer dynamic QRC Payment - Consumer Present Mode (CPM) (MY Merchants) |
803314 | Razer Online Payment ** (MY Merchants) |
803701 | Octopus dynamic QRC Payment - Merchant Present Mode (MPM) (HK Merchants) |
803801 | Dash dynamic QRC Payment - Merchant Present Mode (MPM) (SG Merchants) |
803808 | Dash dynamic QRC Payment - Consumer Present Mode (CPM) (SG Merchants) |
804001 | Boost dynamic QRC Payment - Merchant Present Mode (MPM) (MY Merchants) |
804008 | Boost dynamic QRC Payment - Consumer Present Mode (CPM) (MY Merchants) |
804014 | Boost Online Payment (MY Merchants)** |
804101 | Maybank dynamic QRC Payment - Merchant Present Mode (MPM) (MY Merchants) |
804108 | Maybank dynamic QRC Payment - Consumer Present Mode (CPM) (MY Merchants) |
804114 | Maybank Online Payment (MY Merchants)** |
804201 | GrabPay dynamic QRC Payment - Merchant Present Mode (MPM) (MY and SG Merchants) |
804208 | GrabPay dynamic QRC Payment - Consumer Present Mode (CPM) (MY Merchants) |
804214 | GrabPay Online Payment (MY Merchant)** (SG Merchants)* |
805208 | TrueMoney dynamic QRC Payment - Consumer Present Mode (CPM) (TH Merchants) |
805401 | ThaiQR dynamic QRC Payment - Merchant Present Mode (MPM) (SG and MY Merchants)*** |
805508 | Credit Card: first_data Quick Payment Mode (HK Merchant) |
805514 | Credit Card: first_data Security Verification Payment Mode (HK Merchants) |
802801 | Visa / Mastercard Online Payments |
805601 | GoPay dynamic QRC Payment - Merchant Present Mode (MPM)*** |
805612 | GoPay WAP Payment*** |
Currencies
The below listed currencies are currently available in our payment network. Please consult technical.support@qfpay.global to verify that your API credentials and selected pay_type
support your desired currency.
Code | Description |
---|---|
AED | Arab Emirates Dirham |
CNY | Chinese Yuan |
EUR | Euro |
HKD | Hong Kong Dollar |
IDR | Indonesian Rupiah |
JPY | Japanese Yen |
MMK | Myanmar Kyat |
MYR | Malaysian Ringgit |
SGD | Singapore Dollar |
THB | Thai Baht |
USD | United States Dollar |
CAD | Canadian Dollar |
AUD | Australian Dollar |
API Endpoint for Payments
Request Header:
{
Content-Type: application/x-www-form-urlencoded;
X-QF-APPCODE: D5589D2A1F2E42A9A60C37**********
X-QF-SIGN: 6FB43AC29175B4602FF95F8332028F19
}
Request Body:
{
mchid=ZaMVg*****&out_trade_no=01234567890123&pay_type=800101&txamt=10&txcurrcd=EUR&txdtm=2019-12-25 14:21:28
}
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' #In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '800101' # Alipay CPM = 800108 , MPM = 800101
auth_code='283854702356157409' #CPM only
out_trade_no = '01234567890123'
txdtm = current_time
goods_name = 'test1'
auth_code = '280438849930815813'
mchid = 'ZaMVg*****'
notify_url = 'https://xxx.com/notify/success'
key = client_key
#data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'udid': udid, 'auth_code': auth_code, 'mchid': mchid, 'notify_url': notify_url}
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="800101";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="EUR";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '800101', // Alipay CPM = 800108 , MPM = 800101
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '800101';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //Ascending dictionary sorting A-Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"txdtm": "2019-12-25 14:21:28",
"qrcode": "https://qr.alipay.com/bax01781r3pu4fjaqazt4091",
"pay_type": "800101",
"resperr": "success",
"out_trade_no": "01234567890123",
"syssn": "20191225000200020060996533",
"sysdtm": "2019-12-25 14:22:37",
"paydtm": "2019-12-25 14:22:37",
"txcurrcd": "EUR",
"respmsg": "",
"cardcd": "",
"udid": "qiantai2",
"txamt": "10",
"respcd": "0000",
"chnlsn": ""
}
If you would like to quickly test the payment function in Postman we provide a collection that includes a pre-request script to generate the signature, download the file from here: Payment Request in Postman
HTTP Request
POST ../trade/v1/payment
Listed below are the most common parameters for the payment endpoint. Please refer to the payment scenario applicable to you for additional parameters.
Public Payment Request Parameters
Parameter name | Parameter code | Mandatory | Type | Description |
---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
Payment type | pay_type |
Yes | String(6) | Please refer to the section Payment Codes for a complete list of payment types |
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
Authorization Code | auth_code |
Yes (CPM only) |
String(128) | Specifies the authorization code for scanning a barcode/QR Code. The auth_code returned is unique in each authorization. Each auth_code can be used only once and will automatically expire in one day. For testing CPM with Alipay and WeChat Pay the auth_code can be extracted with any QRC reader or manually found in the consumer wallet below the barcode. |
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. Available for: 800201 - WeChat scan code 800101 - Alipay scan code 801512 - Alipay Hong Kong WAP payment 801501 - Alipay Hong Kong scan code 801107 - Alipay overseas WAP payment 801101 - Alipay overseas scan code 801010 - WeChat Hong Kong APP 801510 - Alipay Hong Kong APP |
Product name identification | goods_name |
No | String(64) | Goods Name / Marking: Cannot exceed 20 alphanumeric or contain special characters. Cannot be empty for app payment. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. |
QF Pay merchant number | mchid |
No | String(16) | May or may not be given to merchant. If MCHID is given, it is mandatory to provide the MCHID .On the contrary, if MCHID is not provided, merchants shall not pass in the MCHID parameter in the API request. |
Time zone | txzone |
No | String(5) | Transaction Time zone: Record of the transaction in local time, default time zone is Beijing time UTC+8 (+0800). |
Device ID | udid |
No | String(40) | Unique transaction device ID. Is displayed on the merchant portal. |
Asynchronous Notification URL | notify_url |
No | String(256) | Asynchronous notification URL |
Public Payment Response Parameters
Parameter name | Parameter code | Type | Description |
---|---|---|---|
Payment type | pay_type |
String(6) | Please refer to the section Payment Codes for a complete list of payment types |
System transaction time | sysdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Request transaction time | txdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss |
Response message | resperr |
String(128) | |
Payment amount | txamt |
Int(11) | |
Other message information | respmsg |
String(128) | |
External transaction number | out_trade_no |
String(128) | External transaction number |
QFPay transaction number | syssn |
String(40) | |
Wallet/Channel transaction number | chnlsn |
String | |
Return code | respcd |
String(4) | 0000 = Request successful. 1143/1145 = merchants are required to continue to query the transaction result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
Transaction Status Codes
Return code | Description |
---|---|
0000 | Transaction successful |
1100 | System under maintenance (1100) |
1101 | Reversal error (1101) |
1102 | Duplicate request (1102) |
1103 | Request format error (1103) |
1104 | Request parameter error (1104) |
1105 | Device not activated (1105) |
1106 | Invalid device (1106) |
1107 | Device not allowed (1107) |
1108 | Signature error (1108) |
1125 | Transaction has been refunded already (1125) |
1136 | The transaction does not exist or is not operational (1136) |
1142 | Order already closed (1142) |
1143 | The order has not been paid for, the password is currently being entered (1143) |
1145 | Please wait while processing (1145) |
1147 | Wechat pay transaction error (1147) |
1150 | Your billing method is T0 and does not support canceling transactions. (1150) |
1155 | Refund request denied (1155) |
1181 | Order expired (1181) |
1201 | Insufficient balance, please use a different payment method (1201) |
1202 | Incorrect or expired payment code, please show the correct payment code or refresh the payment code and retry (1202) |
1203 | Merchant account error, confirm that the payment account is configured correctly (1203) |
1204 | Bank error, confirm that the payment wallet is functionable (1204) |
1205 | The transaction failed. Please try again later (1205) |
1212 | Please use the UnionPay overseas payment code (1212) |
1241 | The store does not exist or the status is incorrect. Do not conduct payments (1241) |
1242 | The store has not been configured correctly, unable to conduct payments (1242) |
1243 | The store has been disabled. Do not conduct payments, contact the owner to confirm (1243) |
1250 | The transaction is forbidden. For more information please contact QFPay Customer Service Team (1250) |
1251 | The store has not been configured correctly, we are currently working to fix this problem (1251) |
1252 | System error when making the order request (1252) |
1254 | A problem occured. We are currently resolving the issue (1254) |
1260 | The order has already been paid for, please confirm the transaction result before conducting more transactions (1260) |
1261 | The order has not been paid for, please confirm the transaction result before conducting more transactions (1261) |
1262 | The order has been refunded, please confirm the order status before conducting more transactions (1262) |
1263 | The order has been cancelled, please confirm the order status before conducting more transactions (1263) |
1264 | The order has been closed, please confirm the order status before conducting more transactions (1264) |
1265 | The transaction cannot be refunded. Refunds for transactions between 11:30pm to 0:30am and special promotions cannot be processed. (1265) |
1266 | The transaction amount is wrong, please confirm the order status (1266) |
1267 | The order information does not match, please confirm the order status (1267) |
1268 | The order does not exist, please confirm the order status (1268) |
1269 | Today's unsettled transaction amount is insufficient. Refunds cannot be processed. Please confirm that the balance is sufficient (1269) |
1270 | This currency does not support partial refunds (1270) |
1271 | The selected transaction does not support partial refunds (1271) |
1272 | The refund amount is greater than the maximum amount that can be refunded for the original transaction (1272) |
1294 | The transaction may be non-compliant and has been prohibited by the bank (1294) |
1295 | The connection is slow, waiting for a network response (1295) |
1296 | The connection is slow, waiting for a network response. Please try again later or use other payment methods (1296) |
1297 | The banking system is busy. Please try again later or use other payment methods (1297) |
1298 | The connection is slow, waiting for a network response. In case you have already paid, do not repeat the payment. Please confirm the result later (1298) |
2005 | The customer payment code is incorrect or has expired, please refresh and restart the transaction process (2005) |
2011 | Transaction serial number repeats (2011) |
Merchant Present Mode (MPM)
MPM API Request
Request Header:
{
Content-Type: application/x-www-form-urlencoded;
charset=UTF-8,
Content-Length: 218,
Chunked: false,
X-QF-APPCODE: A6A49A66B4C********94EA95032,
X-QF-SIGN: 3b020a6349646684ebeeb0ec2cd3d1fb
}
Request Body:
{
expired_time=10&goods_name=qfpay&limit_pay=no_credit&mchid=R1zQrTdJnn&out_trade_no=Native20190722145741431794b8d1&pay_type=800201&txamt=20&txcurrcd=HKD&txdtm=2019-07-22 14:57:42&udid=AA
}
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
from hashids import Hashids
import datetime
import string
import random
# Enter Client Credentials
environment = 'https://openapi-eur.qfapi.com'
app_code = 'FADB8A87E0674012979F5443AA81ECF1'
client_key = 'F644B1389AD24C25BEFE8BE10C31C878'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
print(unsign_str)
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '1' #In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '800101' # Alipay MPM = 800101, WeChat Pay MPM = 800201
#auth_code = '287255838063025836' # CPM only
out_trade_no = random_string
txdtm = current_time
goods_name = 'Food'
mchid = 'lkbqahlRYj' #Hashids("qfpay").encode(2546286, 2532824) #(Agent ID, Merchant ID)
key = client_key
notify_url = 'https://xxx.com/notify/success'
#data = {'txamt': txamt, 'txcurrcd': txcurrcd, 'auth_code': auth_code, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'udid': udid, 'mchid': mchid}
data = {'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="800101";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="EUR";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '800101', // Alipay MPM = 800101, WeChat Pay MPM = 800201
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '800101'; //Alipay MPM = 800101, WeChat Pay MPM = 800201
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //Ascending dictionary sorting A-Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"surcharge_fee": 0,
"qrcode": "https://qr.alipay.com/bax03190uxd47wbekffy6033",
"pay_type": "800101",
"surcharge_rate": 0,
"resperr": "success",
"txdtm": "2020-04-23 11:09:24",
"out_trade_no": "364ZK6BAJGYHMU3TUX0X7MGIGQL4O8KI",
"syssn": "20200423066200020000976054",
"sysdtm": "2020-04-23 11:09:27",
"txcurrcd": "EUR",
"respmsg": "",
"chnlsn2": "",
"cardcd": "",
"udid": "qiantai2",
"txamt": "1",
"respcd": "0000",
"chnlsn": ""
}
POST ../trade/v1/payment
The merchant generates a dynamic QR code based on the Alipay / WeChat Pay protocol and presents it to the customer. The user opens their Alipay / WeChat Pay wallet and scans the displayed QRC in order to complete payment. This szenario applies to offline as well as online payments, for instance on websites.
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameter | — | — | — | — |
Payment mark | pay_tag |
No | String(16) | The default value is: ALIPAYHK Alipay Continental version: ALIPAYCN 801103 - Alipay overseas online refund (QF_BUSICD_ALIPAY_ONLINE_REFUND) 801104 - Alipay overseas online inquiry (QF_BUSICD_ALIPAY_ONLINE_QUERY) 801110 - Alipay overseas online APP payment (QF_BUSICD_ALIPAY_ONLINE_APP) 801501 - Alipay Hong Kong pc scan code 801512 - Alipay Hong Kong WAP payment 801510 - Alipay Hong Kong APP payment |
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. Available for: 800201 - WeChat scan code 800101 - Alipay scan code 801512 - Alipay Hong Kong WAP payment 801501 - Alipay Hong Kong scan code 801107 - Alipay overseas WAP payment 801101 - Alipay overseas scan code 801010 - WeChat Hong Kong APP 801510 - Alipay Hong Kong APP |
Designated payment method | limit_pay |
No | String | The parameter value is specified as no_credit, and credit card payment is prohibited. This setting only applies to mainland China. |
Response Parameters
Parameter code | Secondary parameter code | Parameter type | Parameter name | Description |
---|---|---|---|---|
QR Code | String(512) | QR code link | ||
Public payment parameter | — | — | — | — |
Consumer Present Mode (CPM)
CPM API Request
Request Header:
{
Content-Type: application/x-www-form-urlencoded;
charset=UTF-8,
Content-Length: 218,
Chunked: false
X-QF-APPCODE:A6A49A66B4C********94EA95032
X-QF-SIGN:3b020a6349646684ebeeb0ec2cd3d1fb
}
Request Body:
{
auth_code=13485790*******88557&goods_name=qfpay&mchid=R1zQrTdJnn&out_trade_no=Native201907221520536a25477909&pay_type=800208&txamt=10&txcurrcd=HKD&txdtm=2019-07-22 15:20:54&udid=AA
}
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' #In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '800108' # Alipay CPM = 800108 , WeChat Pay CPM = 800208
auth_code = '280438849930815813' # Mandatory for CPM
out_trade_no = '01234567890123'
txdtm = current_time
goods_name = 'test1'
mchid = 'ZaMVg*****'
notify_url = 'https://xxx.com/notify/success'
key = client_key
#data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'udid': udid, 'auth_code': auth_code, 'mchid': mchid, 'notify_url': notify_url}
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid, 'auth_code': auth_code}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="800108";
String auth_code="280438849930815813";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="EUR";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("auth_code", auth_code);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '800108', // Alipay CPM = 800108, WeChat Pay CPM = 800208
'auth_code': '280438849930815813',
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '800108'; //Alipay CPM = 800108, WeChat Pay CPM = 800208
$auth_code = '280438849930815813';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'auth_code' => urlencode($auth_code),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //Ascending dictionary sorting A-Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"pay_type": "800108",
"sysdtm": "2019-07-22 15:20:54",
"paydtm": "2019-07-22 15:20:56",
"txdtm": "2019-07-22 15:20:54",
"udid": "AA",
"txcurrcd": "EUR",
"txamt": 10,
"resperr": "交易成功",
"respmsg": "OK",
"out_trade_no": "201907221520536a25477909",
"syssn": "20190722000300020081074842",
"respcd": "0000",
"chnlsn": "4200000384201907223585006133"
}
POST ../trade/v1/payment
The customer generates a dynamic QR code in their QR code wallet and presents it to the cashier for scanning. This szenario applies to offline payments only. If the response codes 1143/1145
are returned, the transaction is being processed or the customer is required to input the wallet password. Merchants have to query the transaction result for a final assessment of the transaction status.
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | — |
Authorization Code | auth_code |
Yes (CPM only) |
String(128) | Specifies the authorization code for scanning a barcode/QR Code. The auth_code returned is unique in each authorization. Each auth_code can only be used once and will automatically expire. For testing CPM with Alipay and WeChat Pay the auth_code can be extracted by any QRC reader or manually found in the consumer wallet below the barcode. |
Response Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | — |
Transaction Notes
Merchant can use this interface to add remarks to a transaction. This remarks value will be displayed in the Merchant Management System (MMS) and on the transaction report.
API Endpoint for Transaction Notes
Request Header:
{
Content-Type: application/x-www-form-urlencoded;
X-QF-APPCODE: D5589D2A1F2E42A9A60C37**********
X-QF-SIGN: 6FB43AC29175B4602FF95F8332028F19
}
Request Body:
{
code=A6A49A6******DFE94EA95032¬e=add_note&syssn=20190722000200020081075691
}
The above command returns JSON structured like this:
{
"resperr": "Success",
"respcd": 0000,
"respmsg": "",
"data":
{
"syssn": "20190722000200020081084545"
}
}
HTTP Request
POST ../trade/v1/add_note
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Merchant app code | code |
Yes | String(32) | Provided by QF Pay |
Transaction number | syssn |
Yes | String(40) | QFPay transaction number, returned by the system once payment is completed |
Remarks | note |
Yes | String(200) | Remarks value |
Response Parameters
Parameter code | Parameter type | Parameter name | Description |
---|---|---|---|
resperr |
String(128) | Transaction result description | |
respmsg |
String(128) | Error message | |
respcd |
String(4) | Return code | 0000 = Interface call succeeded |
syssn |
String(40) | Error message | Transaction number returned by the system when payment is completed |
Transaction Enquiry
API Endpoint for Transaction Enquiry
HTTP Request
POST ../trade/v1/query
Request Header:
{
Content-Type: application/x-www-form-urlencoded;
X-QF-APPCODE: D5589D2A1F2E42A9A60C37**********
X-QF-SIGN: 6FB43AC29175B4602FF95F8332028F19
}
Request Body:
{
mchid=ZaMVg*****&syssn=20191227000200020061752831&start_time=2019-12-27 00:00:00&end_time=2019-12-27 23:59:59
}
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
from hashids import Hashids
import datetime
import string
import random
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37**********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
mchid = 'ZaMVg*****' #(Agent ID, Merchant ID)
syssn = '20191227000200020061752831' #Search by transaction number only
out_trade_no = '2019122722001411461404119764' #Search by out_trade_no only
start_time = '2019-12-27 00:00:00'
end_time = '2019-12-27 23:59:59'
key = client_key
#data ={'mchid': mchid, 'syssn': syssn, 'out_trade_no': out_trade_no, 'start_time': start_time, 'end_time': end_time}
data ={'mchid': mchid, 'syssn': syssn}
r = requests.post(environment+"/trade/v1/query",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(make_req_sign(data, key))
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Enquiry {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37**********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****"; // Only Agents must provide the mchid
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String syssn="20191227000300020061662295";
String start_time = "2019-12-27 00:00:00";
String end_time = "2019-12-27 23:59:59";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("syssn", syssn);
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/query", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37**********'
const client_key = '0E32A59A8B454940A2FF39**********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'syssn': '20191231000300020063521806',
'start_time': '2019-12-27 00:00:00',
'end_time': '2019-12-31 23:59:59',
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/query",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/query';
$syssn = '20200311066100020000977841';
//$out_trade_no = 'zCvo0IqTg0SaQkGnHd6w';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get the current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'syssn' => urlencode($syssn),
//'out_trade_no' => urlencode($out_trade_no),
//'start_time' = '2020-03-01 00:00:00',
//'end_time' = '2020-03-04 23:59:59'
);
ksort($fields); //Sort parameters in ascending order from A to Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"respmsg": "",
"resperr": "请求成功",
"respcd": 0000,
"data":
[{
"cardtp": "5",
"cancel": "0",
"pay_type": "800101",
"order_type": "payment",
"clisn": "038424",
"txdtm": "2019-12-27 10:39:39",
"goods_detail": "",
"out_trade_no": "CHZ7D61JN1ANJF2R2K1I7TXP2JTCEWBL",
"syssn": "20191227000200020061752831",
"sysdtm": "2019-12-27 10:40:24",
"paydtm": "2019-12-27 10:42:18",
"goods_name": "",
"txcurrcd": "EUR",
"chnlsn2": "",
"udid": "qiantai2",
"userid": "2605489",
"txamt": "10",
"chnlsn": "2019122722001411461404119764",
"respcd": "0000",
"goods_info": "",
"errmsg": "success"
}],
"page": "1",
"page_size": "10"
}
After making a payment, refund or cancellation request, the merchant can use the query interface to obtain the transaction status.
The merchant can use the query interface to enquire transaction status of one or multiple transactions. In case the interface does not return syssn
in time, use out_trade_no
as a condition to query the transaction status.
If merchants would like to query transactions in a month, they can provide start_time
and end_time
then records will be filtered according to the system transaction time sysdtm
. The interval must be within one calendar month. Otherwise, it is recommended to include the syssn
parameter as a query condition.
When the query transaction is a refund then an additional parameter origssn
will be returned. The origssn
shows the QFPay transaction number of the original transaction that has been refunded.
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Merchant number | mchid |
For Agents | String(16) | If MCHID is given, it is mandatory to provide the mchid. On the contrary, if mchid is not provided, merchants shall not pass the mchid field in the API request. |
QFPay transaction number | syssn |
No | String(128) | Multiple entries are seperated by commas |
API order number | out_trade_no |
No | String(128) | External transaction number / Merchant platform transaction number, multiple entries are seperated by commas |
Payment type | pay_type |
No | String(6) | Multiple entries are seperated by commas |
Transaction return code | respcd |
No | String(4) | Returns all orders with return code status by default |
Starting time | start_time |
No | String(20) | It is ignored when syssn or out_trade_number is provided. The default date time is the start of current month. Cross-month queries must add the time query parameters start_time and end_time . Format: YYYY-MM-DD hh:mm:ss |
End Time | end_time |
No | String(20) | It is ignored when syssn or out_trade_number is provided. The default date time is the end of current month. Cross-month queries must add the time query parameters start_time and end_time . Format: YYYY-MM-DD hh:mm:ss |
Time zone | txzone |
No | String(5) | Used to record the local order time. The default is Beijing time UTC+8 (+0800) |
Number of pages | page |
No | Int(8) | Default value is 1 |
Number of items displayed per page | page_size |
No | Int(8) | By default 10 transactions will be displayed. The maximum page_size value is 100 |
Response Parameters
Parameter name | Parameter code | Parameter type | Description |
---|---|---|---|
Page number | page |
Int(8) | |
Request result description | resperr |
String(128) | |
Display number of items per page | page_size |
Int(8) | |
Request result code | respcd |
String(4) | 0000 - Interface call succeeded |
Query result | data |
Object | JSON format |
QFPay transaction number | syssn |
String(40) | |
API order number | out_trade_no |
String(128) | External transaction number / Merchant platform transaction number |
Wallet/Channel transaction number | chnlsn |
String | |
Product name | goods_name |
String(64) | Goods Name / Marking: Cannot exceed 20 alphanumeric or contain special characters. Cannot be empty for app payment. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. |
Transaction currency | txcurrcd |
String(3) | View the Currencies table for a complete list of available currencies |
Original transaction number | origssn |
String(40) | Refers to the original QFPay transaction number. This parameter is only available when the syssn of a refund is queued |
Payment type | pay_type |
String(6) | Please refer to the section Payment Codes for a complete list of payment types |
Order type | order_type |
String(16) | Payment: Payment transaction Refund: Refund transaction |
Request transaction time | txdtm |
String(20) | Request transaction time provided by merchant in payment and refund request. Format: YYYY-MM-DD hh:mm:ss |
Transaction amount | txamt |
Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
System transaction time | sysdtm |
String(20) | Format: YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Cancellation or refund indicator | cancel |
String(1) | Transaction cancel status: 0 = Not cancelled 1 = For CPM: Transaction reversed or refunded successfully 2 = For MPM: Transaction canceled successfully 3 = Transaction refunded 4 = Alipay Preauth order finished 5 = Transaction partially refunded |
Payment status | respcd |
String(4) | 0000 = transaction succeeded 1143/1145 = Please wait to evaluate the transaction status. All other response codes indicate transaction failure |
Payment status message | errmsg |
String(128) | Payment result description |
Currency exchange rate | exchange_rate |
String | Applied currency conversion exchange rate |
Net payment amount | cash_fee |
String | Actual payment amount by user = transaction amount - discounts |
Payment currency | cash_fee_type |
String | Actual payment currency e.g. CNY |
Net refund amount | cash_refund_fee |
String | Actual refund amount |
Refund currency | cash_refund_fee_type |
String | Actual refund currency e.g. CNY |
Account Statement
The clearing statement for a particular payment channel is downloaded regularly. Additional requests can only be made in the production environment. The system response is in form of a compressed zip file. Data is based on the selected payment channel and contains all merchants therefore the mchid
cannot be passed in as a request parameter.
API Endpoint for Account Statement
HTTP Request
GET ../download/v1/trade_bill
Request Parameter
Request code | Mandatory | Parameter type | Description |
---|---|---|---|
trade_date |
Yes | String(10) | Get a specific account statement for the selected date. Example: 2017-10-17 |
Reversal/ Cancel
For code instructions select Python, Java, Node.js or PHP with the tabs above.
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
from hashids import Hashids
import datetime
import string
import random
# Enter Client Credentials
environment = 'https://test-openapi-eur.qfapi.com'
app_code = '3F504C39125E4886AB4741**********'
client_key = '5744993FBC034DBBB995FA**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
print(unsign_str)
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '2500' #In USD,EUR,etc. Cent
out_trade_no = '4MDGEJ7L496LAAU1V1HBY9HMOGWZWLXQ'
syssn = '20200305066100020000977812'
txdtm = '2020-03-05 16:50:30'
mchid = 'MNxMp11FV35qQN'
key = client_key
#data ={'txamt': txamt, 'out_trade_no': out_trade_no, 'syssn': syssn, 'txdtm': txdtm, 'udid': udid, 'mchid': mchid}
data ={'txamt': txamt, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/reversal",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Refund {
public static void main(String args[]){
String appcode="3F504C39125E4886AB4741**********";
String key="5744993FBC034DBBB995FA**********";
String mchid="MNxMp11FV35qQN"; // Only Agents must provide the mchid
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm="2020-03-05 16:50:30";
String txamt="2500";
String syssn="20200305066100020000977812"; //only syssn or out_trade_no must be provided
String out_trade_no="4MDGEJ7L496LAAU1V1HBY9HMOGWZWLXQ"; //only syssn or out_trade_no must be provided
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("txamt", txamt);
unsortMap.put("syssn", syssn);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
//如果是国内钱台,网址是:https://openapi-test.qfpay.com.
String url="https://test-openapi-eur.qfapi.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/reversal", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
environment = 'https://test-openapi-eur.qfapi.com'
app_code = '3F504C39125E4886AB4741**********'
client_key = '5744993FBC034DBBB995FA**********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '2500',
'out_trade_no': '4MDGEJ7L496LAAU1V1HBY9HMOGWZWLXQ', //only syssn or out_trade_no must be provided
'syssn': '20200305066100020000977812', //only syssn or out_trade_no must be provided
'txdtm': '2020-03-05 16:50:30',
'mchid': 'MNxMp11FV35qQN'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/reversal",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/reversal';
$syssn = '800101';
//$out_trade_no = 'zCvo0IqTg0SaQkGnHd6w';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73E********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B5********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get the current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'syssn' => urlencode($syssn),
//'out_trade_no' => urlencode($out_trade_no),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => date('2020-03-05 16:50:30'),
);
ksort($fields); //Sort parameters in ascending order from A to Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"surcharge_fee": "0",
"resperr": "success",
"txdtm": "2020-03-05 16:50:30",
"syssn": "20200305066100020000977814",
"sysdtm": "2020-03-05 16:54:38",
"txcurrcd": "EUR",
"respmsg": "",
"chnlsn2": "",
"cardcd": "",
"udid": "qiantai2",
"txamt": "2500",
"orig_syssn": "20200305066100020000977813",
"surcharge_rate": "0",
"respcd": "0000",
"chnlsn": ""
}
The reversal API endpoint allows the merchant to cancel/ reverse a transaction that is currently in progress. Transactions that have already been processed successfully (return code 0000 = successful) can no longer be reversed or cancelled. If you would like to revert a successful transaction please refer to the Refund Endpoint.
HTTP Request for Alipay CPM & MPM
GET ..trade/v1/reversal
HTTP Request for WeChat Pay CPM
GET ..trade/v1/reversal
HTTP Request for WeChat Pay MPM
GET ..trade/v1/close
HTTP Request for other qualified Wallets*
GET ..trade/v1/close
*If you would like to use this endpoint on a wallet other than Alipay & Wechat Pay please contact us for instructions.
Request Parameters
Parameter | Mandatory | Type | Description |
---|---|---|---|
mchid |
No | String(16) | Merchant ID allocated by QFPay |
syssn |
Yes* | String(40) | QFPay transaction number, returned by the system once payment is completed |
out_trade_no |
Yes* | String(128) | External transaction number |
txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
udid |
No | String(40) | Unique transaction device ID. Is displayed on the merchant portal. |
*Either the syssn
or out_trade_no
must be provided.
Response Parameters
Parameter | Type | Description |
---|---|---|
orig_syssn |
String(40) | Refers to the original QFPay transaction number |
syssn |
String(40) | QFPay transaction number of the cancel/ reversal |
out_trade_no |
String(128) | External transaction number |
txamt |
Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
txcurrcd |
String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
txdtm |
String(20) | Transaction time. Format: YYYY-MM-DD hh:mm:ss |
sysdtm |
String(20) | System transaction time. Format: YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
chnlsn |
String | Transaction number from payment channel (wallet side) |
respcd |
String(4) | Response code 0000 = Reversal/ cancel successul 1143/1145 = Reversal/ cancel in progress others = Reversal/ cancel failed |
resperr |
String(128) | Result description |
respmsg |
String(128) | Information description |
Refunds
API Endpoint for Refunds
Request Header:
{
Content-Type: application/x-www-form-urlencoded;
X-QF-APPCODE: D5589D2A1F2E42A9A60C37**********
X-QF-SIGN: 6FB43AC29175B4602FF95F8332028F19
}
Request Body:
{
txamt=10&syssn=20191227000200020061752831&out_trade_no=12345678&txdtm=2019-12-27 10:39:39&key=0E32A59A8B454940A2FF39**********&mchid=ZaMVg*****
}
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
from hashids import Hashids
import datetime
import string
import random
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37**********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' #Partial or full refund amount
syssn = '20191227000200020061752831' #Original transaction number
out_trade_no = random_string
txdtm = current_time
key = client_key
mchid = 'ZaMVg*****'
#data ={'txamt': txamt, 'syssn': syssn, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'udid': udid, 'mchid': mchid}
data ={'mchid': mchid, 'txamt': txamt, 'syssn': syssn, 'out_trade_no': out_trade_no, 'txdtm': txdtm}
r = requests.post(environment+"/trade/v1/refund",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Refund {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37**********";
String key="0E32A59A8B454940A2FF39**********";
String mchid="ZaMVg*****"; // Only Agents must provide the mchid
String out_trade_no= "22333444455555";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="15";
String syssn="20191227000300020061662295";
//如果是国内钱台,产品名称对应的字段是goods_name,不是product_name.
//String product_name="Test Name";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("txamt", txamt);
unsortMap.put("syssn", syssn);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
//如果是国内钱台,网址是:https://openapi-test.qfpay.com.
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/refund", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37**********'
const client_key = '0E32A59A8B454940A2FF3***********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'syssn': '20191231000300020063521806',
'txamt': '10',
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/refund",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/refund';
$syssn = '20200311066100020000977840';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get the currend date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'syssn' => urlencode($syssn),
'out_trade_no' => urlencode(GetRandStr(20)),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //Sort parameters in ascending order from A to Z
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"orig_syssn": "20191227000200020061752831",
"sysdtm": "2019-12-27 11:11:23",
"paydtm": "2019-12-27 11:11:26",
"txdtm": "2019-12-27 11:10:38",
"udid": "qiantai2",
"txcurrcd": "EUR",
"txamt": "10",
"resperr": "success",
"respmsg": "",
"out_trade_no": "RGNOEIVU9JZLNP9GGYXWXCW7OEMI720F",
"syssn": "20191227000300020061652643",
"respcd": "0000",
"chnlsn": "2019122722001411461404119764",
"cardcd": ""
}
HTTP Request
POST ../trade/v1/refund
Merchants can use the refund interface to refund transactions. The merchant account must have a sufficient transaction amount on the same trading day in order to refund transactions. The maximum refund amount for a transaction must not exceed to original payment amount. Unless otherwise specified, once a refund request is submitted and accepted, it is not reversible. The refund capability and the maximum time period for refund varies across payment channels. Please contact your QFPay support representative for more information.
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
QF Pay transaction number | syssn |
Yes | String(128) | Original transaction ID syssn that is supposed to be refunded |
API order number | out_trade_no |
Yes | String(128) | External refund transaction number / Merchant platform refund transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Refund amount | txamt |
Yes | Int(11) | Amount of the refund. Unit in cents (i.e. 100 = $1) Required for both full refund and partial refund. Some payment channel may not support partial refund. |
Transaction request time | txdtm |
Yes | String(20) | Format: YYYY-MM-DD hh:mm:ss |
Merchant ID | mchid |
No | String(16) | May or may not be given to merchant. If MCHID is given, it is mandatory to provide the MCHID. On the contrary, if MCHID is not provided, merchants shall not pass the MCHID field in the API request. |
Transaction time | txzone |
No | String(5) | Used to record the local transaction time. The default is Beijing time UTC+8 (+0800) |
Device ID | udid |
No | String(40) | Unique transaction device ID |
Response Parameters
Parameter name | Parameter code | Parameter type | Description |
---|---|---|---|
Refund Transaction ID | syssn |
String(40) | New transaction ID referring to the newly created refund transaction |
Original Transaction ID | orig_syssn |
String(128) | Previous transaction ID referring to the original transaction that has been refunded |
Refund amount | txamt |
Int(11) | Amount of the refund. Unit in cents (i.e. 100 = $1) |
System transaction time | sysdtm |
String(20) | Format: YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Return code | respcd |
String(4) | 0000-Request successful. 1143/1145 - merchants are required to continue to query the refund transaction result. All other return codes indicate transaction failure. Please refer to the section payment status codes for a complete list of return codes. |
Response message | resperr |
String(128) | Error message |
Net payment amount | cash_fee |
String | Actual payment amount by user = transaction amount - discounts |
Payment currency | cash_fee_type |
String | Actual payment currency e.g. CNY |
Net refund amount | cash_refund_fee |
String | Actual refund amount |
Refund currency | cash_refund_fee_type |
String | Actual refund currency e.g. CNY |
Asynchronous Notifications
Notifications are available for payments "notify_type": "payment" and refunds "notify_type": "refund". The request parameters from Asynchronous Notifications may include additional parameters in future versions. Developers must ensure that their programs can support new parameters. In addition, developers can get the latest development documentation from this website.
Description
Upon successful payment and refund, QFPay API will send an asynchronous notification message to the URL address defined by the merchant. Merchant can develop an end-point to receive this notification message and update the status of a transaction accordingly. We recommend merchants to use the query function of the API in conjunction with the asynchronous notification end point to retireve the payment status. Asynchronous notifications only work with ports 80 and 443 due to security requirements.
Asynchronous Notification Rules
1) The merchant will only be notified after the payment or refund transaction has been successful.
2) Please send an email with your notification endpoint URL address to technical.support@qfpay.global for the asynchronous notification setup. Our technical support team will setup the provided URL for you.
3) Upon receiving the notification, merchant shall verifiy the message integrity according to signature verification procedure described below. If the verification is successful, the system is required to response with status code 200 OK and the string SUCCESS in the response body.
4) If our API does not receive a response with status code 200 OK and SUCCESS message, we will send out asynchronous notifications at the following intervals after the first message; 2m, 10m, 10m, 60m, 2h, 6h, 15h. Notifications will stop when the response with status code 200 OK and SUCCESS message is received.
5) One set of app code and key can be setup with one notification URL address only. Patners shall use one notification URL address for their sub-merchants.
6) Method: POST content-type: application/json
Signature Verification
For code instructions select Python with the tabs above.
import hashlib
import json
# Client Credentials
client_key = "3ABB1BFFE2E0497BB9270978B0BXXXXX"
# Raw Content Data
data = {"status": "1", "pay_type": "800101", "sysdtm": "2020-06-15 10:32:58", "paydtm": "2020-06-15 10:33:35", "goods_name": "", "txcurrcd": "THB", "txdtm": "2020-06-15 10:32:58", "mchid": "O37MRh6Qq5", "txamt": "10", "exchange_rate": "", "chnlsn2": "", "out_trade_no": "9G3ZIWTG1R3IVSC2AH2O5EGKJQ7I72QO", "syssn": "20200615000200020000641807", "cash_fee_type": "", "cancel": "0", "respcd": "0000", "goods_info": "", "cash_fee": "0", "notify_type": "payment", "chnlsn": "2020061522001453561406303428", "cardcd": "2088032341453564"}
combine_str = (json.dumps(data)+client_key).encode()
signature = hashlib.md5(combine_str).hexdigest().upper()
print(signature)
Signature String:
"A4021A3B1EBBB0F05451EF94E9EXXXXX"
The signature generation method for notifications is slightly different from other POST requests. In order to generate the signature simply take the raw content and add the client_key
to the end. Then hash the encoded string with the MD5 algorithm.
Step 1: Obtain the signature from the X-QF-SIGN
field in the HTTP request header
Step 2: Attach the key to the end of the request body received by the end point
Step 3: Sign the string from step 2 with MD5 algorithm
Step 4: Compare the MD5 result with the signature from X-QF-SIGN
, return an HTTP response with status code 200 OK and SUCCESS in the response body if the verification is successful
Response Parameters of Asynchronous Notifications
Asynchronous Notifications POST data in JSON form structured like this:
{
"status": "1",
"pay_type": "800101",
"sysdtm": "2020-05-14 12:32:56",
"paydtm": "2020-05-14 12:33:56",
"goods_name": "",
"txcurrcd": "THB",
"txdtm": "2020-05-14 12:32:56",
"mchid": "lkbqahlRYj",
"txamt": "10",
"exchange_rate": "",
"chnlsn2": "",
"out_trade_no": "YEPE7WTW46NVU30JW5N90H7DHD94N56B",
"syssn": "20200514000300020093755455",
"cash_fee_type": "",
"cancel": "0",
"respcd": "0000",
"goods_info": "",
"cash_fee": "0",
"notify_type": "payment",
"chnlsn": "2020051422001453561444935817",
"cardcd": "2088032341453564"
}
Parameter | Send always | Type | Description |
---|---|---|---|
status |
Yes | String | 1 = payment success |
pay_type |
Yes | String | Please refer to the section Payment Codes for a complete list of payment types. |
sysdtm |
Yes | String | Transaction creation time in the system. This parameter value is used as the cut-off time for settlements. |
paydtm |
Yes | String | Payment time of the transaction. |
txcurrcd |
Yes | String | Transaction currency. View the Currencies table for a complete list of available currencies. |
txdtm |
Yes | String | Order creation time provided by the merchant in the payment request. |
txamt |
Yes | String | Transaction amount in Cents. |
out_trade_no |
Yes | String | External transaction number. |
syssn |
Yes | String | QFPay transaction number. |
cancel |
Yes | String | Transaction cancel status: 0 = Not cancelled 1 = For CPM: Transaction reversed or refunded successfully 2 = For MPM: Transaction canceled successfully 3 = Transaction refunded 4 = Alipay Preauth order finished 5 = Transaction partially refunded. |
respcd |
Yes | String | Transaction status - will be 0000 in the async notification message |
notify_type |
Yes | String | Notification Type: payment or refund |
mchid |
No | String | Unique merchant ID. This parameter is only returned to agents. |
goods_name |
No | String | Goods name or marking. Custom goods name. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. |
exchange_rate |
No | String | Applied currency conversion exchange rate |
chnlsn2 |
No | String | Additional transaction number added to the order |
goods_info |
No | String | Product description |
chnlsn |
No | String | Transaction number from the payment channel |
cardcd |
No | String | Card number |
cash_fee |
No | String | Actual payment amount by user = transaction amount - discounts |
cash_fee_type |
No | String | Actual payment currency e.g. CNY |
cash_refund_fee |
No | String | Actual refund amount |
cash_refund_fee_type |
No | String | Actual refund currency e.g. CNY |
Visa / Mastercard Online Payments
We currently support credit card payments in the Hong Kong environment. All major credit card issuers are supported.
PayType | Description |
---|---|
802801 | Visa / Mastercard Online Payments |
Payment Steps
1. DDC request
Merchant server requests a Data collector form before the merchant checkout is loaded. The DDC JWT is returned
curl --location --request POST 'https://openapi-int.qfapi.com/trade/v1/payment' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'X-QF-APPCODE: A060091DA6034502B6075C4F4605061B' \
--header 'X-QF-SIGN: acf9eabe97c36ef065aa2be81e35651e' \
--data-urlencode 'txamt=1' \
--data-urlencode 'txcurrcd=HKD' \
--data-urlencode 'pay_type=802828' \
--data-urlencode 'out_trade_no=464025641' \
--data-urlencode 'txdtm=2021-12-02 05:18:11'
Parameter name | Parameter code | Mandatory | Type | Description |
---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
Payment type | pay_type |
Yes | String(6) | Please refer to the section Payment Codes for a complete list of payment types |
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
2. Get SessionId
On the payment page, create a hidden iframe and submit form POST when the "Pay now" button is just clicked. A Data collector sessionId is returned. View page: DDC_Test.html
<iframe name='iframet' height="1" width="1" style="display: none;"></iframe>
<form target='iframet' method="post" action="https://centinelapistag.cardinalcommerce.com/V1/Cruise/Collect" name="f1">
<table>
<div>
<span>Bin(Card No.) :</span>
<textarea rows="3" cols="200" name="Bin"></textarea>
</div>
<div>
<span>JWT :</span>
<textarea rows="3" cols="200" name="JWT"></textarea>
</div>
</table>
<button type="submit">submit</button>
</form>
<h3 id='referenceId'></h3>
<script type="text/javascript">
window.addEventListener("message", function (event) {
console.log(JSON.parse(event.data));
if (event.origin === "https://centinelapistag.cardinalcommerce.com") {
var data = JSON.parse(event.data);
console.log('Merchant received a message:', data);
if (data !== undefined && data.Status) {
document.getElementById('referenceId').innerHTML = data.SessionId
}
}
}, false);
</script>
3. Payment request
The merchant server collects the payment information with sessionId and sends a payment request to QFPay. Encryption is required for sensitive information such as card number, expiry year/month, CVC, customer information
curl --location --request POST 'https://openapi-int.qfapi.com/trade/v1/payment' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'X-QF-APPCODE: A060091DA6034502B6075C4F4605061B' \
--header 'X-QF-SIGN: d33aadf2df78e568329a2d2dcf82070f' \
--data-urlencode 'txamt=1' \
--data-urlencode 'txcurrcd=HKD' \
--data-urlencode 'pay_type=802801' \
--data-urlencode 'out_trade_no=299085842' \
--data-urlencode 'txdtm=2021-12-02 05:46:23' \
--data-urlencode 'extend_info={"payment_data_enc":"d5715af3e290dae1c5980f4ceacd2fb7633962b26293de08f1fd9137802608c8e197e1f6fb6ef2eafa33e8933474876c46e31e7418021b9bfa5ea367421a307e80a1c4df74253b04112b6617b4bd5c7542327e61192f0010c0822de31f53c7dce6bce1b83d6eaeaa0e3020b98fabb93342433d7f0de43bdbe33a87c2e58220b614c859c3c4dc15afc234a1cec2dd3806b3f9957449cfcf2fee7910dd84ec64bd5a987914ab1d19d4d413e455e66c65eb50da71db62f4ea4a610d050ac051d82879cb83c00f9d78c017674a5f4287ce185db222f8d89f989896c504620c075b688b81a07211f98361587532b848abf488d78e3aaf7e49f398aba8f95561b00bcbc655b7e570df1fb169ddbd3b0289b38d05ae225ccf294c901a7559a382bf0e585e037158de0a479a25d0add81326bd3e91cee2e522b1f0c9e12ab5d7922eb626d6ab7a7132516614f900fa6b6401019ad9199cc09bab0a9b0dbd4b6edac964fd0d1e6f24a2a4c530fb231d24549d9751938b64407bdadf0149be0e687521e595c5f8d350e170cee4157758c7299e4fc4a6578676516e2ceabb1f631f39808eee7f40217e77055b64910658e6e4ad41b3d6a16cd6a53217632eb01c5ed9f6993e7fcd8814ce98ca86578047e741eef31ff9780f91323a9ed0bf18cbb0a9d20983e52b3c2ed1df80e9d48dfd6d3ce2eaf2bc870cbfdf5fb4fc89d34dfe6920f2d829b7751f81b3f68222a4e7187f302f77a0d218adad9540be675205e8ee82a603684ce15865dc3e815db91f723c489122266b85ac705390a4e58796803512dbc5c80b2cb4104db559e55f6d6fc10635b68f5f0a216cd3106558ece137dc7675f3ec9c2e9a3f8cb41fd377c3098941f4b85fd45347f97a55cae4af1549cfb3b8bf","return_url":"https://example.com"}'
Parameter name | Parameter code | Mandatory | Type | Description |
---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
Payment type | pay_type |
Yes | String(6) | Please refer to the section Payment Codes for a complete list of payment types |
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
Credit Card | extend_info |
Yes | json | Json encoded string that contains the following information: |
In extend_info:
Field name | Mandatory | Type | Description |
---|---|---|---|
payment_data_enc |
Yes | String | encrypted payment information, please refer to payment_data table below |
return_url |
Yes | String | The return url after going through 3DS authentication |
Payment data sample:
{
"card": {
"number": "5200000000001096",
"cardholder_name": "chan tai man",
"exp_month": "12",
"exp_year": " 2034",
"cvc": "567"
},
"billing_address": {
"address1": "1007",
"address2": "tins enterprise center",
"address3": "777 lai chi kok road",
"postal_code": "000000",
"city": "hong kong",
"country_code": "HK"
},
"customer": {
"browser": {
"accept_header": "text/html",
"user_agent_header": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36",
"browser_language": "en-GB"
},
"email": "test@example.com",
"session_id": "86650cfa-9ea3-11ec-b909-0242ac120002",
"ip_address": "12.34.56.78",
"reference_id": "0_48af32ba-e658-4b4c-82b1-e85355a0e9f4"
}
}
Encryption code example of the payment_data
Please refer to sample in PHP tab
Please refer to sample in PHP tab
Please refer to sample in PHP tab
Please refer to sample in PHP tab
<?php
$plaintext = '{"card":{"number":"5200000000001096","cardholder_name":"chan tai man","exp_month":"12","exp_year":" 2034","cvc":"567"},"billing_address":{"address1":"1007","address2":"tins enterprise center","address3":"777 lai chi kok road","postal_code":"000000","city":"hong kong","country_code":"HK"},"customer":{"browser":{"accept_header":"text/html","user_agent_header":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36","browser_language":"en-GB"},"email":"test@example.com","session_id":"6227228bc7740","ip_address":"12.34.56.78","reference_id":"0_48af32ba-e658-4b4c-82b1-e85355a0e9f4"}}';
$cipher = "aes-256-cbc";
$key = 'APP_KEY'; // paste your key here
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
$ciphertext = bin2hex(base64_decode($ciphertext));
echo bin2hex(($iv)).$ciphertext;
echo "\n";
// decrypt test
$ciphertext = base64_encode(hex2bin($ciphertext));
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
else {
echo 'algo not support';
}
?>
payment_data scheme before encryption:
Level 2 | Level 3 | Level 4 | Mandatory | Type | Description |
---|---|---|---|---|---|
card |
number |
Y | String | card number | |
exp_month |
Y | String | expiration month in format MM | ||
exp_year |
Y | String | expiration year in format YYYY | ||
cardholder_name |
Y | String | card holder name | ||
cvc |
N | String | card cvc | ||
customer |
ip_address |
Y | String | IP Address of the shopper | |
reference_id |
Y | String | reference id (SessionId) return from DDC | ||
session_id |
Y | String | unique identifier, highly recommend UUID | ||
email |
Y (if 3ds) | String | email address of the shopper | ||
browser |
accept_header |
N | String | browser accept header, default text/html | |
user_agent_header |
Y | String | browser user agent header | ||
browser_language |
Y | String | browser language | ||
billing_address |
address1 |
N | String | First line of address, required if city is not empty |
|
address2 |
N | String | Second line of address | ||
address3 |
N | String | Third line of address | ||
city |
N | String | city, required if address1 is not empty |
||
postal_code |
Y (if 3ds) | String | postal code, e.g. 000000 (if not applicable) | ||
country_code |
Y (if 3ds) | String | country code, refer to ISO 3166. e.g. HK |
Encryption algorithm of the payment_data
- Sensitive data is transformed to JSON string
- Asymmetric encryption using AES-256-CBC with App Key
- Presents encrypted data as hexadecimal encoded string
4. Verification (3DS) form data / payment result
Please pay attention to the variety of the API response, there are two different handlings.
Case 1: 3DS required
Response for 3DS required case
{
"qrcode": "",
"pay_type": "802801",
"resperr": "success",
"txdtm": "2021-12-08 07:04:15",
"out_trade_no": "354267281",
"syssn": "20211208180500020000001637",
"sysdtm": "2021-12-08 15:04:16",
"paydtm": "2021-12-08 15:04:16",
"txcurrcd": "USD",
"respmsg": "",
"respcd": "0000",
"pay_url": "",
"cardcd": "",
"udid": "qiantai2",
"txamt": "1",
"pay_params": {
"3ds_challenge_details": {
"url": "https://centinelapistag.cardinalcommerce.com/V2/Cruise/StepUp",
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJSZXR1cm5VcmwiOiJodHRwczovL2hrLnlhaG9vLmNvbSIsImlzcyI6IjYxNTViZThlYTJlNDhkNzAzYTM5ODI1NSIsIk9iamVjdGlmeVBheWxvYWQiOnRydWUsImp0aSI6IjdmZDkxOGM2LTJkNjItNDE2My1iOTJiLWUxZGU0YjY3MzJmMCIsImlhdCI6MTYzODk0NzA1NywiUGF5bG9hZCI6eyJBQ1NVcmwiOiJodHRwczovLzBtZXJjaGFudGFjc3N0YWcuY2FyZGluYWxjb21tZXJjZS5jb20vTWVyY2hhbnRBQ1NXZWIvY3JlcS5qc3AiLCJUcmFuc2FjdGlvbklkIjoia0VJVlRiWDFRc21DUkpWM05OMzAiLCJQYXlsb2FkIjoiZXlKdFpYTnpZV2RsVkhsd1pTSTZJa05TWlhFaUxDSnRaWE56WVdkbFZtVnljMmx2YmlJNklqSXVNUzR3SWl3aWRHaHlaV1ZFVTFObGNuWmxjbFJ5WVc1elNVUWlPaUppT1dZNVkyVXdOeTB3TldJekxUUTJNR1F0T0RJMk5TMWlNVGt6T1ROaFlqa3dOVFlpTENKaFkzTlVjbUZ1YzBsRUlqb2lOVGRoWVdReU1HSXRNakUyWkMwME0ySmhMVGxqWldJdFl6azNPV00xTkRKa05USmhJaXdpWTJoaGJHeGxibWRsVjJsdVpHOTNVMmw2WlNJNklqQXlJbjAifSwiT3JnVW5pdElkIjoiNjE1NWJlOGU1NjFlNWQyNGNlZWY0MGYyIn0.6xvBDFB4qV4RN-XvceNH7PllWChrQQthmbtskar0qAw"
},
"set-cookie": "machine=0a854015;path=/",
"order_code": "20211208180500020000001637"
},
"chnlsn": ""
}
If there is pay_params.3ds_challenge_details or pay_params.request_3d_secure item, which contains 3DS form POST data, the shopper will be redirected to the bank verification page by composing automatic form POST in frontend.
Case 2: Frictionless
Case 2: Response for 3DS Frictionless flow
{
"qrcode": "",
"pay_type": "802801",
"resperr": "success",
"txdtm": "2022-05-03 02:38:51",
"out_trade_no": "312717842",
"syssn": "20220503180500020000003749",
"sysdtm": "2022-05-03 10:38:51",
"paydtm": "2022-05-03 10:38:51",
"txcurrcd": "USD",
"respmsg": "",
"respcd": "0000",
"pay_url": "",
"cardcd": "",
"udid": "qiantai2",
"txamt": "7000",
"pay_params": {
"payment_method": "VISA_CREDIT-SSL",
"3ds_secure_result": "Cardholder authenticated",
"last_event": "AUTHORISED",
"order_code": "20220503180500020000003749",
"set-cookie": "machine=0a844015;path=/"
},
"chnlsn": ""
}
If the 3DS Frictionless is in place, which decision was made by the bank. Steps 4, 5 will be skipped, the direct response (Step 6.) will be returned
5. Redirect to 3DS page using form POST
Code reference for redirect to 3DS page
<form method="post" action="https://centinelapistag.cardinalcommerce.com/V2/Cruise/StepUp" name="f1">
<table>
<div>
<span>JWT :</span>
<textarea rows="3" cols="200" name="JWT"></textarea>
</div>
</table>
<button type="submit">submit</button>
<script type="text/javascript">
</script>
</form>
Code reference: Browser_Redirect_3DS_input.html
6. Payment Verify
Payment verify request
curl --location --request POST 'https://openapi-int.qfapi.com/trade/v1/payment_verify' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'X-QF-APPCODE: A060091DA6034502B6075C4F4605061B' \
--header 'X-QF-SIGN: cc1b6a456731b85cc136c214a4a1f6c1' \
--data-urlencode 'syssn=20211202180500020000003515' \
--data-urlencode 'txdtm=2021-12-02 06:37:57' \
--data-urlencode 'chnl_ext={"session_id": "86650cfa-9ea3-11ec-b909-0242ac120002","set_cookie": "machine=0a854015;path=/"}' \
--data-urlencode 'out_trade_no=385913749'
Notify QFPay when 3DS is completed, and the payment result can be determined from the API response. We recommend doubling confirm with the payment notification
Level 1 | Level 2 | Mandatory | Type | Description |
---|---|---|---|---|
chnl_ext |
set_cookie |
Y | String | the cookie value return from the payment request, e.g. machine=0a854015;path=/ |
session_id |
Y | String | same unique identifier in the payment request, e.g. 86650cfa-9ea3-11ec-b909-0242ac120002 |
7. Asynchronous Notification
QFPay will also send the asynchronous payment notification for the transaction status update
Sample notification payload
{
"cardtp": "5",
"cancel": "0",
"pay_type": "802801",
"order_type": "payment",
"clisn": "054256",
"txdtm": "2021-12-08 07:04:15",
"goods_detail": "",
"out_trade_no": "354267281",
"syssn": "20211208180500020000001637",
"sysdtm": "2021-12-08 15:04:16",
"paydtm": "2021-12-08 15:06:51",
"goods_name": "",
"txcurrcd": "USD",
"chnlsn2": "",
"cardcd": "",
"udid": "qiantai2",
"userid": "1130000355",
"txamt": "1",
"chnlsn": "",
"respcd": "0000",
"goods_info": "",
"errmsg": "success"
}
Resources
- Postman API example: LINK
Test values
Test values are available for the Sandbox environment for result simulation. By using Test values to simulate an expected result, the Payment steps: DDC request and Get SessionId must pass
Field | Value | Expected Result |
---|---|---|
card - MasterCard | 5200000000001096 | valid |
card - Visa | 4000000000001091 | valid |
card - MasterCard | 5200000000001005 | valid (3DS frictionless) |
card - Visa | 4000000000001000 | valid (3DS frictionless) |
card - MasterCard | 5200000000001120 | failed (at verification) |
card - Visa | 4000000000001125 | failed (at verification) |
card - MasterCard | 5200000000001013 | failed (at 3DS frictionless) |
card - Visa | 4000000000001018 | failed (at 3DS frictionless) |
Alipay Online Payments
For code instructions select Python, Java, Node.js or PHP with the tabs above.
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' #In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '801101' # Alipay Web Payment = 801101
auth_code='283854702356157409' #CPM only
out_trade_no = '01234567890123'
txdtm = current_time
goods_name = 'test1'
mchid = 'ZaMVg*****'
key = client_key
#data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'mchid': mchid}
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="801101";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="EUR";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '801101', // Alipay Web Payment = 801101
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '801101'; //Alipay Web Payment = 801101
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //字典排序A-Z升序方式
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"sysdtm": "2020-04-13 10:30:34",
"paydtm": "2020-04-13 10:30:34",
"txcurrcd": "THB",
"respmsg": "",
"pay_type": "801101",
"cardcd": "",
"udid": "qiantai2",
"txdtm": "2020-04-13 10:30:34",
"txamt": "300",
"resperr": "success",
"out_trade_no": "4K35N374II7UJJ8RGIAE45O2CVHGHFF0",
"syssn": "20200413000300020087033882",
"respcd": "0000",
"pay_url": "https://globalmapi.alipay.com/gateway.do?total_fee=3.0&secondary_merchant_name=###merchant_name###&out_trade_no=20200413000300020087033882&secondary_merchant_industry=7011&service=create_forex_trade&_input_charset=UTF-8&sign=02beb99974ce6167666280b9727c4444¤cy=THB¬ify_url=https%3A%2F%2Fo2.qfpay.com%2Fonline-test%2Ftrade%2Falipay%2Fv1%2Fonline_notify&order_valid_time=1800&secondary_merchant_id=2565075&sign_type=MD5&partner=2088631377368888&product_code=NEW_OVERSEAS_SELLER&order_gmt_create=2020-04-13+10%3A30%3A34&return_url=&subject=###merchant_name###",
"chnlsn": ""
}
Web/WAP Payment
Customers make purchases on a merchant website with Alipay. The user scans the displayed QR code to pay, confimrs the total amount and makes payment. Finally the customer can be redirected to a selected page on the merchant's website using the return_url
parameter. Alipay deducts the payment amount from the consumer's Alipay wallet in real-time in CNY and QFPay settles the payment amount to merchants in local currency.
HTTP Request
POST ../trade/v1/payment
PayType: 801101
Overseas Merchants Web
PayType: 801107
Overseas Merchants WAP
PayType: 801501
Hong Kong Merchants Web
PayType: 801512
Hong Kong Merchants WAP
Request Parameters
Parameter name | Parameter code | Mandatory | Type | Description |
---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
Payment type | pay_type |
Yes | String(6) | Alipay Web Payment = 801101 |
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. Available for: 800201 - WeChat scan code |
Product name identification | goods_name |
No | String(64) | Goods Name / Marking: Cannot exceed 20 alphanumeric or contain special characters. Cannot be empty for app payment. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. |
QF Pay merchant number | mchid |
No | String(16) | May or may not be given to merchant. If MCHID is given, it is mandatory to provide the MCHID .On the contrary, if MCHID is not provided, merchants shall not pass the MCHID field in the API request. |
Time zone | txzone |
No | String(5) | Transaction Time zone: Record of the transaction in local time, default time zone is Beijing time UTC+8 (+0800). |
Device ID | udid |
No | String(40) | Unique transaction device ID. Is displayed on the merchant portal. |
Redirect URL | return_url |
No | String(512) | Address for user redirect after successful payment. Mandatory parameter to submit for GrabPay Online. Alipay WAP restricts the return_url to maximum 200 characters. |
Response Parameters
Parameter name | Parameter code | Type | Description |
---|---|---|---|
Payment type | pay_type |
String(6) | Alipay Web/Wap Payment = 801101/801107 |
System transaction time | sysdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Request transaction time | txdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss |
Response message | resperr |
String(128) | |
Payment amount | txamt |
Int(11) | |
Other message information | respmsg |
String(128) | |
External transaction number | out_trade_no |
String(128) | External transaction number |
QFPay transaction number | syssn |
String(40) | |
Return code | respcd |
String(4) | 0000 = Request successful. 1143/1145 = merchants are required to continue to query the transaction result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
Payment URL | pay_url |
String(512) |
Alipay Service Window H5
For code instructions select Python, Java, Node.js or PHP with the tabs above.
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' #In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '801107' # Alipay Wap Payment = 801107
auth_code='283854702356157409' #CPM only
out_trade_no = '01234567890123'
txdtm = current_time
goods_name = 'test1'
mchid = 'ZaMVg*****'
key = client_key
#data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'mchid': mchid}
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="801107";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="EUR";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '801107', // Alipay Wap Payment = 801107
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '801107'; //Alipay Wap Payment = 801107
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //字典排序A-Z升序方式
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"sysdtm": "2020-04-13 11:32:03",
"paydtm": "2020-04-13 11:32:03",
"txcurrcd": "THB",
"respmsg": "",
"pay_type": "801107",
"cardcd": "",
"udid": "qiantai2",
"txdtm": "2020-04-13 11:32:03",
"txamt": "300",
"resperr": "success",
"out_trade_no": "BUFB3PT9ZDUWEUAE4ATD21JKNHVEIIPV",
"syssn": "20200413000200020087171988",
"respcd": "0000",
"pay_url": "https://globalmapi.alipay.com/gateway.do?total_fee=3.0&secondary_merchant_name=###merchant_name###&out_trade_no=20200413000200020087171988&secondary_merchant_industry=7011&service=create_forex_trade_wap&_input_charset=UTF-8&sign=f16ef36efbb55058d1c1d36fef89bcf8¤cy=THB&timeout_rule=30m¬ify_url=https%3A%2F%2Fo2.qfpay.com%2Fonline-test%2Ftrade%2Falipay%2Fv1%2Fonline_notify&secondary_merchant_id=2565075&sign_type=MD5&partner=2088631377368888&product_code=NEW_WAP_OVERSEAS_SELLER&return_url=&subject=###merchant_name###",
"chnlsn": ""
}
Alipay Service Window H5 Payment (WAP)
Alipay Service Window H5 Payment enables merchants to call the Alipay payment module by using the JSAPI interface to collect payments. The customer checks out on the merchant's mobile website in Alipay, confirms the total amount and makes the payment.
HTTP Request
POST ../trade/v1/payment
PayType: 800107
Step 1: Get User ID For more details about how to acquire the user id please refer to the official Alipay documentation.
Step 2: Request Payment
Payment Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | — |
Alipay authorization code | openid |
Yes | String(64) | The user_id is returned by the interface, e.g. 2088802811715388 |
Redirect URL | return_url |
No | String(512) | Address for user redirect after successful payment |
Designated payment method | limit_pay |
No | String | Only applicable for mainland China |
Response Parameters
Parameter name | Secondary parameter code | Parameter type | Parameter name | Description |
---|---|---|---|---|
pay_params |
tradeNO |
String | Transaction number | Provide the transaction number in the call function |
txcurrcd |
String(3) | Transaction currency. View the Currencies table for a complete list of available currencies | ||
Public response parameters | — | — | — | — |
Step 3: Payout through the cashout interface For more information regarding the cashout interface please refer to the official Alipay documentation.
Alipay Pre-Authorization
Freeze Funds
At the moment only Alipay wallet funds can be used for pre-authorization, credit-cards are not supported. Authorization requests lose their validity after 15min. In case of technical or currency related integration difficulties please contact technical.support@qfpay.global for support. Merchants can, at any time, unfreeze the funds in which case the assets will be available for spending on the original wallet. In addition, merchants can initiate a transfer for a fraction or all of the frozen funds in order to collect money for open customer invoices.
HTTP Request
POST ../trade/v1/payment
Find the correct pay_type
for your checkout szenario from the table below.
PayType | Description |
---|---|
801801 | Alipay Pre-Authorization in-store QRC Payment - Consumer Present Mode (CPM) |
801808 | Alipay Pre-Authorization in-store QRC Payment - Merchant Present Mode (MPM) |
801810 | Alipay Pre-Authorization in-APP Payment |
801814 | Alipay Pre-Authorization Online Payment |
Request Parameters
Request Body:
{
goods_name=goodcode1&mchid=R1zQrTdJnn&out_trade_no=alipay201909261129164551bcdd40&pay_type=801802&txamt=1&txcurrcd=USD&txdtm=2019-09-26 11:29:16&txzone=+0800
}
The above command returns JSON structured like this:
{
"pay_type": "801802",
"sysdtm": "2019-09-26 11:29:18",
"paydtm": "2019-09-26 11:29:19",
"txdtm": "2019-09-26 11:29:16",
"udid": "qiantai2",
"txcurrcd": "USD",
"txamt": "1",
"resperr": "Network busy, don't worry, we are fixing it (1297)",
"respmsg": "预授权发码参数异常或参数缺失",
"out_trade_no": "alipay201909261129164551bcdd40",
"syssn": "20190926000200020016004244",
"respcd": "1297",
"chnlsn": "",
"cardcd": ""
}
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameter | — | — | — | — |
Alipay openid | openid |
No | String(64) | Corresponding to the APP authorization method. |
Alipay payment code | auth_code |
No | String(128) | Specifies the authorization code for scanning a barcode/QR Code. The auth_code returned is unique in each authorization. Each auth_code can only be used once and will automatically expire. For testing CPM with Alipay and WeChat Pay the auth_code can be extracted by any QRC reader or manually found in the consumer wallet below the barcode. |
Response Parameters
Parameter code | Second parameter code | Parameter type | Parameter name | Description |
---|---|---|---|---|
Public response parameters | — | — | — | — |
Unfreeze Funds
Only calls alipay.fund.auth.operation.cancel
when the merchant’s system encounters timeout and has to stop the subsequent processes, or when the pre-auth result is unknown. If you want to perform a similar task for normal pre-auth (freezing) transactions, please call alipay.fund.auth.order.unfreeze
. After submitting the fund authorization call Order Inquiry, and there is no clear authorization result and then call Fund Authorization Cancellation.
HTTP Request
POST ../trade/v1/reversal
Request Parameters
Request Body:
{
mchid=R1zQrTd***&syssn=20190722000300020081074842
}
The above command returns JSON structured like this:
{
"respmsg": "",
"resperr": "请求成功",
"respcd": "0000",
"syssn": "20190722000302320081074842",
"sysdtm": "2019-07-22 15:20:54",
"txamt": "10",
"txdtm": "2019-07-22 15:20:54",
"cardcd": "",
"txcurrcd": "HKD",
"orig_syssn": "20190722000300020081074842",
"respmsg": ""
}
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Merchant ID | mchid |
Yes | String | The unique merchant ID is created by QF Pay during the merchant onboarding process. |
QF Pay transaction number | syssn |
No | String | Multiple entries are separated by English commas |
External transaction number | out_trade_no |
No | String | e.g. Developer platform order number |
Transaction amount | txamt |
No | Int | Whether to pass this parameter depends on the payment channel, Alipay and WeChat Pay do not need to submit this information |
Transaction request time | txdtm |
Yes | String | Format: YYYY-MM-dd hh:mm:ss |
Unique device id | udid |
No | String | |
Time zone | txzone |
No | String | Used to record the local order time. The default is Beijing time GMT+8 (+0800) |
Response Parameter
Parameter code | Parameter type | Parameter name | Description |
---|---|---|---|
syssn |
String(40) | QF Pay transaction number | |
orig_syssn |
String(40) | External transaction number | Developer platform transaction number |
txdtm |
String(20) | Time of the transaction request | Format: YYYY-MM-dd hh:mm:ss |
txamt |
Int(11) | Order payment amount | |
sysdtm |
String(20) | System trading time | Format: YYYY-MM-dd hh:mm:ss This parameter value is used as the cut-off time for settlements. |
respcd |
String(4) | Return code | |
respmsg |
String(128) | Information description | |
resperr |
String(128) | Description error | |
cardcd |
String | Card number | |
txcurrcd |
String(3) | Currency | Transaction currency. View the Currencies table for a complete list of available currencies |
Deduct Funds
HTTP Request
POST ../trade/v1/authtrade
Request Parameters
Request Body:
{
mchid=R1zQrTd***&syssn=20190722000300020081074842&out_trade_no=alipay201909271528139576015cbf&txamt=1&txdtm=2019-09-27 15:28:13&txzone=+0800
}
The above command returns JSON structured like this:
{
"respmsg": "",
"resperr": "请求成功",
"respcd": "0000",
"syssn": "20190722000302320081074842",
"sysdtm": "2019-07-22 15:20:54",
"txamt": "10",
"txdtm": "2019-07-22 15:20:54",
"cardcd": "",
"txcurrcd": "HKD",
"orig_syssn": 20190722000300020081074842
}
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Merchant ID | mchid |
No | String | The unique merchant ID is created by QF Pay during the merchant onboarding process. |
QF Pay transaction number | syssn |
Yes | String | Fund authorization number |
External transaction number | out_trade_no |
Yes | String | Developer platform transaction number |
Transaction amount | txamt |
Yes | int | The actual amount of consumption, the maximum deduction amount cannot exceed the fozen funds |
Transaction request time | txdtm |
Yes | String | Format: YYYY-MM-DD hh:mm:ss |
Device ID | udid |
No | String | Must be unique |
Time zone | txzone |
No | String | Used to record the local order time. The default is Beijing time GMT+8 (+0800) |
Redirect URL | return_url |
No | String | Redirect to address after successful payment. Mandatory parameter to submit for GrabPay Online. Alipay WAP restricts the return_url to maximum 200 characters. |
Response Parameters
Parameter code | Parameter type | Parameter name | Description |
---|---|---|---|
syssn |
String(40) | QF Pay Transaction number | This number is being used when freezing funds, detucting money from the frozen amount as well as unfreezing funds. |
orig_syssn |
String(40) | External transaction number | Developer platform transaction number |
txdtm |
String(20) | Transaction request time | Format: YYYY-MM-DD hh:mm:ss |
txamt |
Int(11) | Transaction amount | |
sysdtm |
String(20) | System transaction time | Format: YYYY-MM-DD hh:mm:ss |
respcd |
String(4) | Return code | |
respmsg |
String(128) | Information description | |
resperr |
String(128) | Description error | |
cardcd |
String | Card number | |
txcurrcd |
String | Currency | Transaction currency. View the Currencies table for a complete list of available currencies |
Alipay in-APP Payments
To download Alipay Oversea SDK , please refer to this link.
To download Alipay HK SDK, please refer to this link.
To know how to trigger Alipay HK SDK , please refer to this link.
HTTP Request
POST ../trade/v1/payment
PayType: 801110 Oversea Merchants
PayType: 801510 Hong Kong Merchants
Request Parameters
Request Body:
txamt=1&txcurrcd=HKD&pay_type=801510&out_trade_no=052711570017898&txdtm=2021-05-27+11%3A57%3A00&goods_name=goods_name&goods_info=goods_info&mchid=nDB64h9qJ1An&trade_name=trade_name&goods_detail=goods_detail&return_url=http%3A%2F%2Fwww.qfpay.com%2F&pay_tag=ALIPAYHK&seller_id=testoverseas9191%40alipay.com
QFPay Response:
{
"pay_type": "801510",
"sysdtm": "2021-05-27 11:57:02",
"paydtm": "2021-05-27 11:57:02",
"udid": "qiantai2",
"txcurrcd": "HKD",
"txdtm": "2021-05-27 11:57:00",
"txamt": "1",
"resperr": "交易成功",
"respmsg": "",
"out_trade_no": "052711570017898",
"syssn": "20210527154100020004180921",
"pay_params": {
"body": "goods_info",
"forex_biz": "FP",
"seller_id": "2088231067382451",
"secondary_merchant_id": "1000007081",
"service": "mobile.securitypay.pay",
"payment_inst": "ALIPAYHK",
"it_b_pay": "30m",
"secondary_merchant_name": "IFlare Hong Kong Limited (external) - online",
"_input_charset": "UTF-8",
"sign": "iU1yXUnsCK7rJAu0DoN61arVexbIfo3GLR5jr3QzjkZ29INSPhcA4e%2F2%2BdPrsf5huzQAkxVKP0CTfvaGPMYqNkxmhoaJWUH0ZhgYDgKugMvtweBvRqOX2W0h3A%2F%2FIdJuxeyOAuh7bHiuazSB3ZH%2BEQwRGP%2Bkk8Jpha930gHwPtw%3D",
"currency": "HKD",
"out_trade_no": "20210527154100020004180921",
"payment_type": "1",
"total_fee": 0.01,
"sign_type": "RSA",
"notify_url": "https://test-o2-hk.qfapi.com/trade/alipay_hk/v1/notify",
"partner": "2088231067382451",
"secondary_merchant_industry": "5941",
"product_code": "NEW_WAP_OVERSEAS_SELLER",
"return_url": "http://www.qfpay.com/",
"subject": "goods_name"
},
"respcd": "0000",
"chnlsn": "",
"cardcd": ""
}
Request using Alipay SDK:
(After you receive the content of pay_params, when you call Alipay SDK,
the orderinfo request parameters need to be adjusted in the following format:
Combine all the array values in the format of key="value", request parameters
in ascending order of parameter name, then use "&" to link the parameters,
"sign" and "sign_type" parameters need to be placed at the end.)
Sample:
_input_charset="UTF-8"&body="goods_info"¤cy="HKD"&forex_biz="FP"&it_b_pay="30m"¬ify_url="https://test-o2-hk.qfapi.com/trade/alipay_hk/v1/notify"&out_trade_no="20210527154100020004180921"&partner="2088231067382451"&payment_inst="ALIPAYHK"&payment_type="1"&product_code="NEW_WAP_OVERSEAS_SELLER"&return_url="http://www.qfpay.com/"&secondary_merchant_id="1000007081"&secondary_merchant_industry="5941"&secondary_merchant_name="IFlare Hong Kong Limited (external) - online"&seller_id="2088231067382451"&service="mobile.securitypay.pay"&subject="goods_name"&total_fee="0.01"&sign="iU1yXUnsCK7rJAu0DoN61arVexbIfo3GLR5jr3QzjkZ29INSPhcA4e%2F2%2BdPrsf5huzQAkxVKP0CTfvaGPMYqNkxmhoaJWUH0ZhgYDgKugMvtweBvRqOX2W0h3A%2F%2FIdJuxeyOAuh7bHiuazSB3ZH%2BEQwRGP%2Bkk8Jpha930gHwPtw%3D"&sign_type="RSA"
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | — |
Product description | goods_info |
No | String | Must not contain special characters |
Payment mark | pay_tag |
No | String(16) | The default value is: ALIPAYHK Alipay Continental version: ALIPAYCN 801103 - Alipay overseas online refund (QF_BUSICD_ALIPAY_ONLINE_REFUND) 801104 - Alipay overseas online inquiry (QF_BUSICD_ALIPAY_ONLINE_QUERY) 801110 - Alipay overseas online APP payment (QF_BUSICD_ALIPAY_ONLINE_APP) 801501 - Alipay Hong Kong pc scan code 801512 - Alipay Hong Kong Wap payment 801510 - Alipay Hong Kong APP payment |
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. Available for: 800201 - WeChat scan code 800101 - Alipay scan code 801512 - Alipay Hong Kong Wap payment 801501 - Alipay Hong Kong scan code 801107 - Alipay overseas Wap payment 801101 - Alipay overseas scan code 801010 - WeChat Hong Kong APP 801510 - Alipay Hong Kong APP |
Response Parameters
Parameter code | Secondary parameter code | Parameter name |
---|---|---|
pay_params |
partner |
Partner |
seller_id |
Unique Alipay user number referencing the Alipay payment account | |
subject |
Product name / transaction number / order number / order keyword, etc. | |
body |
A specific description of a transaction. If it refers to a basket of products, please accumulate the product description string in the body. | |
total_fee |
Total amount | |
notify_url |
Notification address | |
service |
Service | |
cardcd |
Card number | |
payment_type |
Payment type | |
_input_charset |
Encoding format | |
it_b_pay |
Custom timeout parameter | |
return_url |
Redirect URL | |
payment_inst |
Payment institution | |
currency |
Currency | |
product_code |
Product code | |
sign |
Required or not | |
sign_type |
Signature type | |
secondary_merchant_id |
Secondary merchant identification | |
secondary_merchant_name |
Secondary business name | |
secondary_merchant_industry |
Secondary merchant industry | |
chnlsn |
Channel coding | |
Public response parameters | — | — |
WeChat Web QRC Payments
For code instructions select Python, Java, Node.js or PHP with the tabs above.
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' #In USD,EUR,etc. Cent
txcurrcd = 'EUR'
pay_type = '800201'
auth_code='283854702356157409' #CPM only
out_trade_no = '01234567890123'
txdtm = current_time
goods_name = 'test1'
mchid = 'ZaMVg*****'
key = client_key
#data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'goods_name': goods_name, 'udid': udid, 'mchid': mchid}
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="800201";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="EUR";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In USD,EUR,etc. Cent
'txcurrcd': 'EUR',
'pay_type': '800201',
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://test-openapi-eur.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '800201';
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //字典排序A-Z升序方式
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"sysdtm": "2020-04-10 11:45:44",
"paydtm": "2020-04-10 11:45:44",
"txcurrcd": "THB",
"respmsg": "OK",
"qrcode": "weixin://wxpay/bizpayurl?pr=4PsXP5N",
"pay_type": "800201",
"cardcd": "",
"udid": "qiantai2",
"txdtm": "2020-04-10 11:45:44",
"txamt": "300",
"resperr": "success",
"out_trade_no": "3Z6HPCS6RN54J2Y8LUQM8RBDVBA9URYE",
"syssn": "20200410000300020086358791",
"respcd": "0000",
"chnlsn": ""
}
Customers can use WeChat's "Scan" feature to scan a payment code generated by a merchant on a web page to make a payment. Web payments allows order information to be embedded in a unique QR code. By scanning the code in WeChat, users complete the payment after passing obligatory security checks.
Optionally merchants can activate real-name authentication with WeChat. Currently real-name identification is only available for Mainland Chinese citizens and include a person's real name and national ID card number. In case identification is provided the payer's wallet information like a connected bank card must be identical with the data provided by merchants. If customers did not yet bind their WeChat account to a bank card the payment will go through regardless.
HTTP Request
POST ../trade/v1/payment
PayType: 800201
Request Parameters
Parameter name | Parameter code | Secondary parameter | Mandatory | Type | Description |
---|---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) | |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies | |
Payment type | pay_type |
Yes | String(6) | WeChat online payments PayType 800201 |
|
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. | |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
|
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. Available for: 800201 - WeChat scan code |
|
Product name identification | goods_name |
No | String(64) | Goods Name / Marking: Cannot exceed 20 alphanumeric or contain special characters. Cannot be empty for app payment. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. | |
QF Pay merchant number | mchid |
No | String(16) | May or may not be given to merchant. If MCHID is given, it is mandatory to provide the MCHID .On the contrary, if MCHID is not provided, merchants shall not pass the MCHID field in the API request. | |
Time zone | txzone |
No | String(5) | Transaction Time zone: Record of the transaction in local time, default time zone is Beijing time UTC+8 (+0800). | |
Device ID | udid |
No | String(40) | Unique transaction device ID. Is displayed on the merchant portal. | |
RMB Tag | rmb_tag |
No | String(1) | WeChat Pay in Hong Kong uses rmb_tag = Y together with txcurrcd = CNY to indicate that the transaction currency is RMB. |
|
Extended Customer Info | extend_info |
user_creid user_truename |
No | Object | Real name customer identification. This parameter is currently only available for Mainland Chinese citizens and needs to be explicitly activated with WeChat for the selected PayType. The consumer's national ID card number is contained in the parameter user_creid and the payer's real name in encoded form or written in Chinese characters must be provided in user_truename . An example looks like this; extend_info = '{"user_creid":"430067798868676871","user_truename":"\\u5c0f\\u6797"}' |
Response Parameters
Parameter name | Parameter code | Type | Description |
---|---|---|---|
Payment type | pay_type |
String(6) | WeChat online payments PayType 800201 |
System transaction time | sysdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Request transaction time | txdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss |
Response message | resperr |
String(128) | |
Payment amount | txamt |
Int(11) | |
Other message information | respmsg |
String(128) | |
External transaction number | out_trade_no |
String(128) | External transaction number |
QFPay transaction number | syssn |
String(40) | |
Return code | respcd |
String(4) | 0000 = Request successful. 1143/1145 = merchants are required to continue to query the transaction result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
WeChat Pay JSAPI (in wechat browser)
JSAPI Payment Types
Note: Merchants in Canada, please refer to this section for payment request and response parameters with pay_type
800207.
There are two different methods how JSAPI payments can be implemented.
1. JSAPI with Real Name Authenticated Official Accounts
For this kind of integration, merchants shall register their own official account with WeChat and we will bind the official account to the merchant's QF Pay payment account. In this case merchants can create and publish their own content, access customer information and collect their own followers. When choosing this implementation method, merchants have to acquire the oauth_code
, user openid
and trigger WeChat Pay via the official WeChat platform. Merchants only need to refer to the QF Pay transaction enquiry API endpoint.
Step 1: WeChat official account payments are available to developers after they completed real name authentication on the WeChat official account platform. Once authentication has been completed developers can obtain the openid parameter of the certified public account. Please refer to the official WeChat documentation for more information.
Step 2: Request the QFPAY order payment API /trade/v1/payment
by providing the appointed openid
and return the pay_params
data, for further instructions please refer to the API Endpoint for Payments.
Step 3: Open JSAPI Payment Authorization Directory at the time of the merchant certification application to initiate payments. For more details please refer to the official WeChat Pay documentation.
2. JSAPI without a Real Name Registered Official Account
For this kind of payment, merchants can build upon QF Pay's official account. This integration is only applicable to merchants who are using the indirect settlement option (i.e. settlement is provided by QFPay). For this implementation, merchants shall use QFPay's API to get the oauth_code
, user openid
and trigger WeChat Pay as described below.
GET oauth_code
GET WeChat oauth_code request:
{
https://openapi-test.qfpay.com/tool/v1/get_weixin_oauth_code?app_code=5D81D64E602043F7AF51CEXXXXXXXXXX&sign=F4D8FB00894F213993B33116BC1B4E10&redirect_uri=https://sdk.qfapi.com
}
import hashlib
import requests
from flask import Flask, redirect
from flask import request
import json
import random
import datetime
import string
import urllib
import urllib.parse
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = "******"
client_key = "******"
# Create MD5 signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
def get_out_code():
# Body payload
redirect_uri = 'http://49ae4dbd47a6.ngrok.io/getcode'
data = {'app_code': app_code, 'redirect_uri': redirect_uri}
sign = make_req_sign(data, client_key)
return environment+"/tool/v1/get_weixin_oauth_code?app_code="+app_code+"&sign="+sign+"&redirect_uri="+redirect_uri #+"&mchid="+mchid
Redirect to URL after the GET oauth_code request has been successful:
{
"http://xg.fshop.top/index.php/wap/pay/wxredirect?showwxpaytitle=1&code=011QipnO1yMIla1VJdoO1FUrnO1Qipnv"
}
HTTP Request
GET ../tool/v1/get_weixin_oauth_code
Both the app_code
and sign
have to be submitted as parameters instead of in the http header. The URL request has to be send in the WeChat environment. Everytime a payment is initiated the WeChat oauth_code
and openid
have to be obtained again.
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Developer ID | app_code |
Yes | String(32) | The app_code is assigned to partners by QFPay |
Callback URL | redirect_uri |
Yes | String(512) | After the request has been successful the user will be redirected to the callback address |
Merchant ID | mchid |
No | String(16) | The mchid is a unique identification for every merchant assigned by QFPay |
Signature | sign |
Yes | String | Signature obtained according to the unified framework |
GET openid
HTTP Request:
{
https://openapi.qfpay.com/tool/v1/get_weixin_openid?code=011QipnO1yMIla1VJdoO1FUrnO1Qipnv
}
def get_open_id(data):
try:
r = requests.get(environment+"/tool/v1/get_weixin_openid",params=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, client_key)})
print (r.request.url)
print (r.content)
if r.json()["respcd"]=="0000":
return r.json()["openid"]
else:
pass
except:
print("An exception occurred")
The above command returns JSON structured like this:
{
"resperr": "",
"respcd": 0000,
"respmsg": "",
"openid": "oo3Lss8d0hLOuyTuSJMVwLTk68JE"
}
HTTP Request
GET ../tool/v1/get_weixin_openid
Request Parameters
Parameter code | Secondary parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
WeChat oauth_code | code |
Yes | String | The code is returned by the GET oauth_code request. It is unique and can only be used once. |
Merchant ID | mchid |
No | String(16) | The mchid is a unique identification for every merchant assigned by QFPay |
Response Parameters
Parameter code | Secondary parameter code | Parameter type | Parameter name | Description |
---|---|---|---|---|
openid |
String(64) | WeChat openid | Every WeChat user is assigned a unique openid |
POST Payments
For code instructions select Python with the tabs above.
def payment(openid):
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
# Body payload
txamt = '1' #In USD,EUR,etc. Cent
txcurrcd = 'THB'
pay_type = '800207'
letters = string.digits
out_trade_no = ''.join(random.choice(letters) for i in range(20))
txdtm = current_time
key = client_key
data = {'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'sub_openid':openid}
try:
r = requests.post(environment+"/trade/v1/payment",params=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
if r.json()["respcd"]=="0000":
return r.json()['pay_params']
else:
pass
except:
print("An exception occurred")
app = Flask(__name__)
@app.route("/payment",methods=['GET', 'POST'])
def api_payment():
if "MichroMessenger" in request.headers.get('User-Agent'): #get an oauth_code
print (get_out_code())
return redirect(get_out_code(), code=302)
@app.route("/getcode",methods=['GET', 'POST'])
def api_get_code():
print ("------------------------------------")
print (request.args)
print (request.args.get("code"))
code = request.args.get('code')
print (code)
if code != "": # user returned with oauth_code
sub_openid=get_open_id({"code": code}) # get open id using oauth_code
param=payment(sub_openid) # payment request to QFPay
# add necessary parameters and redirect
param["mchntnm"]="Pet Shop"
param["txamt"]="1"
param["currency"]="THB"
param["redirect_url"]="www.example.com"
return redirect("https://o2-th.qfapi.com/q/direct?"+urllib.parse.urlencode(param), code=302) # direct user"""
else:
print("unable to obtain code")
return
if __name__ == '__main__':
app.run(host="127.0.0.1",port = 80)
Optionally merchants can activate real-name authentication with WeChat. Currently real-name identification is only available for Mainland Chinese citizens and include a person's real name and national ID card number. In case identification is provided the payer's wallet information like a connected bank card must be identical with the data provided by merchants. If customers did not yet bind their WeChat account to a bank card the payment will go through regardless.
HTTP Request
POST ../trade/v1/payment
PayType: 800207
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | Refer to the general documentation about transactions |
WeChat authorization code | sub_openid |
Yes | String | WeChat OpenID. Refer to the GET openid documentation |
Designated payment method | limit_pay |
No | String | Used to limit credit card transactions |
Extended Customer Info | extend_info |
No | Object | Real name customer identification. This parameter is currently only available for Mainland Chinese citizens and needs to be explicitly activated with WeChat for the selected PayType. The consumer's national ID card number is contained in the parameter user_creid and the payer's real name in encoded form or written in Chinese characters must be provided in user_truename . An example looks like this; extend_info = '{"user_creid":"430067798868676871","user_truename":"\\u5c0f\\u6797"}' |
Response Parameters
Parameter code | Secondary parameter code | Parameter type | Parameter name | Description |
---|---|---|---|---|
pay_params |
appId |
String(16) | Public number id | The App ID is provided by Tencent once developers register their Mini Program on the WeChat developer portal |
— | timeStamp |
String(32) | Timestamp | Current time |
— | nonceStr |
String(32) | Random string | Random string with no more than 32 bits |
— | package |
String(128) | Transaction details | The value of the prepay_id parameter returned by the interface has the format: prepay_id=** |
— | signType |
String(32) | Signature method | Signature method, default is MD5 |
— | paySign |
String(64) | Signature | Signature method, default is MD5 |
— | Public response parameters | — | — | — |
txcurrcd |
String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
GET WeChat Pay Data
HTTP Request
GET https://o2.qfpay.com/q/direct
Request Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
mchntnm |
Yes | String(128) | Custom business name. Parameter needs to be UTF-8 encoded if it is written in Chinese characters |
txamt |
Yes | Int(11) | Amount |
currency |
Yes | String(3) | |
goods_name |
No | String(64) | Custom goods name. Parameter needs to be UTF-8 encoded if it is written in Chinese characters |
redirect_url |
Yes | String(512) | Redirect URL after Payment is complete. Urlencode handles this parameter |
package |
Yes | String(128) | Parameter return from WeChat after calling the payment API |
timeStamp |
Yes | String(32) | Parameter return from WeChat after calling the payment API |
signType |
Yes | String(32) | Parameter return from WeChat after calling the payment API |
paySign |
Yes | String(64) | Parameter return from WeChat after calling the payment API |
appId |
Yes | String(16) | Parameter return from WeChat after calling the payment API |
nonceStr |
Yes | String(32) | Parameter return from WeChat after calling the payment API |
WeChat Pay H5 (in mobile browser)
HTTP Request
POST ../trade/v1/payment
PayType: 800212
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | Refer to the general documentation about transactions |
Extended Customer Info | extend_info |
Yes | Object |
extend_info:
{"scene_info":{
"h5_info": { // h5支付固定传"h5_info"
"type": "Wap", //场景类型
"wap_url": "https://qfpay.com/h5/pay", //WAP网站URL地址
"wap_name": "qfpay" //WAP 网站名
} },
"spbill_create_ip": "192.168.1.10"// 用户真实ip地址获取指引 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_5
}
extend_info:
Parameter code | Parameter code | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|---|
scene_info |
Yes | Object | |||
h5_info |
Yes | Object | |||
type |
Yes | String | scene type "Wap" | ||
wap_url |
Yes | String | mobile website address | ||
wap_name |
Yes | String | mobile website name | ||
spbill_create_ip |
Yes | String | IP address of user |
Response Parameters
Parameter code | Secondary parameter code | Parameter Type | Parameter name | Description |
---|---|---|---|---|
Public response parameters | — | — | — | — |
Payment URL | pay_url |
Yes | String |
pay_url
https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096
Payment URL after redirect_url inserted
https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096
&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn
WeChat Mini Programs
HTTP Request
POST ../trade/v1/payment
PayType: 800213
Step 1: WeChat real name authentification Before the payment function within WeChat can be used the business personnel must authenticate themselves on the official WeChat platform.
Step 2: Get openid Once real name authentication has been completed, the openid parameter is obtained through the small program of the real name of the merchant. The specific acquisition method is described in the Wechat documentation.
Step 3: Send payment request Initiate a payment request with the parameters below.
Optionally merchants can activate real-name authentication with WeChat. Currently real-name identification is only available for Mainland Chinese citizens and include a person's real name and national ID card number. In case identification is provided the payer's wallet information like a connected bank card must be identical with the data provided by merchants. If customers did not yet bind their WeChat account to a bank card the payment will go through regardless.
For code instructions select Node.js with the tabs above.
qfPayOpenAPI: function () {
let app_code = 'A2BE4E015A8A4B0A8E9D88**********';
let client_key = '498717301B0846D1992B6F**********';
let environment = 'https://openapi-sg.qfapi.com/trade/v1/payment';
let openid = this.data.openid;
let amount = this.data.amount * 100;
let random_number = String(Math.round(Math.random() * 1000000000));
let datetime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
let payload = {
txamt: 100,
txcurrcd: 'SGD',
pay_type: '800213',
out_trade_no: '0123456789',
txdtm: '2020-07-03 03:14:29',
sub_openid: 'oS80_5dxekECAOlVBeQFk34q123s'
};
var ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
var signature = utilMd5.hexMD5(string).toUpperCase()
console.log(signature)
wx.request({
url: environment,
data: payload,
method: 'POST',
header: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': signature,
'content-Type': 'application/x-www-form-urlencoded'
},
success: (res) => {
if (res.statusCode == 200) {
console.log(res)
console.log(res.data)
this.weChatPayment(res);
}
},
fail: (err) => {
console.log(err);
},
complete: (res) => {
wx.hideLoading();
console.log("API request completed")
}
})
},
Request Parameters
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Public payment parameters | — | — | — | Refer to the public payment API documentation |
WeChat authorization code | sub_openid |
Yes | String(128) | |
Order expiration time | expired_time |
No | String(3) | QRC expiration time in unit minutes. The default QRC expiration time for WeChat Mini Programs is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. |
Designated payment method | limit_pay |
No | String | The parameter value is specified as no_credit , and credit card payment is prohibited. This setting is only valid for mainland China. |
Extended Customer Info | extend_info |
No | Object | Real name customer identification. This parameter is currently only available for Mainland Chinese citizens and needs to be explicitly activated with WeChat for the selected PayType. The consumer's national ID card number is contained in the parameter user_creid and the payer's real name in encoded form or written in Chinese characters must be provided in user_truename . An example looks like this; extend_info = '{"user_creid":"430067798868676871","user_truename":"\\u5c0f\\u6797"}' |
Response Parameters
Parameter code | Secondary parameter code | Parameter Type | Parameter name | Description |
---|---|---|---|---|
pay_params |
appId |
String(16) | Public WMP ID | After the developer registers the Mini Program with the WeChat, the appId can be obtained. |
— | timeStamp |
String(32) | Timestamp | Current time |
— | nonceStr |
String(32) | Random string | Random string, no longer than 32 bits |
— | package |
String(128) | Order details extension string | The value of the prepay_id parameter returned by the unified interface is in the format of prepay_id=** |
— | signType |
String(32) | Signature method | Signature type, default is MD5 |
— | paySign |
String(64) | Signature | |
Public response parameters | — | — | — | — |
txcurrcd |
Currency | Transaction currency. View the Currencies table for a complete list of available currencies |
Step 4: Evoke the payment module
For code instructions select Node.js with the tabs above.
weChatPayment: function(res) {
wx.requestPayment(
{
'timeStamp': '1593746074',
'nonceStr': '69d8a67fe34e44ca9bb2a20dd299cc58',
'package': 'prepay_id=wx03111434674853b80d25e0911996417600',
'signType': 'MD5',
'paySign': 'B0AECE676746F2A310CB06F27641E809',
'success': function(res){},
'fail': function(res){},
'complete': function(res){}
})
},
Obtain the pay_params
parameter, and then provide payment details accordingly. For more details, please refer to the
Wechat documentation.
WeChat Mini Program Boilerplate
To get started quickly, download the QF Pay WeChat Mini Program Boilerplate and get access to the MD5 hash algorithm.
Setup Instructions
1) Sign up with QFPay and we bind your WeChat appid to your API credentials.
2) Visit the WeChat MP portal at https://mp.weixin.qq.com and whitelist our environment for incoming server traffic:
开发 -> 开发设置 -> 服务器域名 -> request合法域名: e.g. https://openapi-sg.qfapi.com
3) Copy and paste the files from the zip file to your local harddrive and setup a cloudfunction environment.
4) Obtain the user openid with the cloudfunction "getUserOpenID" and run the API calls accroding to the code.
WeChat in-APP Payments
HTTP Request
POST ../trade/v1/payment
PayType: 800210
WeChat in-APP payments require a formal application on the WeChat Open Platform. Merchants have to register an account and the APP and then receive an appid
to enable payments. For more information, please refer to the official Wechat documentation.
Optionally merchants can activate real-name authentication with WeChat. Currently real-name identification is only available for Mainland Chinese citizens and include a person's real name and national ID card number. In case identification is provided the payer's wallet information like a connected bank card must be identical with the data provided by merchants. If customers did not yet bind their WeChat account to a bank card the payment will go through regardless.
To download Wechat SDK please refer to this link.
Request Parameters
Request Body:
{
goods_info=test_app&goods_name=qfpay&out_trade_no=O5DNgEgL1XpvbvQSfPhN&pay_type=800210&txamt=10&txcurrcd=HKD&txdtm=2019-09-13 04:53:03&udid=AA
}
The above command returns JSON structured like this:
{
"sysdtm": "2019-09-13 12:53:04",
"paydtm": "2019-09-13 12:53:04",
"txcurrcd": "HKD",
"respmsg": "",
"pay_params":
{
"package": "Sign=WXPay",
"timestamp": 1568350384,
"sign": "XwFjohEKWdkhhT4ueg7BxeDn8tT9LcqoZYdXzifTMYyDGe3/tRchpii6vWgOn21tPSaAtqo766gvifXgDEOwR+ILKN8t97r624IJlrH0EkvSUSLh9E/cga9scXGVy0jPWHM/oVvVzJIvXew79CwZFCNTSJok2KmpSm9X9oPg7PGXbqvNMHltf+YlIOsuiz391qVmFtTE5A/cpA50+06T7iW8GYsOJQTTJed75VY+aSzNo5C6ju6WSgJKpAJJ0ocl+ONtmOp6GLVBSQXaMC4PitQcebcoP2J6fFgQ+YcPwHXasCYEnn4LaFN7zT/AjGg3E3gdCx3ksGNBOazYBRVz+g==",
"partnerid": "316525492",
"appid": "wx3c6896fa9b351f2a",
"prepayid": "wx131253044253463a81dc336e1254149882",
"noncestr": "7786db42d9a245c2b1cfc717ac59376e"
},
"pay_type": "800210",
"cardcd": "",
"udid": "AA",
"txdtm": "2019-09-13 04:53:03",
"txamt": "10",
"resperr": "交易成功",
"out_trade_no": "O5DNgEgL1XpvbvQSfPhN",
"syssn": "20190913152100020001567741",
"respcd": "0000",
"chnlsn": ""
}
Parameter name | Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|---|
Merchant ID | mchid |
No | String | The unique merchant ID is created by QF Pay during the merchant onboarding process. |
External transaction number | out_trade_no |
Yes | String | Developer platform transaction number |
Transaction amount | txamt |
Yes | String | The actual amount of consumption, the maximum deduction amount cannot exceed the fozen funds |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
RMB Tag | rmb_tag |
No | String(1) | WeChat Pay in Hong Kong uses rmb_tag = Y together with txcurrcd = CNY to indicate that the transaction currency is RMB. |
Transaction request time | txdtm |
Yes | String | Format: YYYY-MM-DD hh:mm:ss |
Device ID | udid |
No | String | Must be unique |
Time zone | txzone |
No | String | Used to record the local order time. The default is Beijing time GMT+8 (+0800) |
Redirect URL | return_url |
No | String | Redirect to address after successful payment. Mandatory parameter to submit for GrabPay Online. Alipay WAP restricts the return_url to maximum 200 characters. |
Extended Customer Info | extend_info |
No | Object | Real name customer identification. This parameter is currently only available for Mainland Chinese citizens and needs to be explicitly activated with WeChat for the selected PayType. The consumer's national ID card number is contained in the parameter user_creid and the payer's real name in encoded form or written in Chinese characters must be provided in user_truename . An example looks like this; extend_info = '{"user_creid":"430067798868676871","user_truename":"\\u5c0f\\u6797"}' |
Response Parameters
Parameter code | Parameter type | Parameter name | Description |
---|---|---|---|
syssn |
String(40) | QF Pay Transaction number | QFPay transaction number, returned by the system once payment is completed |
orig_syssn |
String(40) | External transaction number | Developer platform transaction number |
txdtm |
String(20) | Transaction request time | Format: YYYY-MM-DD hh:mm:ss |
txamt |
Int(11) | Transaction amount | |
sysdtm |
String(20) | System transaction time | Format: YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
respcd |
String(4) | Return code | |
respmsg |
String(128) | Information description | |
resperr |
String(128) | Description error | |
cardcd |
String | Card number | |
txcurrcd |
String | Currency | Transaction currency. View the Currencies table for a complete list of available currencies |
pay_params |
Object | payment data | Payment data to call Wechat SDK |
PayMe Online Payment
For code instructions select Python, Java, Node.js or PHP with the tabs above.
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '10' # In cents
txcurrcd = 'HKD'
pay_type = '805814' # PayMe Web Payment = 805814
out_trade_no = '01234567890123'
txdtm = current_time
goods_name = 'test1'
mchid = 'ZaMVg*****'
key = client_key
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'mchid': mchid}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String mchid="ZaMVg*****";
String pay_type="805801";
String out_trade_no= "01234567890123";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txamt="10";
String txcurrcd="HKD";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("mchid", mchid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
//unsortMap.put("product_name", product_name);
//unsortMap.put("valid_time", "300");
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var tradenumber = String(Math.round(Math.random() * 1000000000))
console.log(tradenumber)
var payload = {
'txamt': '10', // In cents
'txcurrcd': 'HKD',
'pay_type': '805814', // PayMe Web Payment = 805814
'out_trade_no': tradenumber,
'txdtm': dateTime,
'mchid': 'ZaMVg*****'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://openapi-test.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '805814'; //PayMe Web Payment = 805814
//$mchid = "MNxMp11FV35qQN"; //Only agents must provide this parameter
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
//'mchid' => urlencode($mchid),
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(GetRandStr(20)),
'txcurrcd' => urlencode('EUR'),
'txamt' => urlencode(2200),
'txdtm' => $now_time
);
ksort($fields); //字典排序A-Z升序方式
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"sysdtm": "2020-04-13 10:30:34",
"paydtm": "2020-04-13 10:30:34",
"txcurrcd": "HKD",
"respmsg": "",
"pay_type": "805814",
"cardcd": "",
"udid": "qiantai2",
"txdtm": "2020-04-13 10:30:34",
"txamt": "300",
"resperr": "success",
"out_trade_no": "4K35N374II7UJJ8RGIAE45O2CVHGHFF0",
"syssn": "20200413000300020087033882",
"respcd": "0000",
"pay_url": "https://qr.payme.hsbc.com.hk/2/C5bvYGEyrgXbxxxxxxxxxx",
"chnlsn": ""
}
Web/WAP Payment
Customers make purchases on a merchant website with PayMe. The user scans the displayed QR code to pay, confirms the total amount and makes the payment. Finally the customer can be redirected to a selected page on the merchant's website using the return_url
parameter. PayMe deducts the payment amount from the consumer's PayMe wallet in real-time in HKD and QFPay settles the payment amount to merchants in HKD.
HTTP Request
POST ../trade/v1/payment
PayType: 805814
PayMe Online WEB (in browser Chrome etc.) Payment (HK Merchants)
PayType: 805812
PayMe Online WAP (in mobile browser Chrome etc.) Payment (HK Merchants)
Request Parameters
Parameter name | Parameter code | Mandatory | Type | Description |
---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
Payment type | pay_type |
Yes | String(6) | PayMe Web Payment = 805814 |
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. |
Product name identification | goods_name |
No | String(64) | Goods Name / Marking: Cannot exceed 20 alphanumeric or contain special characters. Cannot be empty for app payment. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. |
QF Pay merchant number | mchid |
No | String(16) | May or may not be given to merchant. If MCHID is given, it is mandatory to provide the MCHID .On the contrary, if MCHID is not provided, merchants shall not pass the MCHID field in the API request. |
Time zone | txzone |
No | String(5) | Transaction Time zone: Record of the transaction in local time, default time zone is Beijing time UTC+8 (+0800). |
Device ID | udid |
No | String(40) | Unique transaction device ID. Is displayed on the merchant portal. |
Redirect URL | return_url |
No | String(512) | URL that the user will be redirected to when the payment finishes. |
Response Parameters
Parameter name | Parameter code | Type | Description |
---|---|---|---|
Payment type | pay_type |
String(6) | PayMe Web/Wap Payment |
System transaction time | sysdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Request transaction time | txdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss |
Response message | resperr |
String(128) | |
Payment amount | txamt |
Int(11) | |
Other message information | respmsg |
String(128) | |
External transaction number | out_trade_no |
String(128) | External transaction number |
QFPay transaction number | syssn |
String(40) | |
Return code | respcd |
String(4) | 0000 = Request successful. 1143/1145 = merchants are required to continue to query the transaction result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
Payment URL | pay_url |
String(512) | generate QR code in Desktop web; redirect URL in WAP |
PayMe Offline Payment
For code instructions select Python, Java, Node.js or PHP with the tabs above.
#coding=utf8
import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
import requests
import datetime
import string
# Enter Client Credentials
environment = 'https://openapi-test.qfpay.com'
app_code = 'D5589D2A1F2E42A9A60C37*********'
client_key = '0E32A59A8B454940A2FF39**********'
# Create parameter values for data payload
current_time = datetime.datetime.now().replace(microsecond=0)
print(current_time)
# Create signature
def make_req_sign(data, key):
keys = list(data.keys())
keys.sort()
p = []
for k in keys:
v = data[k]
p.append('%s=%s'%(k,v))
unsign_str = ('&'.join(p) + key).encode("utf-8")
s = hashlib.md5(unsign_str).hexdigest()
return s.upper()
# Body payload
txamt = '200' # In cents
txcurrcd = 'HKD'
pay_type = '805801' # PayMe Offline Payment = 805801
out_trade_no = '16565588217444950016'
txdtm = current_time
goods_name = 'qfpay_payme'
return_url = 'http://www.qfpay.com'
txzone = '+0800'
udid = 'my_udid'
key = client_key
data ={'txamt': txamt, 'txcurrcd': txcurrcd, 'pay_type': pay_type, 'out_trade_no': out_trade_no, 'txdtm': txdtm, 'udid': udid, 'return_url': return_url, 'txzone': txzone}
r = requests.post(environment+"/trade/v1/payment",data=data,headers={'X-QF-APPCODE':app_code,'X-QF-SIGN':make_req_sign(data, key)})
print(r.json())
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TestMain {
public static void main(String args[]){
String appcode="D5589D2A1F2E42A9A60C37*********";
String key="0E32A59A8B454940A2FF39*********";
String pay_type="805801";
String out_trade_no= "165655882174";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=df.format(new Date());
String txdtm=date;
String txzone="+0800";
String txamt="200";
String txcurrcd="HKD";
String usid="myudid";
String return_url="http://www.qfpay.com";
String goods_name="qfpay_payme";
Map<String, String> unsortMap = new HashMap<>();
unsortMap.put("udid", udid);
unsortMap.put("pay_type", pay_type);
unsortMap.put("out_trade_no", out_trade_no);
unsortMap.put("txdtm", txdtm);
unsortMap.put("txzone", txzone);
unsortMap.put("txamt", txamt);
unsortMap.put("txcurrcd", txcurrcd);
unsortMap.put("return_url", return_url);
unsortMap.put("goods_name", goods_name);
String data=QFPayUtils.getDataString(unsortMap);
System.out.println("Data:\n"+data+key);
String md5Sum=QFPayUtils.getMd5Value(data+key);
System.out.println("Md5 Value:\n"+md5Sum);
String url="https://openapi-test.qfpay.com";
String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode,key);
System.out.println(resp);
}
}
// Enter Client Credentials
const environment = 'https://openapi-test.qfpay.com'
const app_code = 'D5589D2A1F2E42A9A60C37*********'
const client_key = '0E32A59A8B454940A2FF39*********'
// Generate Timestamp
var dateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')
console.log(dateTime)
// Body Payload
const key = client_key
var payload = {
'txamt' : '200' # In cents
'txcurrcd' : 'HKD'
'pay_type' : '805801' # PayMe Offline Payment = 805801
'out_trade_no' : '16565588217444950016'
'txdtm' = dateTime
'goods_name' : 'qfpay_payme'
'return_url' : 'http://www.qfpay.com'
'txzone' : '+0800'
'udid' : 'my_udid'
};
// Signature Generation
const ordered = {};
Object.keys(payload).sort().forEach(function(key) {
ordered[key] = payload[key] });
console.log(ordered)
var str = [];
for (var p in ordered)
if (ordered.hasOwnProperty(p)) {
str.push((p) + "=" + (ordered[p]));
}
var string = str.join("&")+client_key;
console.log(string)
const crypto = require('crypto')
var hashed = crypto.createHash('md5').update(string).digest('hex')
console.log(hashed)
// API Request
var request = require("request");
request({
uri: environment+"/trade/v1/payment",
headers: {
'X-QF-APPCODE': app_code,
'X-QF-SIGN': hashed
},
method: "POST",
form: payload,
},
function(error, response, body) {
console.log(body);
});
<?php
ob_start();
function GetRandStr($length){
$str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
$randstr='';
for($i=0;$i<$length;$i++){
$num=mt_rand(0,$len);
$randstr .= $str[$num];
}
return $randstr;
}
$url = 'https://openapi-test.qfapi.com';
$api_type = '/trade/v1/payment';
$pay_type = '805801'; //PayMe Offline Payment = 805801
$app_code = 'FF2FF74F2F2E42769A4A73*********'; //API credentials are provided by QFPay
$app_key = '7BE791E0FD2E48E6926043B*********'; //API credentials are provided by QFPay
$now_time = date("Y-m-d H:i:s"); //Get current date-time
$fields_string = '';
$fields = array(
'pay_type' => urlencode($pay_type),
'out_trade_no' => urlencode(16565588217444950016),
'txcurrcd' => urlencode('HKD'),
'txamt' => urlencode(200),
'txzone' => urlencode('+0800'),
'txdtm' => $now_time,
'goods_name' => urlencode('qfpay_payme'),
'return_url' => urlencode('http://www.qfpay.com'),
'udid' => urlencode($myudid),
);
ksort($fields); //字典排序A-Z升序方式
print_r($fields);
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&' ;
}
$fields_string = substr($fields_string , 0 , strlen($fields_string) - 1);
$sign = strtoupper(md5($fields_string . $app_key));
//// Header ////
$header = array();
$header[] = 'X-QF-APPCODE: ' . $app_code;
$header[] = 'X-QF-SIGN: ' . $sign;
//Post Data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . $api_type);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
$output = curl_exec($ch);
curl_close($ch);
$final_data = json_decode($output, true);
print_r($final_data);
ob_end_flush();
?>
The above command returns JSON structured like this:
{
"pay_type" : "805801",
"sysdtm" : "2022-06-30 11:14:29",
"paydtm" : "2022-06-30 11:14:29",
"resperr" : "交易成功",
"txcurrcd" : "HKD",
"txdtm" : "2022-06-30 11:13:41",
"txamt" : "200",
"payme_logo" : "https://shopfront.paymebiz.hsbc.com.hk/onboarding/dfbd33ed8dd4282ccb0f0db0e9301a9a04ab29db0b7e2b7597bff9fd7382776d/businessLogo_300x300.png",
"respmsg" : "PR001:Request for Payment Initiated",
"out_trade_no" : "16565588217444950016",
"syssn" : "20220630154100020012764799",
"respcd" : "0000",
"qrcode" : "https://qr.payme.hsbc.com.hk/2/CmJBJZAXRgCh8GmBXJKuXG",
"udid" : "my_udid",
"pay_url" : "",
"chnlsn" : "5f43d654-d309-4560-8585-fb78f8dc694b",
"cardcd" : ""
}
Offline Payment
For MPM Mode, The merchant generates a dynamic QR code based on the Payme protocol and presents it to the customer. The user opens their PayMe wallet and scans the displayed QR Code in order to complete payment. For CPM Mode, the customer generates a dynamic QR code in their QR code wallet and presents it to the cashier for scanning. If the response codes 1143/1145 are returned, the transaction is being processed or the customer is required to input the wallet password. Merchants have to query the transaction result for a final assessment of the transaction status.
HTTP Request
POST ../trade/v1/payment
PayType: 805801
PayMe Merchant Presented QR Code Payment in store (MPM) (HK Merchants)
PayType: 805808
PayMe Consumer Presented QR Code Payment (CPM) (HK Merchants)
Request Parameters
Parameter name | Parameter code | Mandatory | Type | Description |
---|---|---|---|---|
Payment amount | txamt |
Yes | Int(11) | Amount of the transaction. Unit in cents (i.e. 100 = $1) |
Currency | txcurrcd |
Yes | String(3) | Transaction currency. View the Currencies table for a complete list of available currencies |
Payment type | pay_type |
Yes | String(6) | PayMe Web Payment = 805814 |
API Order Number | out_trade_no |
Yes | String(128) | External transaction number / Merchant platform transaction number: This parameter must be unique for each payment and refund request under the same merchant account in the system. |
Request transaction time | txdtm |
Yes | String(20) | Transaction time format: YYYY-MM-DD hh:mm:ss |
Order expiration time | expired_time |
No (MPM only) |
String(3) | QRC expiration time in unit minutes. The default expiration time is 30 minutes. The parameter can manually be adjusted to a minimum of 5 minutes, and up to a maximum of 120 minutes. |
Product name identification | goods_name |
No | String(64) | Goods Name / Marking: Cannot exceed 20 alphanumeric or contain special characters. Cannot be empty for app payment. Parameter needs to be UTF-8 encoded if it is written in Chinese characters. |
QF Pay merchant number | mchid |
No | String(16) | May or may not be given to merchant. If MCHID is given, it is mandatory to provide the MCHID .On the contrary, if MCHID is not provided, merchants shall not pass the MCHID field in the API request. |
Time zone | txzone |
No | String(5) | Transaction Time zone: Record of the transaction in local time, default time zone is Beijing time UTC+8 (+0800). |
Device ID | udid |
No | String(40) | Unique transaction device ID. Is displayed on the merchant portal. |
Redirect URL | return_url |
No | String(512) | URL that the user will be redirected to when the payment finishes. |
Response Parameters
Parameter name | Parameter code | Type | Description |
---|---|---|---|
Payment type | pay_type |
String(6) | PayMe Web/Wap Payment |
System transaction time | sysdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss This parameter value is used as the cut-off time for settlements. |
Request transaction time | txdtm |
String(20) | Format:YYYY-MM-DD hh:mm:ss |
Response message | resperr |
String(128) | |
Payment amount | txamt |
Int(11) | |
Other message information | respmsg |
String(128) | |
External transaction number | out_trade_no |
String(128) | External transaction number |
QFPay transaction number | syssn |
String(40) | |
Return code | respcd |
String(4) | 0000 = Request successful. 1143/1145 = merchants are required to continue to query the transaction result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
Payment URL | pay_url |
String(512) | generate QR code in Desktop web; redirect URL in WAP |
Customs Declaration
Customs declaration API auto-sends the WeChat/Alipay payment data to the customs to simplify the clearance process and saves time for online cross-border stores.
Push Customs Declaration
HTTP Request
POST ../custom/v1/declare
Request Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
trade_type |
Y | String(8) | weixin or alipay |
syssn |
Y | String(32) | QFPay transaction number |
customs |
Y | String(20) | Customs for declaration. Example:SHANGHAI_ZS |
mch_customs_no |
Y | String(20) | Customs registration No. of the merchant |
action_type |
N | String(256) | Declaration type. Only valid when trate_type is "wechat". "ADD" - new appplication, "MODIFY" - modification of applied declaration |
mch_customs_name |
N | String(256) | Merchant customs record name. Must be passed when trate_type is "alipay". Exaple: jwyhanguo_card |
out_request_no |
N | String(32) | Merchant order number. Must be passed when trate_type is "alipay". Exaple: 15725904083420588032 |
amount |
N | String(20) | Declaration amount. Must be passed when trate_type is "alipay". Example: 2.00 |
The following fields should be passed in case splitting or modifying order:
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
sub_order_no |
C | String(64) | Merchant sub-order No. It is required if there is a split order. Example:1111960490 |
fee_type |
C | String(8) | Currency. Must be passed when trate_type is "wechat". It can only be CNY |
order_fee |
C | String(8) | Sub-order amount (in 0.01 CNY). Cannot exceed the original order amount. order_fee=transport_fee+product_fee. It is required if there is a split order Example:888 |
product_fee |
C | String(8) | Product price (in 0.01 CNY). It is required if there is a split order. Example:888 |
transport_fee |
C | String(8) | Logistics fee (in 0.01 CNY). It is required if there is a split order. Example:888 |
Response Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
syssn |
String(40) | QFPay transaction number | |
respcd |
String(4) | 0000 = Declaration successful. 1143/1145 = merchants are required to continue to query the declaration result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
|
resperr |
String(128) | Response message | |
respmsg |
String(128) | Other message information | |
verify_department |
Verification organization | ||
verify_department_trade_id |
Transaction number of verification organization |
Query Customs Declaration
Merchants query declaration status by QFPay transaction number.
HTTP Request
POST/GET ../custom/v1/query
Request Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
trade_type |
Y | String(8) | weixin or alipay |
customs |
Y | String(20) | Customs for declaration. Example:SHANGHAI_ZS |
syssn |
Y | String(32) | QFPay transaction number |
sub_order_no |
N | String(40) | Sub order number. It is required if there is a split order. |
Response Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
syssn |
String(40) | QFPay transaction number | |
respcd |
String(4) | 0000 = Declaration successful. 1143/1145 = merchants are required to continue to query the declaration result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
|
resperr |
String(128) | Response message | |
respmsg |
String(128) | Other message information | |
data |
Customs declaration details [{"resperr" : "", "errmsg" : null, "sub_order_no" : "15752730835729139712", "verify_department" : "OTHERS", "verify_department_trade_id" : "4200000459201911265585026208"}] |
Repush Customs Declaration
If additional order information has been submitted to the customs but is lost in the electronic port, the customs declaration re-push API can be used to push the information to the customs again.
HTTP Request
POST ../custom/v1/redeclare
Request Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
trade_type |
Y | String(8) | weixin or alipay |
customs |
Y | String(20) | Customs for declaration. Example:SHANGHAI_ZS |
syssn |
Y | String(32) | QFPay transaction number |
mch_customs_no |
Y | String(20) | Customs registration No. of the merchant. Example: 110084111 |
sub_order_no |
N | String(40) | Sub order number. It is required if there is a split order. |
Response Parameters
Parameter code | Mandatory | Parameter type | Description |
---|---|---|---|
syssn |
String(40) | QFPay transaction number | |
respcd |
String(4) | 0000 = Declaration successful. 1143/1145 = merchants are required to continue to query the declaration result. All other return codes indicate transaction failure. Please refer to the page Transaction Status Codes for a complete list of response codes. |
|
resperr |
String(128) | Response message | |
respmsg |
String(128) | Other message information |
FAQs
Q1. In provided test account credentials which one is the partner or the merchant identifier?
A1. If you're an agent which provides payment services for merchants; X-QF-APPCODE and ClientKey are the partner while MCHID is the merchant identifier.
If you're a merchant then X-QF-APPCODE and ClientKey are merchant identifier and MCHID is not provided.
Q2. Can I use given (Test/Production) account in another country?
A2. No. An (Test/Production) account is country specific.
Q3. How can I test a transaction?
A3. Since test environment is a replica of production you can use production wallets to test transactions. If you need any assistance please contact tech support.
Q4. I receive a 1143/1145
response code, what should I do next?
A4. It's advised to keep querying transaction status. If partner/merchant wants to process as binary status (success/fail), can mark the transaction as failed and upon a successful asynchronous notifications can apply for refund at backend.
Q5. There is no specific title for the payment method I want to integrate, what should I do?
A5. You should use Public Payment Parameters and check the special cases in Notes section at the end of the Payment Codes.
Q6. Can I refund a transaction made n day(s) ago?
A6. Only if the total transaction of the day is equal or greater than the refund amount you can refund.
Q7. Do you transfer funds to our bank account when we test transactions in the sandbox environment?
A7. There are no settlements possible in the sandbox environment. Please make sure that you refund test transactions immediately after testing.
Q8. Can I use my overseas Alipay wallet to pay?
A8. No, currently only real-name identified Alipay wallets which belong to Mainland Chinese citizens can be used to conduct cross-border transactions.