支付宝

简介

支付宝是什么不用多说了,本次教程适合初学者

前提准备

话不多说,干就完了

1.注册开发者账号,设置公钥私钥

首先进入支付宝开发者平台:传送门 ,有账号直接登录,没账号用你平时用来付款收钱的账号登录,然后用这个账号激活注册成开发者账号就行了

登录之后点开发中心:

先用沙箱测试一下,点研发服务

跳转到此页面,设置一个公钥

先点查看公钥生成:

进入开发文档:传送门  根据文档步骤下载秘钥生成工具,根据说明文档运行程序

本次教程使用的是Python,所以一定要选非java,然后点击生成秘钥

再点打开秘钥路径就可以看到已经生成了两个文件:

用这里的公钥和私钥,填入刚才那个设置页面:

如果在保存时提示什么公钥签名啥啥的,反正不成功的提示,利用秘钥生成工具重新生成一次填入即可

接着按这里的文档走就行:传送门  需要一个安卓手机下载沙箱版的支付宝作为测试

2.安装sdk

之前使用支付宝,都要去github上download大神写的sdk,现在pyi社区已经有官方的sdk了:传送门  所以,直接pip install alipay-sdk-python 安装:

但是到最后会报错:

然后看到说是因为安装支付宝的sdk时,由于需要安装这个库,而这个库需要依赖微软的visual c++才行,这就尴尬了,据查,要安装visual C++最好的办法是,下载visual Studio集成开发工具,注意不是visual studio code,两个是不同的软件,虽然都可以当开发工具,我的理解就是visual studio比visual studio code多了那些需要的运行库

visual studio官方下载链接最新版   另外据网查,可以只安装visual studio 2015版就行,visual studio 2015下载链接 ,总共有几个G,解压并安装,只安装这个c++ 2015

等好长一会儿:

然后再安装支付宝的sdk看看,还是不行

按报错提示,那个c1.exe不被认识,所以还要设置环境变量:变量名:VCINSTALLDIR,变量值就是vc的路径

设置了环境变量后关闭cmd,重新打开cmd, 先使用命令设置:set CL=/FI"%VCINSTALLDIR%\\INCLUDE\\stdint.h" 这条命令是为了让刚才我们设置的环境变量生效

然后pip 安装,终于成功了

注:如果报错:UnicodeDecodeError: 'utf-8' codec can't decode byte... 将CMD的终端编码用“CHCP 65001”命令改为“UTF-8”后再安装即可

进入支付宝支付开发

好接着进入真正的开发阶段了,本次选用【电脑页面支付】:

然后进到支付宝的api文档:传送门

选用  统一收单下单支付接口

进入的目录就不展示了,里面的参数配置就是正式的支付接口,这个就根据自己实际情况配置了,本次我们只用沙箱账号测试一下就行了,和正式的配置是一样的,因为正式的配置要认证商家才行,所以为了方便且精简的演示,就用沙箱账号了

在前面设置公钥私钥那个页面,把安卓版的沙箱支付宝下载安装好,使用这里的账号密码登录:

准备好之后,现在进入真正的开发了。

1.创建一个django项目,一个简单的支付宝充值话费的

配置文件里导入rest_framework  app,设置url,其中pay则是一会儿要用到的测试url

html文件:

view,就按照pypi社区上支付宝给的案例调整代码就行:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import traceback

from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.FileItem import FileItem
from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.domain.AlipayTradePayModel import AlipayTradePayModel
from alipay.aop.api.domain.GoodsDetail import GoodsDetail
from alipay.aop.api.domain.SettleDetailInfo import SettleDetailInfo
from alipay.aop.api.domain.SettleInfo import SettleInfo
from alipay.aop.api.domain.SubMerchant import SubMerchant
from alipay.aop.api.request.AlipayOfflineMaterialImageUploadRequest import AlipayOfflineMaterialImageUploadRequest
from alipay.aop.api.request.AlipayTradeAppPayRequest import AlipayTradeAppPayRequest
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from alipay.aop.api.request.AlipayTradePayRequest import AlipayTradePayRequest
from alipay.aop.api.response.AlipayOfflineMaterialImageUploadResponse import AlipayOfflineMaterialImageUploadResponse
from alipay.aop.api.response.AlipayTradePayResponse import AlipayTradePayResponse

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(message)s',
    filemode='a',)
