在自有的android app中加入支付宝支付功能,app内点击支付按钮,需app调起本机上的支付宝app进行支付。
上图为支付宝官方支付流程图,由图可见,商户服务端需要提供3个接口,其中2个接口供商户客户端调用,另一个接口供支付宝服务端回调。
注:开发文档路径:访问蚂蚁金服开发平台文档中心https://open.alipay.com
上栏菜单【文档中心】-下拉菜单【开发文档】-应用接入-【网页&APP】-左侧菜单展开【全部文档】-【app支付】
支付宝提供了官方sdk,通过以下命令安装【注意:sdk中的demo比较简单,缺少一些常用接口的使用说明。如果懒于研究此SDK使用方式,可以非官方的sdk代替】
pip install aliyun-sdk-python
生成app私钥及公钥,可直接使用官方提供的密钥生成工具:https://docs.open.alipay.com/291/106097/
将私钥保存到本地文件alipay_app_private_key.txt (沙箱:alipay_app_private_key_test.txt) 中。
注意:为安全起见,沙箱环境应用私钥跟正式环境应用私钥不要一致。
在蚂蚁开放平台https://openhome.alipay.com,进入开发中心】-【研发服务】-【沙箱应用】页面
app公钥
进入【RSA2(SHA256)密钥(推荐)】-【查看应用公钥】-修改,将商户应用公钥复制并粘贴至此,保存。
支付宝公钥
进入【RSA2(SHA256)密钥(推荐)】-【查看支付宝公钥】,复制并保存到本地文件alipay_alipay_public_key_test.txt中。
APPID获取
复制本页面的APPID,保存(供程序接口使用)。
PID(商户id)获取
复制本页面的商户UID,保存(供程序接口使用)。
支付宝网关
复制本页面的支付宝网关,保存(供程序接口使用)。
沙箱钱包
下载沙箱钱包,安装至测试机(供程序调测使用)。
app公钥
在蚂蚁开放平台https://openhome.alipay.com,【开发中心】-【网页&移动应用】
点击指定APP的【查看详情】-【应用信息】-【查看应用公钥】-修改,将商户应用公钥复制并粘贴至此,保存。
支付宝公钥
在蚂蚁开放平台https://openhome.alipay.com,【开发中心】-【网页&移动应用】
点击指定APP的【查看详情】-【应用信息】-【查看支付宝公钥】,复制并保存到本地文件alipay_alipay_public_key.txt中。
APPID获取
在蚂蚁开放平台https://openhome.alipay.com,【开发中心】-【网页&移动应用】
复制本页面的指定APP的APPID,保存(供程序接口使用)。
如果没有创建app应用,需要在此页面创建app应用,并签约app支付功能
PID(商户id)获取
在蚂蚁开放平台https://openhome.alipay.com/platform/accountSetting.htm,【账户中心】-【账户管理】-【合作伙伴管理】
复制本页面的商户UID,保存(供程序接口使用)。
支付宝网关
在蚂蚁开放平台https://openhome.alipay.com,【开发中心】-【网页&移动应用】
点击指定APP的【查看详情】-【应用信息】-【支付宝网关】
复制本页面的支付宝网关,保存(供程序接口使用)。
创建订单,对订单数据进行签名,让签名后的订单数据及签名返回给商户客户端APP,商户客户端APP拿到数据后,调起支付宝支付。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import traceback
import json
import pymysql
import logging
import datetime
import random
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.request.AlipayTradeAppPayRequest import AlipayTradeAppPayRequest
from alipay.aop.api.constant.ParamConstants import *
from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel
MYSQL = dict(
host='127.0.0.1', user='mysql_user', passwd='mysql_pwd', db='mydb', charset="utf8mb4"
)
logger = logging.getLogger(__name__)
conn = pymysql.connect(**MYSQL)
cur_dict = conn.cursor(pymysql.cursors.DictCursor)
cur = conn.cursor()
###############################################
############# 支付宝支付配置 #################
###############################################
# 正式
if True:
ALIPAY_CALLBACK_API = 'http://xxxx.com/api/alipay_rollback/'
# 支付宝 rsa密钥文件路径
ALIPAY_PRIVATE_KEY_PATH = '/vel/config/alipay_app_private_key.txt'
ALIPAY_PUBLIC_KEY_PATH = '/vel/config/alipay_alipay_public_key.txt'
ALIPAY_APPID = '2019051464471111'
ALIPAY_PID = '2088431013232222'
# 支付宝网关
ALIPAY_GATEWAY = 'https://openapi.alipay.com/gateway.do'
# 沙箱
else:
ALIPAY_CALLBACK_API = 'http://xx.xx.xx.xx/api/alipay_rollback/'
# 支付宝 rsa密钥文件路径
ALIPAY_PRIVATE_KEY_PATH = '/vel/config/alipay_app_private_key_test.txt'
ALIPAY_PUBLIC_KEY_PATH = '/vel/config/alipay_alipay_public_key_test.txt'
ALIPAY_APPID = '2016092900622323'
ALIPAY_PID = '2088102177822323'
# 支付宝网关
ALIPAY_GATEWAY = 'https://openapi.alipaydev.com/gateway.do'
def create_order_number():
"""
生成订单号
:return:
"""
date = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
# 生成4为随机数作为订单号的一部分
random_str = str(random.randint(1, 9999))
random_str = random_str.rjust(4, '0')
rtn = '%s%s' % (date, random_str)
return rtn
def alipay(app_pay_model, app_id, app_private_key_path, alipay_public_key_path, notify_url):
"""
设置配置,包括支付宝网关地址、app_id、应用私钥、支付宝公钥等,其他配置值可以查看AlipayClientConfig的定义。
"""
alipay_client_config = AlipayClientConfig()
alipay_client_config.server_url = ALIPAY_GATEWAY
alipay_client_config.app_id = app_id
alipay_client_config.app_private_key = open(app_private_key_path).read()
alipay_client_config.alipay_public_key = open(alipay_public_key_path).read()
alipay_client_config.sign_type = 'RSA2'
"""
得到客户端对象。
注意,一个alipay_client_config对象对应一个DefaultAlipayClient,定义DefaultAlipayClient对象后,alipay_client_config不得修改,如果想使用不同的配置,请定义不同的DefaultAlipayClient。
logger参数用于打印日志,不传则不打印,建议传递。
"""
client = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)
request = AlipayTradeAppPayRequest(biz_model=app_pay_model)
udf_params = dict()
# 设置回调url
udf_params[P_NOTIFY_URL] = notify_url
request.udf_params = udf_params
response = client.sdk_execute(request)
return response
def create_order(app_pay_model, app_id):
"""
创建订单信息,存入库中
:return:
"""
insert_sql = ''' insert into orders(app_id, seller_id, out_trade_no, body, total_amount, subject)
values ('{app_id}', '{seller_id}', '{out_trade_no}', '{body}', '{total_amount}', '{subject}')'''
insert_sql = insert_sql.format(
app_id=app_id,
seller_id=app_pay_model.seller_id,
out_trade_no=app_pay_model.out_trade_no,
body=app_pay_model.body,
subject=app_pay_model.subject,
total_amount=app_pay_model.total_amount
)
rtn = cur_dict.execute(insert_sql)
conn.commit()
return rtn
def alipay_create_order(request):
"""
【API】:创建订单,供商户客户端app调用
"""
res = {
'code': 1,
'msg': 'success'
}
try:
"""
构造唤起支付宝客户端支付时传递的请求串示例:alipay.trade.app.pay
"""
model = AlipayTradeAppPayModel()
model.timeout_express = "90m"
model.total_amount = '0.99'
model.seller_id = ALIPAY_PID
model.product_code = "QUICK_MSECURITY_PAY"
# 商品信息,支付宝回调时回传给商户服务器
body = {
'xid': 1,
'day': 3,
'pay_type': 1,
'name': '一支笔'
}
model.body = json.dumps(body)
model.subject = '一支笔'
# 商户订单号
model.out_trade_no = create_order_number()
if create_order(model, ALIPAY_APPID):
order_info = alipay(model, ALIPAY_APPID, ALIPAY_PRIVATE_KEY_PATH, ALIPAY_PUBLIC_KEY_PATH, ALIPAY_CALLBACK_API)
res['order_info'] = order_info
res['out_trade_no'] = model.out_trade_no
else:
res['code'] = -1
res['msg'] = '创建订单失败!'
except Exception:
traceback.print_exc()
finally:
return json.dumps(res)
支付宝将支付信息同步给商户客户端app后,异步回调通知商户服务端。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import traceback
import pymysql
from alipay.aop.api.util import SignatureUtils
MYSQL = dict(
host='127.0.0.1', user='mysql_user', passwd='mysql_pwd', db='mydb', charset="utf8mb4"
)
conn = pymysql.connect(**MYSQL)
cur_dict = conn.cursor(pymysql.cursors.DictCursor)
cur = conn.cursor()
###############################################
############# 支付宝支付配置 #################
###############################################
# 正式
if True:
ALIPAY_CALLBACK_API = 'http://xxxx.com/api/alipay_rollback/'
# 支付宝 rsa密钥文件路径
ALIPAY_PRIVATE_KEY_PATH = './alipay_app_private_key.txt'
ALIPAY_PUBLIC_KEY_PATH = './alipay_alipay_public_key.txt'
ALIPAY_APPID = '2019051464471111'
ALIPAY_PID = '2088431013232222'
# 支付宝网关
ALIPAY_GATEWAY = 'https://openapi.alipay.com/gateway.do'
# 沙箱
else:
ALIPAY_CALLBACK_API = 'http://xx.xx.xx.xx/api/alipay_rollback/'
# 支付宝 rsa密钥文件路径
ALIPAY_PRIVATE_KEY_PATH = './alipay_app_private_key_test.txt'
ALIPAY_PUBLIC_KEY_PATH = './alipay_alipay_public_key_test.txt'
ALIPAY_APPID = '2016092900622323'
ALIPAY_PID = '2088102177822323'
# 支付宝网关
ALIPAY_GATEWAY = 'https://openapi.alipaydev.com/gateway.do'
def sorted_params(params):
"""
参数按ascii码排序
:param params:
:return: a=1&b=2&c=3
"""
if not len(params) > 0:
return False
key_sorted = sorted(params.keys())
content = ''
for key in key_sorted:
if key not in ["sign", "sign_type"]:
if len(params[key]) > 0:
content = content + key + "=" + params[key] + "&"
content = content[:-1]
content = content.encode("utf-8")
return content
def alipay_rollback(request):
"""
【API】:支付宝支付结果回调接口,供支付宝服务端调用
"""
try:
res = "success"
gmt_create = request.POST.get('gmt_create')
seller_email = request.POST.get('seller_email')
subject = request.POST.get('subject')
body = request.POST.get('body')
buyer_id = request.POST.get('buyer_id')
invoice_amount = request.POST.get('invoice_amount')
notify_id = request.POST.get('notify_id')
fund_bill_list = request.POST.get('fund_bill_list')
notify_type = request.POST.get('notify_type')
trade_status = request.POST.get('trade_status')
receipt_amount = request.POST.get('receipt_amount')
app_id = request.POST.get('app_id')
buyer_pay_amount = request.POST.get('buyer_pay_amount')
seller_id = request.POST.get('seller_id')
gmt_payment = request.POST.get('gmt_payment')
notify_time = request.POST.get('notify_time')
version = request.POST.get('version')
out_trade_no = request.POST.get('out_trade_no')
total_amount= request.POST.get('total_amount')
trade_no = request.POST.get('trade_no')
auth_app_id = request.POST.get('auth_app_id')
buyer_logon_id = request.POST.get('buyer_logon_id')
point_amount = request.POST.get('point_amount')
charset = request.POST.get('charset')
# 交易状态:
# WAIT_BUYER_PAY(交易创建,等待买家付款)、
# TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、
# TRADE_SUCCESS(交易支付成功)、
# TRADE_FINISHED(交易结束,不可退款)
if trade_status == 'TRADE_SUCCESS':
status = 1
elif trade_status == 'WAIT_BUYER_PAY':
status = 2 # 等待付款
else:
status = 0 # 交易失败
sign = request.POST.get('sign')
sign_type = request.POST.get('sign_type')
message = {
'gmt_create': gmt_create,
'seller_email': seller_email,
'subject': subject,
'body': body,
'buyer_id': buyer_id,
'invoice_amount': invoice_amount,
'notify_id': notify_id,
'notify_type': notify_type,
'notify_time': notify_time,
'fund_bill_list': fund_bill_list,
'out_trade_no': out_trade_no,
'trade_status': trade_status,
'trade_no': trade_no,
'seller_id': seller_id,
'receipt_amount': receipt_amount,
'app_id': app_id,
'buyer_pay_amount': buyer_pay_amount,
'gmt_payment': gmt_payment,
'version': version,
'total_amount': total_amount,
'auth_app_id': auth_app_id,
'buyer_logon_id': buyer_logon_id,
'point_amount': point_amount,
'charset': charset,
'sign_type': sign_type,
'sign': sign
}
data = sorted_params(message)
# 异步回调验签
verifi = SignatureUtils.verify_with_rsa(open(ALIPAY_PUBLIC_KEY_PATH).read(), data, sign)
if verifi:
update_sql = ''' update orders set gmt_create='{gmt_create}', seller_email='{seller_email}',
subject='{subject}', body='{body}', buyer_id='{buyer_id}', invoice_amount='{invoice_amount}',
notify_id='{notify_id}', notify_type='{notify_type}', notify_time='{notify_time}',
fund_bill_list='{fund_bill_list}', out_trade_no='{out_trade_no}', trade_status='{trade_status}',
trade_no='{trade_no}', seller_id='{seller_id}', receipt_amount='{receipt_amount}', app_id='{app_id}',
buyer_pay_amount='{buyer_pay_amount}', gmt_payment='{gmt_payment}', version='{version}',
total_amount='{total_amount}', auth_app_id='{auth_app_id}', buyer_logon_id='{buyer_logon_id}',
point_amount='{point_amount}', charset='{charset}', status='{status}'
where out_trade_no='{out_trade_no}'
'''
update_sql = update_sql.format(
gmt_create=gmt_create,
seller_email=seller_email,
subject=subject,
body=body,
buyer_id=buyer_id,
invoice_amount=invoice_amount,
notify_id=notify_id,
notify_type=notify_type,
notify_time=notify_time,
fund_bill_list=fund_bill_list,
out_trade_no=out_trade_no,
trade_status=trade_status,
trade_no=trade_no,
seller_id=seller_id,
receipt_amount=receipt_amount,
app_id=app_id,
buyer_pay_amount=buyer_pay_amount,
gmt_payment=gmt_payment,
version=version,
total_amount=total_amount,
auth_app_id=auth_app_id,
buyer_logon_id=buyer_logon_id,
point_amount=point_amount,
charset=charset,
status=status
)
cur_dict.execute(update_sql)
conn.commit()
else:
res = "error: verify failed! "
except Exception:
traceback.print_exc()
finally:
return res
商户app调用此接口查询【支付宝服务端】某个订单的支付状态,并同步更新商户服务端订单状态。
可根据需要,将此步骤做成异步任务,实时更新商户服务端订单状态。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import traceback
import json
import pymysql
import logging
from alipay.aop.api.constant.ParamConstants import *
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.request.AlipayTradeQueryRequest import AlipayTradeQueryRequest
from alipay.aop.api.constant.ParamConstants import *
from alipay.aop.api.domain.AlipayTradeQueryModel import AlipayTradeQueryModel
MYSQL = dict(
host='127.0.0.1', user='mysql_user', passwd='mysql_pwd', db='mydb', charset="utf8mb4"
)
logger = logging.getLogger(__name__)
conn = pymysql.connect(**MYSQL)
cur_dict = conn.cursor(pymysql.cursors.DictCursor)
cur = conn.cursor()
###############################################
############# 支付宝支付配置 #################
###############################################
# 正式
if True:
ALIPAY_CALLBACK_API = 'http://xxxx.com/api/alipay_rollback/'
# 支付宝 rsa密钥文件路径
ALIPAY_PRIVATE_KEY_PATH = '/vel/config/alipay_app_private_key.txt'
ALIPAY_PUBLIC_KEY_PATH = '/vel/config/alipay_alipay_public_key.txt'
ALIPAY_APPID = '2019051464471111'
ALIPAY_PID = '2088431013232222'
# 支付宝网关
ALIPAY_GATEWAY = 'https://openapi.alipay.com/gateway.do'
# 沙箱
else:
ALIPAY_CALLBACK_API = 'http://xx.xx.xx.xx/api/alipay_rollback/'
# 支付宝 rsa密钥文件路径
ALIPAY_PRIVATE_KEY_PATH = '/vel/config/alipay_app_private_key_test.txt'
ALIPAY_PUBLIC_KEY_PATH = '/vel/config/alipay_alipay_public_key_test.txt'
ALIPAY_APPID = '2016092900622323'
ALIPAY_PID = '2088102177822323'
# 支付宝网关
ALIPAY_GATEWAY = 'https://openapi.alipaydev.com/gateway.do'
def alipay_order_query(app_id, app_private_key_path, alipay_public_key_path, order_info):
"""
支付宝支付订单查询
order_info = {'out_trade_no': '20150320010101001', 'trade_no': '2014112611001004680', 'org_pid': '2088101117952222'}
:return:
"""
alipay_client_config = AlipayClientConfig()
alipay_client_config.server_url = ALIPAY_GATEWAY
alipay_client_config.app_id = app_id
alipay_client_config.app_private_key = open(app_private_key_path).read()
alipay_client_config.alipay_public_key = open(alipay_public_key_path).read()
alipay_client_config.sign_type = 'RSA2'
client = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)
request = AlipayTradeQueryRequest()
model = AlipayTradeQueryModel()
model.org_pid = order_info['org_pid']
model.out_trade_no = order_info['out_trade_no']
model.trade_no = order_info['trade_no']
request.biz_content = model
response = client.execute(request)
return response
def alipay_orderquery(request):
"""
【API】:支付状态查询,根据商户订单号查询,供商户客户端app调用
"""
res = {
'code': 1,
'status': 0,
'msg': '支付失败!未知错误!'
}
# 商户订单号
out_trade_no = request.POST.get('out_trade_no')
try:
select_sql = '''select id, app_id, trade_no, seller_id, status from orders
where out_trade_no={out_trade_no} '''
select_sql = select_sql.format(out_trade_no=out_trade_no)
cur_dict.execute(select_sql)
order_data = cur_dict.fetchone()
if order_data:
id = order_data['id']
# 支付成功
if order_data['status'] == 1:
res['status'] = 1
res['msg'] = '支付成功!'
# 支付失败
elif order_data['status'] == 0:
res['status'] = 0
res['msg'] = '支付失败!'
# 支付过程中, 查询支付宝服务器支付状态
else:
params_dict = {
'out_trade_no': out_trade_no,
'trade_no': order_data['trade_no'],
'org_pid': ALIPAY_PID
}
request = alipay_order_query(ALIPAY_APPID, ALIPAY_PRIVATE_KEY_PATH, ALIPAY_PUBLIC_KEY_PATH, params_dict)
request = json.loads(request)
if request['msg'] == 'Success':
# request['buyer_user_type']
buyer_logon_id = request['buyer_logon_id']
buyer_pay_amount = request['buyer_pay_amount']
seller_id = request['buyer_user_id']
invoice_amount = request['invoice_amount']
out_trade_no = request['out_trade_no']
point_amount = request['point_amount']
receipt_amount = request['receipt_amount']
total_amount = request['total_amount']
trade_no = request['trade_no']
trade_status = request['trade_status']
gmt_create = request['send_pay_date']
# 交易状态:
# WAIT_BUYER_PAY(交易创建,等待买家付款)、
# TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、
# TRADE_SUCCESS(交易支付成功)、
# TRADE_FINISHED(交易结束,不可退款)
if trade_status == 'TRADE_SUCCESS':
status = 1
res['msg'] = "支付成功! "
elif trade_status == 'WAIT_BUYER_PAY':
status = 2 # 等待付款
res['msg'] = "交易创建,等待买家付款! "
else:
status = 0 # 交易失败
res['msg'] = '交易失败!'
# 更新本服务器的订单状态
update_sql = ''' update orders set gmt_create='{gmt_create}', invoice_amount='{invoice_amount}',
out_trade_no='{out_trade_no}', trade_status='{trade_status}',
trade_no='{trade_no}', seller_id='{seller_id}', receipt_amount='{receipt_amount}',
buyer_pay_amount='{buyer_pay_amount}',
total_amount='{total_amount}', buyer_logon_id='{buyer_logon_id}',
point_amount='{point_amount}', status='{status}'
where out_trade_no='{out_trade_no}'
'''
update_sql = update_sql.format(
gmt_create=gmt_create,
invoice_amount=invoice_amount,
out_trade_no=out_trade_no,
trade_status=trade_status,
trade_no=trade_no,
seller_id=seller_id,
receipt_amount=receipt_amount,
buyer_pay_amount=buyer_pay_amount,
total_amount=total_amount,
buyer_logon_id=buyer_logon_id,
point_amount=point_amount,
status=status
)
logger.info(update_sql)
cur_dict.execute(update_sql)
conn.commit()
else:
res['msg'] = '查询请求失败!'
else:
res['status'] = 0
res['msg'] = "订单号不存在!"
except Exception:
traceback.print_exc()
finally:
return json.dumps(res)