接入威富通聚合支付平臺

開發(fā)文檔:https://open.swiftpass.cn/openapi?code=hsty
這個聚合平臺大概是集合了微信、支付寶、云閃付等支付平臺,大概有下圖這么多:

企業(yè)微信截圖_af26a470-e849-4a32-a9c5-19a3041298c6.png

一、付款碼支付
付款碼的大概業(yè)務功能:
收銀員使用掃碼設備讀取用戶微信/支付寶/云閃付等APP付款碼以后,二維碼或條碼信息傳送至商戶收銀臺,由商戶收銀臺或者商戶后臺調用該接口發(fā)起支付對用戶進行收款
備注:我使用的是flask框架
1、支付

import time

import flask
from flask import Flask

from tool import *

app = Flask(__name__)


@app.route('/Pay', methods=['GET', 'POST'])
def gello_world():
    # 提交付款碼支付API
    url = 'https://pay.hstypay.com/v2/pay/gateway'
    try:
        form_data = flask.request.get_data()  # 獲取未經(jīng)處理過的原始數(shù)據(jù)而不管內容類型
        json_data = json.loads(form_data.decode('utf-8'))  # 字符串轉化為字典
        for i in ('service', 'mch_id', 'out_trade_no', 'body', 'total_fee', 'mch_create_ip', 'auth_code'):
            if i not in json_data.keys():
                # 將沒有指定參添加上
                json_data[i] = None
        # 獲取請求參數(shù),并增加必帶參數(shù)
        data = {
            "service": "unified.trade.micropay",  # 接口類型
            "mch_id": MCH_ID,  # 門店編號
            "out_trade_no": json_data["out_trade_no"],  # 商戶訂單號
            "body": json_data["body"],  # 商品描述
            "total_fee": json_data["total_fee"],  # 總金額
            "mch_create_ip": get_ip(),  # 終端IP
            "auth_code": json_data["auth_code"],  # 授權碼
            "nonce_str": random_str()  # 隨機字符串
        }

        sign = get_sign(data)  # 生成簽名
        data["sign"] = sign  # 請求的參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))  # 將xml數(shù)據(jù)轉為python中的dict字典數(shù)據(jù)
        try:
            if result['xml']['err_code'] == 'USERPAYING':  # 需要輸入密碼的情況
                # 然后開始查詢
                for i in range(6):
                    print(i+1, '次')
                    time.sleep(5)
                    rest = query_tool(data)
                    if i != 5 and rest['xml']['trade_state'] != 'SUCCESS':
                        continue
                    elif rest['xml']['trade_state'] == 'SUCCESS':
                        print('成功支付', rest)
                        result = query_tool(data)
                        result['xml']['pay_code'] = "支付成功"
                        return result
                    elif rest['xml']['trade_state'] != 'SUCCESS' and i == 5:  # 需要輸入密碼的情況
                        revoke_tool(data)  # 調用撤銷接口
                        result = query_tool(data)
                        return result
                    else:
                        result = query_tool(data)
                        return result
            else:
                return result

        except:
            return result

    except Exception as e:
        print(e)
        raise e

這里我有使用到查詢訂單、簽名、遠程調取配置信息、生成隨機字符串、字典轉XML、獲取本機ip,方法如下:
(遠程調取配置信息)


def get_configuration_info():
    """
    獲取前置機賬戶配置
    """
    url = "http://*******"
    Port = "***"
    CpnID = "***"
    res = requests.request('post', url, data={"Port": Port, "CpnID": CpnID})
    result = json.loads(res.content)
    result["data"] = json.loads(result["data"])
    aws_s3_url = result["data"]["cpnActMchinRcd"][0]["ActCfg"]
    res = requests.request('GET', aws_s3_url)
    aws_str = str(res.content)
    awx_json = parse.unquote(aws_str)
    for a in json.loads(awx_json[2:-1]):
        if a["Keyname"] == "mch_id":
            MCH_ID = a["Keyval"]
        elif a["Keyname"] == "key":
            MD5_KEY = a["Keyval"]
    return {'MCH_ID': MCH_ID, 'MD5_KEY': MD5_KEY}


MCH_ID = get_configuration_info()['MCH_ID']  # mch_id
MD5_KEY = get_configuration_info()['MD5_KEY']  # MD5密鑰

