renran-文章打赏




服务端集成支付接口实现打赏的功能

支付接口,可以实现网络转账。
很多的第三方支付接口:
国外:万事达,applePay,PayPAl,Visa,八达通,西联[邮政汇款]
国内:支付宝,微信,京东钱包,百度钱包,贝宝[PayPal中国版]

支付宝

支付宝开发平台登录

https://open.alipay.com/platform/home.htm

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sFS71XCH-1595162910475)(E:/python八期/北京python27期荏苒项目/day125/assets/1584442807263.png)]

支付宝的申请需要企业资质,但是我们作为开发者可以使用支付宝提供的测试账号先开发功能,将来调整账号即可用于公司项目的正式运营。

地址:https://openhome.alipay.com/platform/developerIndex.htm

沙箱环境

  • 是支付宝提供给开发者的模拟支付的环境

  • 沙箱环境跟真实环境是分开的,项目上线时必须切换对应的配置服务器地址和开发者ID和密钥。

  • 沙箱应用开发文档:https://docs.open.alipay.com/200/105311

  • 沙箱账号:https://openhome.alipay.com/platform/appDaily.htm?tab=account

真实的支付宝网关:   https://openapi.alipay.com/gateway.do
	
沙箱的支付宝网关:   https://openapi.alipaydev.com/gateway.do

支付宝开发者文档

  • 文档主页:https://openhome.alipay.com/developmentDocument.htm
  • 产品介绍:https://docs.open.alipay.com/270

电脑网站支付流程

【前后端不分离】时序图[ 时间顺序流程图 ]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Ik5a4IA-1595162910477)(E:/python八期/北京python27期荏苒项目/day125/assets/电脑网站支付流程图.png)]

【前后端分离】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y20fly7P-1595162910480)(E:/python八期/北京python27期荏苒项目/day125/assets/1584445167899.png)]

RSA算法,属于非堆成加密,一旦加密以后不能解密的。
可以通过密钥来进行验证。
密钥成对生成的。分公钥和私钥。
公钥用于验证数据[ 解签 ]
私钥用于加密数据[ 签名 ]

开发支付功能

cd renranapi/apps
python ../../manage.py startapp payments

注册子应用

INSTALLED_APPS = [
	。。。。
    'payments',
]

配置秘钥

1. 生成应用的私钥和公钥

下载对应系统的秘钥生成工具: https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1

windows操作系统

生成如下,安装软件时需要管理员身份来安装.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOToDfqU-1595162910483)(E:/python八期/北京python27期荏苒项目/day125/assets/1566548916758.png)]

Linux系统

生成如下:

openssl
OpenSSL> genrsa -out app_private_key.pem 2048                         # 生成私钥到指定文件中
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem  # 导出公钥
OpenSSL> exit

应用公钥复制粘贴到支付宝网站页面中.

点击修改以后,粘贴进去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMPbznY6-1595162910486)(E:/python八期/北京python27期荏苒项目/day125/assets/1554192143494.png)]

2. 保存应用私钥文件

在payments应用中新建keys目录,用来保存秘钥文件。

将应用私钥文件app_private_key.pem复制到payment/keys目录下。

windows系统生成的私钥必须在上下两行加上以下标识:

-----BEGIN RSA PRIVATE KEY-----
私钥
-----END RSA PRIVATE KEY-----

3. 保存支付宝公钥到项目中

在payments/key目录下新建alipay_public_key.pem文件,用于保存支付宝的公钥文件。

将支付宝的公钥内容复制到alipay_public_key.pem文件中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bec2pVcG-1595162910487)(E:/python八期/北京python27期荏苒项目/day125/assets/支付宝公钥.png)]

-----BEGIN PUBLIC KEY-----
公钥
-----END PUBLIC KEY-----

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5dcptc7-1595162910489)(E:/python八期/北京python27期荏苒项目/day125/assets/1554200221417.png)]

4. 使用支付宝的sdk开发支付接口

SDK:https://docs.open.alipay.com/270/106291/

python版本的支付宝SDK文档:https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

