接入支付宝支付逻辑
支付流程:
return_url:用户支付成功返回的页面
notify_url:通知商户用户支付成功与否的页面
1-用户点击支付,调用支付宝登录的接口(携带订单编号,总金额,return_url,notify_url参数),获取到支付宝登录的url参数,拼接到支付宝支付的url中
2-登录支付宝,登录成功后输入支付密码
3-支付,支付成功后跳转到支付成功页面
4-支付宝携带支付结果数据重定向到商户指定的return_url页面,显示订单支付成功
5-浏览器携带支付结果数据访问notify_url页面,后端保存支付结果
对称/非对称加密
对称:公钥和私钥的加密方法一样
非对称:公钥和私钥的加密方法不一样
商户和支付宝互相存储对方的公钥,双方发送消息给对方时用各自的私钥验证身份是否合法
支付宝接入
- 创建应用
- 配置密钥
- 搭建和配置开发环境
- 接口调用
创建数据库模型类
创建应用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
将公钥内容复制给支付宝
④保存支付宝公钥
在payment/keys目录下新建alipay_public_key.pem文件,用于保存支付宝的公钥文件。
将支付宝的公钥内容复制到alipay_public_key.pem文件中
注意,还需要在公钥文件中补充开始与结束标志
-----BEGIN PUBLIC KEY----- 此处是公钥内容 -----END PUBLIC KEY-----
发起支付的接口
接口设计
1-请求方式:GET /orders/?P<order_id>\d+/payment/
2-请求参数:路径参数order_id
3-返回参数:支付宝支付的url
具体实现
1-校验order_id
2-调用sdk中api获取支付参数
3-将支付参数拼接到支付宝的url
4-返回
# 发起支付接口,获取支付宝支付的url # GET /orders/(?P<order_id>\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)