(生成16位隨機字符串)

def random_str(randomlength=16):
    """
    生成隨機字符串
    :param randomlength: 字符串長度
    :return:
    """
    strs = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        strs += chars[random.randint(0, length)]
    return strs

(計算簽名)

def get_sign(data_dict):
    """
    簽名函數(shù)
    :param data_dict: 需要簽名的參數(shù),格式為字典
    :param key: 密鑰 ,即上面的MD5_KEY
    :return: 字符串
    注意:簽名是需要將所有的請求的參賽參與計算,空值不參與計算
    """
    data = {}
    for k in sorted(data_dict.keys()):  # 遍歷字典參數(shù)名ASCII字典序排序后的key
        v = data_dict.get(k)  # 取出字典中key對應的value
        if type(v) == list:  # 添加XML標記
            v = '![CDATA[{}]]'.format(v)
        data[k] = v
    params_list = sorted(data.items(), key=lambda e: e[0], reverse=False)  # 參數(shù)字典參數(shù)名ASCII字典序排序為列表
    params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + MD5_KEY  # 組織參數(shù)字符串并在末尾添加商戶交易密鑰
    md5 = hashlib.md5()  # 使用MD5加密模式
    md5.update(params_str.encode('utf-8'))  # 將參數(shù)字符串傳入
    sign = md5.hexdigest().upper()  # 完成加密并轉為大寫
    return sign

(字典轉XML)

def trans_dict_to_xml(data_dict):
    """
    定義字典轉XML的函數(shù)
    :param data_dict:
    :return:
    """
    data_xml = []
    for k in data_dict.keys():  # 遍歷字典的key
        v = data_dict.get(k)  # 取出字典中key對應的value
        if type(v) == list:  # 添加XML標記
            v = '![CDATA[{}]]'.format(v)
        data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
    return '<xml>{}</xml>'.format(''.join(data_xml))  # 返回XML

(獲取本機ip)

def get_ip():
    # 獲取當前ip
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('1.1.1.1', 80))
    ip = s.getsockname()[0]
    s.close()
    return ip

(查詢訂單)

def query_tool(json_data):
    """
    查詢
    """
    try:
        # 必帶參數(shù)
        data = {
            "service": "unified.trade.query",  # 接口類型
            "mch_id": MCH_ID,  # 門店編號
            "nonce_str": random_str(),  # 隨機字符串
            "out_trade_no": json_data["out_trade_no"]  # 商戶訂單號
        }
        sign = get_sign(data)  # 生成簽名
        data['sign'] = sign  # 必帶參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', "https://pay.hstypay.com/v2/pay/gateway", data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))
        return result
    except Exception as e:
        raise e

2、查詢訂單(方法調用的是上面的查詢方法)

@app.route('/Query', methods=['GET', 'POST'])
def query():
  # 查詢
  try:
      form_data = flask.request.get_data()  # 獲取未經(jīng)處理過的原始數(shù)據(jù)而不管內容類型
      json_data = json.loads(form_data.decode('utf-8'))  # 字符串轉化為字典
      result = query_tool(json_data)
      return result
  except Exception as e:
      print(e)
      raise e

3、撤銷訂單

@app.route('/Revoke', methods=['GET', 'POST'])
def revoke():
    # 撤銷
    try:
        form_data = flask.request.get_data()  # 獲取到請求帶的信息
        json_data = json.loads(form_data.decode('utf-8'))  # 轉為json格式
        result = revoke_tool(json_data)
        return result
    except Exception as e:
        print(e)
        raise e

(撤銷訂單方法)

def revoke_tool(json_data):
    """
    撤銷訂單
    """
    url = "https://pay.hstypay.com/v2/pay/gateway"  # 撤銷請求接口
    try:
        # 必帶參數(shù)
        data = {
            "service": "unified.micropay.reverse",
            "mch_id": MCH_ID,
            "out_trade_no": json_data['out_trade_no'],
            "nonce_str": random_str(),
            }
        sign = get_sign(data)  # 生成簽名
        data['sign'] = sign  # 必帶參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))  # 將xml數(shù)據(jù)轉為python中的dict字典數(shù)據(jù)
        return result
    except Exception as e:
        raise e

