美文网首页支付金融支付互联网科技
微信, 支付宝扫码支付总结

微信, 支付宝扫码支付总结

作者: single430 | 来源:发表于2017-03-05 11:14 被阅读300次

    好不容易折腾微信和支付宝的开发文档才搞出来的


    一: 支付宝扫码支付

    import datetime
    import random
    import hashlib
    import requests
    import time
    import base64
    
    from urllib import parse
    from OpenSSL.crypto import load_privatekey, FILETYPE_PEM, sign
    from OpenSSL import crypto
    
    
    # 支付宝 扫码支付
    def alipay_url(order_id, total_amount, timeout_express, subject, body=None):
        '''
        TODO: 扫码支付 - 430
        公共参数
        app_id          String  是   32  支付宝分配给开发者的应用ID  2014072300007148
        method          String  是   128 接口名称    alipay.trade.app.pay
        charset         String  是   10  请求使用的编码格式,如utf-8,gbk,gb2312等    utf-8
        sign_type       String  是   10  商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2   RSA2
        sign            String  是   256 商户请求参数的签名串,详见签名 详见示例
        timestamp       String  是   19  发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50
        version         String  是   3   调用的接口版本,固定为:1.0 1.0
        notify_url      String  是   256 支付宝服务器主动通知商户服务器里指定的页面http/https路径。建议商户使用https   https://api.xx.com/receive_notify.htm
        biz_content     String  是   -   业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档
    
        业务参数
        body            String  否   128 对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。 Iphone6 16G
        subject         String  是   256 商品的标题/交易标题/订单标题/订单关键字等。 大乐透
        out_trade_no    String  是   64  商户网站唯一订单号   70501111111S001111119
        timeout_express String  否   6   设置未付款支付宝交易的超时时间,一旦超时,该笔交易就会自动被关闭。当用户进入支付宝收银台页面(不包括登录页面),会触发即刻创建支付宝交易,此时开始计时。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。   90m
        total_amount    String  是   9   订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]   9.00
        seller_id       String  否   16  收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID  2088102147948060
        product_code    String  是   64  销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY  QUICK_MSECURITY_PAY
        goods_type      String  否   2   商品主类型:0—虚拟类商品,1—实物类商品 注:虚拟类商品不支持使用花呗渠道  0
    
        app_id=2015052600090779
        &biz_content={
            "timeout_express":"30m",
            "seller_id":"",
            "product_code":"QUICK_MSECURITY_PAY",
            "total_amount":"0.01",
            "subject":"1",
            "body":"我是测试数据",
            "out_trade_no":"IQJZSRC1YMQB5HU"
        }
        &charset=utf-8
        &format=json
        &method=alipay.trade.app.pay
        &notify_url=http://domain.merchant.com/payment_notify
        &sign_type=RSA2
        &timestamp=2016-08-25 20:26:31
        &version=1.0
        &sign=cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj+y48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp/M45w1ZsDOiduBbduGfRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ77Lo5J0PpUUWwyQGt0M4cj8g=
        '''
        url = {
            'app_id': ALIPAY_APP_ID,
            'version': '1.0',
            'format': 'json',
            'sign_type': 'RSA',
            'method': ALIPAY_PRECREATE,
            'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'notify_url': ALIPAY_NOTIFY_URL,
            'charset': 'utf-8',
        }
    
        biz_content = {
            "timeout_express": "{}".format(timeout_express),
            # "product_code": "QUICK_MSECURITY_PAY",
            "total_amount": "{}".format(total_amount),
            "subject": subject,
            "body": body,
            "out_trade_no": "{}".format(order_id),
            "goods_type": "0",
        }
        # print(biz_content)
        url['biz_content'] = str(biz_content)
    
        s_url = format_data(url, False)
        key = load_privatekey(FILETYPE_PEM, open("you_private_key.pem").read())
        sign_alipay = sign(key, s_url, 'sha1')
        sign_alipay = base64.b64encode(sign_alipay)
        url['sign'] = str(sign_alipay, encoding='utf-8')
    
        return url
    
    

    上面就是支付宝扫码支付,如果成功会返回二维码链接,只需调用库生成二维码就可以。

    if result_dict['code'] == "10000" and result_dict['msg'] == "Success":
        print("订单号: {0} 支付宝扫码支付二维码url为: {1}".format(order_id, result_dict['qr_code'].replace('\\', '')))
    

    字典排序函数

    def format_data(data, urlencode):
        """格式化参数,签名过程需要使用"""
        keys = sorted(data.keys())
        buff = []
        for key in keys:
            value = parse.quote(data[key]) if urlencode else data[key]
            buff.append("{0}={1}".format(key, value))
    
        return "&".join(buff)
    

    微信扫码支付

    # 微信 扫码支付
    class WeiXinPay(object):
        """
        TODO: 微信 - 430
        """
    
        # 随机字符串生成(15<num<32位)
        def create_nonce_str(self):
            length = random.randrange(15, 32)
            chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            strs = []
            for x in range(length):
                strs.append(chars[random.randrange(0, len(chars))])
            return "".join(strs)
    
        def get_sign(self, obj):
            """生成签名"""
            # 签名步骤一:按字典序排序参数
            str = format_data(obj, False)
            # 签名步骤二:在string后加入KEY
            str = "{0}&key={1}".format(str, WEIXIN_KEY)
            # 签名步骤三:MD5加密
            str = hashlib.md5(str.encode('utf-8')).hexdigest()
            # 签名步骤四:所有字符转为大写
            sign = str.upper()
            return sign
    
        def dict_to_xml(self, dict):
            """dict转xml"""
            xml = ["<xml>"]
            for key in dict:
                if dict[key]:
                    xml.append("<{0}>{1}</{0}>".format(key, dict[key]))
                else:
                    xml.append("<{0}><![CDATA[{1}]]></{0}>".format(key, dict[key]))
            xml.append("</xml>")
            return "".join(xml)
    
        def weixin_native_pay_url(self, order_id, total_fee, ip, time_expire = 5):
            '''
            字段名 变量名 必填  类型  示例值 描述
            应用ID    appid   是   String(32)  wxd678efh567hg6787  微信开放平台审核通过的应用APPID
            商户号 mch_id  是   String(32)  1230000109  微信支付分配的商户号
            设备号 device_info 否   String(32)  013467007045764 终端设备号(门店号或收银设备ID),默认请传"WEB"
            随机字符串   nonce_str   是   String(32)  5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
            签名  sign    是   String(32)  C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
            签名类型    sign_type   否   String(32)  HMAC-SHA256 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
            商品描述    body    是   String(128) 腾讯充值中心-QQ会员充值
            商品描述交易字段格式根据不同的应用场景按照以下格式:
            APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
            商品详情    detail  否   String(8192)
            {
                "goods_detail":[
                    {
                        "goods_id":"iphone6s_16G",
                        "wxpay_goods_id":"1001",
                        "goods_name":"iPhone6s 16G",
                        "quantity":1,
                        "price":528800,
                        "goods_category":"123456",
                        "body":"苹果手机"
                    },
                    {
                        "goods_id":"iphone6s_32G",
                        "wxpay_goods_id":"1002",
                        "goods_name":"iPhone6s 32G",
                        "quantity":1,
                        "price":608800,
                        "goods_category":"123789",
                        "body":"苹果手机"
                    }
                ]
            }
            商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
            goods_detail 服务商必填 []:
            └ goods_id String 必填 32 商品的编号
            └ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号
            └ goods_name String 必填 256 商品名称
            └ quantity Int 必填 商品数量
            └ price Int 必填 商品单价,单位为分
            └ goods_category String 可选 32 商品类目ID
            └ body String 可选 1000 商品描述信息
            附加数据    attach  否   String(127) 深圳分店    附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
            商户订单号   out_trade_no    是   String(32)  20150806125346  商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
            货币类型    fee_type    否   String(16)  CNY 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
            总金额 total_fee   是   Int 888 订单总金额,单位为分,详见支付金额
            终端IP    spbill_create_ip    是   String(16)  123.12.12.123   用户端实际ip
            交易起始时间  time_start  否   String(14)  20091225091010  订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
            交易结束时间  time_expire 否   String(14)  20091227091010
            订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
            注意:最短失效时间间隔必须大于5分钟
            商品标记    goods_tag   否   String(32)  WXG 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
            通知地址    notify_url  是   String(256) http://www.weixin.qq.com/wxpay/pay.php  接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
            交易类型    trade_type  是   String(16)  APP 支付类型
            指定支付方式  limit_pay   否   String(32)  no_credit   no_credit--指定不能使用信用卡支付
            '''
            order_time = datetime.datetime.now()
            weixin_data = {
                'appid': WEIXIN_APPID,
                'mch_id': WEXIIN_MCHID,
                'device_info': WEIXIN_DEVICE_INFO,
                'nonce_str': self.create_nonce_str(),
                'sign_type': 'MD5',
                'body': '费用支付',
                # 'detail': {},
                'out_trade_no': '{}'.format(order_id),
                'total_fee': int(total_fee*100),    # 分,不能有小数
                'spbill_create_ip': '{}'.format(ip),
                'time_start': order_time.strftime('%Y%m%d%H%M%S'),
                'time_expire': (order_time + datetime.timedelta(minutes=time_expire)).strftime('%Y%m%d%H%M%S'),
                'notify_url': WEIXIN_NOTIFY_URL,
                'trade_type': 'NATIVE'
            }
            weixin_data['sign'] = self.get_sign(weixin_data)
            url = format_data(weixin_data, False)
    
            # 检测必填参数
            if any(weixin_data[key] is None for key in ("out_trade_no", "body", "total_fee", "notify_url", "trade_type")):
                raise ValueError("丢失必要参数")
    
            # return 'weixin://wxpay/bizpayurl?' + url
            return self.dict_to_xml(weixin_data)
    

    上面就是微信扫码支付,如果成功会返回二维码链接,只需调用库生成二维码就可以。不过返回的结果是xml格式的,需要自己调用库区解析。

    from xml.etree import ElementTree
    xml_tree = ElementTree.fromstring(weixin_result.text)
    if xml_tree.find('return_code').text == "SUCCESS" and xml_tree.find('result_code').text == "SUCCESS":
        print("订单号: {0} 微信扫码支付二维码url为: {1}".format(order_id, xml_tree.find('code_url').text))
    

    最后在写一个银联的签名过程,也是折腾了一番

        # 1. 按ascii排序。【注意不是字母顺序】
        # 2. 对1的结果sha1得到byte数组。
        # 3. 对2的结果用16进制小写字符串表示。【注意必须是小写】
        # 4. 对3的结果取byte数组。【注意不是16进制字符串转byte数组,而是当普通字符串转】
        # 5. 对4的结果用私钥算签名,算法为rsawithsha1,得到一个byte数组。
        # 6. 对5的结果做base64,得到一个字符串就是签名。
        # * C语言开发请特别注意测试公私钥可能和生产的长度不同,实现时请注意不要固定长度做。
        
        data = {'自己的参数': '自己的数据'}
        fs = open('700000000000001_acp.pfx', 'rb')    # 官方测试私钥
        cert_pfx = crypto.load_pkcs12(fs.read(), '000000')
        fs.close()
    
        cert_id = cert_pfx.get_certificate().get_serial_number()
    
        data_str = format_data(data, False)
        sign_digest = hashlib.sha1(data_str.encode('utf-8')).hexdigest().lower()
        private_key = cert_pfx.get_privatekey()
        soft_sign = sign(private_key, sign_digest, 'sha1')
        base_sign = base64.b64encode(soft_sign)
    

    银联官方技术服务之签名算法与注意点

    推荐一个工具,做get, post等测试的时候很不错
    Postman

    • 上述所有大写参数请自行添加
    • 写的不好,还望见谅与建议
    By: single430

    相关文章

      网友评论

        本文标题:微信, 支付宝扫码支付总结

        本文链接:https://www.haomeiwen.com/subject/ynysgttx.html