凭证识别

# 代码示例

  • 代码示例仅供参考,具体参数说明请参考请求参数说明

TopPayRequestUtil点击获取代码示例


import com.google.gson.JsonObject;
public class demo {

    private static final String PLAT_PUBLIC_KEY = "MIGfMA0GCSqG***AQAB";
    private static final String MCH_PRIVATE_KEY = "MIICeAI******IBIuhihgi";
    private static final String URL = "https://tl-openapi.toppay.asia/gateway/order/check";

    public static void main(String[] args) throws Exception {
        query();
    }
    private static void query() throws Exception {
        Map<String, String> requestParams = new TreeMap<>();
        requestParams.put("mchId", "S012031948905787");
        requestParams.put("orderNum", "A123456789");
        requestParams.put("qrCode", "002123124716478189479838124781BHU678T778gB100.0072BHSJC7N");
        List<String> paramNameList = new ArrayList<>();
        for (String key : maps.keySet()) {
            paramNameList.add(key);
        }
        Collections.sort(paramNameList);
        StringBuilder stringBuilder = new StringBuilder();
        for (String key : paramNameList) {
            stringBuilder.append(requestParams.get(key));
        }

        String keyStr = stringBuilder.toString();
        System.out.println("keyStr:" + keyStr);
        String signedStr = TopPayRequestUtil.privateEncrypt(keyStr, TopPayRequestUtil.getPrivateKey(MCH_PRIVATE_KEY));
        requestParams.put("sign", signedStr);
        String postJson = new Gson().toJson(requestParams);
        System.out.println("Post Json Params:" + postJson);

        String responseJson = TopPayRequestUtil.doPost(URL, postJson);
        System.out.println("Response Msg:" + responseJson);
    }
}


<?php
    //商户私钥,用工具生成(这里只是测试案例)
    //密钥请检查格式,不要带有空格
    $mchPrivateKey = 'MIICe*******AoUbZT/';
    mchId = 'S012031948905787';
    orderNum = 'A123456789';
    qrCode = '002123124716478189479838124781BHU678T778gB100.0072BHSJC7N';
    
    $params = array(
        'mchId' => mchId,
        'orderNum' => orderNum,
        'qrCode' => qrCode,
    );

    ksort($params);
    $params_str = '';
    foreach ($params as $key => $val) {
        $params_str = $params_str . $val;
    }
    //加密
    $sign = pivate_key_encrypt($params_str, $mchPrivateKey);
    $params['sign'] = $sign;
    $params_string = json_encode($params);
    $url = 'https://tl-openapi.toppay.asia/gateway/order/check';
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
    curl_setopt($ch, CURLOPT_POSTFIELDS, $params_string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($params_string))
    );
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

    //execute post
    $request = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    if($httpCode == 200)
    {
        $result = json_decode($request, true);
        echo "message :". $result['message'] . "<br>";
        $thbData = array();
        // 打印满足条件的数据
        print_r($thbData);
    }
    else {
        echo $httpCode;
    }
    //加密方法
    function pivate_key_encrypt($data, $pivate_key)
    {
        $pivate_key = '-----BEGIN PRIVATE KEY-----'."\n".$pivate_key."\n".'-----END PRIVATE KEY-----';
        $pi_key = openssl_pkey_get_private($pivate_key);
        $crypto = '';
        foreach (str_split($data, 117) as $chunk) {
            openssl_private_encrypt($chunk, $encryptData, $pi_key);
            $crypto .= $encryptData;
        }
        return base64_encode($crypto);
    }
?>

using demo.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace demo.demos
{
    class demo
    {
        private static readonly string PLAT_PUBLIC_KEY = "MIGfMA0GCSqG******6wIDAQAB";
        private static readonly string MCH_PRIVATE_KEY = "MIICdwIBADAN*****qzuBvIGAe3OWNg="; 
        private static readonly string payUrl = "https://tl-openapi.toppay.asia/gateway/order/check";
        public static void requestQuery()
        {
            long currenttimemillis = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
            string  mchId = 'S012031948905787';
            string orderNum = 'A123456789';
            string qrCode = '002123124716478189479838124781BHU678T778gB100.0072BHSJC7N';
            Dictionary<string, object> VarPost = new Dictionary<string, object>(); ;
            VarPost.Add("mchId", mchId);
            VarPost.Add("orderNum", orderNum);
            VarPost.Add("qrCode", qrCode);
            
            List<string> paramNameList = new List<string>();
            foreach (string key in VarPost.Keys)
            {
                paramNameList.Add(key);
            }
            paramNameList.Sort();// 排序key
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < paramNameList.Count; i++)
            {
                string key = paramNameList[i];
                stringBuilder.Append(VarPost[key]);
            }
            string keyStr = stringBuilder.ToString();
            int len = keyStr.Length;
            string signedStr = "";
            try
            {
                RSAForJava rsa = new RSAForJava();
                signedStr = rsa.EncryptByPrivateKey(keyStr, MCH_PRIVATE_KEY);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            VarPost.Add("sign", signedStr);
            WebClient w = new WebClient();
            w.Headers[HttpRequestHeader.ContentType] = "application/json";
            string jsonStr = JsonNewtonsoft.SerializeDictionaryToJsonString(VarPost);
            Console.WriteLine("postJson:" + jsonStr); 
            string sRemoteInfo = w.UploadString(payUrl, "POST", jsonStr);
        }

    }
}

