支付流程:
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
\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)