4、退款

@app.route('/Refund', methods=['GET', 'POST'])
def refund():
    # 退款
    try:
        url = 'https://pay.hstypay.com/v2/pay/gateway'
        form_data = flask.request.get_data()  # 獲取到請求帶的信息
        json_data = json.loads(form_data.decode('utf-8'))  # 轉為json格式
        data = {
            "service": "unified.trade.refund",  # 接口類型
            "mch_id": MCH_ID,  # 門店編號
            "out_trade_no": json_data["out_trade_no"],  # 訂單號
            "out_refund_no": json_data["out_refund_no"],  # 商戶退款單號
            "total_fee": json_data["total_fee"],  # 總金額
            "refund_fee": json_data["refund_fee"],  # 退款金額
            "op_user_id": json_data["op_user_id"],  # 操作員
            "nonce_str": random_str(),  # 隨機字符串
            "body": json_data["body"]  # 退款原因
        }  # 必帶參數(shù)

        sign = get_sign(data)  # 生成簽名
        data['sign'] = sign  # 必帶參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))  # 將xml數(shù)據(jù)轉為python中的dict字典數(shù)據(jù)
        print(result)
        if result['xml']['result_code'] == 0:
            result = query_refund_tool(data)
            return result
        else:
            return result

    except Exception as e:
        raise e

5、退款查詢

@app.route('/QueryRefund', methods=['GET', 'POST'])
def query_refund():
    # 退款查詢
    try:
        form_data = flask.request.get_data()  # 獲取到請求帶的信息
        json_data = json.loads(form_data.decode('utf-8'))  # 轉為json格式
        result = query_refund_tool(json_data)
        return result
    except Exception as e:
        raise e

(退款查詢方法)

def query_refund_tool(json_data):
    """
    查詢退款
    """
    url = "https://pay.hstypay.com/v2/pay/gateway"  # 退款請求接口
    try:
        # 必帶參數(shù)
        data = {
            "service": "unified.trade.refundquery",
            "mch_id": MCH_ID,
            "out_trade_no": json_data["out_trade_no"],
            "out_refund_no": json_data["out_refund_no"],
            "nonce_str": random_str(),  # 隨機字符串
            }
        sign = get_sign(data)
        data['sign'] = sign
        xml_str = trans_dict_to_xml(data)
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))
        return result

    except Exception as e:
        raise e

備注:如果看暈了 可以直接拷貝下面的整體
因為我就只寫了2個腳本,1個用于跑主代碼、1個跑工具
pay.py(跑主要代碼的)

import time

import flask
from flask import Flask

from tool import *

app = Flask(__name__)


@app.route('/Pay', methods=['GET', 'POST'])
def gello_world():
    # 提交付款碼支付API
    url = 'https://pay.hstypay.com/v2/pay/gateway'
    try:
        form_data = flask.request.get_data()  # 獲取未經(jīng)處理過的原始數(shù)據(jù)而不管內容類型
        json_data = json.loads(form_data.decode('utf-8'))  # 字符串轉化為字典
        for i in ('service', 'mch_id', 'out_trade_no', 'body', 'total_fee', 'mch_create_ip', 'auth_code'):
            if i not in json_data.keys():
                # 將沒有指定參添加上
                json_data[i] = None
        # 獲取請求參數(shù),并增加必帶參數(shù)
        data = {
            "service": "unified.trade.micropay",  # 接口類型
            "mch_id": MCH_ID,  # 門店編號
            "out_trade_no": json_data["out_trade_no"],  # 商戶訂單號
            "body": json_data["body"],  # 商品描述
            "total_fee": json_data["total_fee"],  # 總金額
            "mch_create_ip": get_ip(),  # 終端IP
            "auth_code": json_data["auth_code"],  # 授權碼
            "nonce_str": random_str()  # 隨機字符串
        }

        sign = get_sign(data)  # 生成簽名
        data["sign"] = sign  # 請求的參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))  # 將xml數(shù)據(jù)轉為python中的dict字典數(shù)據(jù)
        try:
            if result['xml']['err_code'] == 'USERPAYING':  # 需要輸入密碼的情況
                # 然后開始查詢
                for i in range(6):
                    print(i+1, '次')
                    time.sleep(5)
                    rest = query_tool(data)
                    if i != 5 and rest['xml']['trade_state'] != 'SUCCESS':
                        continue
                    elif rest['xml']['trade_state'] == 'SUCCESS':
                        print('成功支付', rest)
                        result = query_tool(data)
                        result['xml']['pay_code'] = "支付成功"
                        return result
                    elif rest['xml']['trade_state'] != 'SUCCESS' and i == 5:  # 需要輸入密碼的情況
                        revoke_tool(data)  # 調用撤銷接口
                        result = query_tool(data)
                        return result
                    else:
                        result = query_tool(data)
                        return result
            else:
                return result

        except:
            return result

    except Exception as e:
        print(e)
        raise e


