文章目录
一、前言
本文主要针对 Python for Django 在对接支付宝电脑网站、手机网站、App支付过程中具体实现步骤进行详解;
相信大家支付功能也写了不少,但时间一长,再次用到的时候有些细节难免会忘记,此篇文章就为方便后续快速实现支付宝各支付功能集成而作。
支付Demo源码地址详见:《Alipay_demo》 下载即可使用
关联文章 《30分钟未支付自动取消订单的设计方案》 将在下一篇分享~~好文章 记得收藏+点赞+关注额 !!!
---- Nick.Peng
二、支付宝开发者相关文档
- 支付宝开发文档: https://openhome.alipay.com/developmentDocument.htm
- 支付快速接入文档: https://docs.open.alipay.com/270/105899/
- 官方SDK地址: https://docs.open.alipay.com/270/106291/
- 非官方支付宝SDK地址: https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
- 开发帮助中心: https://opensupport.alipay.com/support/helpcenter/192
对接过程中如果遇到难题,可以在帮助中心寻找答案
三、各支付功能支付流程图
- 支付流程文字叙述如下,以电脑网站支付为例
- 用户点击下单,生成订单信息,前端携带后端生成返回的订单id,调取获取支付宝支付链接口,接口返回支付链接(包含订单编号,总金额,return_url,notify_url 等参数);
- 前端获取到支付宝支付链接后,构建form表单,向支付宝服务器发起支付请求;
- 进入支付宝支付页面,登录支付宝,登录成功后输入支付密码,进行支付;
- 支付成功后跳转到支付成功页面,支付宝携带支付结果数据重定向到商户指定的 return_url 页面;
- 前端携带支付成功结果数据,向同步回调通知接口发起请求,后端验签成功后返回支付成功业务逻辑响应;
- 同时支付宝服务器也会根据提供的 notify_url 异步请求异步通知接口,后端验签成功,保存支付结果,并返回 success 字符给支付宝服务器;
- 到此支付流程结束
四、支付宝接入前准备
4.1 创建应用
- 安装SDK:
pip install python-alipay-sdk==1.7.1
- 创建项目:
django-admin startproject alipay_demo
- 创建应用:
python manage.py startapp payment
- 本Demo目录结构
4.2 配置公私钥
此步主要完成两个配置文件,即:应用私钥:app_private_key.pem,支付宝公钥:alipay_public_key.pem
-
生成应用的私钥和公钥,Windows用户请下载签名工具
# 打开终端,输入openssl回车,一次执行以下命令生成应用公私钥 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 #退出OpenSSL程序
-
保存应用私钥文件
在 payment 应用中新建 keys 目录,用来保存秘钥文件。
将应用私钥文件 app_private_key.pem 复制到 payment/keys 目录下, 内容如下。-----BEGIN RSA PRIVATE KEY----- 此处为上面生成的应用私钥 -----BEGIN RSA PRIVATE KEY-----
-
查看公钥
cat app_publict_key.pem
将应用公钥内容复制粘贴到支付宝控制台进行配置,会得到支付宝公钥;如图,第一次应该是设置应用公钥
注意:对应加密方式进行配置,推荐RSA2。
-
保存支付宝公钥
在 payment/keys 目录下新建 alipay_public_key.pem 文件,用于保存支付宝的公钥文件。
将上一步生成的支付宝的公钥内容复制到 alipay_public_key.pem 文件中,格式如下:
注意:还需要在公钥文件中补充开始与结束标志,格式如下:-----BEGIN PUBLIC KEY----- 此处是公钥内容 -----END PUBLIC KEY-----
五、支付视图接口实现
5.0 封装支付宝支付对象
-
代码如下: 写到 payment 应用里的 utils.py 工具里
# -*- coding: utf-8 -*- import os from alipay import AliPay # python-alipay-sdk from django.conf import settings def my_ali_pay(notify_url=None): """ 支付宝支付对象 :param notify_url: 支付成功支付宝服务器异步通知默认回调url,会向这个地址发送POST请求,接口实现校验是否支付已经完成,注意:此地址需要能在公网进行访问 :return: 支付对象 """ ali_pay_obj = AliPay( appid=settings.ALI_PAY_APP_ID, app_notify_url=notify_url, # 支付成功支付宝服务器异步通知默认回调url, 即会向这个地址发送POST请求 app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"), # 支付宝的公钥,验证支付宝回传消息使用 alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"), sign_type="RSA2", # RSA 或者 RSA2 debug=settings.ALI_PAY_DEBUG # 是否是沙箱环境, 默认False ) return ali_pay_obj
5.1 获取支付链接接口实现
-
代码如下: 以下为视图,写在 payment 应用的 views.py 文件里
import random from urllib import parse from django.conf import settings from django.db.transaction import atomic from django.http import JsonResponse, HttpResponse from django.utils import timezone from django.views.decorators.csrf import csrf_exempt from apps.payment.utils import my_ali_pay, is_app_pay from utils.common import get_domain @csrf_exempt def get_pay_url(request): """ 获取支付宝支付链接 :return: 支付链接ali_pay_url """ if request.method == "GET": order_id = request.GET.get('order_id', 0) # 前端传回的订单id money = request.GET.get('price') # 前端传回的金额数据 if not all([order_id, money]): return JsonResponse(dict(message="参数错误")) # 此处可增加根据订单id查询判断该订单是否存在相关业务逻辑 # 组织订单编号:当前时间字符串 + 6位随机数 ---> 20200808154711123456 out_trade_no = timezone.now().strftime('%Y%m%d%H%M%S') + ''.join(map(str, random.sample(range(0, 9), 6))) # 生成支付宝支付链接地址 domain_name = get_domain(request) notify_url = domain_name + '/payment/update_order/' ali_pay = my_ali_pay(notify_url) order_string = ali_pay.api_alipay_trade_page_pay( out_trade_no=out_trade_no, # 订单编号 total_amount=str(money), # 交易金额(单位: 元 保留俩位小数) 这里一般是从前端传过来的数据 subject=f"产品名称-{out_trade_no}", # 商品名称或产品名称 return_url=domain_name + "/payment/get_result/", # 支付成功后跳转的页面,App支付此参数无效,集成支付宝SDK自带跳转 ) # 拼接支付链接,注意:App支付不需要返回支付宝网关 ali_pay_url = order_string if is_app_pay(order_string) else settings.ALI_PAY_URL + "?" + order_string return JsonResponse(dict(ali_pay_url=ali_pay_url)) return JsonResponse(dict(ali_pay_url=""))
5.2 支付成功同步回调通知接口实现
-
代码如下:
@csrf_exempt def pay_result(request): """ 前端同步回调通知(支付完成后,前端url会接收支付宝支付完成后回传的form参数,将其全部传给该接口进行验签),参数示例如下: ?charset=utf-8&out_trade_no=20200808154711123456&method=alipay.trade.page.pay.return&total_amount=0.01&sign=FtDkDtsDE9dW3RB18BfiAeFqkSQAK......E1wE9tgsoUi50%2B0IH7w%3D%3D&trade_no=2020080622001460481436975535&auth_app_id=2016101000655892&version=1.0&app_id=2016101000655892&sign_type=RSA2&seller_id=2087811328364696×tamp=2020-08-06+12%3A44%3A44 :return: 根据业务需求自定义返回信息 """ if request.method == "GET": data = request.GET.dict() ali_pay = my_ali_pay() sign = data.pop('sign', None) success = ali_pay.verify(data, sign) print("同步回调验签状态: ", success) if success: # 此处写支付验签成功的相关业务逻辑 return JsonResponse(dict(message="支付成功")) return JsonResponse(dict(message="支付失败")) return JsonResponse(dict(message="支付失败"))
5.3 支付成功异步通知接口实现
-
代码如下:
@csrf_exempt @atomic() def update_order(request): """ 支付成功后,支付宝服务器异步通知回调(用于修改订单状态) :return: success or fail """ if request.method == "POST": body_str = request.body.decode('utf-8') data = parse.parse_qs(body_str) # data = parse.parse_qs(parse.unquote(body)) # 前端回传的url如果被编码,这里需要用unquote解码再转换成字典 data = {k: v[0] for k, v in data.items()} ali_pay = my_ali_pay() sign = data.pop('sign', None) success = ali_pay.verify(data, sign) # 返回验签结果, True/False print("异步通知验证状态: ", success) if success: # 此处写支付验签成功修改订单状态相关业务逻辑 return HttpResponse('success') # 返回success给支付宝服务器, 若支付宝收不到success字符会重复发送通知 return HttpResponse('fail') return HttpResponse('fail')