Django中实现支付宝支付

接入支付宝支付逻辑

支付流程:

return_url:用户支付成功返回的页面

notify_url:通知商户用户支付成功与否的页面

1-用户点击支付,调用支付宝登录的接口(携带订单编号,总金额,return_url,notify_url参数),获取到支付宝登录的url参数,拼接到支付宝支付的url中

2-登录支付宝,登录成功后输入支付密码

3-支付,支付成功后跳转到支付成功页面

4-支付宝携带支付结果数据重定向到商户指定的return_url页面,显示订单支付成功

5-浏览器携带支付结果数据访问notify_url页面,后端保存支付结果

对称/非对称加密

对称:公钥和私钥的加密方法一样

非对称:公钥和私钥的加密方法不一样

商户和支付宝互相存储对方的公钥,双方发送消息给对方时用各自的私钥验证身份是否合法

支付宝接入

  1. 创建应用
  2. 配置密钥
  3. 搭建和配置开发环境
  4. 接口调用

创建数据库模型类
创建应用payment

from django.db import models

from meiduo_mall.utils.models import BaseModel
from orders.models import OrderInfo

    class Payment(BaseModel):
        """
        支付信息
        """
        order = models.ForeignKey(OrderInfo, on_delete=models.CASCADE, verbose_name='订单')
        trade_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="支付编号")
    
        class Meta:
            db_table = 'tb_payment'
            verbose_name = '支付信息'
            verbose_name_plural = verbose_name

python对接支付宝SDK安装