安装命令:

pip install python-alipay-sdk --upgrade

后端提供发起支付的接口url地址

创建模型,保存打赏记录

from django.db import models
from renranapi.utils.models import BaseModel
from users.models import User
from article.models import Article

class Reward(BaseModel):
    REWARD_TYPE = (
        (1, "支付宝"),
        (2, "余额"),
    )
    user = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name="用户")
    money = models.DecimalField(decimal_places=2, max_digits=6, verbose_name="打赏金额")
    article = models.ForeignKey(Article, on_delete=models.DO_NOTHING, verbose_name="文章")
    status = models.BooleanField(default=False, verbose_name="打赏状态")
    trade_no = models.CharField(max_length=255, null=True, blank=True, verbose_name="流水号")
    out_trade_no = models.CharField(max_length=255, null=True, blank=True, verbose_name="支付平台返回的流水号")
    reward_type = models.IntegerField(default=1, verbose_name="打赏类型")
    message = models.TextField(null=True,blank=True, verbose_name="打赏留言")

    class Meta:
        db_table = "rr_user_reward"
        verbose_name = "打赏记录"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.user.nickname+"打赏了"+self.article.user.nickname+"的文章《" +self.article.title+"》"+self.money+"元"

注册子应用

INSTALLED_APPS = [
    # ....
	'payments',
]

迁移迁移

python manage.py makemigrations
python manage.py migrate

编写视图提供支付的url地址

from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from .models import Reward
from rest_framework.permissions import IsAuthenticated
import random
from datetime import datetime
from alipay import AliPay
from django.conf import settings
from rest_framework.response import Response

class AliPayAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def post(self,request):
        """生成支付的链接地址"""
        # 创建打赏记录
        user = request.user
        # 验证用户打赏的文章是否存在
        trade_no = datetime.now().strftime("%Y%m%d%H%M%S") + ("%06d" % user.id) + ("%06d"% random.randint(1,999999))
        reward = Reward.objects.create(
            user=user,
            money=request.data.get("money"),
            article_id=request.data.get("article_id"),
            status=False,
            trade_no=trade_no,
            out_trade_no=None,
            reward_type=request.data.get("type"),
            message = request.data.get("message"),
        )
        if reward.reward_type == 1:
            # 生成支付链接
            # 初始化支付对象
            app_private_key_string = open(settings.ALIAPY_CONFIG["app_private_key_path"]).read()
            alipay_public_key_string = open(settings.ALIAPY_CONFIG["alipay_public_key_path"]).read()
            alipay = AliPay(
                appid=settings.ALIAPY_CONFIG["appid"],
                app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
                app_private_key_string=app_private_key_string,
                # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                alipay_public_key_string=alipay_public_key_string,
                sign_type=settings.ALIAPY_CONFIG["sign_type"],
                debug = settings.ALIAPY_CONFIG["debug"]  # 默认False
            )

            # 调用接口
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=reward.trade_no,
                total_amount=float(reward.money),
                subject="打赏文章",
                return_url=settings.ALIAPY_CONFIG["return_url"],
                notify_url=settings.ALIAPY_CONFIG["notify_url"]  # 可选, 不填则使用默认notify url
            )

            url = settings.ALIAPY_CONFIG["gateway_url"] + order_string
        else:
            # 进行其他类型的支付方式
            url = ""

        return Response(url)

在配置文件中编辑支付宝的配置信息[实际的值根据自己的账号而定]

setttins/dev.py,代码:

ALIAPY_CONFIG = {
    # "gateway_url": "https://openapi.alipay.com/gateway.do?", # 真实支付宝网关地址
    "gateway_url": "https://openapi.alipaydev.com/gateway.do?", # 沙箱支付宝网关地址
    "appid": "2016091600523592",
    "app_notify_url": None,
    "app_private_key_path": os.path.join(BASE_DIR, "apps/payments/keys/app_private_key.pem"),
    "alipay_public_key_path": os.path.join(BASE_DIR, "apps/payments/keys/alipay_public_key.pem"),
    "sign_type": "RSA2",
    "debug": False,
    "return_url": "http://www.moluo.net:8080/", # 同步回调地址
    "notify_url": "http://api.renran.cn:8000/payments/result", # 异步结果通知
}

