您当前的位置:首页 > 计算机 > 编程开发 > Python

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

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

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))
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