zoukankan      html  css  js  c++  java
  • Django对接支付宝实现支付宝充值金币功能

    很多网站里都有金币、积分之类的虚拟货币,获取这些往往需要充值。那么问题来了,如何在Django中对接支付宝实现支付宝充值金币的功能呢?网上很多资料都是电商的,那些都会带有订单系统之类比较复杂,而充值金币功能不需要实现那么多功能。

    效果图如下:

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    现在就来实现Django对接支付宝支付功能吧!

    登录支付宝开放平台

    点击进入蚂蚁金服开放平台https://open.alipay.com/platform/home.htm
    进入支付宝沙箱环境https://openhome.alipay.com/platform/appDaily.htm?tab=info

    在这里插入图片描述
    如图,这里是你沙箱环境的配置,左侧沙箱工具有沙箱支付宝安卓版下载,沙箱账号是你的测试账号。

    下载支付宝开放平台开发助手

    点击打开下载链接

    在这里插入图片描述
    打开工具,生成密钥,然后妥善保管好!!
    在这里插入图片描述
    在这里插入图片描述

    保存密钥

    在这里插入图片描述
    把刚刚那个应用公钥2048重命名为:pub_2048.txt,把应用私钥2048重命名为:private_2048.txt,把这两个文件放在项目目录下。
    注意:密钥的开始和结束一定要加上如下的字符串!!!

    -----BEGIN PRIVATE KEY-----
    这里粘贴里面的密钥
    -----END PRIVATE KEY-----
    

    在这里插入图片描述

    复制支付宝的公钥并保存

    在这里插入图片描述
    把这段支付宝公钥复制,重命名为alipay_key_2048.txt,保存到项目目录中,注意加上如下字符串

    -----BEGIN PRIVATE KEY-----
    这里粘贴里面的密钥
    -----END PRIVATE KEY-----
    

    设置公钥

    把刚刚的pub_2048.txt里面的字符串复制到如下:

    在这里插入图片描述

    在这里插入图片描述

    调试支付宝支付接口

    from datetime import datetime
    
    
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from urllib.parse import quote_plus
    from base64 import decodebytes, encodebytes
    
    import json
    
    
    class AliPay(object):
        """
        支付宝支付接口
        """
    
        def __init__(self, appid, app_notify_url, app_private_key_path,
                     alipay_public_key_path, return_url, debug=False):
            self.appid = appid
            self.app_notify_url = app_notify_url
            self.app_private_key_path = app_private_key_path
            self.app_private_key = None
            self.return_url = return_url
            with open(self.app_private_key_path) as fp:
                self.app_private_key = RSA.importKey(fp.read())
    
            self.alipay_public_key_path = alipay_public_key_path
            with open(self.alipay_public_key_path) as fp:
                self.alipay_public_key = RSA.import_key(fp.read())
    
            if debug is True:
                self.__gateway = "https://openapi.alipaydev.com/gateway.do"
            else:
                self.__gateway = "https://openapi.alipay.com/gateway.do"
    
        def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
            biz_content = {
                "subject": subject,
                "out_trade_no": out_trade_no,
                "total_amount": total_amount,
                "product_code": "FAST_INSTANT_TRADE_PAY",
                # "qr_pay_mode":4
            }
    
            biz_content.update(kwargs)
            data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
            return self.sign_data(data)
    
        def build_body(self, method, biz_content, return_url=None):
            data = {
                "app_id": self.appid,
                "method": method,
                "charset": "utf-8",
                "sign_type": "RSA2",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "version": "1.0",
                "biz_content": biz_content
            }
    
            if return_url is not None:
                data["notify_url"] = self.app_notify_url
                data["return_url"] = self.return_url
    
            return data
    
        def sign_data(self, data):
            data.pop("sign", None)
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
            sign = self.sign(unsigned_string.encode("utf-8"))
            # ordered_items = self.ordered_data(data)
            quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
    
            # 获得最终的订单信息字符串
            signed_string = quoted_string + "&sign=" + quote_plus(sign)
            return signed_string
    
        def ordered_data(self, data):
            complex_keys = []
            for key, value in data.items():
                if isinstance(value, dict):
                    complex_keys.append(key)
    
            # 将字典类型的数据dump出来
            for key in complex_keys:
                data[key] = json.dumps(data[key], separators=(',', ':'))
    
            return sorted([(k, v) for k, v in data.items()])
    
        def sign(self, unsigned_string):
            # 开始计算签名
            key = self.app_private_key
            signer = PKCS1_v1_5.new(key)
            signature = signer.sign(SHA256.new(unsigned_string))
            # base64 编码,转换为unicode表示并移除回车
            sign = encodebytes(signature).decode("utf8").replace("
    ", "")
            return sign
    
        def _verify(self, raw_content, signature):
            # 开始计算签名
            key = self.alipay_public_key
            signer = PKCS1_v1_5.new(key)
            digest = SHA256.new()
            digest.update(raw_content.encode("utf8"))
            if signer.verify(digest, decodebytes(signature.encode("utf8"))):
                return True
            return False
    
        def verify(self, data, signature):
            if "sign_type" in data:
                sign_type = data.pop("sign_type")
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
            return self._verify(message, signature)
    
    
    def get_alipay_url(app_id, order_sn, order_mount):
        alipay = AliPay(
            appid=app_id,
            app_notify_url="http://127.0.0.1:8000/alipay/return/",
            app_private_key_path="../trade/keys/private_2048.txt",
            alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            debug=True,  # 默认False,
            return_url="http://127.0.0.1:8000/alipay/return/"
        )
    
        url = alipay.direct_pay(
            subject=order_sn,
            out_trade_no=order_sn,
            total_amount=order_mount,
        )
        re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    
        return re_url
    
    
    if __name__ == "__main__":
        url = get_alipay_url(
            '2016092600597838',
            '201902923423436',
            1.00
        )
        print(url)
    

    如果输出的url能够打开并且使用沙箱账号支付,说明前面的配置成功了,可以进行视图的编写。

    设计数据库订单模型

    from django.db import models
    from django.conf import settings
    from django.utils.encoding import python_2_unicode_compatible
    
    from questioning.utils.models import CreatedUpdatedMixin
    
    @python_2_unicode_compatible
    class OrderInfo(CreatedUpdatedMixin, models.Model):
        '''
        充值订单详情
        '''
        ORDER_STATUS = (
        ('TRADE_SUCCESS', '交易支付成功'),
        ('TRADE_CLOSED', '未付款交易超时关闭'),
        ('WAIT_BUYER_PAY', '交易创建'),
        ('TRADE_FINISHED', '交易结束'),
        ('paying', '待支付'),
    )
        user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='用户')
        order_sn = models.CharField(max_length=30, null=True, blank=True, unique=True, verbose_name='订单号')
        trade_no = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name='支付订单号')
    
        pay_status = models.CharField(choices=ORDER_STATUS, max_length=40, verbose_name='订单状态', default='paying')
        order_mount = models.DecimalField(verbose_name="充值金额", max_digits=10,
                                    decimal_places=2, default=0.00)
    
    
        class Meta:
            verbose_name = "充值订单"
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.order_sn
    

    注意充值的金额要设置为models.DecimalField货币类型
    然后导入到数据库中

    视图函数编写

    class AlipayView(LoginRequiredMixin, AuthorRequiredMixin, View):
        """
        支付宝支付
        get方法实现支付宝return_url,如果没有实现也无所谓,post同样可以更新状态
        post方法实现支付宝notify_url,异步更新
    
        支付宝返回的url如下:
        #http://127.0.0.1:8000/alipay/return/?
        # charset=utf-8&
        # out_trade_no=201902923423436&
        # method=alipay.trade.page.pay.return&
        # total_amount=1.00&
        # sign=CDBMY9NBsp4KICdQoBEVxGWobd0N8y4%2BU09stzUWwlNtLr7ZpELJdM5js20wXv%2FCPp0FGPbRW1YS9DRx0CnKJULZZMqysBUMH2FL39sS0Fgstgy1ydTs7ySXdHziJV0inI%2BDWAsebQqtjk5gQEweUstc%2B%2BnzjdgAulpvWzfJsbknS%2BqUfktSdF2ZOWGhr1CFlfsMFEDS2nzQv4K3E%2BNaeylkzUnRe9M1sjIL%2FYR0wVZ5A3OfHLPf9HzC2B8%2FLu4g7N5Vctkqp2aerDvIkN5SNmDnRGyjOt2b%2BOsLMqG4X06JSsrZT6Ln8PimsrkSOIGbj0gCqscx7BwZfmCQePlCw%3D%3D&
        # trade_no=2019082622001426981000041778&
        # auth_app_id=2016092600597838&
        # version=1.0&app_id=2016092600597838&
        # sign_type=RSA2&
        # seller_id=2088102177296610&
        # timestamp=2019-08-26+13%3A51%3A01
        """
        def dispatch(self, request, *args, **kwargs):
            self.alipay = AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=settings.APP_NOTIFY_URL,
                app_private_key_path=settings.APP_PRIVATE_KEY_PATH,
                alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH,
                debug=settings.ALIPAY_DEBUG,
                return_url=settings.RETURN_URL
            )
            #处理返回的url参数
            callback_data = {}
            for key, value in request.GET.items():
                callback_data[key] = value
            sign = callback_data.pop('sign', None)
            self.order_sn = callback_data.get('out_trade_no', None) #订单号
            self.trade_no = callback_data.get('trade_no', None) #支付宝订单号
    
            # 验证签名
            self.verify = self.alipay.verify(callback_data, sign)
            return super(AlipayView, self).dispatch(request, *args, **kwargs)
    
    
        def get(self, request):
            """处理支付宝return_url返回"""
    
            if self.verify:
                self.deposit()
                #返回个人中心页面
                return redirect(reverse('users:detail', kwargs={
                    'username': request.user.username
                }))
    
        def post(self, request):
            """
            处理notify_url
            """
            if self.verify:
                self.deposit()
    
            return Response('success')
    
        def deposit(self):
            """充值操作
    
            1.更新用户的金币信息
            2.更新订单状态为交易成功
            """
    
            # 数据库中查询订单记录
            order = OrderInfo.objects.get(order_sn=self.order_sn)
            order.trade_no = self.trade_no  # 支付宝订单号
    
            # 把人民币转换成对应的金币
            rmb = order.order_mount
            money = convert_rmb_to_money(rmb)
    
            # 更新用户的金币
            order.user.money += Decimal(money)
            order.user.save()
            # 订单状态置为交易成功
            order.pay_status = 'TRADE_SUCCESS'
            order.save()
    
    

    当用户付款成功后,会跳转回来,那个就是return_url,处理这个的是get方法,用来更新用户的金币和订单。

  • 相关阅读:
    haproxy 2.5 发布
    cube.js sql 支持简单说明
    基于graalvm 开发一个cube.js jdbc driver 的思路
    apache kyuubi Frontend 支持mysql 协议
    oceanbase 资源池删除说明
    基于obd 的oceanbase 扩容说明
    jfilter一个方便的spring rest 响应过滤扩展
    cube.js schema 定义多datasource 说明
    typescript 编写自定义定义文件
    meow 辅助开发cli 应用的工具
  • 原文地址:https://www.cnblogs.com/PyKK2019/p/11413468.html
Copyright © 2011-2022 走看看