import datetime import hashlib import time from urllib.parse import quote import requests import xmltodict from Ansjer.config import BASE_DIR 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.refund_url = 'https://api.mch.weixin.qq.com/secapi/pay/refund' # 退款url self.downloadbill_url = 'https://api.mch.weixin.qq.com/pay/downloadbill' # 账单url self.error = None self.params = None def get_parameter(self, order_id, body, total_fee, spbill_create_ip, notify_url, attach): 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', 'attach' : attach } 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 = "" for k, v in self.params.items(): # v = v.encode('utf8') # k = k.encode('utf8') xml += '<' + k + '>' + v + '' 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['packageId'] = "Sign=WXPay" self.params['timestamp'] = str(int(time.time())) return self.params else: return 'failure' else: return 'failure' def re_finall(self,orderid = None): """得到prepay_id后再次签名,返回给终端参数. """ self.get_prepay_id() if self.error: return False sign_again_params = { 'appid': self.params['appid'], 'noncestr': self.params['nonce_str'], 'package': self.params['packageId'], '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'] sign_again_params['packageId'] = sign_again_params['package'] sign_again_params['out_trade_no'] = orderid return sign_again_params # 返回给app def get_notifypay(self, data): success = self.get_sign(data) # print('success', success) if success: success.pop("sign", None) success.pop("sign_type", None) # return success return True else: return False def weixinpay_call_back(self, data): args = str(data, 'utf-8') if args is None: return None print(args) # 验证平台签名 resp_dict = self.handle_wx_response_xml(args) if resp_dict is None: print('签名验证失败!!!') return None return resp_dict def handle_wx_response_xml(self, params): """ 处理微信支付返回的xml格式数据 """ try: resp_dict = xmltodict.parse(params)['xml'] if not resp_dict or len(resp_dict) < 1: print('resp_dict is zero+++++++++') return None return_code = resp_dict.get('return_code') if return_code == 'SUCCESS': # 仅仅判断通信标识成功,非交易标识成功,交易需判断result_code return resp_dict except Exception as e: print(e) return None return None @staticmethod def xml_to_dict(params): """ 拼接XML """ if not isinstance(params, dict): return None xml = "" for k, v in params.items(): # v = v.encode('utf8') # k = k.encode('utf8') xml += '<' + k + '>' + v + '' xml += "" return xml def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee): params = { 'appid': self.APPID, 'mch_id': self.MCHID, 'nonce_str': self.getNonceStr(), 'out_trade_no': out_trade_no, # 商户订单号 'out_refund_no': out_refund_no, # 商户退款单号 'total_fee': total_fee, # 订单金额 'refund_fee': refund_fee # 退款金额 } # doc: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3 string_sign = "&".join(['{}={}'.format(k, params[k]) for k in sorted(params)] + ['key={}'.format(self.APIKEY)]) params['sign'] = hashlib.md5(string_sign.encode('utf8')).hexdigest().upper() # MD5签名 xml = "{}".format("".join(['<{k}>{v}'.format(k=k, v=v) for k, v in params.items()])) # 数据拼接成xml格式 cert = '{}/Ansjer/file/wechatpay/apiclient_cert.pem'.format(BASE_DIR) key = '{}/Ansjer/file/wechatpay/apiclient_key.pem'.format(BASE_DIR) r = requests.post(url=self.refund_url, headers={'Content-Type': 'text/xml'}, data=xml.encode('utf-8'), cert=(cert, key), verify=True) response = xmltodict.parse(r.text) if response['xml']['result_code'] == 'SUCCESS': return True else: return False def download_bill(self, date): params = { 'appid': self.APPID, # 公众账号ID 'mch_id': self.MCHID, # 商户号 'nonce_str': self.getNonceStr(), # 随机字符串 'bill_date': date, # 对账单日期 'bill_type': 'ALL', # 账单类型 } string_sign = "&".join(['{}={}'.format(k, params[k]) for k in sorted(params)] + ['key={}'.format(self.APIKEY)]) params['sign'] = hashlib.md5(string_sign.encode('utf8')).hexdigest().upper() # MD5签名 xml = "{}".format("".join(['<{k}>{v}'.format(k=k, v=v) for k, v in params.items()])) # 数据拼接成xml格式 r = requests.post(url=self.downloadbill_url, headers={'Content-Type': 'text/xml'}, data=xml.encode('utf-8')) order_text_list = r.text.split('\r\n') key_list = order_text_list[0].split(',') order_list = [] for item in order_text_list[1:-3]: order_list.append(dict(zip(key_list, item.split(',')))) return order_list