注册url地址:payments/urls.py,代码:

from django.urls import path,re_path
from . import views
urlpatterns = [
    path(r"alipay/",views.AlipayAPIView.as_view()),
]

总路由,代码:

    path('payments/', include('payments.urls')),

客户端实现点击打赏请求




用户支付完成以后的支付结果处理

客户端接收支付宝跳转发送回来的同步结果参数,并发起请求服务端的同步处理结果API接口







路由,代码:

import Wallet from "@/components/Wallet"
      {
       name:"Wallet",
       path:"/usr/wallet",
       component: Wallet,
     },

服务端完成同步支付结果的处理

视图代码:

# Create your views here.
from .models import Reward
from rest_framework.viewsets import ViewSet
from alipay import AliPay
from rest_framework.permissions import IsAuthenticated
from datetime import datetime
import random
from rest_framework import status
from rest_framework.response import Response
from django.conf import settings
from django.db import transaction
from article.models import Article

class AliPayAPIViewSet(ViewSet):
    permission_classes = [IsAuthenticated]

    def get_alipay(self):
        # 初始化支付对象
        app_private_key_string = open(settings.ALIAPY_CONFIG["app_private_key_path"]).read()
        alipay_public_key_string = open(settings.ALIAPY_CONFIG["alipay_public_key_path"]).read()

        alipay = AliPay(
            appid=settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_string=app_private_key_string,
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_string=alipay_public_key_string,
            sign_type=settings.ALIAPY_CONFIG["sign_type"],
            debug=settings.ALIAPY_CONFIG["debug"]  # 默认False
        )
        return alipay

    def post(self,request):
        """生成支付的链接地址"""
        # 创建打赏记录
        user = request.user
        # 随机流水号
        trade_no = datetime.now().strftime("%Y%m%d%H%M%S") + ("%06d" % user.id) + ("%06d" % random.randint(1, 999999))

        money = request.data.get("money")
        if money < 0:
            return Response("对不起,支付金额不能小于0元", status=status.HTTP_400_BAD_REQUEST)

        reward = Reward.objects.create(
            user=user,
            money=money,
            article_id=request.data.get("article_id"),
            status=0,
            trade_no=trade_no,
            out_trade_no=None,
            reward_type=request.data.get("pay_type"),
            message=request.data.get("content"),
            orders=0,
        )

        if reward.reward_type == 0:
            # 生成支付链接
            alipay = self.get_alipay()
            # 调用接口
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=reward.trade_no,
                total_amount=float(reward.money), # 打赏金额
                subject="打赏文章",
                return_url=settings.ALIAPY_CONFIG["return_url"],
                notify_url=settings.ALIAPY_CONFIG["notify_url"]  # 可选, 不填则使用默认notify url
            )

            url = settings.ALIAPY_CONFIG["gateway_url"] + order_string
        else:
            # 进行其他类型的支付方式
            url = ""

        return Response(url)

    def return_result(self,request):
        """支付宝同步结果处理"""
        data = request.query_params.dict()
        signature = data.pop("sign")
        alipay = self.get_alipay()
        success = alipay.verify(data, signature)
        if success:
            """支付结果处理"""
            # 开启ORM的mysql事务的自动提交,在with语句范围内,所有的SQL会全部被事务控制,要么一起提交,要么一起不提交
            with transaction.atomic():
                # 设置事务的回滚点,用于指定在事务失败时,在哪一部分的SQL语句无效
                save_id = transaction.savepoint()
                try:
                    # 修改打赏记录的状态为已付款
                    reward = Reward.objects.get(
                        trade_no=data.get("out_trade_no"),
                        status=0,
                    )
                    reward.status = 1
                    reward.save()

                    # 增加文章的打赏人数
                    article = Article.objects.get(pk=reward.article.id)
                    article.reward_count += 1
                    article.save()
                    print(article.user.money)
                    print(reward.money)
                    # 给用户资产增加打赏的资金
                    article.user.money= int((article.user.money + reward.money) * 100) / 100
                    article.user.save()

                    # 参考打赏,实现一个资金流水记录[专门显示在钱包位置]

                except Reward.DoesNotExist:
                    transaction.savepoint_rollback(save_id)
                    return Response("当前打赏已经处理完成!请不要重复提交!")
                except:
                    transaction.savepoint_rollback(save_id)
                    return Response({"message": "支付结果处理有误!"})

                return Response({"message":"支付处理成功!"})
        else:
            return Response({"message": "支付失败!"}, status=status.HTTP_400_BAD_REQUEST)

