python支付宝二维码支付源代码

本文介绍了使用Python实现支付宝二维码支付的步骤,包括沙箱环境配置、密钥设置、第三方库安装、支付流程及扫码流程说明,并提供了核心代码示例,适用于Django框架。最后,文章提供了二维码聚合SDK下载链接及后记。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python支付宝支付

微信、支付宝二维码聚合SDK下载

点我下载

1、沙箱环境配置

在支付宝提供的沙箱环境中进行开发,用自己的支付宝账号去申请一个沙箱账号。

在这里插入图片描述

可以参考沙箱使用说明获取AppID等,然后根据说明下载安卓版的支付宝钱包,使用沙箱环境的买家账号登录测试。

在平台上填写授权回调地址,以及选择的加签方式(RSA2)。

注:如果是正式上线,不能使用沙箱的AppID,应该到支付宝开放平台创建一个生活号或者小程序应用获取。

2、配置密钥、公钥。

可以使用官方的密钥生成工具来帮助我们生成两种密钥,密钥长度选择RSA2(2048),密钥格式选择PKCS1。将生成完的密钥上传到支付宝后台。

在这里插入图片描述

可以参考官方说明

3、在虚拟环境安装第三方库

pip install alipay-sdk-python==3.3.398

GitHub参考

注:在windows下安装需要在虚拟环境中先执行一条命令

set CL=/FI"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include\\stdint.h"

然后再执行

pip install alipay-sdk-python==3.3.398

如果有使用到Crypto库来进行对称加密的话,需要再导一个库

pip install pycryptodome

可以参考博文安装点我跳转

4、支付流程说明

在这里插入图片描述

具体可以参考支付宝官方文档说明,有当面付、App支付、手机网站支付等不同支付场景。

5、扫码流程说明

1、用户扫描二维码

2、后端判断扫码来源是否为支付宝

3、去支付宝请求auth_code

4、通过auth_code获取用户的user_id

5、扫码成功后用户手机端显示支付金额与确认支付按钮

6、点击确认支付

7、获取支付宝支付url返回给前端,前端调起支付宝收银台

8、用户输入支付密码完成支付

9、支付成功后前端同步通知

10、后端等支付宝异步通知修改订单状态

6、核心代码

【注】以下代码是基于Django框架的

6.1 验证用户是否使用微信、支付宝扫码
def user_agent_auth(func):
    # 验证用户是否使用微信、支付宝扫码
    @wraps(func)
    def wrapper(request, *args, **kwargs):
        user_agent = request.META.get('HTTP_USER_AGENT')
        if 'MicroMessenger' not in user_agent and 'AlipayClient' not in user_agent:
            return render(request, 'error.html', context={'message': '请使用微信或支付宝扫码'})
        return func(request, *args, **kwargs)

    return wrapper


@user_agent_auth
def scan_qrcode(request, *args, **kwargs):
    # 判断用户是用微信还是支付宝扫码
    user_agent = request.META.get('HTTP_USER_AGENT')
    params = request.GET.get('params') # 用户扫码后,从url中解析出主要参数
    
	# 设置回调url为 http://你的域名/order/pay_page/
    redirect_uri = '{}{}?params={}'.format(request.build_absolute_uri('/'), 'order/pay_page/', params)
    if 'AlipayClient' in user_agent:
        # 使用支付宝扫码
        # 请求支付宝,获取auth_code,然后获取用户user_id
        # 参考官方文献 https://opendocs.alipay.com/open/289/105656
        # 获取后台设置的支付参数
        alipay_setting = PayConfig.objects.filter(is_use__in=['pron', 'test'], ali_appid__isnull=False) 
        if alipay_setting:
            return HttpResponseRedirect(AliUserInfo().get_ali_auth_code(alipay_setting.first(), redirect_uri))
        message = '还没有配置支付宝支付参数'
    else:
        message = '请使用支付宝扫码支付'
    return render(request, 'error.html', context={'message': message})

