2025年3月22日 星期六 甲辰(龙)年 月廿一 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

django --微信h5,JSAPI支付,退款;支付宝支付

时间:08-16来源:作者:点击数:25

django --微信h5,JSAPI支付,退款;支付宝支付

H5支付

  • from xml.etree import ElementTree as ET
  • def md5(string):
  • '''md5加密'''
  • import hashlib
  • m = hashlib.md5()
  • m.update(string.encode('utf-8'))
  • return m.hexdigest()
  • class WX_Payment(APIView):
  • '''微信h5支付'''
  • def get(self, request, *args, **kwargs):
  • # 按照微信的规则生成请求参数
  • info = {
  • 'appid': wx_appid, # 商户id
  • 'mch_id': wx_merchant_number, #商户号
  • 'device_info': "WEB",
  • 'nonce_str': ''.join([chr(random.randint(65, 90)) for _ in range(12)]),
  • 'sign_type': 'MD5',
  • 'body': '金币充值',
  • 'out_trade_no': str(time.strftime('%Y%m%d%H%M%S') + ''.join(map(str, random.sample(range(0, 9), 6)))),
  • # 订单号
  • 'total_fee': 0.01, # 总金额
  • 'spbill_create_ip': str(self.request.META.get('REMOTE_ADDR')), # 用户ip
  • 'notify_url': 'http://www.baidu.com', # 异步通知地址
  • 'trade_type': 'MWEB',
  • 'scene_info': {"h5_info": {"type": "Wap", "wap_url": "https://www.baisanxinxi.cn", "wap_name": "金币充值"}}
  • }
  • # 签名
  • temp = '&'.join(
  • ['{0}={1}'.format(k, info[k]) for k in sorted(info)] + ['{0}={1}'.format('key', wx_pay_key, ), ])
  • sign = md5(temp).upper()
  • info['sign'] = sign
  • # https://api.mch.weixin.qq.com/pay/unifiedorder 发请求
  • xml_string = "<xml>{0}</xml>".format(''.join(['<{0}>{1}</{0}>'.format(k, v) for k, v in info.items()]))
  • prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
  • print(prepay.content.decode('utf-8'))
  • # 从xml中提取 preay_id
  • root = ET.XML(prepay.content.decode('utf-8'))
  • prepay_dict = {child.tag: child.text for child in root} # 把xml转为字典
  • return Response(prepay_dict)

小程序支付

  • class WXPayment(APIView):
  • """
  • 支付
  • :param {参数名: total_fee, 类型: int, 是否必选: 必选, 说明: 支付金额}
  • :param {参数名: user_id, 类型: int, 是否必选: 必选, 说明: 用户id}
  • :param {参数名: obtain_hams, 类型: str, 是否必选: 必选, 说明: 支付后得到的哈姆币}
  • :return 生成用于支付的签名
  • """
  • def get(self, request, *args, **kwargs):
  • # 按照微信的规则生成请求参数
  • total_fee = self.request.GET.get('total_fee') # 支付金额
  • user_id = self.request.GET.get('user_id') # 主键
  • obtain_hams = self.request.GET.get('obtain_hams') # 支付后得到的哈姆币
  • user = User.objects.get(user_id=user_id)
  • out_trade_no = str(time.strftime('%Y%m%d%H%M%S') + ''.join(map(str, random.sample(range(0, 9), 6)))) # 订单号
  • info = {
  • 'appid': appid,
  • 'mch_id': much_id,
  • 'nonce_str': ''.join([chr(random.randint(65, 90)) for _ in range(12)]),
  • 'sign_type': 'MD5',
  • 'body': '哈姆币充值',
  • 'out_trade_no': out_trade_no,
  • # 订单号
  • 'total_fee': int(total_fee) * 100, # 总金额
  • 'openid': str(user.open_id),
  • 'spbill_create_ip': str(self.request.META.get('REMOTE_ADDR')), # 用户ip
  • 'notify_url': 'https://www.hamudongmanshe.com/front_end/api/wxpayment/', # 异步通知地址
  • 'trade_type': 'JSAPI',
  • }
  • print(info)
  • # 签名
  • temp = '&'.join(
  • ['{0}={1}'.format(k, info[k]) for k in sorted(info)] + ['{0}={1}'.format('key', pay_key, ), ])
  • sign = WXPayment.md5(temp).upper()
  • info['sign'] = sign
  • print(temp)
  • # https://api.mch.weixin.qq.com/pay/unifiedorder 发请求
  • xml_string = "<xml>{0}</xml>".format(''.join(['<{0}>{1}</{0}>'.format(k, v) for k, v in info.items()]))
  • prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
  • print(prepay.content.decode('utf-8'))
  • # 从xml中提取 preay_id
  • root = ET.XML(prepay.content.decode('utf-8'))
  • prepay_dict = {child.tag: child.text for child in root} # 把xml转为字典
  • prepay_id = prepay_dict.get('prepay_id')
  • print(prepay_dict)
  • # -------再次签名-------
  • info_dict = {
  • 'appId': appid,
  • 'timeStamp': str(int(time.time())),
  • 'nonceStr': ''.join([chr(random.randint(65, 90)) for _ in range(12)]),
  • 'package': 'prepay_id={0}'.format(prepay_id),
  • 'signType': 'MD5',
  • }
  • temp = '&'.join(
  • ['{0}={1}'.format(k, info_dict[k]) for k in sorted(info_dict)] + ['{0}={1}'.format('key', pay_key, ), ])
  • sign2 = WXPayment.md5(temp).upper()
  • info_dict['sign'] = sign2
  • info_dict['out_trade_no'] = out_trade_no
  • WXPayment.establish(user_id, total_fee, obtain_hams, out_trade_no)
  • return Response(info_dict)

