Bestin Pay API 文档

基本信息

  • 版本:1.0.0
  • 协议:HTTP/HTTPS
  • 响应格式:JSON
  • API 地址:https://api.bestinpay.com

认证

所有接口均需通过以下 Header 进行认证:

x-app-id: your_app_id
x-secret: your_app_secret
x-timestamp: 1764989924791

认证参数说明:

  • x-app-id:应用ID
  • x-secret:应用密钥
  • x-timestamp:当前时间戳(毫秒),用于防重放攻击,允许5分钟误差

代收接口

POST/payin

创建代收交易订单,支持多种支付方式和渠道。

请求参数:

参数名类型必填说明
req_idstring商户请求ID,唯一标识
ccystring币种代码(仅支持:INR)
amountstring交易金额
trx_methodstring交易方式(upi/bank_card等)
trx_modestring交易模式(api/checkout)
notify_urlstring异步通知地址
return_urlstring同步跳转地址

API 模式请求示例:

{
  "req_id": "PAY20241022001",
  "ccy": "INR",
  "amount": "101.00",
  "trx_method": "upi",
  "trx_mode": "api",
  "product_id": "product_001",
  "user_ip": "192.168.1.1",
  "notify_url": "https://merchant.com/notify",
  "return_url": "https://merchant.com/return"
}

响应示例:

{
  "code": "0000",
  "msg": "Success",
  "data": {
    "trx_id": "PI01KAG1YM11Z3Y7BG4VW9K86ZRJ",
    "trx_type": "payin",
    "req_id": "5af6bebb-16aa-4cbd-ad8d-936bdc71780f",
    "trx_method": "upi",
    "trx_mode": "api",
    "ccy": "INR",
    "amount": "101.0000",
    "status": "pending",
    "link": "upi://pay?pa=1234****@freecharge&pn=&am=101...",
    "links": {
      "paytm": "paytmmp://cash_wallet?...",
      "phonepe": "phonepe://native?...",
      "upi": "upi://pay?..."
    }
  }
}

代付接口

POST/payout

创建代付交易订单,向指定账户转账。支持UPI支付和银行卡转账。

UPI 支付参数:

参数名类型必填说明
req_idstring商户请求ID
ccystring币种代码(INR)
amountstring交易金额
trx_methodstring交易方式(upi)
upistring收款方UPI地址
user_ipstring用户真实IP

请求示例:

{
  "req_id": "PAYOUT20241022001",
  "ccy": "INR",
  "amount": "101.00",
  "trx_method": "upi",
  "upi": "1234****@ibl",
  "user_ip": "127.0.0.1"
}

取消订单

POST/cancel

取消指定的交易订单(代收或代付)。

请求示例:

{
  "trx_id": "TRX20241022001",
  "trx_type": "payin"
}

查询交易

POST/query

根据请求ID或交易ID查询交易状态和详情。

请求参数:

参数名类型必填说明
req_idstring商户请求ID(与trx_id二选一)
trx_idstring交易ID(与req_id二选一)
trx_typestring交易类型(payin/payout)

请求示例:

{
  "req_id": "f5a8d8f9-cafb-45b6-bf92-35fbea37e8eb",
  "trx_type": "payin"
}

查询账户余额

POST/balance

查询商户账户的各币种余额信息。

响应示例:

{
  "code": 200,
  "msg": "success",
  "data": [
    {
      "ccy": "INR",
      "balance": "10000.00",
      "available_balance": "9500.00",
      "frozen_balance": "500.00"
    }
  ]
}

交易状态说明

  • pending:处理中
  • confirming:确认中
  • success:成功
  • failed:失败
  • canceled:已取消
  • expired:已过期

支付方式说明

  • upi:UPI支付
  • bank_transfer:银行转账

通知回调

商户在创建交易时可设置 notify_url,当交易状态发生变化时,系统会向该地址发送 POST 通知。

通知数据结构:

{
  "notify_id": "NOTIFY20241022001",
  "event_type": "trx_update",
  "data": {
    "mid": "M01K7BKY89ZKEJEF7S313X6KM21",
    "trx_id": "PI01KAG1YM11Z3Y7BG4VW9K86ZRJ",
    "req_id": "PAY20241022001",
    "trx_type": "payin",
    "status": "success",
    "amount": "101.00",
    "ccy": "INR"
  },
  "timestamp": 1729641600000,
  "sign": "QL/JqWlEO/D6k0CE4cSZMXbYzwwBp..."
}