package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"sort"
	"strings"
)

func main() {
	//公私钥(仅用于测试)
	cryptor := NewCryptor()
	cryptor.Pub64 = "MIGfMA0GCSqGSIb3******K4yD37Bj0wIDAQAB"
	cryptor.Pri64 = "MIICdwIBAD*********HKRJwSWWBDEfuk="
	
	url := "https://tl-openapi.toppay.asia/gateway/order/check"
	//具体查询参数内容
	request := RequestBody{
		mchId: "S012031948905787",
		orderNum: "A123456789",
		qrCode: "002123124716478189479838124781BHU678T778gB100.0072BHSJC7N",
	}
	// 将结构体的字段按JSON标签排序并拼接其值
	sortedValues, err := sortedConcatenatedValues(request)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return
	}
	//将排序拼接后的值,使用私钥进行加密,并进行base64编码
	encryptedData := cryptor.PriEncrypt([]byte(sortedValues))
	encodedString := base64.StdEncoding.EncodeToString(encryptedData)
	//将获得签名放入请求结构中
	request.Sign = encodedString
	jsonBytes, err := json.Marshal(request)
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
		return
	}
	fmt.Println("json2", bytes.NewBuffer(jsonBytes))
	//进行http请求
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes))
	if err != nil {
		fmt.Println("Error creating HTTP request:", err)
		return
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error sending HTTP request:", err)
		return
	}
	defer resp.Body.Close()
	// 读取并打印响应体
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error reading response body:", err)
		return
	}
	fmt.Println("Response Body:", string(respBody))
}