logger = logging.getLogger('')

if __name__ == '__main__':
    """
    设置配置,包括支付宝网关地址、app_id、应用私钥、支付宝公钥等,其他配置值可以查看AlipayClientConfig的定义。
    """
    alipay_client_config = AlipayClientConfig()
    alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
    alipay_client_config.app_id = '[your app_id]'
    alipay_client_config.app_private_key = '[your app private key]'
    alipay_client_config.alipay_public_key = '[alipay public key]'

    """
    得到客户端对象。
    注意,一个alipay_client_config对象对应一个DefaultAlipayClient,定义DefaultAlipayClient对象后,alipay_client_config不得修改,如果想使用不同的配置,请定义不同的DefaultAlipayClient。
    logger参数用于打印日志,不传则不打印,建议传递。
    """
    client = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)

    """
    系统接口示例:alipay.trade.pay
    """
    # 对照接口文档,构造请求对象
    model = AlipayTradePayModel()
    model.auth_code = "
    model.body = "Iphone6 16G"
    goods_list = list()
    goods1 = GoodsDetail()
    goods1.goods_id = "apple-01"
    goods1.goods_name = "ipad"
    goods1.price = 10
    goods1.quantity = 1
    goods_list.append(goods1)
    model.goods_detail = goods_list
    model.operator_id = "yx_001"
    model.out_trade_no = "20180510AB014"
    model.product_code = "FACE_TO_FACE_PAYMENT"
    model.scene = "bar_code"
    model.store_id = ""
    model.subject = "huabeitest"
    model.timeout_express = "90m"
    model.total_amount = 1
    request = AlipayTradePayRequest(biz_model=model)
    # 如果有auth_token、app_auth_token等其他公共参数,放在udf_params中
    # udf_params = dict()
    # from alipay.aop.api.constant.ParamConstants import *
    # udf_params[P_APP_AUTH_TOKEN] = "xxxxxxx"
    # request.udf_params = udf_params
    # 执行请求,执行过程中如果发生异常,会抛出,请打印异常栈
    response_content = None
    try:
        response_content = client.execute(request)
    except Exception as e:
        print(traceback.format_exc())
    if not response_content:
        print("failed execute")
    else:
        response = AlipayTradePayResponse()
        # 解析响应结果
        response.parse_response_content(response_content)
        print(response.body)
        if response.is_success():
            # 如果业务成功,则通过respnse属性获取需要的值
            print("get response trade_no:" + response.trade_no)
        else:
            # 如果业务失败,则从错误码中可以得知错误情况,具体错误码信息可以查看接口文档
            print(response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg)

    """
    带文件的系统接口示例:alipay.offline.material.image.upload
    """
    # 如果没有找到对应Model类,则直接使用Request类,属性在Request类中
    request = AlipayOfflineMaterialImageUploadRequest()
    request.image_name = "我的店"
    request.image_type = "jpg"
    # 设置文件参数
    f = open("/Users/foo/Downloads/IMG.jpg", "rb")
    request.image_content = FileItem(file_name="IMG.jpg", file_content=f.read())
    f.close()
    response_content = None
    try:
        response_content = client.execute(request)
    except Exception as e:
        print(traceback.format_exc())
    if not response_content:
        print("failed execute")
    else:
        response = AlipayOfflineMaterialImageUploadResponse()
        response.parse_response_content(response_content)
        if response.is_success():
            print("get response image_url:" + response.image_url)
        else:
            print(response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg)

    """
    页面接口示例:alipay.trade.page.pay
    """
    # 对照接口文档,构造请求对象
    model = AlipayTradePagePayModel()
    model.out_trade_no = "pay201805020000226"
    model.total_amount = 50
    model.subject = "测试"
    model.body = "支付宝测试"
    model.product_code = "FAST_INSTANT_TRADE_PAY"
    settle_detail_info = SettleDetailInfo()
    settle_detail_info.amount = 50
    settle_detail_info.trans_in_type = "userId"
    settle_detail_info.trans_in = "
    settle_detail_infos = list()
    settle_detail_infos.append(settle_detail_info)
    settle_info = SettleInfo()
    settle_info.settle_detail_infos = settle_detail_infos
    model.settle_info = settle_info
    sub_merchant = SubMerchant()
    sub_merchant.merchant_id = "
    model.sub_merchant = sub_merchant
    request = AlipayTradePagePayRequest(biz_model=model)
    # 得到构造的请求,如果http_method是GET,则是一个带完成请求参数的url,如果http_method是POST,则是一段HTML表单片段
    response = client.page_execute(request, http_method="GET")
    print("alipay.trade.page.pay response:" + response)

    """
    构造唤起支付宝客户端支付时传递的请求串示例:alipay.trade.app.pay
    """
    model = AlipayTradeAppPayModel()
    model.timeout_express = "90m"
    model.total_amount = "9.00"
    model.seller_id = "
    model.product_code = "QUICK_MSECURITY_PAY"
    model.body = "Iphone6 16G"
    model.subject = "iphone"
    model.out_trade_no = "
    request = AlipayTradeAppPayRequest(biz_model=model)
    response = client.sdk_execute(request)
    print("alipay.trade.app.pay response:" + response)