接收响应要求:

  • • HTTP 状态码必须为 200
  • • 响应内容必须为字符串 "success"

签名验证

为确保通知的真实性和完整性,系统会对所有通知进行数字签名。

验签公钥:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA37PVibAurnVe+0l0k9DC
Pc3whYA6QscL5KBxh7jdUGEN3RO1vd3AAMPhOqmhWBjckEzlZmojF9GPjqAPZH4s
iNYRE60tV96cCDFOLspxaFmfOqyYOpUhcfDQf+QsFi3njVbEX5X1qcH4U0SL4ctm
yTyJorcbn6qa2XuHgWeomvQ9yyZapjknmiypHd8Mn5niYqUCSlDs9AtCUqX8h38v
HoUD714j5HUdMFhYCRwOJT0vmzCmFPdaI1GKxSp8y5CN+RPsZJ6CKD/8F/T+XHqW
I5HtTKttZqevBG/XJDpThxreQzKb9l0JbjOHZ5fbZH6DSoyd5vfRdao+TQq7eR9n
5QIDAQAB
-----END PUBLIC KEY-----

签名算法:

  • 算法:RSA-SHA512
  • 签名格式:PKCS#1 v1.5
  • 编码方式:Base64

验签步骤:

  1. 提取 data 字段中的非空值
  2. 将字段按键名字典序升序排序
  3. 使用 key=value&key2=value2 格式拼接
  4. 使用 SHA512 对签名字符串进行哈希
  5. 使用公钥和 RSA-PKCS1-v1_5 算法验证签名

代码示例

Go 示例

package main

import (
    "crypto"
    "crypto/rsa"
    "crypto/sha512"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "fmt"
    "sort"
    "strings"
)

func BuildSignString(data map[string]interface{}) string {
    keys := make([]string, 0, len(data))
    for k, v := range data {
        if v != nil && v != "" {
            keys = append(keys, k)
        }
    }
    sort.Strings(keys)
    
    parts := make([]string, 0, len(keys))
    for _, k := range keys {
        parts = append(parts, fmt.Sprintf("%s=%v", k, data[k]))
    }
    return strings.Join(parts, "&")
}

func VerifySignature(data map[string]interface{}, signature string) bool {
    // 验证签名逻辑...
    return true
}

PHP 示例

<?php

function buildSignString($data) {
    $filtered = array_filter($data, function($value) {
        return $value !== null && $value !== '';
    });
    
    ksort($filtered);
    
    $parts = [];
    foreach ($filtered as $key => $value) {
        $parts[] = $key . '=' . $value;
    }
    return implode('&', $parts);
}

function verifySignature($data, $signature) {
    $signStr = buildSignString($data);
    $sig = base64_decode($signature);
    $publicKey = openssl_pkey_get_public(PUBLIC_KEY);
    
    $result = openssl_verify(
        $signStr, 
        $sig, 
        $publicKey, 
        OPENSSL_ALGO_SHA512
    );
    
    return $result === 1;
}

Java 示例

import java.security.*;
import java.util.*;

public class SignatureVerifier {
    
    public static String buildSignString(Map<String, String> data) {
        TreeMap<String, String> sortedMap = new TreeMap<>();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                sortedMap.put(entry.getKey(), entry.getValue());
            }
        }
        
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
            if (sb.length() > 0) sb.append('&');
            sb.append(entry.getKey()).append('=').append(entry.getValue());
        }
        return sb.toString();
    }
    
    public static boolean verifySignature(
        Map<String, String> data, 
        String signatureStr
    ) throws Exception {
        String signStr = buildSignString(data);
        
        byte[] publicKeyBytes = Base64.getDecoder().decode(PUBLIC_KEY);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        
        byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
        
        Signature signature = Signature.getInstance("SHA512withRSA");
        signature.initVerify(publicKey);
        signature.update(signStr.getBytes());
        
        return signature.verify(signatureBytes);
    }
}

错误响应格式

{
  "code": 400,
  "msg": "请求参数错误",
  "data": null
}