// 使用结构体的 JSON 标签按照ASCII进行排序并拼接对应值为一个字符串
func sortedConcatenatedValues(v interface{}) (string, error) {
	valueOf := reflect.ValueOf(v)
	typeOf := valueOf.Type()
	kv := make(map[string]string)
	for i := 0; i < valueOf.NumField(); i++ {
		field := typeOf.Field(i)
		jsonTag := field.Tag.Get("json")
		if jsonTag == "" || jsonTag == "-" {
			continue
		}
		tagName := strings.Split(jsonTag, ",")[0]
		kv[tagName] = fmt.Sprint(valueOf.Field(i).Interface())
	}
	keys := make([]string, 0, len(kv))
	for k := range kv {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	var concatenatedValues strings.Builder
	for _, key := range keys {
		concatenatedValues.WriteString(kv[key])
	}
	return concatenatedValues.String(), nil
}

import json
import requests
import base64
import easyocr
from M2Crypto import RSA
from collections import OrderedDict

class TopQueryDemo:
    # 商户私钥存储地址
    MCH_PRI_KEY_FILE = '\data\company_rsa_private_key.pem'
    # 平台公钥保存地址
    MCH_PUBLIC_KEY_FILE = '\data\plat_rsa_public_key.pem'
    
    # API查询地址
    url = "https://tl-openapi.toppay.asia/gateway/order/check"

    @staticmethod
    def cash():
        request_params = OrderedDict()
        request_params["mchId"] = "S012031948905787"
        request_params["orderNum"] = "A123456789"
        request_params["qrCode"] = "002123124716478189479838124781BHU678T778gB100.0072BHSJC7N"

        # 根据key进行参数排序
        key_str = ''.join([str(value) for value in request_params.values()])
        print("keyStr:", key_str)

        # 加密
        signed_str = TopQueryDemo.private_encrypt(key_str, TopQueryDemo.MCH_PRI_KEY_FILE)
        request_params["sign"] = signed_str
        # 发送请求
        post_json = json.dumps(request_params)
        print("Post Json Params:", post_json)

        response_json = TopQueryDemo.do_post(TopQueryDemo.url, post_json)
        print("Response Msg:", response_json)

        check = TopQueryDemo.public_key_decrypt(signed_str, TopQueryDemo.MCH_PUBLIC_KEY_FILE);
        print("check:", check)

    @staticmethod
    def private_encrypt(data, prikey_file):
        rsa_pri = RSA.load_key(prikey_file)
        # 使用私钥加密数据
        # M2Crypto.RSA.pkcs1_padding 是一种常见的填充方式
        crypto = b''
        for chunk in [data[i:i + 117] for i in range(0, len(data), 117)]:
            encrypt_data = rsa_pri.private_encrypt(chunk.encode('utf-8'), RSA.pkcs1_padding)
            crypto += encrypt_data
        return base64.b64encode(crypto).decode('utf-8')


    @staticmethod
    def public_key_decrypt(data, public_file):
        rsa_key = RSA.load_pub_key(public_file)
        data = base64.b64decode(data)
        crypto = b''
        for chunk in [data[i:i+128] for i in range(0, len(data), 128)]:
            decrypt_data = rsa_key.public_decrypt(chunk, RSA.pkcs1_padding)
            crypto += decrypt_data
        return crypto.decode('utf-8')
    @staticmethod
    def do_post(url, data):
        headers = {"Content-Type": "application/json"}
        response = requests.post(url, data=data, headers=headers)
        if response.status_code == 200:
            return response.json()
        return None

if __name__ == '__main__':
    TopPayDemo.pay()
const { encryptWithPrivateKey } = require('./RSAUtil');
const axios = require('axios');

//请求结构
//具体下单参数内容
var mchId = 'S012031948905787'
var orderNum = 'A123456789'
var qrCode = '002123124716478189479838124781BHU678T778gB100.0072BHSJC7N'


const data = {
    mchId: mchId,
    orderNum: orderNum,
    qrCode: qrCode,
};

//将对象的键名按ASCII码排序
const sortedKeys = Object.keys(data).sort();

//使用排序后的键名拼接它们的值
let sortedString = '';
sortedKeys.forEach(key => {
    sortedString += data[key];
});

console.log(sortedString);
var sign = encryptWithPrivateKey(sortedString);

// 将sign添加到data对象中
data.sign = sign;
const jsonData = JSON.stringify(data);
console.log(jsonData)

var url = 'https://tl-openapi.toppay.asia/gateway/order/check'
// 请求选项
axios.post(url, jsonData,{
    headers: {
        'Content-Type': 'application/json'
    }
})
    .then((response) => {
        console.log('status:', response.status);
        console.log('data:', response.data);
    })
    .catch((error) => {
        console.error('error:', error);
    });

# 请求地址

  • 请求方式 : POST
  • 请求地址 : https://tl-openapi.toppay.asia/gateway/order/check

# 请求参数

参数 必填 类型 描述 示例
mchId Y String 商户ID S012031948905787
orderNum Y String 商户订单号 A123456789
qrCode Y String 图片内容(qrCode) qrCode:002123124716478189479838124781BHU678T778gB100.0072BHSJC7N
sign Y String RSA签名 ja6R8eukQ...

# 请求报文示例

{
  "mchId": "S012031948905787",
  "orderNum": "A123456789",
  "qrCode": "002123124716478189479838124781BHU678T778gB100.0072BHSJC7N",
  "sign": "X/o+IQUzLJqYe9Feid9Uww72mJGOvhJSJEIfo1EUChrZyVZnzGHtd61QhOqRmXCtAwk7V7k="
}

# 响应参数

参数 必填 类型 描述 示例
success Y boolean 接口响应 true/false
code Y int 接口响应码 1000代表请求成功,其他的都为请求失败
message Y String 接口响应信息 返回具体响应信息
data Y Json 接口响应参数 data
titleCode Y String 识别结果状态码 1000代表成功,其他的都为失败
titleMsg Y String 识别结果 返回具体响应信息

# 响应报文示例

{
  "success": true,
  "code": 1000,
  "message": "SUCCESS",
  "data": {
    "titleMsg": "Payment voucher recognition information incomplete",
    "titleCode": "1003"
  }
}

# 识别结果状态码(titleCode)

TitleCode TitleMsg Desc
200 Order replacement successful, please confirm 补单成功,请确认
301 Frequent requests, please try again later 请求频繁,请稍后再试
302 Image too large to be recognized 图片过大,无法识别
303 Order inquiry service malfunction, please contract customer service 查单服务异常,请联系客服
1001 Payment voucher cannot be recognized 付款凭证无法识别,请人工查询
1002 Payment voucher recognition failed 付款凭证识别失败,请人工查询
1003 Payment voucher recognition information incomplete 付款凭证识别信息不完整,请人工查询
1004 Voucher payment time is not within the last 3 days 凭证付款时间非近3天,请人工查询
1005 Payment voucher has been recognized twice 该付款凭证已重复识别,请人工确认
2001 The current voucher's receiving card is not TopPay 当前凭证收款卡非TopPay收款,请人工确认
2002 Automatic order replacement is not currently supported 暂不支持自动补单,请人工确认
3001 Payment voucher already has a corresponding order 该付款凭证已有相应订单
3002 Multiple receiving cards correspond 对应多张收款卡,请人工查询
3004 Voucher matched multiple suspected orders 凭证匹配到多笔疑似订单
3005 Voucher matched suspected order 凭证匹配到疑似订单
3006 Incorrect order provided 提供的订单错误
3007 Abnormal order status 提供的订单状态异常
3008 Order amount provided does not match voucher amount 提供订单金额与凭证金额不一致
3011 Amount too large the order 金额过大,请人工确认订单