路由:

from django.urls import path,re_path
from . import views
urlpatterns = [
    path("alipay/", views.AliPayAPIViewSet.as_view({"post":"post"})),
    path("alipay/result/", views.AliPayAPIViewSet.as_view({"get":"return_result"})),
]

处理支付宝的异步通知

from django.shortcuts import render

# Create your views here.
from .models import Reward
from rest_framework.viewsets import ViewSet
from alipay import AliPay
from rest_framework.permissions import IsAuthenticated
from datetime import datetime
import random
from rest_framework import status
from rest_framework.response import Response
from django.conf import settings
from django.db import transaction
from article.models import Article

class AliPayAPIViewSet(ViewSet):
    permission_classes = [IsAuthenticated]

    def get_alipay(self):
        # 初始化支付对象
        app_private_key_string = open(settings.ALIAPY_CONFIG["app_private_key_path"]).read()
        alipay_public_key_string = open(settings.ALIAPY_CONFIG["alipay_public_key_path"]).read()

        alipay = AliPay(
            appid=settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_string=app_private_key_string,
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_string=alipay_public_key_string,
            sign_type=settings.ALIAPY_CONFIG["sign_type"],
            debug=settings.ALIAPY_CONFIG["debug"]  # 默认False
        )
        return alipay

    def post(self,request):
        """生成支付的链接地址"""
        # 创建打赏记录
        user = request.user
        # 随机流水号
        trade_no = datetime.now().strftime("%Y%m%d%H%M%S") + ("%06d" % user.id) + ("%06d" % random.randint(1, 999999))

        money = request.data.get("money")
        if money < 0:
            return Response("对不起,支付金额不能小于0元", status=status.HTTP_400_BAD_REQUEST)

        reward = Reward.objects.create(
            user=user,
            money=money,
            article_id=request.data.get("article_id"),
            status=0,
            trade_no=trade_no,
            out_trade_no=None,
            reward_type=request.data.get("pay_type"),
            message=request.data.get("content"),
            orders=0,
        )

        if reward.reward_type == 0:
            # 生成支付链接
            alipay = self.get_alipay()
            # 调用接口
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=reward.trade_no,
                total_amount=float(reward.money), # 打赏金额
                subject="打赏文章",
                return_url=settings.ALIAPY_CONFIG["return_url"],
                notify_url=settings.ALIAPY_CONFIG["notify_url"]  # 可选, 不填则使用默认notify url
            )

            url = settings.ALIAPY_CONFIG["gateway_url"] + order_string
        else:
            # 进行其他类型的支付方式
            url = ""

        return Response(url)

    def return_result(self,request):
        """支付宝同步结果处理"""
        data = request.query_params.dict()
        signature = data.pop("sign")
        alipay = self.get_alipay()
        success = alipay.verify(data, signature)
        if success:
            """支付结果处理"""
            # 开启ORM的mysql事务的自动提交,在with语句范围内,所有的SQL会全部被事务控制,要么一起提交,要么一起不提交
            with transaction.atomic():
                # 设置事务的回滚点,用于指定在事务失败时,在哪一部分的SQL语句无效
                save_id = transaction.savepoint()
                try:
                    # 修改打赏记录的状态为已付款
                    reward = Reward.objects.get(
                        trade_no=data.get("out_trade_no"),
                        status=0,
                    )
                    reward.status = 1
                    reward.save()

                    # 增加文章的打赏人数
                    article = Article.objects.get(pk=reward.article.id)
                    article.reward_count += 1
                    article.save()
                    print(article.user.money)
                    print(reward.money)
                    # 给用户资产增加打赏的资金
                    article.user.money= int((article.user.money + reward.money) * 100) / 100
                    article.user.save()

                    # 参考打赏,实现一个资金流水记录[专门显示在钱包位置]

                except Reward.DoesNotExist:
                    transaction.savepoint_rollback(save_id)
                    return Response("当前打赏已经处理完成!请不要重复提交!")
                except:
                    transaction.savepoint_rollback(save_id)
                    return Response({"message": "支付结果处理有误!"})

                return Response({"message":"支付处理成功!"})
        else:
            return Response({"message": "支付失败!"}, status=status.HTTP_400_BAD_REQUEST)


    def notify_result(self,request):
        """支付宝异步结果处理"""
        data = request.data.dict()
        signature = data.pop("sign")
        alipay = self.get_alipay()
        success = alipay.verify(data, signature)
        if success:
            """支付结果处理"""
            # 开启ORM的mysql事务的自动提交,在with语句范围内,所有的SQL会全部被事务控制,要么一起提交,要么一起不提交
            with transaction.atomic():
                # 设置事务的回滚点,用于指定在事务失败时,在哪一部分的SQL语句无效
                save_id = transaction.savepoint()
                try:
                    # 修改打赏记录的状态为已付款
                    reward = Reward.objects.get(
                        trade_no=data.get("out_trade_no"),
                        status=0,
                    )
                    reward.status = 1
                    reward.save()

                    # 增加文章的打赏人数
                    article = Article.objects.get(pk=reward.article.id)
                    article.reward_count += 1
                    article.save()
                    print(article.user.money)
                    print(reward.money)
                    # 给用户资产增加打赏的资金
                    article.user.money= int((article.user.money + reward.money) * 100) / 100
                    article.user.save()

                    # 参考打赏,实现一个资金流水记录[专门显示在钱包位置]

                except Reward.DoesNotExist:
                    transaction.savepoint_rollback(save_id)
                    return Response("当前打赏已经处理完成!请不要重复提交!")
                except:
                    transaction.savepoint_rollback(save_id)
                    return Response({"message": "支付结果处理有误!"})

                return Response({"message":"支付处理成功!"})
        else:
            return Response({"message": "支付失败!"}, status=status.HTTP_400_BAD_REQUEST)

