NexaPay 支付 API 集成文档 (v 1.3)

1.API 环境与端点

访问凭证

NexaPay线下提供,区分为UAT和生产两套环境, 开通需要联系商户专员获取, 具体信息包括:
  • api_key用作区分开发者接入身份标识
  • secret_key用作双方通讯的密钥, 主要用于信息签名和验签

基础 URL

身份验证

必要请求头
  • api_key: 用于验证开发者身份标识符
  • 内容类型: application/json; charset=UTF-8
  • 签字: 数字签名,采用hmc-sha256算法, 用于确保安全通信,具体见后面的代码示例

2.接口列表

2.1 直接支付

接口地址:/api/v1/directPay

2.1.1.场景

商户拥有独立收银系统,直接向 Nexapay 端发送支付请求字段。

2.1.2 工作流程步骤

2.1.2.1.1 买家发起支付请求
买家在商户平台提交支付请求,选择支付方式(银行卡、电子钱包等),填写交易金额及相关信息,并确认支付。
2.1.2.1.2 商户服务器调用 PSP/Nexapay Pay API
商户服务器收集买家支付信息,并通过 API 将支付请求发送至 PSP/Nexapay 进行处理。
2.1.2.1.3 Nexapay 处理请求并返回响应
Nexapay 接收支付请求后,进行交易处理,并将支付结果(成功、失败、处理中)返回至商户服务器。
2.1.2.1.4 商户服务器向买家返回支付结果
商户服务器根据 Nexapay 返回的支付结果,向买家展示交易状态: Nexapay
  • 支付成功页面提示 "支付成功",并展示交易详情。
  • 支付失败提示失败原因,并提供重试或更换支付方式的选项。
  • 支付处理中提示:"支付处理中",引导用户稍后查询订单状态。
2.1.2.1.5 获取支付结果
  • 被动通知模式Nexapay:主动向 PSP/商户服务器推送支付结果,并支持失败重试机制(默认最多 3 次)。
  • 主动查询模式商户可在订单有效期内,主动向 Nexapay 查询支付状态,以获取最新的交易结果。

2.1.3 整体交互流程

2.1.4 请求

2.1.5 响应

2.1.6 示例

接口地址:/api/v1/directPay
请求
				
					curl --location --request POST 'http://118.31.222.186/api/v1/directPay'\
