# coding=utf-8 import uuid import requests import json import xmltodict import time from hashlib import md5 from django.conf import settings from utils.exceptions import CustomError # 微信支付APP_ID WEIXIN_APP_ID = settings.WECHAT['appid'] # 微信支付MCH_ID 【登录账号】 WEIXIN_MCH_ID = settings.WECHAT['mchid'] # 微信支付sign_type WEIXIN_SIGN_TYPE = 'MD5' # 服务器IP地址 WEIXIN_SPBILL_CREATE_IP = settings.WECHAT['spbill_create_ip'] # 微信支付用途 WEIXIN_BODY = settings.PAY_SUBJECT # 微信KEY值 【API密钥】 WEIXIN_KEY = settings.WECHAT['merchant_key'] # 微信统一下单URL WEIXIN_UNIFIED_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 微信查询订单URL WEIXIN_QUERY_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/orderquery' # 微信支付回调API WEIXIN_CALLBACK_API = settings.WECHAT['notify_url'] # pc端支付 class WeChatResponse(): def __init__(self): self.params = { 'appid': WEIXIN_APP_ID, 'mch_id': WEIXIN_MCH_ID, 'nonce_str': '', 'sign_type': WEIXIN_SIGN_TYPE, 'sign': '', 'out_trade_no': '', } self.prepay_id = None def getAppString(self): data = { 'appid': self.params['appid'], 'partnerid': self.params['mch_id'], 'prepayid': self.prepay_id, 'package': "prepay_id={}".format(self.prepay_id), 'noncestr': generate_nonce_str(), 'timestamp': str(int(time.time())) } data['sign'] = generate_sign(data) return data # 查询订单 def orderquery(self, out_trade_no): self.params['out_trade_no'] = out_trade_no self.params['nonce_str'] = generate_nonce_str() self.params['sign'] = generate_sign(self.params) data = xmltodict.unparse({'xml': self.params}, pretty=True, full_document=False).encode('utf-8') headers = {'Content-Type': 'application/xml'} res = requests.post(WEIXIN_QUERY_ORDER_URL, data=data, headers=headers) if res.status_code != 200: raise CustomError(u'微信请求失败!') result = json.loads(json.dumps(xmltodict.parse(res.content))) if result['xml']['return_code'] != 'SUCCESS': raise CustomError(u'微信通信失败![%s]' % result['xml']['return_msg']) if result['xml']['trade_state'] != 'SUCCESS': raise CustomError(u'微信交易状态![%s]' % (result['xml']['trade_state_desc'])) return result['xml']['total_fee'] def unifiedOrder(self, out_trade_no, total_fee): self.params['out_trade_no'] = out_trade_no self.params['total_fee'] = int(round(total_fee * 100, 0)) self.params['nonce_str'] = generate_nonce_str() self.params['body'] = WEIXIN_BODY self.params['spbill_create_ip'] = WEIXIN_SPBILL_CREATE_IP self.params['notify_url'] = WEIXIN_CALLBACK_API self.params['trade_type'] = 'NATIVE' self.params['sign'] = generate_sign(self.params) data = xmltodict.unparse({'xml': self.params}, pretty=True, full_document=False).encode('utf-8') headers = {'Content-Type': 'application/xml'} res = requests.post(WEIXIN_UNIFIED_ORDER_URL, data=data, headers=headers) if res.status_code != 200: raise CustomError(u'微信请求失败!') result = json.loads(json.dumps(xmltodict.parse(res.content))) if result['xml']['return_code'] != 'SUCCESS': raise CustomError(u'微信通信失败![%s]' % result['xml']['return_msg']) if result['xml']['result_code'] != 'SUCCESS': raise CustomError(u'微信交易失败![%s:%s]' % (result['xml']['err_code'], result['xml']['err_code_des'])) self.prepay_id = result['xml']['prepay_id'] return result['xml']['code_url'] #小程序支付 class WechatAppletPay(): def __init__(self): self.params = { "appid": settings.WEAPP['appid'], "body": WEIXIN_BODY, "mch_id": WEIXIN_MCH_ID, "nonce_str": '', "notify_url": WEIXIN_CALLBACK_API, "openid": '', # 获取小程序openid "out_trade_no": '', "spbill_create_ip": WEIXIN_SPBILL_CREATE_IP, "total_fee": '', "trade_type": 'JSAPI', "sign":'' } self.prepay_id = None def weChatUnifiedOrder(self, openid, out_trade_no, total_fee): self.params['out_trade_no'] = out_trade_no self.params['total_fee'] = int(round(float(total_fee) * 100, 0)) self.params['nonce_str'] = generate_nonce_str() self.params['openid'] = openid self.params['sign'] = generate_sign(self.params) # 拿到封装好的xml数据 xml_data = xmltodict.unparse({'xml': self.params}, pretty=True, full_document=False).encode('utf-8') # 请求微信统一下单URL headers = {'Content-Type': 'application/xml'} res = requests.post(WEIXIN_UNIFIED_ORDER_URL, data=xml_data, headers=headers) if res.status_code != 200: raise CustomError(u'微信请求失败!') # 回复数据为xml,将其转为字典 content = json.loads(json.dumps(xmltodict.parse(res.content))) if content['xml']['return_code'] != 'SUCCESS': raise CustomError(u'微信通信失败![%s]' % content['xml']['return_msg']) if content['xml']['result_code'] != 'SUCCESS': raise CustomError(u'微信交易失败![%s:%s]' % (content['xml']['err_code'],content['xml']['err_code_des'])) self.prepay_id = content['xml']['prepay_id'] return self.getPaymentInfo() def getPaymentInfo(self): data = { 'appId': self.params['appid'], 'package': "prepay_id={}".format(self.prepay_id), 'nonceStr': generate_nonce_str(), 'timeStamp': str(int(time.time())), 'signType':'MD5' } data['paySign'] = generate_sign(data) return data class WechatPayNotify(): def __init__(self, params): self.params = params def handle(self): resp_dict = json.loads(json.dumps(xmltodict.parse(self.params)))['xml'] return_code = resp_dict['return_code'] if return_code != 'SUCCESS': return None if not validate_sign(resp_dict): return None return resp_dict @staticmethod def response_ok(): return_info = { 'return_code': 'SUCCESS', 'return_msg': 'OK' } return generate_response_data(return_info) @staticmethod def response_fail(): return_info = { 'return_code': 'FAIL', 'return_msg': 'FAIL' } return generate_response_data(return_info) def generate_nonce_str(): """ 生成随机字符串 """ return str(uuid.uuid4()).replace('-', '') def generate_sign(params): """ 生成md5签名的参数 """ if 'sign' in params: params.pop('sign') src = '&'.join(['%s=%s' % (k, v) for k, v in sorted(params.items())]) + '&key=%s' % WEIXIN_KEY return md5(src.encode('utf-8')).hexdigest().upper() def validate_sign(resp_dict): """ 验证微信返回的签名 """ if 'sign' not in resp_dict: return False wx_sign = resp_dict['sign'] sign = generate_sign(resp_dict) if sign == wx_sign: return True return False def generate_response_data(resp_dict): """ 字典转xml """ return xmltodict.unparse({'xml': resp_dict}, pretty=True, full_document=False).encode('utf-8')