@app.route('/Query', methods=['GET', 'POST'])
def query():
    # 查詢
    try:
        form_data = flask.request.get_data()  # 獲取未經(jīng)處理過的原始數(shù)據(jù)而不管內容類型
        json_data = json.loads(form_data.decode('utf-8'))  # 字符串轉化為字典
        result = query_tool(json_data)
        return result
    except Exception as e:
        print(e)
        raise e


@app.route('/Revoke', methods=['GET', 'POST'])
def revoke():
    # 撤銷
    try:
        form_data = flask.request.get_data()  # 獲取到請求帶的信息
        json_data = json.loads(form_data.decode('utf-8'))  # 轉為json格式
        result = revoke_tool(json_data)
        return result
    except Exception as e:
        print(e)
        raise e


@app.route('/Refund', methods=['GET', 'POST'])
def refund():
    # 退款
    try:
        url = 'https://pay.hstypay.com/v2/pay/gateway'
        form_data = flask.request.get_data()  # 獲取到請求帶的信息
        json_data = json.loads(form_data.decode('utf-8'))  # 轉為json格式
        data = {
            "service": "unified.trade.refund",  # 接口類型
            "mch_id": MCH_ID,  # 門店編號
            "out_trade_no": json_data["out_trade_no"],  # 訂單號
            "out_refund_no": json_data["out_refund_no"],  # 商戶退款單號
            "total_fee": json_data["total_fee"],  # 總金額
            "refund_fee": json_data["refund_fee"],  # 退款金額
            "op_user_id": json_data["op_user_id"],  # 操作員
            "nonce_str": random_str(),  # 隨機字符串
            "body": json_data["body"]  # 退款原因
        }  # 必帶參數(shù)

        sign = get_sign(data)  # 生成簽名
        data['sign'] = sign  # 必帶參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))  # 將xml數(shù)據(jù)轉為python中的dict字典數(shù)據(jù)
        print(result)
        if result['xml']['result_code'] == 0:
            result = query_refund_tool(data)
            return result
        else:
            return result

    except Exception as e:
        raise e


@app.route('/QueryRefund', methods=['GET', 'POST'])
def query_refund():
    # 退款查詢
    try:
        form_data = flask.request.get_data()  # 獲取到請求帶的信息
        json_data = json.loads(form_data.decode('utf-8'))  # 轉為json格式
        result = query_refund_tool(json_data)
        return result
    except Exception as e:
        raise e

if __name__ == '__main__':
    app.run()

tools.py(跑工具的)

import hashlib
import json
import os
import socket
from collections import defaultdict
from random import Random
from urllib import parse

import xmltodict

import requests
import OpenSSL


def get_configuration_info():
    """
    獲取前置機賬戶配置
    """
    url = "http://******"
    Port = "4013"
    CpnID = "001111"
    res = requests.request('post', url, data={"Port": Port, "CpnID": CpnID})
    result = json.loads(res.content)
    result["data"] = json.loads(result["data"])
    aws_s3_url = result["data"]["cpnActMchinRcd"][0]["ActCfg"]
    res = requests.request('GET', aws_s3_url)
    aws_str = str(res.content)
    awx_json = parse.unquote(aws_str)
    for a in json.loads(awx_json[2:-1]):
        if a["Keyname"] == "mch_id":
            MCH_ID = a["Keyval"]
        elif a["Keyname"] == "key":
            MD5_KEY = a["Keyval"]
    return {'MCH_ID': MCH_ID, 'MD5_KEY': MD5_KEY}