--header 'api_key: 3258e9eaec8bdc65b84e8bbfd387c076'\
--header 'Content-Type: application/json' (内容类型:应用程序/json
--header 'signature: xN/ewpHtq4NLkzTKZufv+2Wna1JHeH3ljydG099q5KQ=' \
--data '{
    "amount":"100",
    "currency":"BRL"、
    "payCurrent":"BRL"、
    "merchantId":"225",
    "subMerchantId":"225",
    "付款人{
        "type":2,
        "名称":"test"、
        "电子邮件":"1xample@example.com"、
        "电话":"+5511987654321",
        "文件": "12.345.678/0001-90"12.345.678/0001-90"
    },
    "orderId":"1863911682520730001",
    "requestId":"1863911682520730002",
    "orderName":"样品订单"、
    "goodsName":"样品货物"、
    "notifyUrl":"https://example.com/notify"
}'
				
			
响应
				
					{
    "代码":"0",
    "msg":"成功"、
    "数据":{
        "payStatus":"P",
        "merchantId":"225",
        "subMerchantId":"225",
        "orderId":"1863911682520730006",
        "requestId":"1863911681733920735",
        "txId":"202412110210110100000000018",
        "payAmount":"100.89",
        "currency":"BRL"、
        "payCurrency":"BRL"、
        "payLoad":{
            "qrCode":"00020126910014BR.GOV.BCB.PIX2569api-pix-h.bancobs2.com.br/spi/v2/4034530b-7a61-4032-a0fa-20b28d56cbd15204000053039865406100.895802BR5907Nexapay6014Belo Horizonte61083038040362070503***63040CC9",
            "过期":"600",
            "创建日期"1733921082562"
        },
        "收费": [[
            {
                "feeType":"PAYER_FEE"、
                "收费金额":"0.50",
                "手续费货币": "BRL":"BRL
            },
            {
                "feeType":"IOF"、
                "feeAmount":"0.39",
                "手续费货币": "BRL":"BRL
            }
        ]
    },
    "succ": true
}
				
			

2.2 收银台支付

接口地址:/api/v1/directPay/order/create

2.2.1 场景

商户使用我方收银系统,直接完成支付。

2.2.2 工作流程步骤

2.2.2.1 买家发起支付请求
买家在商户平台提交支付请求,选择支付方式(银行卡、电子钱包等),填写支付金额及交易信息,并确认支付。
2.2.2.2 商户服务器调用 PSP/Nexapay Pay API
商户服务器收集买家支付信息,并通过 API 将支付请求转发至 PSP/Nexapay 进行处理。
2.2.2.3 返回收银台地址
Nexapay 处理支付请求后,返回 Nexapay 收银台地址至商户服务器。
2.2.2.4 收银台跳转
商户系统引导买家跳转至 Nexapay 收银台,完成支付操作。
2.2.2.5 支付成功跳转
支付成功后,系统自动回跳至商户页面,并展示支付结果及相关订单信息。
2.2.2.6 获取支付结果
  • 被动通知模式Nexapay:主动向 PSP/商户服务器推送支付结果,并支持失败重试机制(默认最多 3 次)。
  • 主动查询模式商户可在订单有效期内,主动向 Nexapay 查询支付状态,以获取最新交易结果(可选)。

2.2.3 整体交互流程

2.2.4 请求

2.2.5 响应

2.2.6 示例

请求:
				
					curl --location --request POST 'http://118.31.222.186/api/v1/directPay/order/create'\
--header 'api_key: 3258e9eaec8bdc65b84e8bbfd387c076'\
--header 'Content-Type: application/json' (内容类型:应用程序/json
--header 'signature: xN/ewpHtq4NLkzTKZufv+2Wna1JHeH3ljydG099q5KQ=' \
--data '{
    "amount":"9200",
    "currency":"USD"、
    "merchantId":"1840690697213079001",
    "subMerchantId":"1840690697213079002",
    "付款人":{
        "type":1,
        "name":"test"、
        "电子邮件":"test@qq.com"、
        "电话":"+5511987654321",
        "文件":"12345678900"
    },
    "orderId"(订单号):"186391168252074000H",
    "requestId":"184069069721307000I",
    "orderName":"样品订单"、
    "goodsName":"样品货物"、
    "notifyUrl":"http://example.com/notifyUrl"、
    "callbackUrl":"http://example.com/callbackUrl"
}'
				
			
响应
				
					{
    "code": "0",
    "msg": "成功",
    "data": {
        "cashierPayUrl": "http://www.nexapay.com.cn/product/checkout/ac93e9ee-e600-4b0b-ada0-c44e05f206f7?md5=f1212836c4bed479a500784a5d7f7f91&timestamp=1738737032342&signature=%2Fz5RQ4TxWXtx5J7qRnt8CCokFZ%2Bvlu%2B4aYIbG0ajM%2Fs%3D",
        "txId": "202502050210110100000000001",
        "orderId": "186391168252074000H"
    },
    "succ": true
}
				
			

2.3 支付结果通知

  • 签名验证商户端仍需验证回调数据的签名以确保数据安全。(商户的签名结果和附件返回的签名进行比较)
  • 配置回调 URL由商户端在支付订单创建需求中指定,具体见获取二维码中notifyUrl
  • 通知机制: 针对银行已返回支付成功的订单会定时推送商户方, 直到签收成功。200状态码即视为成功。

2.3.1 响应参数

示例
				
					curl --location --request POST 'http://*.
--header 'Content-Type: application/json' (内容类型:应用程序/json
--header 'signature: xN/ewpHtq4NLkzTKZufv+2Wna1JHeH3ljydG099q5KQ=' \
--data '{
{
    "notifyType":"payment_result"、
    "merchantId":"189897777807097711",
    "subMerchantId":"189897777807097712",
    "orderId":"189897777807097711"189897777807097712",
    "txId":"189897777807097712"189897777807097713",
    "requestId":"189897777807097714",
    "payStatus":"T",
    "payAmount":"100.8900000000000000",
    "payCurrency":"BRL"、
    "payTime":"2024-10-01 11:37:47",
    "chargeFees":[
        {
            "feeType":"PAYER_FEE"、
            "收费金额":"0.50",
            "手续费货币": "BRL":"BRL
        },
        {
            "feeType":"IOF"、
            "feeAmount":"0.39",
            "手续费货币": "BRL":"BRL
        }
    ]
}'
				
			

2.4 支付结果查询

接口地址:/api/v1/directPay/query

2.4.1 请求参数

2.4.2 响应参数

2.4.3 示例

请求
				
					curl --location --request POST 'http://118.31.222.186/api/v1/directPay/query'\
--header 'api_key: 3258e9eaec8bdc65b84e8bbfd387c076'\
--header 'Content-Type: application/json' (内容类型:应用程序/json
--header 'signature: xN/ewpHtq4NLkzTKZufv+2Wna1JHeH3ljydG099q5KQ=' \
--data '{
    "orderId":"1863911682520739003",
    "txId":"202412080210110100000000010"
}
				
			
响应
				
					{
    "代码":"0",
    "msg":"成功"、
    "数据":{
        "orderId":"1863911682520739003",
        "requestId":"1863911681733715402",
        "txId":"202412090210110100000000001",
        "payStatus":"S",
        "merchantId":"225",
        "subMerchantId":"225",
        "支付金额":"100.8900000000000000",
        "payCurrency":"BRL"、
        "payTime":"2024-12-09 11:37:47",
        "chargeFees":[
            {
                "feeType":"PAYER_FEE"、
                "收费金额":"0.50",
                "手续费货币": "BRL":"BRL
            },
            {
                "feeType":"IOF"、
                "feeAmount":"0.39",
                "手续费货币": "BRL":"BRL
            }
        ]
    },
    "succ": true
}
				
			

附录

支付状态代码

费用类型

常见返回代码及描述

备注

签名生成细节

  • 将 secret_key作为密钥,与参数拼接后计算哈希值。
  • 拼接参数字符串:将 params 按字典顺序排序并拼接。
  • 用 secret_key作为密钥计算 HMAC-SHA-256:使用 secret_key来生成哈希,类似于 HMAC(哈希消息认证码)的方式。以下是Java代码示例
  • Json工具类由商户侧自行实现,请根据Accsic码 排序后,压缩,以下示例仅供参考

Java

				
					import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject
import com.alibaba.fastjson2.JSONObject;
导入 lombok.extern.slf4j.Slf4j.JSON 对象;

import javax.crypto.Mac;
导入 javax.crypto.spec.SecretKeySpec;
导入 javax.xml.bind.DatatypeConverter;
import javax.xml.bind.DatatypeConverter; import javax.nio.charset.StandardCharsets;
导入 java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
导入 java.nio.charset.StandardCharsets

@Slf4j
public class HMACSHA256Util {
    public static void main(String[] args) throws Exception {
        String secretKey = "secret_key";

        String reqJson = "{\"amount\":\"73800\",\"currency\":\"BRL\",\"goodsName\":\"锂电池\",\"merchantId\":\"1840690697213079001\",\"notifyUrl\":\"https://example.com/notify\",\"orderId\":\"1863816181848834001\",\"orderName\":\"展示名字\",\"payer\":{\"document\":\"12345678900\",\"email\":\"s.jycv@qq.com\",\"name\":\"Jack\",\"phoneNumber\":\"+8612345678901\",\"type\":1},\"requestId\":\"1863816181848834001\",\"subMerchantId\":\"1840690697213079002\"}\n";
        System.out.println("排序前待签报文:" + reqJson);

        String sign = sign(reqJson, secretKey);
        System.out.println("签定值:" + sign);

        boolean verify = verify(reqJson, secretKey, sign);
        System.out.println("验签结果:" + verify);
    }

    public static String sign(String reqJson, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
        // 解析 JSON 字符串为 JSONObject
        JSONObject jsonObject = JSON.parseObject(reqJson);

        // 对 JSONObject 进行排序
        TreeMap sortedParameters = sortJSONObject(jsonObject);

        // 将排序后的 TreeMap 转换为 JSON 字符串
        String jsonString = JSON.toJSONString(sortedParameters);

        // 生成 hmac-sha256 签名
        Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        hmacSHA256.init(secretKeySpec);
        log.info("排序后待签报文:{}", jsonString);
        byte[] hash = hmacSHA256.doFinal(jsonString.getBytes(StandardCharsets.UTF_8));
        String sign = DatatypeConverter.printBase64Binary(hash);
        log.info("签名:{}", sign);
        返回 sign;
    }

    // 验证签名的方法
    public static boolean verify(String reqJson, String secretKey, String providedSignature) throws Exception { // 生成签名的方法
        // 生成签名
        String generatedSignature = sign(reqJson, secretKey);

        // 比较生成的签名和提供的签名
        return generatedSignature.equals(providedSignature);
    }

    public static TreeMap sortJSONObject(JSONObject jsonObject) {
        if (jsonObject == null) {
            返回 null;
        }

        TreeMap sortedMap = new TreeMap();
        for (String key : jsonObject.keySet()) {
            Object value = jsonObject.get(key);
            if (value instanceof JSONObject) {
                sortedMap.put(key, sortJSONObject((JSONObject) value));
            } else {
                sortedMap.put(key, value);
            }
        }
        return sortedMap;
    }
}
				
			

Python

				
					导入 hmac
导入 hashlib
导入 base64
导入 json

def generate_hmac_sha256_signature(secret_key, data):
    # 创建 hmac-sha256 对象
    hasher = hmac.new(secret_key.encode(), data.encode(), digestmod=hashlib.sha256)
    # 获取签名的二进制结果
    hash_bytes = hasher.digest()
    # 将签名转换为 Base64 编码
    signature = base64.b64encode(hash_bytes).decode()
    返回签名

if __name__ == "__main__":
    secret_key = "e10adc3949ba59abbe56e057f20f883e"
    signature_json = {
    "amount":"73800",
    "currency":"BRL"、
    "requestId":"1863816181848834001",
    "merchantId":"1840690697213079559",
    "subMerchantId":"1840690697213079559"1840690697213079560",
    "付款人":{
        "type":1,
        "姓名":"Jack"、
        "电子邮件":"s.jycv@qq.com"、
        "电话":"+8612345678901",
        "文件":"12345678900"
    },
    "orderId":"1863816181848834001",
    "订单名称":"展示名称"、
    "商品名称":"锂电池"、
    "notifyUrl":"https://example.com/notify"
}
    signature_str = json.dumps(signature_json, sort_keys=True, ensure_ascii=False, separators=(',', ':'))
    # signature_str = signature_str.replace(" ", "")
    print("signature_str:",signature_str)
    # 生成签名
    signature = generate_hmac_sha256_signature(secret_key, signature_str)
    print("signature:",signature)
				
			

PHP

				
					<?php

class HMACSHA256Util {

    public static function main() {
        $secretKey = "secret_key";

        $reqJson = '{"amount":"73800","currency":"BRL","goodsName":"锂电池","merchantId":"1840690697213079001","notifyUrl":"https://example.com/notify","orderId":"1863816181848834001","orderName":"展示名字","payer":{"document":"12345678900","email":"s.jycv@qq.com","name":"Jack","phoneNumber":"+8612345678901","type":1},"requestId":"1863816181848834001","subMerchantId":"1840690697213079002"}';
        echo "排序前待签报文: " . json_encode(json_decode($reqJson, true), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";

        $sign = self::sign($reqJson, $secretKey);
        echo "签值: $sign\n";

        $verify = self::verify($reqJson, $secretKey, $sign);
        echo "验签结果: " . ($verify ? "true" : "false") . "\n";
    }

    public static function sign($reqJson, $secretKey) {
        // 解析 JSON 字符串为数组
        $jsonArray = json_decode($reqJson, true);

        // 对数组进行排序
        $sortedParameters = self::sortArray($jsonArray);

        // 将排序后的数组转换为 JSON 字符串,同时防止中文和斜杠被转义
        $jsonString = json_encode($sortedParameters, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        echo "排序后待签报文: $jsonString\n";

        // 生成 HMAC-SHA256 签名
        $hash = hash_hmac('sha256', $jsonString, $secretKey, true);
        $sign = base64_encode($hash);
        return $sign;
    }

    // 验证签名的方法
    public static function verify($reqJson, $secretKey, $providedSignature) {
        // 生成签名
        $generatedSignature = self::sign($reqJson, $secretKey);

        // 比较生成的签名和提供的签名
        return $generatedSignature === $providedSignature;
    }

    public static function sortArray($array) {
        if (!is_array($array)) {
            return [];
        }

        ksort($array);
        foreach ($array as &$value) {
            if (is_array($value)) {
                $value = self::sortArray($value);
            }
        }
        return $array;
    }
}

// 运行主方法
HMACSHA256Util::main();
				
			

交易限制条款

  • 最小交易金额:1.00 Brl。
  • 退款:可在原始交易日期起 90 天内申请退款,退款将退回至原支付账户
  • 其它限制条款具体见双方合同约定

测试/UAT 指导

  • 测试环境使用 Nexapay 提供的 UAT 环境进行测试。
  • 测试用例
    • 成功支付使用有效数据模拟支付成功场景。
    • 签名失败提供无效签名测试系统错误处理。
    • 无效货币使用:非 brl 货币代码测试系统验证。

nexapay-api

滚动至顶部