微信支付完成的异步通知

  • class NotifyView(APIView):
  • '''微信支付完成的通知'''
  • def post(self, request, *args, **kwargs):
  • root = ET.XML(request.body.decode('utf-8'))
  • result = {child.tag: child.text for child in root}
  • # 校验签名是否正确
  • sign = result.pop('sign')
  • # 商户自己的支付key
  • temp = '&'.join(
  • ['{0}={1}'.format(k, result[k]) for k in sorted(result)] + ['{0}={1}'.format('key', wx_pay_key, ), ])
  • local_sign = md5(temp).upper()
  • if local_sign == sign:
  • out_trade_no = result.get('out_trade_no') # 自己生成的订单号
  • resp = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'
  • return Response(resp)
  • else:
  • return Response('签名失败')

支付宝支付

PC 或 h5 支付

https://github.com/fzlee/alipay/blob/master/docs/apis.zh-hans.md

  • '''
  • from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
  • from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
  • from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayModel, AlipayTradeWapPayRequest
  • from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
  • from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
  • '''
  • class Payment(APIView):
  • # 支付宝支付(h5, 移动端)
  • def get(self, request, *args, **kwargs):
  • alipay_client_config = AlipayClientConfig()
  • alipay_client_config.server_url = server_url
  • alipay_client_config.app_id = ALIPAY_APPID
  • alipay_client_config.app_private_key = app_private_key
  • alipay_client_config.alipay_public_key = alipay_public_key
  • client = DefaultAlipayClient(alipay_client_config)
  • out_trade_no = str(time.strftime('%Y%m%d%H%M%S') + ''.join(map(str, random.sample(range(0, 9), 6)))) # 订单号
  • PC_OR_MOVE = self.request.GET.get('PC_OR_MOVE')
  • recharge_id = self.request.GET.get('recharge_id')
  • user_id = self.request.GET.get('user_id')
  • recharge = Recharge.objects.filter(recharge_id=recharge_id).first()
  • total_amount = str(round(float(recharge.recharge_money_numbers), 2))
  • notify_url = 'http://www.baisanxinxi.cn/front_end/api/alipay_notify/'
  • return_url = 'https://www.baisanxinxi.cn/static/index.html#/recharge'
  • if PC_OR_MOVE == 'MOVE':
  • model = AlipayTradeWapPayModel() # h5移动端
  • model.out_trade_no = out_trade_no
  • model.total_amount = total_amount
  • model.subject = "金币充值"
  • model.product_code = 'QUICK_WAP_WAY'
  • request = AlipayTradeWapPayRequest(biz_model=model)
  • request.return_url = return_url
  • request.notify_url = notify_url
  • alipay_url = client.page_execute(request, "GET")
  • else:
  • model = AlipayTradePagePayModel() # PC端
  • model.out_trade_no = out_trade_no
  • model.total_amount = total_amount
  • model.subject = "金币充值"
  • model.product_code = 'FAST_INSTANT_TRADE_PAY'
  • request = AlipayTradePagePayRequest(biz_model=model)
  • request.return_url = return_url
  • request.notify_url = notify_url
  • alipay_url = client.page_execute(request, "GET")
  • return Response({'alipay_url': alipay_url})