pypi社区上支付宝官方案例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from rest_framework.views import APIView
from rest_framework.response import Response
from django.shortcuts import render, redirect, HttpResponse

def alipayclient():
    """
    设置配置,包括支付宝网关地址、app_id、应用私钥、支付宝公钥等,其他配置值可以查看AlipayClientConfig的定义。
    """
    alipay_client_config = AlipayClientConfig()
    alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
    alipay_client_config.app_id = '您的app_Id'
    with open("keys/private_key.txt") as f:
        alipay_client_config.app_private_key = f.read()
    # 阿里的公钥
    with open("keys/public_key.txt") as f:
        alipay_client_config.alipay_public_key = f.read()

    """
    得到客户端对象。
    注意,一个alipay_client_config对象对应一个DefaultAlipayClient,定义DefaultAlipayClient对象后,alipay_client_config不得修改,如果想使用不同的配置,请定义不同的DefaultAlipayClient。
    logger参数用于打印日志,不传则不打印,建议传递。
    """
    client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
    return client

"""
系统接口示例:alipay.trade.pay
"""

class AlipayView(APIView):
    def get(self, request):
        return render(request, 'pay.html')

    def post(self, request):
        money = request.data.get('money')
        if not money.isdigit():
            return Response('错误,只能是数字')
        money = int(money)
        client = alipayclient()

        """
        页面接口示例:alipay.trade.page.pay
        """
        # 对照接口文档,构造请求对象
        model = AlipayTradePagePayModel()
        import time
        model.out_trade_no = 'pay201805020000226'
        model.total_amount = money
        model.subject = "测试"
        model.body = "支付宝测试"
        model.product_code = "FAST_INSTANT_TRADE_PAY"
        request = AlipayTradePagePayRequest(biz_model=model)
        # 得到构造的请求,如果http_method是GET,则是一个带完成请求参数的url,如果http_method是POST,则是一段HTML表单片段
        response = client.page_execute(request, http_method="GET")
        print("alipay.trade.page.pay response:" + response)
        return redirect(response)

views

from django.contrib import admin
from django.urls import path
from app.views import AlipayView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('pay/',AlipayView.as_view()),
]

urls

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付宝电脑端支付-话费充值</title>
</head>
<body>

