|
@@ -0,0 +1,173 @@
|
|
|
+from flask import current_app
|
|
|
+import hashlib
|
|
|
+import time
|
|
|
+from urllib.parse import quote
|
|
|
+
|
|
|
+import requests
|
|
|
+import xmltodict
|
|
|
+
|
|
|
+
|
|
|
+class WechatPayObject:
|
|
|
+ """配置账号信息"""
|
|
|
+ # 微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ # 开发者调用支付统一下单API生成预交易单
|
|
|
+ self.APPID = 'wx2a9f5ef9baf2760f'
|
|
|
+ # 商户id
|
|
|
+ self.MCHID = '1508209741'
|
|
|
+ # 异步通知url,商户根据实际开发过程设定
|
|
|
+ self.NOTIFY_URL = 'test'
|
|
|
+ self.TRADE_TYPE = 'APP'
|
|
|
+ self.APIKEY = 'ZHansjeransjeransjer680301000000'
|
|
|
+ self.url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 微信请求url
|
|
|
+ self.error = None
|
|
|
+ self.params = None
|
|
|
+
|
|
|
+ def get_parameter(self, order_id, body, total_fee, spbill_create_ip, notify_url):
|
|
|
+ self.params = {
|
|
|
+ 'appid': self.APPID, # appid
|
|
|
+ 'mch_id': self.MCHID, # 商户号
|
|
|
+ 'nonce_str': self.getNonceStr(),
|
|
|
+ 'body': body, # 商品描述
|
|
|
+ 'out_trade_no': str(order_id), # 商户订单号
|
|
|
+ 'total_fee': str(int(total_fee)),
|
|
|
+ 'spbill_create_ip': spbill_create_ip, # 127.0.0.1
|
|
|
+ 'trade_type': self.TRADE_TYPE, # 交易类型
|
|
|
+ 'notify_url': notify_url, # 微信支付结果异步通知地址
|
|
|
+ 'receipt': 'Y'
|
|
|
+ }
|
|
|
+ return self.params
|
|
|
+
|
|
|
+ def getNonceStr(self, length=32):
|
|
|
+ """生成随机字符串"""
|
|
|
+ import random
|
|
|
+ chars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
|
|
+ strs = []
|
|
|
+ for x in range(length):
|
|
|
+ strs.append(chars[random.randrange(0, len(chars))])
|
|
|
+ return "".join(strs)
|
|
|
+
|
|
|
+ def key_value_url(self, value, urlencode):
|
|
|
+ """
|
|
|
+ 将键值对转为 key1=value1&key2=value2
|
|
|
+ 对参数按照key=value的格式,并按照参数名ASCII字典序排序
|
|
|
+ """
|
|
|
+ slist = sorted(value)
|
|
|
+ buff = []
|
|
|
+ for k in slist:
|
|
|
+ v = quote(value[k]) if urlencode else value[k]
|
|
|
+ buff.append("{0}={1}".format(k, v))
|
|
|
+
|
|
|
+ return "&".join(buff)
|
|
|
+
|
|
|
+ def get_sign(self, params):
|
|
|
+ """
|
|
|
+ 生成sign
|
|
|
+ 拼接API密钥
|
|
|
+ """
|
|
|
+ stringA = self.key_value_url(params, False)
|
|
|
+ stringSignTemp = stringA + '&key=' + self.APIKEY # APIKEY, API密钥,需要在商户后台设置
|
|
|
+ sign = (hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest()).upper()
|
|
|
+ params['sign'] = sign
|
|
|
+ return params
|
|
|
+
|
|
|
+ def get_req_xml(self):
|
|
|
+ """
|
|
|
+ 拼接XML
|
|
|
+ """
|
|
|
+ self.get_sign(self.params)
|
|
|
+ xml = "<xml>"
|
|
|
+ for k, v in self.params.items():
|
|
|
+ # v = v.encode('utf8')
|
|
|
+ # k = k.encode('utf8')
|
|
|
+ xml += '<' + k + '>' + v + '</' + k + '>'
|
|
|
+ xml += "</xml>"
|
|
|
+ return xml.encode("utf-8")
|
|
|
+
|
|
|
+ def get_prepay_id(self):
|
|
|
+ """
|
|
|
+ 请求获取prepay_id
|
|
|
+ """
|
|
|
+ xml = self.get_req_xml()
|
|
|
+ respone = requests.post(self.url, xml, headers={'Content-Type': 'application/xml'})
|
|
|
+ msg = respone.text.encode('ISO-8859-1').decode('utf-8')
|
|
|
+ xmlresp = xmltodict.parse(msg)
|
|
|
+ if xmlresp['xml']['return_code'] == 'SUCCESS':
|
|
|
+ if xmlresp['xml']['result_code'] == 'SUCCESS':
|
|
|
+ prepay_id = xmlresp['xml']['prepay_id']
|
|
|
+ self.params['prepay_id'] = prepay_id
|
|
|
+ self.params['package'] = "Sign=WXPay"
|
|
|
+ self.params['timestamp'] = str(int(time.time()))
|
|
|
+ return self.params
|
|
|
+ else:
|
|
|
+ return 'failure'
|
|
|
+ else:
|
|
|
+ return 'failure'
|
|
|
+
|
|
|
+ def re_finall(self):
|
|
|
+ """得到prepay_id后再次签名,返回给终端参数
|
|
|
+ """
|
|
|
+ self.get_prepay_id()
|
|
|
+ if self.error:
|
|
|
+ return
|
|
|
+ sign_again_params = {
|
|
|
+ 'appid': self.params['appid'],
|
|
|
+ 'noncestr': self.params['nonce_str'],
|
|
|
+ 'package': self.params['package'],
|
|
|
+ 'partnerid': self.params['mch_id'],
|
|
|
+ 'timestamp': self.params['timestamp'],
|
|
|
+ 'prepayid': self.params['prepay_id']
|
|
|
+ }
|
|
|
+ self.get_sign(sign_again_params)
|
|
|
+ sign_again_params['sign'] = sign_again_params['sign']
|
|
|
+ return sign_again_params # 返回给app
|
|
|
+
|
|
|
+ def get_notifypay(self, data):
|
|
|
+ dictdata = dict(data)
|
|
|
+ _dictdata = dict(dictdata['xml'])
|
|
|
+ success = self.get_sign(_dictdata)
|
|
|
+ # print('success', success)
|
|
|
+ if success:
|
|
|
+ success.pop("sign", None)
|
|
|
+ success.pop("sign_type", None)
|
|
|
+ return success
|
|
|
+ else:
|
|
|
+ return None
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def xml_to_dict(params):
|
|
|
+ """
|
|
|
+ 拼接XML
|
|
|
+ """
|
|
|
+ if not isinstance(params, dict):
|
|
|
+ return None
|
|
|
+ xml = "<xml>"
|
|
|
+ for k, v in params.items():
|
|
|
+ # v = v.encode('utf8')
|
|
|
+ # k = k.encode('utf8')
|
|
|
+ xml += '<' + k + '>' + v + '</' + k + '>'
|
|
|
+ xml += "</xml>"
|
|
|
+ return xml
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ # 调用统一支付接口
|
|
|
+ # 订单id
|
|
|
+ order_id = '12345678903424'
|
|
|
+ # 描述信息
|
|
|
+ body = 'soober支付'
|
|
|
+ # 价格
|
|
|
+ total_fee = '50'
|
|
|
+ # 调用微信支付API的机器IP
|
|
|
+ spbill_create_ip = '127.0.0.1'
|
|
|
+ pay = WechatPayObject()
|
|
|
+ # 获取参数
|
|
|
+ parameter_dict = pay.get_parameter(order_id, body, total_fee, spbill_create_ip)
|
|
|
+ print('parameter_dict', parameter_dict)
|
|
|
+ # parameter_dict 参数中获取MWEB_URL 调转页面在路径后面添加redirect_url
|
|
|
+ # 统一调用接口
|
|
|
+ response = pay.re_finall()
|
|
|
+ # 回调函数
|
|
|
+ print('response', response)
|
|
|
+ # 调起支付接口
|