有关支付宝支付的核心代码都放在alipay_util.py文件中,alipay_util.py代码如下:

# -*- coding: utf-8 -*-
import json
import threading
import urllib

from apps.orderinfo.models import create_transaction, MallGoods, PaymentTransaction, payed_action
from apps.paysettings.models import PayConfig

try:
    """
    注:windows下可能安装Crypto、alipay-sdk-python会失败,现提供解决方案如下
    先在虚拟环境中先执行一条命令
    set CL=/FI"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include\\stdint.h"
    然后再执行
    pip install alipay-sdk-python==3.3.398
    pip install pycryptodome
    """

    from alipay.aop.api.AlipayClientConfig import AlipayClientConfig  # 用于创建客户端对象所需的配置类
    from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient  # 用于创建客户端对象
    # 手机网站支付所需的类
    from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayModel, AlipayTradeWapPayRequest
    from alipay.aop.api.domain.AlipayTradeQueryModel import AlipayTradeQueryModel
    from alipay.aop.api.domain.AlipayTradeRefundModel import AlipayTradeRefundModel
    from alipay.aop.api.request.AlipayTradeQueryRequest import AlipayTradeQueryRequest
    from alipay.aop.api.request.AlipayTradeRefundRequest import AlipayTradeRefundRequest
    from alipay.aop.api.util.SignatureUtils import get_sign_content, verify_with_rsa
except:
    AlipayClientConfig = None
    DefaultAlipayClient = None
    AlipayTradeWapPayModel = None
    AlipayTradeWapPayRequest = None
    AlipayTradeQueryModel = None
    AlipayTradeRefundModel = None
    AlipayTradeQueryRequest = None
    AlipayTradeRefundRequest = None
    get_sign_content = None
    verify_with_rsa = None


class AliPayUtil():
    # 支付宝支付接口 官方文档 https://opendocs.alipay.com/apis/api_1/alipay.trade.query
    def get_client(self, params):
        # 实例化客户端
        alipay_client_config = AlipayClientConfig(params.get('debug'))  # 创建配置类, True: 沙箱环境, False: 正式环境
        alipay_client_config.app_id = params.get('appid')  # 指定appid
        # 指定自己的私钥, 用于支付宝验签
        alipay_client_config.app_private_key = params.get('app_private_key')
        # 指定支付宝的公钥, 用于支付宝验签
        alipay_client_config.alipay_public_key = params.get('alipay_public_key')
        return DefaultAlipayClient(alipay_client_config)  # 实例化客户端对象

    def get_pay_url(self, params):
        # 获取支付url
        client = self.get_client(params)  # 实例化客户端对象
        # 构造跳转链接所需的参数(此处我只填了必填的几个参数, 具体可参考开发文档的请求参数说明)
        model = AlipayTradeWapPayModel()
        model.out_trade_no = params.get('out_trade_no')  # 订单编号, 自己的后台生成(不能重复)
        model.total_amount = params.get('total_amount')  # 需要支付的价格
        model.subject = params.get('subject')  # 订单抬头信息说明
        model.product_code = "QUICK_WAP_WAY"  # 手机网站支付的固定参数
        model.passback_params = params.get('passback_params')  # 附加参数
        model.quit_url = params.get('return_url')  # 必选参数,string,用户付款中途退出返回商户网站的地址
        request = AlipayTradeWapPayRequest(biz_model=model)  # 创建请求对象

        request.return_url = params.get('return_url')  # 支付成功后的回跳地址
        request.notify_url = params.get('notify_url')  # 支付成功后的通知地址
        # 执行API调用
        alipay_url = client.page_execute(request, "GET")  # 生成跳转链接url, 也可以指定POST方式的跳转, 不过前端处理起来比GET方式稍复杂
        return alipay_url  # 返回跳转链接给前端页面

    def check_pay_sign(self, key, params, sign):  # 定义检查支付结果的函数
        if "sign_type" in params:
            sign_type = params.pop("sign_type")
        sign_content = get_sign_content(params)
        try:
            return verify_with_rsa(key, sign_content.encode('utf-8'), sign)  # 验证签名并获取结果
        except:  # 如果验证失败,返回假值。
            return False

    def get_query_response(self, params):
        # 获取交易记录查询结果
        client = self.get_client(params)  # 实例化客户端对象
        model = AlipayTradeQueryModel()
        model.out_trade_no = params.get('out_trade_no')
        model.query_options = ['trade_settle_info']
        request = AlipayTradeQueryRequest(biz_model=model)
        return json.loads(client.execute(request))

    def refund(self, params):
        # 退款
        client = self.get_client(params)  # 实例化客户端对象
        model = AlipayTradeRefundModel()
        model.trade_no = params.get('tran_trade_no')
        model.out_request_no = 'HZ01RF001'
        model.refund_amount = params.get('total_amount')
        model.refund_reason = u'正常退款'
        request = AlipayTradeRefundRequest(biz_model=model)
        response = json.loads(client.execute(request))
        return response