pip install python-alipay-sdk --upgrade`

配置秘钥

①生成应用的私钥和公钥

openssl
OpenSSL> genrsa -out app_private_key.pem 2048  # 生成私钥RSA2
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 配对公钥生成私钥,导出公钥

OpenSSL> exit

②保存应用私钥文件

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

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

③ 查看公钥

cat app_publict_key.pem

将公钥内容复制给支付宝

Django中实现支付宝支付_第1张图片

④保存支付宝公钥

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

将支付宝的公钥内容复制到alipay_public_key.pem文件中
在这里插入图片描述

注意,还需要在公钥文件中补充开始与结束标志

-----BEGIN PUBLIC KEY-----
此处是公钥内容
-----END PUBLIC KEY-----

发起支付的接口

接口设计

1-请求方式:GET /orders/?P\d+/payment/

2-请求参数:路径参数order_id

3-返回参数:支付宝支付的url

具体实现

1-校验order_id

2-调用sdk中api获取支付参数

3-将支付参数拼接到支付宝的url

4-返回

# 发起支付接口,获取支付宝支付的url
# GET /orders/(?P\d+)/payment/
class PayMentView(APIView):
    permission_classes = [IsAuthenticated]  # 仅登陆认证的用户才能访问

    def get(self, request, order_id):
        # 校验订单是否存在
        try:
            order = OrderInfo.objects.get(
                order_id=order_id,  # 订单编号
                user=request.user,  # 当前用户
                pay_ment=OrderInfo.PAY_METHODS_ENUM['ALIPAY'],  # 支付宝支付
                status=OrderInfo.ORDER_STATUS_ENUM['UNPAID']  # 待支付
            )
        except OrderInfo.DoesNotExist:
            return Response({
     'message': '订单信息有误'}, status=status.HTTP_400_BAD_REQUEST)

        # 向支付宝发起请求,获取支付链接参数
        # 通过sdk中的api(AliPay)构造一个支付对象
        alipay_client = AliPay(
            appid=settings.ALIPAY_APPID,
            app_notify_url=None,  # 默认回调url
            # 私钥  路径方式指定
            app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                              "keys/app_private_key.pem"),
            # 支付宝公钥  路径方式指定
            alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                                "keys/alipay_public_key.pem"),  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            sign_type="RSA2",  # RSA 或者 RSA2
            debug=settings.ALIPAY_DEBUG  # 默认False  是否是沙箱环境
        )

        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        # 支付对象调用sdk中的api(api_alipay_trade_page_pay)构造支付链接参数
        order_string = alipay_client.api_alipay_trade_page_pay(
            out_trade_no=order_id,  # 订单编号
            total_amount=str(order.total_amount),  # 总金额
            subject='美多商城订单%s' % order_id,
            return_url="http://www.meiduo.site:8080/pay_success.html",  # 支付成功之后返回的页面
            notify_url=None  # 可选, 不填则使用默认notify url  支付成功与否告诉商户的页面
        )

        # 拼接支付链接网址
        alipay_url = settings.ALIPAY_URL + '?' + order_string

        # 返回
        return Response({
     'alipay_url': alipay_url})

在配置文件中编辑支付宝的配置信息

# 支付宝  沙箱环境配置
ALIPAY_APPID = "2016081600258081"
ALIPAY_URL = "https://openapi.alipaydev.com/gateway.do"
ALIPAY_DEBUG = True

保存支付结果的接口

接口设计

1-请求方式: PUT /payment/status/?支付宝参数

2-请求参数:查询字符串参数,支付宝返回的

3-返回的参数:支付凭证 trade_id

具体实现

接收参数,校验

保存支付结果

修改订单状态

# 支付结果保存
# PUT /payment/status/?支付宝参数
# charset=utf-8
# &out_trade_no=20180704082900000000001
# &method=alipay.trade.page.pay.return
# &total_amount=3788.00
# &trade_no=2018070421001004630200569950
# &auth_app_id=2016081600258081
# &version=1.0&app_id=2016081600258081&sign_type=RSA2&seller_id=2088102171419163×tamp=2018-07-04+16%3A31%3A49

# &sign=UNn3nCckqp4E3MJAonwiywZBtP5Wiia6eJVUta0iimZeLdUuMhH%2FdyRmPGgaQ6xHn0u5KCQbeof4dsXyh%2FdG42cLho9LYCcRqwa6qv3BbEx1J3Y9Qxp6ye%2BTmQq9UbW3%2FoXdAjVJ0gChPQNjm%2BCMI0XbLPT9ARyclb3oKMHrNB7kixMma8OIQbztylSbIwnQilQlxhIWzDqhxCXRgAXjRir7748YpkzW%2FlpkTyuxU1mKI4VwvxV8Of4PQqZcLU%2BbXo2SI%2Bm0Vy%2FgMae4hZIRf%2BbTI1C8lw203HpOMDDeZiUea3GpF9WzuZkTPc4Ryv%2F8K3F6e2IvInpeQt48nqNC%2BQ%3D%3D
class PaymentStatusView(APIView):

    def put(self, request):
        # 接收参数
        # 校验
        alipay_req_data = request.query_params  # QueryDict
        if not alipay_req_data:
            return Response({
     'message': "缺少参数"}, status=status.HTTP_400_BAD_REQUEST)

        alipay_req_dict = alipay_req_data.dict()  # 将传过来的支付宝参数转为普通字典
        sign = alipay_req_dict.pop('sign')  # 取出传过来的公钥

        alipay_client = AliPay(
            appid=settings.ALIPAY_APPID,
            app_notify_url=None,  # 默认回调url
            app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
            alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                                "keys/alipay_public_key.pem"),  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            sign_type="RSA2",  # RSA 或者 RSA2
            debug=settings.ALIPAY_DEBUG  # 默认False  是否是沙箱环境
        )

        # 通过本身的私钥验证传过来的公钥,看是否匹配
        # 返回验证结果,True False
        result = alipay_client.verify(alipay_req_dict, sign)

        if result:  # 如果为True,修改订单状态为待发货,保存支付结果
            order_id = alipay_req_dict.get('out_trade_no')
            trade_id = alipay_req_dict.get('trade_no')
            # 保存支付结果数据Payment
            Payment.objects.create(
                order_id=order_id,
                trade_id=trade_id
            )
            # 修改订单状态为待发货
            OrderInfo.objects.filter(order_id=order_id).update(status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'])
            return Response({
     'trade_id': trade_id})
        else:
            return Response({
     'message': '参数有误'}, status=status.HTTP_400_BAD_REQUEST)

你可能感兴趣的:(python)