python支付宝支付

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

​点我下载​

1、沙箱环境配置

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

python支付宝二维码支付源代码_微信

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

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

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

2、配置密钥、公钥。

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

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

可以参考官方说明

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、支付流程说明

python支付宝二维码支付源代码_客户端_03

具体可以参考​​支付宝官方文档说明​​,有当面付、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、界面展示

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

python支付宝二维码支付源代码_客户端_05

8、订单查询

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

python支付宝二维码支付源代码_微信_06

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

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

​点我下载​

后记

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

如果文章对您有帮助,请我喝杯咖啡吧!

公众号

python支付宝二维码支付源代码_微信_07


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