<form method="post">
    {% csrf_token %}
    <label for="money">金额:</label>
    <input type="text" placeholder="请输入充值金额" id="money" name="money">
    <label for="phone">号码:</label>
    <input type="text" value="183XXXXX" id="phone" name="phone">
    <input type="submit" value="立即支付宝充值">
</form>
</body>
</html>

pay.html

然后重启项目:

注意,有坑,真的有坑,照这么设置启动,点击支付的时候会跳到这个页面:

第一个坑:

反正就这两个页面,无效的appID,其实我用的那个ID就是支付宝沙箱给的ID号,所以不可能有错,但是注意了,我们用的是沙箱账号,而我们直接拷贝的支付宝给的案例,用的url是【openapi.alipay.com】,这个url是正式的url,并不是沙箱测试的url,所以沙箱测试的url是【https://openapi.alipaydev.com/gateway.do】

第二个坑: 

修改url之后,再次提交,还是说订单信息有错误,

到底是哪里的问题呢?再检查打码,还是刚才的逻辑,沙箱账号啊,那么在初始化支付宝配置时,读源码得,需要添加这个参数 【sandbox_debug = True】:

第三个坑:

按上面的修改重启,还是报错:

查看支付宝给的这个错误代码解析,交易信息被篡改,好像也没有说清楚啥问题

我试着改了下这个流水号,把它改成了用事件戳随机生成的,【model.out_trade_no = "pay" + str(time.time())】

重启项目,输入金额23

点击立即支付宝充值,这页面终于出来了

那么那个流水号有多大作用呢?我直接删除看看:

那么就得必须带上它了

但是支付宝官方sdk里给的案例也貌似没有说清楚啊,只是说看着像时间戳:

但是也没说这玩意得按时间戳来实时接收,还不能写死了,其实呢,仔细一想,这个流水号是不是得按当时付款时间来啊?难道以后每个时间段支付的都是同一个流水号?这不乱套了吗?支付宝官方没有提的原因,我猜啊,很大可能,是因为之前不是有大神写过sdk了嘛,那么那些参数或许在哪个大神的sdk里有解释了,而官方的这个sdk是之后发布的,所以可能开发者以为之前这老哥都开发过了,所以这些没必要再提了。好的,反正这里有个坑,注意就行了

接着后面的操作,打开手机,用那个沙箱支付宝app登录,扫码支付:

2.完善代码

可以支付了对吧,然后按个订单流水号就是刚才设置的那个咯,但是电脑页面还是这样,没有任何反馈啊,作为商家我们根本不知道买家那边什么情况,到底支付没有也不知道对吧,不可能一个一个的去自己账单里查账吧?如果人多呢,几个人同时支付怎么办呢?

所以,这里还得配置一下,再写一个url来接收一下数据,并返回支付结果:

url:

view:

主视图类添加两个url属性

新的url对应的识图类:

重启项目,支付查看结果:

终端打印的结果:

OK,终于完事儿了

相关代码:

from django.contrib import admin
from django.urls import path
from app.views import AlipayView
from app.views import PayHandlerView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('pay/',AlipayView.as_view()),
    path('alipay_handler',PayHandlerView.as_view())
]

url

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from rest_framework.views import APIView
from rest_framework.response import Response
from django.shortcuts import render, redirect, HttpResponse

def alipayclient():
    """
    设置配置,包括支付宝网关地址、app_id、应用私钥、支付宝公钥等,其他配置值可以查看AlipayClientConfig的定义。
    """
    alipay_client_config = AlipayClientConfig(sandbox_debug=True)
    alipay_client_config.server_url = 'https://openapi.alipaydev.com/gateway.do'
    alipay_client_config.app_id = '您的app_id'
    with open("keys/private_key.txt") as f:
        alipay_client_config.app_private_key = f.read()
    # 阿里的公钥
    with open("keys/public_key.txt") as f:
        alipay_client_config.alipay_public_key = f.read()

    """
    得到客户端对象。
    注意,一个alipay_client_config对象对应一个DefaultAlipayClient,定义DefaultAlipayClient对象后,alipay_client_config不得修改,如果想使用不同的配置,请定义不同的DefaultAlipayClient。
    logger参数用于打印日志,不传则不打印,建议传递。
    """
    client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
    return client

