Django轻量级任务追踪管理平台开发:六


前言

今天主要介绍项目的套餐购买系统,需要接入支付宝的api,使用官方提供的沙箱环境进行支付。

在这里插入图片描述
点击“价格”进入套餐购买界面,输入数量,点击“立即购买”,进入订单确认界面。

在这里插入图片描述
再点击“立即支付”,跳转到支付宝完成支付,支付完成之后再跳转回来。
要想跳转到支付宝,就要调用支付宝的api接口。


一、沙箱环境准备

登录支付宝开放平台,点击控制台,再找到下方的“沙箱”,即可进入沙箱应用。
在这里插入图片描述
进入沙箱应用之后是如下的界面,请保存APPID,后期会用到。同时将支付宝网关地址也记录下来。
在这里插入图片描述
开发信息–>接口加密方式使用系统默认秘钥,并选择公钥模式,点击查看,进入秘钥界面。
在这里插入图片描述
根据自己的情况查看应用私钥,并将应用公钥、应用私钥、支付宝公钥全部复制保存下来。
至此,沙箱环境的必备数据就都准备好了。

二、沙箱版支付宝

要想实现支付宝付款,必须有一个支付宝账户。针对沙箱环境,支付宝为我们准备了一个沙箱版支付宝。
在这里插入图片描述
点击扫码下载即可,目前仅提供安卓版本。下载好之后使用沙箱账号中的买家账户进行登录。
在这里插入图片描述

三、接入API

通过 支付宝开放平台开发助手 生成密钥之后,可使用支付宝开放平台 SDK 、支付宝开放平台研发助手及自行实现三种方式进行签名。这里我们使用的是自行实现。

1. 原理介绍

  1. 生成签名方(通常为商家)首先将所有参数和值放入一个字典 中,并按照 key 值升序排列(调用sorted)。然后将所有参数拼接起来,去掉 key 或 value 为空的参数,并用 & 连接,组成签名原文。最后使用 RSA 的私钥对签名原文进行签名。
  2. 验签方(通常为开放平台的服务端):获取响应中的签名原文和签名,然后使用 RSA 的公钥通过签名原文验证该签名,验证结果为 true 则验证成功,否则验证未通过。

2.所需的参数

这里是官网给出的全部参数列表,凡是必填的都必须放入字典中当做参数传入。
在这里插入图片描述
最后构建出这样一个字典:

params = {
            'app_id': 支付宝分配给开发者的应用ID,
            'method': 'alipay.trade.page.pay',
            'format': 'JSON',
            'return_url': 支付完成之后跳转的url,get请求,
            'notity_url': 支付宝向商户通知结果的url,post请求,
            'charset': 'utf-8',
            'sign_type': 'RSA2',
            'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'version': '1.0',
            'biz_content': json.dumps({
                'out_trade_no': 订单号,
                'product_code': 'FAST_INSTANT_TRADE_PAY',
                'total_amount': 金额,
                'subject': 'tracer套餐购买'
            }, separators=(',', ':'))
        }

3. 代码实现

封装一个Alipay的类,实现签名和验签。需要提前安装一个模块crypto

pip install crypto
# views/app01/utils/alipay/instant_pay.py

import json
import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from base64 import encodebytes, decodebytes

