WechatPayObject.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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, attach):
  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. 'attach' : attach
  34. }
  35. return self.params
  36. def getNonceStr(self, length=32):
  37. """生成随机字符串"""
  38. import random
  39. chars = "abcdefghijklmnopqrstuvwxyz0123456789"
  40. strs = []
  41. for x in range(length):
  42. strs.append(chars[random.randrange(0, len(chars))])
  43. return "".join(strs)
  44. def key_value_url(self, value, urlencode):
  45. """
  46. 将键值对转为 key1=value1&key2=value2
  47. 对参数按照key=value的格式,并按照参数名ASCII字典序排序
  48. """
  49. slist = sorted(value)
  50. buff = []
  51. for k in slist:
  52. v = quote(value[k]) if urlencode else value[k]
  53. buff.append("{0}={1}".format(k, v))
  54. return "&".join(buff)
  55. def get_sign(self, params):
  56. """
  57. 生成sign
  58. 拼接API密钥
  59. """
  60. stringA = self.key_value_url(params, False)
  61. stringSignTemp = stringA + '&key=' + self.APIKEY # APIKEY, API密钥,需要在商户后台设置
  62. sign = (hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest()).upper()
  63. params['sign'] = sign
  64. return params
  65. def get_req_xml(self):
  66. """
  67. 拼接XML
  68. """
  69. self.get_sign(self.params)
  70. xml = "<xml>"
  71. for k, v in self.params.items():
  72. # v = v.encode('utf8')
  73. # k = k.encode('utf8')
  74. xml += '<' + k + '>' + v + '</' + k + '>'
  75. xml += "</xml>"
  76. return xml.encode("utf-8")
  77. def get_prepay_id(self):
  78. """
  79. 请求获取prepay_id
  80. """
  81. xml = self.get_req_xml()
  82. respone = requests.post(self.url, xml, headers={'Content-Type': 'application/xml'})
  83. msg = respone.text.encode('ISO-8859-1').decode('utf-8')
  84. xmlresp = xmltodict.parse(msg)
  85. if xmlresp['xml']['return_code'] == 'SUCCESS':
  86. if xmlresp['xml']['result_code'] == 'SUCCESS':
  87. prepay_id = xmlresp['xml']['prepay_id']
  88. self.params['prepay_id'] = prepay_id
  89. self.params['packageId'] = "Sign=WXPay"
  90. self.params['timestamp'] = str(int(time.time()))
  91. return self.params
  92. else:
  93. return 'failure'
  94. else:
  95. return 'failure'
  96. def re_finall(self,orderid = None):
  97. """得到prepay_id后再次签名,返回给终端参数.
  98. """
  99. self.get_prepay_id()
  100. if self.error:
  101. return False
  102. sign_again_params = {
  103. 'appid': self.params['appid'],
  104. 'noncestr': self.params['nonce_str'],
  105. 'package': self.params['packageId'],
  106. 'partnerid': self.params['mch_id'],
  107. 'timestamp': self.params['timestamp'],
  108. 'prepayid': self.params['prepay_id']
  109. }
  110. self.get_sign(sign_again_params)
  111. sign_again_params['sign'] = sign_again_params['sign']
  112. sign_again_params['packageId'] = sign_again_params['package']
  113. sign_again_params['out_trade_no'] = orderid
  114. return sign_again_params # 返回给app
  115. def get_notifypay(self, data):
  116. success = self.get_sign(data)
  117. # print('success', success)
  118. if success:
  119. success.pop("sign", None)
  120. success.pop("sign_type", None)
  121. # return success
  122. return True
  123. else:
  124. return False
  125. def weixinpay_call_back(self, data):
  126. args = str(data, 'utf-8')
  127. if args is None:
  128. return None
  129. print(args)
  130. # 验证平台签名
  131. resp_dict = self.handle_wx_response_xml(args)
  132. if resp_dict is None:
  133. print('签名验证失败!!!')
  134. return None
  135. return resp_dict
  136. def handle_wx_response_xml(self, params):
  137. """
  138. 处理微信支付返回的xml格式数据
  139. """
  140. try:
  141. resp_dict = xmltodict.parse(params)['xml']
  142. if not resp_dict or len(resp_dict) < 1:
  143. print('resp_dict is zero+++++++++')
  144. return None
  145. return_code = resp_dict.get('return_code')
  146. if return_code == 'SUCCESS': # 仅仅判断通信标识成功,非交易标识成功,交易需判断result_code
  147. return resp_dict
  148. except Exception as e:
  149. print(e)
  150. return None
  151. return None
  152. @staticmethod
  153. def xml_to_dict(params):
  154. """
  155. 拼接XML
  156. """
  157. if not isinstance(params, dict):
  158. return None
  159. xml = "<xml>"
  160. for k, v in params.items():
  161. # v = v.encode('utf8')
  162. # k = k.encode('utf8')
  163. xml += '<' + k + '>' + v + '</' + k + '>'
  164. xml += "</xml>"
  165. return xml