WechatPayObject.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import hashlib
  2. import time
  3. from urllib.parse import quote
  4. import requests
  5. import xmltodict
  6. class WechatPayObject:
  7. """配置账号信息"""
  8. # 微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
  9. def __init__(self):
  10. # 开发者调用支付统一下单API生成预交易单
  11. self.APPID = 'wx2a9f5ef9baf2760f'
  12. # 商户id
  13. self.MCHID = '1508209741'
  14. # 异步通知url,商户根据实际开发过程设定
  15. self.NOTIFY_URL = 'test'
  16. self.TRADE_TYPE = 'APP'
  17. self.APIKEY = 'ZHansjeransjeransjer680301000000'
  18. self.url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 微信请求url
  19. self.error = None
  20. self.params = None
  21. def get_parameter(self, order_id, body, total_fee, spbill_create_ip, notify_url):
  22. self.params = {
  23. 'appid': self.APPID, # appid
  24. 'mch_id': self.MCHID, # 商户号
  25. 'nonce_str': self.getNonceStr(),
  26. 'body': body, # 商品描述
  27. 'out_trade_no': str(order_id), # 商户订单号
  28. 'total_fee': str(int(total_fee)),
  29. 'spbill_create_ip': spbill_create_ip, # 127.0.0.1
  30. 'trade_type': self.TRADE_TYPE, # 交易类型
  31. 'notify_url': notify_url, # 微信支付结果异步通知地址
  32. 'receipt': 'Y'
  33. }
  34. return self.params
  35. def getNonceStr(self, length=32):
  36. """生成随机字符串"""
  37. import random
  38. chars = "abcdefghijklmnopqrstuvwxyz0123456789"
  39. strs = []
  40. for x in range(length):
  41. strs.append(chars[random.randrange(0, len(chars))])
  42. return "".join(strs)
  43. def key_value_url(self, value, urlencode):
  44. """
  45. 将键值对转为 key1=value1&key2=value2
  46. 对参数按照key=value的格式,并按照参数名ASCII字典序排序
  47. """
  48. slist = sorted(value)
  49. buff = []
  50. for k in slist:
  51. v = quote(value[k]) if urlencode else value[k]
  52. buff.append("{0}={1}".format(k, v))
  53. return "&".join(buff)
  54. def get_sign(self, params):
  55. """
  56. 生成sign
  57. 拼接API密钥
  58. """
  59. stringA = self.key_value_url(params, False)
  60. stringSignTemp = stringA + '&key=' + self.APIKEY # APIKEY, API密钥,需要在商户后台设置
  61. sign = (hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest()).upper()
  62. params['sign'] = sign
  63. return params
  64. def get_req_xml(self):
  65. """
  66. 拼接XML
  67. """
  68. self.get_sign(self.params)
  69. xml = "<xml>"
  70. for k, v in self.params.items():
  71. # v = v.encode('utf8')
  72. # k = k.encode('utf8')
  73. xml += '<' + k + '>' + v + '</' + k + '>'
  74. xml += "</xml>"
  75. return xml.encode("utf-8")
  76. def get_prepay_id(self):
  77. """
  78. 请求获取prepay_id
  79. """
  80. xml = self.get_req_xml()
  81. respone = requests.post(self.url, xml, headers={'Content-Type': 'application/xml'})
  82. msg = respone.text.encode('ISO-8859-1').decode('utf-8')
  83. xmlresp = xmltodict.parse(msg)
  84. if xmlresp['xml']['return_code'] == 'SUCCESS':
  85. if xmlresp['xml']['result_code'] == 'SUCCESS':
  86. prepay_id = xmlresp['xml']['prepay_id']
  87. self.params['prepay_id'] = prepay_id
  88. self.params['package'] = "Sign=WXPay"
  89. self.params['timestamp'] = str(int(time.time()))
  90. return self.params
  91. else:
  92. return 'failure'
  93. else:
  94. return 'failure'
  95. def re_finall(self):
  96. """得到prepay_id后再次签名,返回给终端参数
  97. """
  98. self.get_prepay_id()
  99. if self.error:
  100. return
  101. sign_again_params = {
  102. 'appid': self.params['appid'],
  103. 'noncestr': self.params['nonce_str'],
  104. 'packageId': self.params['package'],
  105. 'partnerid': self.params['mch_id'],
  106. 'timestamp': self.params['timestamp'],
  107. 'prepayid': self.params['prepay_id']
  108. }
  109. self.get_sign(sign_again_params)
  110. sign_again_params['sign'] = sign_again_params['sign']
  111. return sign_again_params # 返回给app
  112. def get_notifypay(self, data):
  113. dictdata = dict(data)
  114. _dictdata = dict(dictdata['xml'])
  115. success = self.get_sign(_dictdata)
  116. # print('success', success)
  117. if success:
  118. success.pop("sign", None)
  119. success.pop("sign_type", None)
  120. return success
  121. else:
  122. return None
  123. @staticmethod
  124. def xml_to_dict(params):
  125. """
  126. 拼接XML
  127. """
  128. if not isinstance(params, dict):
  129. return None
  130. xml = "<xml>"
  131. for k, v in params.items():
  132. # v = v.encode('utf8')
  133. # k = k.encode('utf8')
  134. xml += '<' + k + '>' + v + '</' + k + '>'
  135. xml += "</xml>"
  136. return xml
  137. if __name__ == '__main__':
  138. # 调用统一支付接口
  139. # 订单id
  140. order_id = '12345678903424'
  141. # 描述信息
  142. body = 'soober支付'
  143. # 价格
  144. total_fee = '50'
  145. # 调用微信支付API的机器IP
  146. spbill_create_ip = '127.0.0.1'
  147. pay = WechatPayObject()
  148. # 获取参数
  149. parameter_dict = pay.get_parameter(order_id, body, total_fee, spbill_create_ip)
  150. print('parameter_dict', parameter_dict)
  151. # parameter_dict 参数中获取MWEB_URL 调转页面在路径后面添加redirect_url
  152. # 统一调用接口
  153. response = pay.re_finall()
  154. # 回调函数
  155. print('response', response)
  156. # 调起支付接口