class AliUserInfo():
    # 获取支付宝用户信息

    def get_ali_auth_code(self, alipay_setting, redirect_uri):
        """
        获取支付宝返回的重定向的url
        :return:
        """
        if alipay_setting.is_use == 'test':
            domain = 'alipaydev.com'
        else:
            domain = 'alipay.com'
        url = 'https://openauth.{}/oauth2/publicAppAuthorize.htm?app_id={}&scope={}&redirect_uri={}'.format(
            domain,
            alipay_setting.ali_appid,
            'auth_base',
            urllib.parse.quote(redirect_uri)
        )
        return url

    def get_ali_user_id(self, alipay_setting, auth_code):
        # 获取支付宝生活号用户唯一标识
        from alipay.aop.api.AlipayClientConfig import AlipayClientConfig  # 用于创建客户端对象所需的配置类
        from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient  # 用于创建客户端对象
        from alipay.aop.api.request.AlipaySystemOauthTokenRequest import AlipaySystemOauthTokenRequest
        from alipay.aop.api.response.AlipaySystemOauthTokenResponse import AlipaySystemOauthTokenResponse

        alipay_client_config = AlipayClientConfig(
            False if alipay_setting.is_use == 'pron' else True)  # 创建配置类, True: 沙箱环境, False: 正式环境
        alipay_client_config.app_id = alipay_setting.ali_appid  # 指定appid
        alipay_client_config.app_private_key = alipay_setting.app_private_key
        alipay_client_config.alipay_public_key = alipay_setting.alipay_public_key
        client = DefaultAlipayClient(alipay_client_config)  # 实例化客户端对象
        request = AlipaySystemOauthTokenRequest()

        request.code = auth_code
        request.grant_type = "authorization_code"
        # 执行API调用,即向支付宝发送请求
        try:
            response_content = client.execute(request)
        except Exception as e:
            return {'error': 1, 'message': str(e)}

        if not response_content:
            return {'error': 1, 'message': '获取支付宝用户信息请求失败'}
        else:
            response = AlipaySystemOauthTokenResponse()
            # 解析响应结果
            response.parse_response_content(response_content)
            if response.is_success():
                # 如果业务成功,可以通过response属性获取需要的值
                auth_token = response.access_token
                user_id = response.user_id
                return {'error': 0, 'auth_token': auth_token, 'user_id': user_id}
            # 响应失败的业务处理
            else:
                # 如果业务失败,可以从错误码中可以得知错误情况,具体错误码信息可以查看接口文档
                message = response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg
                return {'error': 1, 'message': message}


