NAV
python php java go

简介

FlyPay聚合支付系统

快速接入

支付流程图

avatar

聚合页面示例图

avatar

支付方式(pay_type)

支付方式 pay_type值 code 是否固定金额列表 是否开通 额度限制
电话卡 1 phonecard 按实际面额
MoMo 4 momo 上限2000万,下限1000
网银聚合 36 Banking 上限5000万,下限10万
ZALO 39 ViettelPayQR 上限500万,下限10万
VIETTELPAY 40 ZaloPayQR 上限500万,下限10万

API签名规则

为了确保交易的安全性,本系统对所有交互API采用了一套加密机制,所有API交互均需要使用此套加密规则,以确保合作伙伴资金财产的安全性。加密规则具体如下:

  1. 从《支付系统》获取到secret_key;
  2. 设所有发送或者接收到的数据为集合M,将集合M内参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串String1;
  3. 在String1字符串后面追加您的secret_key,形成String2;
  4. 对String2进行MD5运算,再将得到的字符串所有字符转换为大写,得到值hash;
# hash 计算方法
def gen_http_request_sign(req_params, secret_key):
      # req_params (kv 字典)
      keys = req_params.keys()
      keys.remove("hash")
      s_keys = sorted(keys)
      prestr_list = []
      for key in s_keys:
          value = req_params.get(key)
          if value is None:
              value = ""
          s = str(key) + "=" + str(value)
          prestr_list.append(s)
      prestr = "&".join(prestr_list)
      prestr += secret_key
      sign = gen_md5(prestr)
      sign = sign.upper()
      return sign
// hash 计算方法
function gen_http_request_sign($req_params, $secret_key){
    // req_params (kv array)
    unset($req_params['hash']);
    ksort($req_params);
    $signstr = '';
    foreach ($req_params as $key => $value) {
        $signstr .= $key . '=' . $value . '&';
    }
    $signstr = rtrim($signstr, '&');
    $signstr .= $secret_key;
    $signstr = md5($signstr);
    $signstr = strtoupper($signstr);
    return $signstr;
}
// hash 计算方法
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.SortedMap;
import java.security.MessageDigest;
import java.util.Iterator;
import java.math.BigInteger;