"""
系统接口示例:alipay.trade.pay
"""

class AlipayView(APIView):
    def get(self, request):
        return render(request, 'pay.html')

    def post(self, request):
        money = request.data.get('money')
        if not money.isdigit():
            return Response('错误,只能是数字')
        money = int(money)
        client = alipayclient()

        """
        页面接口示例:alipay.trade.page.pay
        """
        # 对照接口文档,构造请求对象
        model = AlipayTradePagePayModel()
        import time
        model.out_trade_no = 'pay' + str(time.time())
        model.total_amount = money
        model.subject = "测试"
        model.body = "支付宝测试"
        model.product_code = "FAST_INSTANT_TRADE_PAY"
        request = AlipayTradePagePayRequest(biz_model=model)
        # 得到构造的请求,如果http_method是GET,则是一个带完成请求参数的url,如果http_method是POST,则是一段HTML表单片段
        # get请求 用户支付成功后返回的页面请求地址,这个可以是公网地址
        request.return_url = "http://127.0.0.1:8002/alipay_handler"
        # post请求 用户支付成功通知商户的请求地址
        request.notify_url = "http://127.0.0.1:8002/alipay_handler"
        response = client.page_execute(request, http_method="GET")
        print("alipay.trade.page.pay response:" + response)
        return redirect(response)

class PayHandlerView(APIView):
    def get(self, request):
        # return_url的回调地址
        print(request.data)
        # 用户支付成功之后回到哪
        return HttpResponse("用户支付成功")

    def post(self, request):
        print(request.data)
        return HttpResponse("notify_url")

views

html文件没变,同上

总结:

  • 就是安装sdk的时候很烦人,如果你觉得不习惯,可以用github上那些大神写的sdk。我个人觉得,官方的只是安装有点繁琐,后面的配置是挺简单的
  • 在使用沙箱测试支付的时候的时候注意那三个坑就行了,因为沙箱测试和正式的支付还是与差距的

前后端分离djangorestframework—— 接入支付宝支付平台的更多相关文章

  1. 前后端分离djangorestframework—— 接入第三方的验证码平台

    关于验证码部分,在我这篇文章里说的挺详细的了:Python高级应用(3)—— 为你的项目添加验证码 这里还是再给一个前后端分离的实例,因为极验官网给的是用session作为验证的,而我们做前后端分离的 ...

  2. 前后端分离djangorestframework—— 接入微信模板消息推送

    微信 什么是微信也不多说,跟前面的支付宝一样的 微信支付 微信支付也有个沙箱环境,沙箱环境官方文档 由文档中那句很显眼的话所得,即使是测试环境也需要真实的商户号,所以这个就没法想支付宝那样用沙箱账号来 ...

  3. 前后端分离djangorestframework—— 在线视频平台接入第三方加密防盗录视频

    加密视频 在以后的开发项目中,很可能有做在线视频的,而在线视频就有个问题,因为在线播放,就很有可能视频数据被抓包,如果这个在线视频平台有付费视频的话,这样就会有人做点倒卖视频的生意了,针对这个问题,目 ...

  4. 前后端分离djangorestframework——分页组件

    Pagination 为什么要分页也不用多说了,大家都懂,DRF也自带了分页组件 这次用  前后端分离djangorestframework——序列化与反序列化数据  文章里用到的数据,数据库用的my ...

  5. 前后端分离djangorestframework——路由组件

    在文章前后端分离djangorestframework——视图组件 中,见识了DRF的视图组件强大,其实里面那个url也是可以自动生成的,就是这么屌 DefaultRouter urls文件作如下调整 ...

  6. 前后端分离djangorestframework——视图组件

    CBV与FBV CBV之前说过就是在view.py里写视图类,在序列化时用过,FBV就是常用的视图函数,两者的功能都可以实现功能,但是在restful规范方面的话,CBV更方便,FBV还要用reque ...

  7. [转] 前后端分离开发模式的 mock 平台预研

    引入 mock(模拟): 是在项目测试中,对项目外部或不容易获取的对象/接口,用一个虚拟的对象/接口来模拟,以便测试. 背景 前后端分离 前后端仅仅通过异步接口(AJAX/JSONP)来编程 前后端都 ...

  8. 前后端分离djangorestframework——序列化与反序列化数据

    我们写好后端的代码,要把数据交给前端的展示的,这个数据以什么类型给前端呢?学到这里,我们已经知道这个数据最好是json字符串才行,因为网络间的传输,只认字符串或者二进制,字符串就是我们的数据,二进制就 ...

  9. python drf+xadmin+react+dva+react-native+sentry+nginx 搭建前后端分离的博客完整平台

    前言: 经过差不多半年的开发,搭建从前端到服务器,实现了前后端分离的一个集PC端.移动端的多端应用,实属不易,今天得空,好好写篇文章,记录这些天的成果.同时也做个分享. 演示网站地址: http:// ...