class Alipay:
    def __init__(self,app_id, return_url, notify_url, private_key, ali_public_key,):
    	"""
		private_key: 应用私钥
		ali_public_key: 支付宝公钥
		"""
        self.app_id = app_id
        self.return_url = return_url
        self.notify_url = notify_url
        self.private_key_path = private_key
        with open(self.private_key_path) as fp:
            self.private_key = RSA.importKey(fp.read())
        self.ali_public_key_path = ali_public_key
        with open(self.ali_public_key_path) as fp:
            self.ali_public_key = RSA.importKey(fp.read())

    def sign(self, order_num, amount):
        """生成支付链接"""
        params = {
            'app_id': self.app_id,
            'method': 'alipay.trade.page.pay',
            'format': 'JSON',
            'return_url': self.return_url,
            'notity_url': self.notify_url,
            'charset': 'utf-8',
            'sign_type': 'RSA2',
            'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'version': '1.0',
            'biz_content': json.dumps({
                'out_trade_no': order_num,
                'product_code': 'FAST_INSTANT_TRADE_PAY',
                'total_amount': amount,
                'subject': 'tracer套餐购买'
            }, separators=(',', ':'))
        }
        # 排序
        unsigned_string = '&'.join(["{}={}".format(x, params[x]) for x in sorted(params)])
        # SHA256编码
        private_key = self.private_key
        signer = PKCS1_v1_5.new(private_key)
        signature = signer.sign(SHA256.new(unsigned_string.encode('utf-8')))
        # base64编码
        sign_string = encodebytes(signature).decode('utf8').replace('\n', '')
        result = "&".join(["{}={}".format(k, quote_plus(params[k])) for k in sorted(params)])
        result = result + '&sign=' + quote_plus(sign_string)      
        return result

    def ordered_data(self, data):
        complex_keys = []
        for key, value in data.items():
            if isinstance(value, dict):
                complex_keys.append(key)

        # 将字典类型的数据dump出来
        for key in complex_keys:
            data[key] = json.dumps(data[key], separators=(',', ':'))

        return sorted([(k, v) for k, v in data.items()])

    def _verify(self, raw_content, signature):
        # 开始计算签名
        key = self.ali_public_key
        signer = PKCS1_v1_5.new(key)
        digest = SHA256.new()
        digest.update(raw_content.encode("utf8"))
        if signer.verify(digest, decodebytes(signature.encode("utf8"))):
            return True
        return False

    def verify(self, data, signature):
        if "sign_type" in data:
            sign_type = data.pop("sign_type")
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
        return self._verify(message, signature)

4. 后端接入

# app01/views/price.py

from app01.utils.alipay.instant_pay import Alipay


def payment(request, price_policy_id):
    """确认支付"""
    conn = get_redis_connection()
    key = 'payment_{}'.format(request.tracer.phone)
    context_string = conn.get(key)
    if not context_string:
        return redirect("app01:price")
    context = json.loads(context_string.decode("utf-8"))

    order = encoder(uid(request.tracer.phone))
    models.Transaction.objects.create(
        status=1,
        order=order,
        user=request.tracer,
        price_policy_id=context['policy_id'],
        count=context['number'],
        price=context['total_balance']
    )
    # 签名生成支付链接并跳转
    alipay = Alipay(
        app_id = local_settings.ALI_APPID,
        return_url = local_settings.RETURN_URL,
        notify_url = local_settings.NOTIFY_URL,
        private_key = local_settings.APP_PRIVATE_KEY_PATH,
        ali_public_key = local_settings.ALI_PUBLIC_KEY_PATH,
    )
    result = alipay.sign(order, context['total_balance'])
    pay_url = "{}?{}".format(local_settings.GATE_WAY_URL, result)
    return redirect(pay_url)


def notify(request):
	"""通知商户支付成功的函数,由于目前公网无法访问此网站,相关逻辑放在下方函数中了"""
    return HttpResponse("success")


def pay_return(request):
    """支付完成之后跳转的页面,对于数据库的处理应该放在nofity中,这里就先放在此函数里了"""
    params = request.GET.dict()
    sign = params.pop("sign", None)
    alipay = Alipay(
            app_id = local_settings.ALI_APPID,
            return_url = local_settings.RETURN_URL,
            notify_url = local_settings.NOTIFY_URL,
            private_key = local_settings.APP_PRIVATE_KEY_PATH,
            ali_public_key = local_settings.ALI_PUBLIC_KEY_PATH,
        )    
    status = alipay.verify(params, sign)
    if status:
        current_datetime = datetime.datetime.now()
        out_trade_no = params['out_trade_no']
        _object = models.Transaction.objects.filter(order=out_trade_no).first()

        _object.status = 2
        _object.start_time = current_datetime
        _object.end_time = current_datetime + datetime.timedelta(days=365 * _object.count)
        _object.save()
        return HttpResponse('<h1>支付成功</h1>') 

完成之后就可以正常跳转到支付宝了,然后就可以用前面下载的沙箱版支付宝进行支付了,不出意外的话,付款完成也会顺利进行页面跳转~
由于沙箱版支付宝很不稳定,这里建议小伙伴们直接在电脑上输入账号密码进行登录,不要扫码,否则容易出现扫码支付完成页面却没有动静的情况!


四、总结

此篇文档使用的是支付宝的API,当然官网提供的有python的SDK,感兴趣的小伙伴也可以调用SDK试一下!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值