支付交互流程

这里呢,就不多说了,先上图

后台调用,只需要调用统一下单API,就可以了。

获取openid

这是第一步,openid是微信用户在你的小程序中的唯一标识,只需调用vx.login即可。

import requests

from config import APPID, SECRET


class OpenidUtils(object):

    def __init__(self, jscode):
        self.url = "https://api.weixin.qq.com/sns/jscode2session"
        self.appid = APPID  # 小程序id
        self.secret = SECRET  # 不要跟后面支付的key搞混
        self.jscode = jscode    # 前端传回的动态jscode

    def get_openid(self):
        # url一定要拼接,不可用传参方式
        url = self.url + "?appid=" + self.appid + "&secret=" + self.secret + "&js_code=" + self.jscode + "&grant_type=authorization_code"
        r = requests.get(url)
        print(r.json())
        openid = r.json()['openid']

        return openid

支付请求

import requests
import json
import hashlib
import time
import random
import string
import xmltodict

class WX_PayToolUtil():
    """ 微信支付工具 """

    def __init__(self, APP_ID, MCH_ID, API_KEY, NOTIFY_URL):
        self._APP_ID = APP_ID  # 小程序ID
        self._MCH_ID = MCH_ID  # 商户号
        self._API_KEY = API_KEY
        self._UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"  # 接口链接
        self._NOTIFY_URL = NOTIFY_URL  # 异步通知

    def generate_sign(self, param):
            '''生成签名'''
            stringA = ''
            ks = sorted(param.keys())
            # 参数排序
            for k in ks:
                stringA += (k + '=' + param[k] + '&')
            # 拼接商户KEY
            stringSignTemp = stringA + "key=" + self._API_KEY
            # md5加密,也可以用其他方式
            hash_md5 = hashlib.md5(stringSignTemp.encode('utf8'))
            sign = hash_md5.hexdigest().upper()
            return sign

    def getPayUrl(self, orderid, openid, goodsPrice, **kwargs):
        """向微信支付端发出请求,获取url"""
        # key = self._API_KEY
        nonce_str = ''.join(random.sample(string.ascii_letters + string.digits, 30))  # 生成随机字符串,小于32位
        params = {
            'appid': self._APP_ID,
            'mch_id': self._MCH_ID,
            'nonce_str': nonce_str,
            "body": '艺术品订单',
            'out_trade_no': orderid,
            'total_fee': str(goodsPrice),
            'spbill_create_ip': "127.0.0.1",
            'notify_url': self._NOTIFY_URL,
            'trade_type': "JSAPI",
            "openid": openid,
         }
        # 生成签名
        params['sign'] = self.generate_sign(params)
        param = {'root': params}
        xml = xmltodict.unparse(param)
        response = requests.post(self._UFDODER_URL, data=xml.encode('utf-8'), headers={'Content-Type': 'text/xml'})
        # xml 2 dict
        msg = response.text
        xmlmsg = xmltodict.parse(msg)
        # 4. 获取prepay_id
        if xmlmsg['xml']['return_code'] == 'SUCCESS':
            if xmlmsg['xml']['result_code'] == 'SUCCESS':
                prepay_id = xmlmsg['xml']['prepay_id']
                # 时间戳
                timeStamp = str(int(time.time()))
                # 5. 五个参数
                data = {
                    "appId": self._APP_ID,
                    "nonceStr": nonce_str,
                    "package": "prepay_id=" + prepay_id,
                    "signType": 'MD5',
                    "timeStamp": timeStamp,
                }
                # 6. paySign签名
                paySign = self.generate_sign(data)
                data["paySign"] = paySign  # 加入签名
                # 7. 传给前端的签名后的参数
                return data

当然你可能会遇到的错误有签名错误,一般的情况是你的appSecret和商户号的API密钥两个弄错了,当然如果不是还有可能是其他问题,解决方案链接https://www.cnblogs.com/wanghuijie/p/wxpay_sign_error.html。

很可能遇到的错误

比如返回invalid total_fee,这是因为,微信支付和支付宝不同,他的计量单位是分,所以不能出现小数点。所以我们系统如果是以元为单位要处理下金额,即先乘以100,再去小数点