随机推荐

  1. PHP实现用户注册并保存数据到文件

    首先我们实现功能时,分析实现的步骤是什么,就这个而言,我们应该接收用户提交的数据并进行校验,然后保存在文件,最后给用户反馈. 这里需要注意的是为了避免嵌套过深,这里使用自定义函数来实现,其代码如下: ...

  2. 知识扩展--if...else...与switch...case...的执行原理

    一.简述 编程语言中的条件分支结构有两种:if-else和switch-case,这两种条件分支之间可以相互转换,但是也存在一些区别,那么什么时候该用if-else,什么时候该用switch-case ...

  3. Spring Data Jpa接口简介

    Repository接口 public interface Repository<T, ID> {....} 提供了按方法名称的查询方式: 提供了@Query的查询方式 可能遇到的错误: ...

  4. Python内置函数(17)——divmod

    英文文档: divmod(a, b) Take two (non complex) numbers as arguments and return a pair of numbers consisti ...

  5. Python内置函数(49)——pow

    英文文档: pow(x, y[, z]) Return x to the power y; if z is present, return x to the power y, modulo z (co ...

  6. 边缘计算 VS 云计算,谁才是未来?

    计算是互联网中一个永恒的话题,设备的所有运行都可以看成是 0 和 1 的运算.在计算中近些年有两个越来越响亮的技术:云计算和边缘计算.现如今是云计算方兴未艾,边缘计算已经有了燎原之势,本文将对这两种技 ...

  7. vue踩坑--TypeError: __WEBPACK_IMPORTED_MODULE_1_vuex__.a.store is not a constructor

    今天在使用vuex的时候遇到这么个问题,虽然后来解决了,是首字母大写的原因,但我还是不知道为什么.这里先记录下来. 这是vuex/store.js import Vue from 'vue' impo ...

  8. Chapter 5 Blood Type——24

    "She's just a little faint," he reassured the startled nurse. "They're blood typing i ...

  9. Asp.Net SignalR 集线器不使用代理的实现

    不使用生成代理JS的实现 可能有同学会觉得使用集线器很麻烦,要么引入虚拟目录,要么在生成期间生成js文件,再引入js文件进行开发.难道就没有比较清爽的方式吗?这个当然是有的,先不要(。・∀・)ノ゙嗨皮 ...

  10. Java基础之 数组详解

    前言:Java内功心法之数组详解,看完这篇你向Java大神的路上又迈出了一步(有什么问题或者需要资料可以联系我的扣扣:734999078) 数组概念 同一种类型数据的集合.其实数组就是一个容器. 数组 ...