class ALIPAYPaymentTransaction():
    # 支付宝统一下单支付url,查询支付宝交易记录,退款
    def alipay_page_params(self, auth_code, goods_ids, total_amount, params, payconfig, pay_params_url):
        # 获取支付宝页面支付参数
        # 获取生活号用户信息
        user_info = AliUserInfo().get_ali_user_id(payconfig, auth_code)
        if user_info['error']:
            return {'error': 1, 'message': user_info['message']}
        user_id = user_info['user_id']
        kw = {
            'amount': total_amount,
            'scan_source': '支付宝',
            'user_unionid': user_id,
            'payconfig_id': payconfig.id,
            'goods_ids': goods_ids,
        }

        transaction = create_transaction(kw)
        if transaction['error']:
            return transaction
        goods_queryset = MallGoods.objects.filter(id__in=goods_ids)

        ali_pay_params = {
            'error': 0,
            'total_amount': total_amount,  # 金额
            'partner_name': '用户',  # 客户名称
            'goods_queryset': goods_queryset,  # 商品列表
            'company_name': '在线聚合支付',  # 公司名称
            'pay_params_url': pay_params_url,  # 获取支付参数url
            'user_id': user_id,  # 用户user_id
            'params': params,  # 扫码携带的加密参数
            'transaction_id': transaction['transaction_id'],  # 交易记录id
        }
        return ali_pay_params

    def get_alipay_url(self, params):
        """
        获取支付宝支付url
        :param total_amount: 支付金额
        :param out_trade_no: 订单号
        :param attach: 附加参数,payment.acquirer中的id
        :param notify_url: 后端异步回调url
        :param return_url: 前端同步回调url
        :param payment_acquirer: 在线支付配置参数对象
        :return: 支付url
        """
        alipay_setting = params.get('payconfig')
        pay_params = {
            'debug': False if alipay_setting.is_use == 'pron' else True,  # pron表示使用正式环境
            'appid': alipay_setting.ali_appid,
            'app_private_key': alipay_setting.app_private_key,
            'alipay_public_key': alipay_setting.alipay_public_key,
            'out_trade_no': params.get('out_trade_no'),
            'total_amount': params.get('total_amount'),
            'subject': "聚合支付",
            'passback_params': params.get('attach'),
            'return_url': params.get('return_url'),
            'notify_url': params.get('notify_url'),
        }
        url = AliPayUtil().get_pay_url(pay_params)
        return url

    def alipay_update_order(self, kw):
        """
        如果支付成功,支付宝会向这个地址发送POST请求(校验是否支付已经完成)
        :param args:
        :param kw:
        {'gmt_create': '2020-10-29 16:13:02',
        'charset': 'utf-8',
        'seller_email': 'nyyxxx@sandbox.com',
        'subject': '在线聚合支付',
        'sign': 'f5CzBrY13s',
        'buyer_id': '20881xxxxx52',
        'invoice_amount': '9.00',
        'notify_id': '202010290 xxxx94250508020641',
        'fund_bill_list': '[{"amount":"9.00","fundChannel":"ALIPAYACCOUNT"}]',
        'notify_type': 'trade_status_sync',
        'trade_status': 'TRADE_SUCCESS',
        'receipt_amount': '9.00',
        'buyer_pay_amount': '9.00',
        'app_id': '201609xxxxxxx2540',
        'sign_type': 'RSA2',
        'seller_id': '20881xxxxxx411',
        'gmt_payment': '2020-10-29 16:13:02',
        'notify_time': '2020-10-29 16:13:03',
        'passback_params': '{"transaction_id": "176", "scan_type": "invoice"}',
        'version': '1.0',
        'out_trade_no': 'xxx',
        'total_amount': '9.00',
        'trade_no': 'xxx',
        'auth_app_id': 'xxx',
        'buyer_logon_id': 'irb***@sandbox.com',
        'point_amount': '0.00'}
        :return:
        """
        # 修改支付宝支付订单 payment.transaction

        if kw.get('trade_status') == 'TRADE_SUCCESS':
            # 交易成功
            alipay_sign = kw.pop('sign')
            payconfig = PayConfig.objects.filter(is_use__in=['pron', 'test']).first()

            if AliPayUtil().check_pay_sign(payconfig.alipay_public_key, kw, alipay_sign):
                # 验证参数签名 验签成功
                # 修改交易记录为已支付
                passback_params = json.loads(kw.get('passback_params'))  # 附加参数
                if passback_params and passback_params.get('transaction_id'):
                    transaction_id = passback_params['transaction_id']
                    PaymentTransaction.objects.filter(id=transaction_id).update(tran_trade_no=kw.get('trade_no'),
                                                                                state='payed')
                    th = threading.Thread(target=payed_action, args=(transaction_id,))
                    th.daemon= False # 非守护线程,不随主线程挂掉而挂掉
                    th.start()
                    return 'success'
        return 'fail'

    def query_state(self, params):
        # 查看订单状态 O:未支付  P:已支付 C:已取消 R:已退款 I:支付中  N:订单不存在  F:支付失败 T:订单超时
        # return: 字符串[O, P, C, R, I, N, F, T]
        alipay_setting = params.get('payconfig')
        query_params = {
            'debug': False if alipay_setting.is_use == 'pron' else True,  # pron表示使用正式环境
            'appid': alipay_setting.ali_appid,
            'app_private_key': alipay_setting.app_private_key,
            'alipay_public_key': alipay_setting.alipay_public_key,
            'out_trade_no': params.get('out_trade_no'),
        }
        response = AliPayUtil().get_query_response(query_params)
        if response.get('code') == '10000':
            if response.get('trade_status') == 'TRADE_SUCCESS':
                return 'P', response.get('trade_no')
            elif response.get('trade_status') == 'WAIT_BUYER_PAY':
                return 'I', None
            elif response.get('trade_status') == 'TRADE_CLOSED':
                return 'R', None
        return 'N', None

    def refund(self, params):
        # 退款
        alipay_setting = params.get('payconfig')
        total_fee = params.get('amount')
        refund_fee = params.get('refund_fee') if params.get('refund_fee') else total_fee
        refund_params = {
            'debug': False if alipay_setting.is_use == 'pron' else True,  # pron表示使用正式环境
            'appid': alipay_setting.ali_appid,
            'app_private_key': alipay_setting.app_private_key,
            'alipay_public_key': alipay_setting.alipay_public_key,
            'tran_trade_no': params.get('tran_trade_no'),
            'total_amount': refund_fee,
        }
        response = AliPayUtil().refund(refund_params)
        if response.get('code') == '10000':
            if response.get('fund_change') == 'Y':
                return {'error': 0,
                        'message': response.get('msg'),
                        'tran_trade_no': response['trade_no'],
                        'refund_order_no': response['trade_no'],
                        'refund_reason': '正常退款',
                        'refund_person': response['buyer_user_id'],
                        'refund_time': response['gmt_refund_pay'],
                        'refund_amount': response['refund_fee'],
                        'refund_way': 'alipay',
                        }
            return {'error': 1, 'message': '退款失败,已经退过款了'}
        return {'error': 1, 'message': response.get('sub_msg')}

7、界面展示

在这里插入图片描述

在这里插入图片描述

8、订单查询

用户点击确认支付付款成功后,可以在后台查看到订单变化:

在这里插入图片描述

可以通过状态查询、退款操作订单。

微信、支付宝二维码聚合SDK下载

点我下载

后记

【后记】为了让大家能够轻松学编程,我创建了一个公众号【轻松学编程】,里面有让你快速学会编程的文章,当然也有一些干货提高你的编程水平,也有一些编程项目适合做一些课程设计等课题。

也可加我微信【1257309054】,拉你进群,大家一起交流学习。
如果文章对您有帮助,请我喝杯咖啡吧!

公众号

公众号

关注我,我们一起成长~~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东木月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值