路由代码:

from django.urls import path,re_path
from . import views
urlpatterns = [
    path("alipay/", views.AliPayAPIViewSet.as_view({"post":"post"})),
    path("alipay/result/", views.AliPayAPIViewSet.as_view({"get":"return_result","post":"notify_result"})),
]

d.save()

                # 增加文章的打赏人数
                article = Article.objects.get(pk=reward.article.id)
                article.reward_count += 1
                article.save()
                print(article.user.money)
                print(reward.money)
                # 给用户资产增加打赏的资金
                article.user.money= int((article.user.money + reward.money) * 100) / 100
                article.user.save()

                # 参考打赏,实现一个资金流水记录[专门显示在钱包位置]

            except Reward.DoesNotExist:
                transaction.savepoint_rollback(save_id)
                return Response("当前打赏已经处理完成!请不要重复提交!")
            except:
                transaction.savepoint_rollback(save_id)
                return Response({"message": "支付结果处理有误!"})

            return Response({"message":"支付处理成功!"})
    else:
        return Response({"message": "支付失败!"}, status=status.HTTP_400_BAD_REQUEST)

路由代码:

```python
from django.urls import path,re_path
from . import views
urlpatterns = [
    path("alipay/", views.AliPayAPIViewSet.as_view({"post":"post"})),
    path("alipay/result/", views.AliPayAPIViewSet.as_view({"get":"return_result","post":"notify_result"})),
]

你可能感兴趣的:(web)