public  String gen_http_request_sign(Map<String,Object> params, String secret)
{
    params.remove("hash");
    // 先将参数以其参数名的字典序升序进行排序
    Map<String, Object> sortedTree = new TreeMap<String, Object>(params);
    // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
    StringBuilder basestring = new StringBuilder();

    int count = 0;
    Iterator<String> it = sortedTree.keySet().iterator();
    while(it.hasNext()){
        String key = it.next();
        String value = sortedTree.get(key).toString();
        if(count > 0){
            basestring.append("&");
        }
        basestring.append(key).append("=").append(value);
        count++;
    }
    basestring.append(secret);
    try {
        // 使用MD5对待签名串求签
        byte[] bytes = null;
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        bytes = md5.digest(basestring.toString().getBytes("UTF-8"));
        //16是表示转换为16进制数
        String sign = new BigInteger(1, bytes).toString(16).toUpperCase();
        return sign;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

}
// hash 计算方法
func gen_http_request_sign(sourceMap map[string]interface{}, bizKey string) string {
    orderedString := orderParam(sourceMap, bizKey)
    md5Ctx := md5.New()
    md5Ctx.Write([]byte(orderedString))
    signString := md5Ctx.Sum(nil)
    return strings.ToUpper(hex.EncodeToString(signString))
}
func orderParam(source map[string]interface{}, bizKey string) string {
    var tempArr []string
    i := 0
    for k, v := range source {
        tempArr = append(tempArr, fmt.Sprintf("%v=%v", k, v))
        i++
    }
    sort.Strings(tempArr)
    temString := ""
    for n, v := range tempArr {
        if n+1 < len(tempArr) {
            temString = temString + v + "&"
        } else {
            temString = temString + v + bizKey
        }
    }
    return temString
}

支付页面param参数生成规则

  1. 设所有发送或者接收到的数据为集合M,计算集合M的hash (方法见API签名规则)
  2. 将集合hash添加到集合M 生成新的集合M1;
  3. 集合M1 json 序列化 生成新的字符串string1;
  4. 集合string1计算base64 url encode 得到param;
  1. 示例代码,见右:
# 支付页面param参数生成方法
def gen_param(req_params,secret_key):
      signstr = gen_http_request_sign(req_params,secret_key)
      req_params['hash'] = signstr
      param_str=json.dumps(req_params)
      param=base64.urlsafe_b64encode(param_str)
      return param
# 支付页面param参数生成方法
function gen_param($req_params,$secret_key){
    $signstr = gen_http_request_sign($req_params,$secret_key);
    $req_params['hash'] = $signstr;
    $param_str=json_encode($req_params);
    $param=base64_encode($param_str);
    return $param;
}
// 支付页面param参数生成方法
public  String gen_param(Map<String, Object> params,   String secret_key) {
    String signstr = gen_http_request_sign(params, secret_key);
    params.put("hash", signstr);
    String param_str = JSON.toJSONString(params);
    try {
        return  Base64.getEncoder().encodeToString(param_str.getBytes("utf-8"));
    }catch (Exception e) {
        throw new RuntimeException(e);
    }
}

// 支付页面param参数生成方法
func gen_param(params map[string]interface{}, secret_key string) string {
    sign := gen_http_request_sign(params, secret_key)
    params["hash"] = sign
    paramsStr, _ := json.Marshal(params)
    param := base64.URLEncoding.EncodeToString([]byte(paramsStr))
    return param
}

mch_url & 通知callback param参数解析规则

  1. 将param base64 url decone 得到字符串string1;
  2. 示例代码,见右:
# mch_url & 通知callback param参数解析方法
def parse_url_param(param):
    params_str = base64.urlsafe_b64decode(param)
    params={}
    for  key_value  in  params_str.split( '&' ):
        kv = key_value.split('=')
        params[kv[0]] = kv[1] 
    return params
# mch_url & 通知callback param参数解析方法
function parse_url_param($param){
    $param_str=base64_decode($param);
    parse_str($param_str,$params);
    return  $params
}
// mch_url & 通知callback param参数解析方法

import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public Map<String, Object> parse_url_param(String param) {
    String paramstr = new  String(Base64.getDecoder().decode(param));
    Map<String, Object> map = new HashMap<String, Object>();
    String[] keyValues = paramstr.split("&");
    for (int i = 0; i < keyValues.length; i++) {
        String key = keyValues[i].substring(0, keyValues[i].indexOf("="));
        String value = keyValues[i].substring(keyValues[i].indexOf("=") + 1);
        map.put(key, value);
    }
    return map;
}
// mch_url & 通知callback param参数解析方法
func parse_url_param(param string) map[string]string{
    base64.URLEncoding.DecodeString(param)
    decodeBytes, err := base64.StdEncoding.DecodeString(encodeString)
    if err != nil {
        //process err
        return nil
    }
    params:=map[string]string{}
    for _,v :=range strings.Split(string(decodeBytes),"&") {
        kv:=strings.Split(v,"=")
        params[kv[0]]=kv[1]
    }
    return params
}

API endpoint

接口

1. 创建flypay支付页面(创建支付订单)

key 类型 长度说明 含义 说明 取值
mch_id string 商户ID,平台分配 支付平台为商户分配mch_id, 与商户一一对应 请联系支付平台工作人员获取 -
mch_uid string max 48 bytes 商户自己的用户uid,由商户平台自己决定赋值内容 - -
mch_order_id string max 48 bytes 商户订单ID
equipment_type int - 访问类型 - 0: 未知类型; 1: PC浏览器; 2: 移动设备浏览器
expected_amount int - 支付金额 - -
mch_url string max 1024 bytes 商户的结果页(订单支付有结果后自动跳转) 必须url safe encode (padding 模式)
show_types string 格式化字符串,"+"分割支付类型 - 定制聚合支付页面中的支付类型 eg: show_types=1+4 表示支付主页显示电话卡支付及momo支付 取值见 支付方式(pay_type),不填则使用平台默认支持的支付方式
attach string 最大512 商户附加参数 只支持ascii 字符,不支持中文,越南文等 -
hash string - API校验规则生成的hash值 详见 API签名规则 -
# 聚合页面param 生成示例
req_params={
"mch_id":'100000',
"mch_uid":"xxxxxx",
"mch_order_id":'02202008101010000020081034811088',
"equipment_type": 1,
"expected_mount":50000,
"mch_url":"http://xxxxx",
"show_types":"3"
}
secret_key = 'N111110dQfPLTxc'
param = gen_param(req_params,secret_key)
url = endpoint + "/public/comm/index.html?param=" + param
# 聚合页面param 生成示例
$req_params =array("mch_id"=> '100000',
"mch_uid"=> "xxxxxx",
"mch_order_id"=> '02202008101010000020081034811088',
"equipment_type"=> 1,
"expected_mount"=> 50000,
"mch_url"=> "http://xxxxx",
"show_types"=>"3");
$secret_key = 'N111110dQfPLTxc';
$param = gen_param($req_params,$secret_key);
$url = $endpoint . "/public/comm/index.html?param=" . $param

2. 支付订单查询接口

key 类型 长度说明 含义 说明 取值
mch_id string 商户ID,平台分配 支付平台为商户分配的mch_id 请联系支付平台工作人员获取 -
mch_order_id string 商户订单号
hash string - API校验规则生成的hash值 详见API签名规则 -
请求举例:
{"mch_id":"000100",
"mch_order_id":"0220200328000100f66c126e70ce11eab7f1595321913560",
"hash":"EF962FBF690F1DDB1D62BE61B6633AC9"
}
key 类型 长度说明 含义 说明 取值
ret int 0:成功 其他失败
msg string ret!=0时错误描述
data object 订单信息
data.mch_id string 商户ID 请联系支付平台工作人员获取
data.mch_order_id string 商户订单号
data.attach string 商户附加参数 -
data.pay_type int 支付方式
data.amount int 支付金额(越南盾)
data.svr_transaction_id string flypay订单号
data.status int 支付状态详见 支付订单状态
返回举例:
{
    "data": {
        "mch_id": "000100",
        "mch_order_id": "0220200328000100f66c126e70ce11eab7f1595321913560",
        "pay_type": 4,
        "amount": 10000,
        "svr_transaction_id": "V220200721010001003556434114268020992",
        "status": 1,
        "attach":"xxxx"
    },
    "ret": 0
}

3.支付订单通知回调

key 含义 说明 取值
mch_id 商户ID - -
mch_order_id 商户订单号 - -
svr_transaction_id 支付平台的交易ID 唯一、字符串、max 64
amount 交易越南盾 -
ts 回调时间戳 -
status 订单状态 支付状态详见 支付订单状态
error 失败错误码 - -
error_descr 失败描述 - -
pay_type 支付类型 - -
attach 商户附加参数 - -
hash API校验规则生成的hash值 详见 API签名规则 -

4. 代付订单创建

key 类型 长度说明 含义 说明 取值
mch_id string 支付平台为商户分配mch_id, 与商户一一对应 请联系支付平台工作人员获取 -
mch_uid string max 48 bytes 商户自己的用户uid,由商户平台自己决定赋值内容 - -
mch_order_id string max 48 bytes 商户订单ID
pay_type int - 支付类型 - 取值见 支付方式(pay_type)
amount int - 支付金额 - -
notify_url string 通知商户代付结果地址
account string 收款人账号 -
bank_name string 收款银行 pay_type=13 才有此字段
payee_name string 收款人姓名 pay_type=13 才有此字段
attach string 代付备注 -
hash string - API校验规则生成的hash值 详见 API签名规则 -
请求举例:
{
    "pay_type":13,
    "account":"收款人账号",
    "hash":"xxxxxxxxxxxxxxxxxx",
    "bank_name":"收款银行",
    "payee_name":"收款人姓名",
    "amount":10000,
    "mch_id":"100000",
    "notify_url":"https://www.baidu.com",
    "mch_order_id":"xxxxxxxxxxxxxxx",
    "attach":"funx"
}
key 类型 长度说明 含义 说明 取值
ret int 0:成功 其他失败
msg string ret!=0时错误描述
data object 订单信息
data.error_no int 错误码
data.error_user_msg string 错误描述
data.mch_order_id string 商户订单号
data.svr_transaction_id string flypay订单号
data.status int 代付状态详见 代付订单状态
返回举例:
{
    "data": {
        "mch_order_id": "0220200328000100f66c126e70ce11eab7f1595321913560",
        "svr_transaction_id": "V220200721010001003556434114268020992",
        "status": 0
    },
    "ret": 0
}

5. 代付订单查询

key 类型 长度说明 含义 说明 取值
mch_id string 商户ID,平台分配 支付平台为商户分配mch_id, 与商户一一对应 请联系支付平台工作人员获取 -
mch_order_id string max 48 bytes 商户订单ID
hash string - API校验规则生成的hash值 详见 API签名规则 -
请求举例:
{
    "mch_id":"100",
    "hash":"099881BB4FA17280F0764735FCE32164",
    "mch_order_id":"02202008140001009d4eed9ede2411eaa786784f439badb1"
}
key 类型 长度说明 含义 说明 取值
ret int 0:成功 其他失败
msg string ret!=0时错误描述
data object 订单信息
data.mch_id string 支付平台为商户分配mch_id, 与商户一一对应 请联系支付平台工作人员获取 -
data.mch_order_id string 商户订单ID
data.amount int - 支付金额 - -
data.pay_type int 支付方式 取值见 支付方式(pay_type)
data.account string - 收款人账号 -
data.status int 代付状态详见 代付订单状态
data.error_no int 错误码
data.error_user_msg string 错误描述
返回举例:
{
    "data": {
        "account":"xxx",
        "amount":10000,
        "pay_type":13,
        "mch_id":"100000",
        "mch_order_id": "02202008140001009d4eed9ede2411eaa786784f439badb1",
        "svr_transaction_id": "V220200721010001003556434114268020992",
        "status": 0
    },
    "ret": 0
}

6. 代付订单通知回调

key 含义 说明 取值
mch_id - - -
mch_order_id - - -
svr_transaction_id 支付平台的交易ID 唯一、字符串、max 64
amount 交易越南盾 -
ts 回调时间戳 -
status 订单状态 代付状态详见 代付订单状态
pay_type 支付类型 - -
error_no 错误码 代付失败时值有效 详见 代付订单通知错误码
hash API校验规则生成的hash值 详见 API签名规则 -

沙盒测试

电话卡沙盒测试

baokim 网银沙盒测试

momo zalopay 银行卡转账

错误码 error

TODO

支付订单状态 status

status 含义
0 未支付
1 支付成功
2 支付失败

代付订单状态 status

status 含义
0 未代付
1 代付成功
2 代付失败

代付订单通知错误码 error_no

error_no 含义
0 未知
1 没有绑定银行卡
2 未注册
3 无效账号