DRF实操——支付宝的介绍与对接支付宝
1. 支付宝的介绍
实际上线环境:
- 在浏览器搜索“支付宝开放平台”,扫码登录。
2. 进入控制台
3. 网页移动应用——创建网页移动应用
4. 填写信息,会生成这个应用的appid,会绑定真实的账户,进行对接。
开发环境:
我们在开发过程中使用的是沙箱环境,进行调试和开发。
应用appid,商家信息,用户信息,都是模拟的
-
在控制台中找到“沙箱”
-
沙箱中有一个appid,上线之后,直接改成在实际生成的ID即可
-
沙箱应用——开发信息——接口加签方式——自定义秘钥——公钥模式,点击启用——支付宝秘钥生成器——秘钥工具下载
-
打开下载的秘钥工具,点击生成秘钥
5. 新建两个记事本txt文件,分别保存生成的应用公钥和应用私钥
- 将应用公钥填写到加签管理这个页面(即第3步的页面),点击保存显示加签完成,这时候会生成一个支付宝公钥。
2. DRF对接支付宝
1. 创建配置文件
这里要用公钥进行加密,用私钥进行解密。配置错误会导致无法核销。
- 在config文件夹下创建一个名为alipay_key的文件夹
- 在该文件夹下创建一个alipay_public的文件用来存放支付宝公钥。开头要加上
-----BEGIN PUBLKIKC KEY-----
,结尾要加上-----END PUBLKIKC KEY-----
- 再创建一个app_private_key的文件用来存放应用私钥。开头要加上
-----BEGIN RSA PRIVATE KEY-----
,结尾要加上-----END RSA PRIVATE KEY-----
- 创建一个appid的文件,将沙箱环境中的appid复制过来
2. 在setting文件中,做支付宝配置
#支付宝配置
APPID = open(f'{BASE_DIR}/config/alipay_key/appid').read()
APP_PRIVATE_KEY_STRING = open(f'{BASE_DIR}/config/alipay_key/app_private_key').read()
ALIPAY_PUBLIC_KEY_STRING = open(f'{BASE_DIR}/config/alipay_key/alipay_public').read()
3. 安装支付宝第三方库
pip install python-alipay-sdk -i https://pypi.douban.com/simple
4. 在setting文件中实例化支付宝对象
# 实例化支付宝对象
alipay = AliPay(
appid=APPID,
app_notify_url =None,
app_private_key_string=APP_PRIVATE_KEY_STRING,
alipay_public_key_string=ALIPAY_PUBLIC_KEY_STRING,
sign_type='RSA2',
# 如果是沙箱环境,要将debug设置为True
debug=True
)
5.创建模型,保存订单的支付交易号
- 创建一个支付信息模型
class Payment(DateTimeModelMixin):
trade_id = models.CharField(max_length=100,unique=True,verbose_name='支付宝交易号')
order = models.ForeignKey(Order,on_delete=models.CASCADE,verbose_name='订单')
class Meta:
ordering=['-create_time']
db_table = 'payment'
verbose_name = '支付信息'
verbose_name_plural = verbose_name
- 创建序列化器
class PaymentSerializer(ModelSerializer):
class Meta:
model = Payment
fields = '__all__'
- 创建视图,并实现支付接口。
路由记得自己配置。
class OrderCartViewSet(ReadOnlyModelViewSet,CreateModelMixin):
serializer_class = OrderCartSerializer
permission_classes = [IsAuthenticated]
# 返回当前登录用户的数据
def get_queryset(self):
# 使用管理器:源模型类小写_set
return self.request.user.order_set.all()
@action(methods=['put'],detail=False)
def status(self,request):
"""
支付成功后将订单状态从待支付改为待发货
"""
# 得到支付宝回调过来的数据,在请求地址里卖弄
query_dict = request.query_params #得到地址栏的参数 比如:https://www.baidu.com?sign=xxx
data = query_dict.dict()
# 不能无脑直接修改订单状态,核实身份
try:
#类似于token
# pop 方法会从字典中删除指定的键(在这里是 'sign')
# 如果键存在,它会返回该键的值;如果键不存在,则会引发 KeyError
sign = data.pop('sign') #删除data里面的sign
except KeyError:
return Response(status=HTTP_400_BAD_REQUEST)
# 验证sign。data的其他数据加密后会得到sign,这里把data传入alipay进行加密在跟sign进行比对校验
res = alipay.verify(data,sign)
if not res:
return Response(status=HTTP_403_FORBIDDEN)
#得到订单编号,支付宝交易号
order_id = data.get('out_trade_no') #订单编号 out_trade_no固定参数
trade_no = data.get('trade_no' ) #支付宝交易号 trade_no固定参数
# 保存支付信息,修改订单状态
# 这两个动作要一起完成,所以加入事务
with transaction.atomic():
save_point = transaction.savepoint()
try:
Payment.objects.create(order_id=order_id,trade_id=trade_no)
Order.objects.filter(order_id=order_id,status=1).update(status=2)
except Exception as e:
transaction.savepoint_rollback(save_point)
raise serializers.ValidationError(e)
else:
transaction.savepoint_commit(save_point)
return Response({'order_id':order_id,'trade_id':trade_no})
class PaymentViewSet(ReadOnlyModelViewSet):
queryset = Payment.objects.all()
serializer_class = PaymentSerializer
permission_classes = [IsAuthenticated]
@action(methods=['get'],detail=True)
def pay(self,request,pk):
"""
返回支付页面
"""
#获取订单信息,必须是当前登录的用户的订单,并且是没有支付的
try:
order = Order.objects.get(order_id=pk,user=request.user,status=1)
except Order.DoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
# 对接支付宝,返回支付页面
# 通过支付宝对象处理订单信息
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order.order_id, #订单号传过去
total_amount = str(order.total_amount), #要支付的金额
subject=f'PROMOTE系统{order.order_id}',#标题
# 这里可以配置到配置文件中去,前端更新,方便我们同步更新
return_url='http://localhost:8080/home/order/success', #回调地址,成功后就跳转到前端的支付成功的页面
)
# 将订单信息发送给支付宝
#即沙箱环境中的支付宝网关地址
pay_url = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?' + order_string
# 返回支付页面,前端拿到这个地址后发起请求,就会跳转到支付宝的支付页面
return Response({'pay_url':pay_url})
- 点击支付后,页面会跳转到支付宝的支付页面
- 在支付宝开放平台找到买家账号密码进行登录
- 支付成功后会根据回调地址跳转到我们前端的支付成功页面
- 在加载这个页面时,前端就会拿到这个页面中的订单参数,去访问OrderCartViewSet中的status接口,修改订单状态为待发货。