支付宝app支付功能-服务端的实现-python3版

支付宝app支付功能-服务端的实现-python3版

  • 一:需求说明
  • 二:支付宝app支付处理流程
  • 三:所需依赖
    • 3.1 依赖库
    • 3.2 支付配置
      • 3.2.1 沙箱环境配置
      • 3.2.2 正式环境配置
  • 四:接口开发
    • 4.1 创建订单接口
    • 4.2 支付宝异步回调接口
    • 4.3 订单状态查询

一:需求说明

在自有的android app中加入支付宝支付功能,app内点击支付按钮,需app调起本机上的支付宝app进行支付。

二:支付宝app支付处理流程

支付宝app支付功能-服务端的实现-python3版_第1张图片
上图为支付宝官方支付流程图,由图可见,商户服务端需要提供3个接口,其中2个接口供商户客户端调用,另一个接口供支付宝服务端回调。

注:开发文档路径:访问蚂蚁金服开发平台文档中心https://open.alipay.com
上栏菜单【文档中心】-下拉菜单【开发文档】-应用接入-【网页&APP】-左侧菜单展开【全部文档】-【app支付】

三:所需依赖

3.1 依赖库

支付宝提供了官方sdk,通过以下命令安装【注意:sdk中的demo比较简单,缺少一些常用接口的使用说明。如果懒于研究此SDK使用方式,可以非官方的sdk代替】

pip install aliyun-sdk-python

3.2 支付配置

生成app私钥及公钥,可直接使用官方提供的密钥生成工具:https://docs.open.alipay.com/291/106097/
支付宝app支付功能-服务端的实现-python3版_第2张图片
将私钥保存到本地文件alipay_app_private_key.txt (沙箱:alipay_app_private_key_test.txt) 中。

注意:为安全起见,沙箱环境应用私钥跟正式环境应用私钥不要一致。

3.2.1 沙箱环境配置

在蚂蚁开放平台https://openhome.alipay.com,进入开发中心】-【研发服务】-【沙箱应用】页面
app公钥
进入【RSA2(SHA256)密钥(推荐)】-【查看应用公钥】-修改,将商户应用公钥复制并粘贴至此,保存。
支付宝公钥
进入【RSA2(SHA256)密钥(推荐)】-【查看支付宝公钥】,复制并保存到本地文件alipay_alipay_public_key_test.txt中。
APPID获取
复制本页面的APPID,保存(供程序接口使用)。
PID(商户id)获取
复制本页面的商户UID,保存(供程序接口使用)。
支付宝网关
复制本页面的支付宝网关,保存(供程序接口使用)。
沙箱钱包
下载沙箱钱包,安装至测试机(供程序调测使用)。

3.2.2 正式环境配置

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的【查看详情】-【应用信息】-【支付宝网关】
复制本页面的支付宝网关,保存(供程序接口使用)。

四:接口开发

4.1 创建订单接口

创建订单,对订单数据进行签名,让签名后的订单数据及签名返回给商户客户端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)

4.2 支付宝异步回调接口

支付宝将支付信息同步给商户客户端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

4.3 订单状态查询

商户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)

你可能感兴趣的:(python)