MCH_ID = get_configuration_info()['MCH_ID']  # mch_id
MD5_KEY = get_configuration_info()['MD5_KEY']  # MD5密鑰


def random_str(randomlength=16):
    """
    生成隨機字符串
    :param randomlength: 字符串長度
    :return:
    """
    strs = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        strs += chars[random.randint(0, length)]
    return strs


def get_sign(data_dict):
    """
    簽名函數(shù)
    :param data_dict: 需要簽名的參數(shù),格式為字典
    :param key: 密鑰 ,即上面的MD5_KEY
    :return: 字符串
    注意:簽名是需要將所有的請求的參賽參與計算,空值不參與計算
    """
    data = {}
    for k in sorted(data_dict.keys()):  # 遍歷字典參數(shù)名ASCII字典序排序后的key
        v = data_dict.get(k)  # 取出字典中key對應的value
        if type(v) == list:  # 添加XML標記
            v = '![CDATA[{}]]'.format(v)
        data[k] = v
    params_list = sorted(data.items(), key=lambda e: e[0], reverse=False)  # 參數(shù)字典參數(shù)名ASCII字典序排序為列表
    params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + MD5_KEY  # 組織參數(shù)字符串并在末尾添加商戶交易密鑰
    md5 = hashlib.md5()  # 使用MD5加密模式
    md5.update(params_str.encode('utf-8'))  # 將參數(shù)字符串傳入
    sign = md5.hexdigest().upper()  # 完成加密并轉為大寫
    return sign


def trans_dict_to_xml(data_dict):
    """
    定義字典轉XML的函數(shù)
    :param data_dict:
    :return:
    """
    data_xml = []
    for k in data_dict.keys():  # 遍歷字典的key
        v = data_dict.get(k)  # 取出字典中key對應的value
        if type(v) == list:  # 添加XML標記
            v = '![CDATA[{}]]'.format(v)
        data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
    return '<xml>{}</xml>'.format(''.join(data_xml))  # 返回XML


def query_tool(json_data):
    """
    查詢
    """
    try:
        # 必帶參數(shù)
        data = {
            "service": "unified.trade.query",  # 接口類型
            "mch_id": MCH_ID,  # 門店編號
            "nonce_str": random_str(),  # 隨機字符串
            "out_trade_no": json_data["out_trade_no"]  # 商戶訂單號
        }
        sign = get_sign(data)  # 生成簽名
        data['sign'] = sign  # 必帶參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', "https://pay.hstypay.com/v2/pay/gateway", data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))
        return result
    except Exception as e:
        raise e


def query_refund_tool(json_data):
    """
    查詢退款
    """
    url = "https://pay.hstypay.com/v2/pay/gateway"  # 退款請求接口
    try:
        # 必帶參數(shù)
        data = {
            "service": "unified.trade.refundquery",
            "mch_id": MCH_ID,
            "out_trade_no": json_data["out_trade_no"],
            "out_refund_no": json_data["out_refund_no"],
            "nonce_str": random_str(),  # 隨機字符串
            }
        sign = get_sign(data)
        data['sign'] = sign
        xml_str = trans_dict_to_xml(data)
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))
        return result

    except Exception as e:
        raise e


def revoke_tool(json_data):
    """
    撤銷訂單
    """
    url = "https://pay.hstypay.com/v2/pay/gateway"  # 撤銷請求接口
    try:
        # 必帶參數(shù)
        data = {
            "service": "unified.micropay.reverse",
            "mch_id": MCH_ID,
            "out_trade_no": json_data['out_trade_no'],
            "nonce_str": random_str(),
            }
        sign = get_sign(data)  # 生成簽名
        data['sign'] = sign  # 必帶參數(shù)中添加簽名
        xml_str = trans_dict_to_xml(data)  # 字典轉換xml
        res = requests.request('post', url, data=xml_str.encode())  # 以POST方式向微信公眾平臺服務器發(fā)起請求
        result = json.loads(json.dumps(xmltodict.parse(res.content)))  # 將xml數(shù)據(jù)轉為python中的dict字典數(shù)據(jù)
        return result
    except Exception as e:
        raise e


def get_ip():
    # 獲取當前ip
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('1.1.1.1', 80))
    ip = s.getsockname()[0]
    s.close()
    return ip




?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容