支付宝异步通知

  • @api_view(['POST', ])
  • def alipay_notify(request: HttpRequest) -> HttpResponse:
  • '''金币充值支付宝支付异步通知'''
  • params = request.POST.dict()
  • out_trade_no = params.get('out_trade_no')
  • rec = RechargeRecord.objects.filter(recharge_record_out_trade_no=str(out_trade_no)).last() # 数据库的订单号
  • if rec.recharge_record_out_trade_no == out_trade_no: # 示例验证订单号,实际则验证签名
  • # 逻辑
  • return Response('success')
  • else:
  • return Response('fail')

微信退款

官文

  • https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4

demo

  • import random
  • import time
  • from abc import abstractmethod, ABCMeta
  • from decimal import Decimal
  • from decimal import getcontext
  • import requests
  • from examination.settings import appid, much_id, pay_key, notify_url, BASE_DIR
  • from xml.etree import ElementTree as ET
  • def Singleton(cls):
  • '''单例装饰器'''
  • _instance = {}
  • def _singleton(*args, **kargs):
  • if cls not in _instance:
  • _instance[cls] = cls(*args, **kargs)
  • return _instance[cls]
  • return _singleton
  • class WxPay(metaclass=ABCMeta):
  • '''微信支付(元类)'''
  • def __init__(self, total_fee, out_trade_no, body, open_id, notify_url):
  • self.total_fee = int(Decimal(str(total_fee)) * Decimal(str(100)))
  • self.out_trade_no = out_trade_no
  • self.body = body
  • self.open_id = open_id
  • self.notify_url = notify_url
  • @property
  • def pay_keys(self):
  • return {'appid': appid, 'much_id': much_id, 'pay_key': pay_key}
  • @property
  • def structure(self):
  • return {
  • 'appid': self.pay_keys.get('appid'),
  • 'mch_id': self.pay_keys.get('much_id'),
  • 'nonce_str': ''.join([chr(random.randint(65, 90)) for _ in range(12)]),
  • 'sign_type': 'MD5',
  • 'body': self.body,
  • 'out_trade_no': self.out_trade_no, # 订单号
  • 'total_fee': self.total_fee, # 总金额
  • 'openid': str(self.open_id),
  • 'spbill_create_ip': str('127.0.0.1'), # 用户ip
  • 'notify_url': self.notify_url, # 异步通知地址
  • 'trade_type': 'JSAPI',
  • }
  • @staticmethod
  • def md5(string):
  • '''md5加密'''
  • import hashlib
  • m = hashlib.md5()
  • m.update(string.encode('utf-8'))
  • return m.hexdigest()
  • def fist_sign(self):
  • '''首次加密'''
  • info: dict = self.structure
  • print('预首次签名:' + str(info))
  • temp = '&'.join(
  • ['{0}={1}'.format(k, info[k]) for k in sorted(info)] + [
  • '{0}={1}'.format('key', self.pay_keys.get('pay_key'), ), ])
  • info['sign'] = WxPay.md5(temp).upper()
  • return info
  • def request_pay(self):
  • '''
  • 请求拿预支付标识
  • :return prepay_dict
  • '''
  • info: dict = self.fist_sign()
  • xml_string = "<xml>{0}</xml>". \
  • format(''.join(['<{0}>{1}</{0}>'.format(k, v) for k, v in info.items()]))
  • prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
  • print(prepay.content.decode('utf-8'))
  • root = ET.XML(prepay.content.decode('utf-8'))
  • prepay_dict = {child.tag: child.text for child in root} # 把xml转为字典
  • if not prepay_dict.get('prepay_id'):
  • raise Exception
  • return prepay_dict
  • def jsapi_sign(self):
  • '''JSAPI 支付加签'''
  • info_dict = {
  • 'appId': appid,
  • 'timeStamp': str(int(time.time())),
  • 'nonceStr': ''.join([chr(random.randint(65, 90)) for _ in range(12)]),
  • 'package': 'prepay_id={0}'.format(self.request_pay().get('prepay_id')),
  • 'signType': 'MD5',
  • }
  • temp = '&'.join(
  • ['{0}={1}'.format(k, info_dict[k]) for k in sorted(info_dict)] +
  • ['{0}={1}'.format('key', self.pay_keys.get('pay_key'), ), ])
  • info_dict['sign'] = self.md5(temp).upper()
  • info_dict['out_trade_no'] = self.out_trade_no
  • print('JSAPI返回参数')
  • print(info_dict)
  • return info_dict
  • def pay_notify(self, xml_str: str):
  • '''
  • 解析支付回调
  • :param xml_str: 微信传来的xml参数
  • :return 校验结果;微信规定好的返回值
  • '''
  • root = ET.XML(xml_str)
  • result = {child.tag: child.text for child in root}
  • sign = result.pop('sign') # 校验签名
  • temp = '&'.join(
  • ['{0}={1}'.format(k, result[k]) for k in sorted(result)] +
  • ['{0}={1}'.format('key', self.pay_keys.get('pay_key'), ), ])
  • local_sign = WxPay.md5(temp).upper()
  • if local_sign == sign:
  • resp = '<xml>' \
  • '<return_code><![CDATA[SUCCESS]]></return_code>' \
  • '<return_msg><![CDATA[OK]]></return_msg>' \
  • '</xml>'
  • return True, resp
  • return False, '<xml>fail</xml>'
  • @Singleton # 单例装饰器
  • class WxPayBack(object):
  • '''微信退款'''
  • @classmethod
  • def startback(cls, out_trade_no, out_refund_no, total_fee, refund_fee) -> bool:
  • '''
  • :param out_trade_no: 创建订单时自动生成的订单号
  • :param out_refund_no: 商户退款单号
  • :param total_fee: 订单金额
  • :param refund_fee: 退款金额
  • :return:
  • '''
  • info = {
  • 'appid': appid,
  • 'mch_id': much_id,
  • 'nonce_str': ''.join([chr(random.randint(65, 90)) for _ in range(12)]),
  • 'out_trade_no': out_trade_no,
  • 'out_refund_no': out_refund_no,
  • 'total_fee': int(float(total_fee) * 100),
  • 'refund_fee': int(float(refund_fee) * 100),
  • }
  • string = "&".join([f"{k}={info[k]}" for k in sorted(info)] + [f"{'key'}={pay_key}"])
  • info['sign'] = WxPay.md5(string).upper()
  • xml = "<xml>{}</xml>".format("".join([f"<{k}>{v}</{k}>" for k, v in info.items()]))
  • cert = f"{BASE_DIR}/apiclient_cert.pem"
  • key = f"{BASE_DIR}/apiclient_key.pem"
  • res = requests.post(
  • url='https://api.mch.weixin.qq.com/secapi/pay/refund',
  • data=xml.encode('utf-8'),
  • headers={
  • 'Accept-Language': 'zh-CN,zh;q=0.9'
  • },
  • cert=(cert, key),
  • verify=True
  • )
  • result = {child.tag: child.text for child in ET.XML(res.content.decode('utf-8'))}
  • if result.get('return_code') == 'SUCCESS':
  • print('参数合法,成功进入请求成功环节!')
  • if result.get('result_code') == 'SUCCESS':
  • print('退款请求申请成功')
  • return True
  • else:
  • print(f'失败信息描述:{result.get("err_code_des")}')
  • print(f'微信返回值dict:{result}')
  • else:
  • print(f'提交业务失败,失败原因:{result}')
  • return False
  • if __name__ == '__main__':
  • '''
  • data = {
  • 'total_fee': 0.01,
  • 'out_trade_no': '112121',
  • 'body': '考研',
  • 'open_id': 'oOKN-5L0G1VHFLyuj8cD315r3Bd0',
  • 'notify_url': notify_url
  • }
  • a = WxPay(**data)
  • a.jsapi_sign()
  • '''
  • print(WxPayBack().startback(out_trade_no='112121', out_refund_no='123456', total_fee=0.01, refund_fee=0.01))
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