坑爹的微信支付

坑爹的微信支付


上節課我們講了支付寶支付,這節課我們講微信支付,微信支付比支付寶支付稍微簡單一點,原因在於沒有各種非對稱加密,不過有一點我們要記得,需要我們自己設計密鑰。

class NeoTenpayAgent(object):
    def __init__(self):
        self.partner_id = Config.WEIXIN_MCH_ID
        self.partner_key = Config.WEIXIN_MCH_KEY
        self.app_id = Config.WEIXIN_APP_ID
        self.notify_url = Config.WEIXIN_NOTIFY_URL
        self.session = requests.Session()

    def generate_params(self, user_id, order_no,
        item_name, item_description, price):
        params = {
        'body': item_name,
        'fee_type': 'CNY',
        'notify_url': self.notify_url,
        'out_trade_no': order_no,
        'mch_id': self.partner_id,
        'spbill_create_ip': '196.168.1.1',
        'total_fee': str(price),
        'appid': self.app_id,
        'nonce_str': uuid.uuid4().hex,
        'trade_type': 'APP',
        }
        chunk = '&'.join(
        '{}={}'.format(k, v)
        for k, v in sorted(params.items())
        )
        chunk += '&key=' + self.partner_key
        params['sign'] = hashlib.md5(chunk.encode('utf-8')).hexdigest().upper()

        xml_params = E.xml(
        E.body(params['body']),
        E.fee_type(params['fee_type']),
        E.notify_url(params['notify_url']),
        E.out_trade_no(params['out_trade_no']),
        E.mch_id(params['mch_id']),
        E.spbill_create_ip(params['spbill_create_ip']),
        E.total_fee(params['total_fee']),
        E.appid(params['appid']),
        E.nonce_str(params['nonce_str']),
        E.sign(params['sign']),
        E.trade_type(params['trade_type']),
        )
        data = lxml.etree.tostring(xml_params, encoding='utf-8')
        page = self.session.post(
        'https://api.mch.weixin.qq.com/pay/unifiedorder',
        data=data,
        )
        page.encoding = 'utf-8'
        resp = lxml.etree.fromstring(page.text)
        if resp.xpath('/xml/err_code/text()'):
        for item in resp.xpath('/xml/err_code/text()'):
            err_code = item
        for item in resp.xpath('/xml/err_code_des/text()'):
            err_code_des = item
        if err_code == 'ORDERPAID':
        # 訂單已支付,無需再次嘗試
            raise ValueError(err_code_des)
        else:
            raise Exception('({}) {}'.format(
        err_code, err_code_des
        ))
        # 調起支付的參數
        for item in resp.xpath('/xml/prepay_id/text()'):
            params = {
            'appid': params['appid'],
            'noncestr': params['nonce_str'],
            'package': 'Sign=WXPay',
            'partnerid': self.partner_id,
            'prepayid': item,
            'timestamp': _timestamp_from_datetime(datetime.now()),
            }
        chunk = '&'.join(
        '{}={}'.format(k, v)
        for k, v in sorted(params.items())
        )
        chunk += '&key=' + self.partner_key
        params['sign'] = hashlib.md5(
        chunk.encode('utf-8')
        ).hexdigest().upper()

        return params

    def verify(self, params):
        params = lxml.etree.fromstring(params)
        if not params.xpath('/xml/sign/text()'):
        raise ValueError('參數 sign 不存在')

        # 值爲空的字段不參與簽名
        sign_params = {}
        for item in params.xpath('/xml')[0]:
            if item.tag not in ['sign'] and item.text:
                sign_params[item.tag] = item.text
                chunk = '&'.join(
                '{}={}'.format(
                k.decode('utf-8') if isinstance(k, str) else k,
                v.decode('utf-8') if isinstance(v, str) else v,
                )
                for k, v in sorted(sign_params.items())
        )
        chunk = (chunk + '&key=' + self.partner_key).encode('utf-8')
        signature = hashlib.md5(chunk).hexdigest().upper()
        if params.xpath('/xml/sign/text()')[0] != signature:
            raise ValueError('簽名不正確')

這就是根據微信後臺API設計的商戶後臺服務器。

值得注意的是,商戶回調需要我們自己設計。

def handle_weipay():
    NeoTenpayAgent().verify(request.data.decode('utf-8'))
    import xml.etree.cElementTree as ET
    data = ET.fromstring(request.data.decode('utf-8'))
    data = {d.tag: d.text for d in data}
    if data.get('return_code') == 'SUCCESS':
        order_id = data.get('out_trade_no')
        Ten_or_Ali_order_id = data.get('transaction_id')
        payment_type = '微信'
        payment_state = data.get('result_code')
        subject = data.get('attach', '證件照')
        fee = data.get('total_fee')
        """
        寫數據庫
        """
        print(payment_state)
    else:
        print('支付失敗')
    with open('static/logg/weipaylog.txt', 'at') as f:
        f.write(str(data))
    from xml.etree.cElementTree import Element, tostring
    elem = Element('xml')
    for key, value in {'return_code': 'SUCCESS', 'return_msg': 'OK'}.items():
        child = Element(key)
        child.text = value
        elem.append(child)
    return tostring(elem, encoding='utf-8')

最後我們返回給微信服務器必須要像我那樣寫。其實代碼也不復雜,只不顧業務邏輯比較繁瑣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章