前后端分离djangorestframework—— 接入支付宝支付平台
支付寶
簡(jiǎn)介
支付寶是什么不用多說了,本次教程適合初學(xué)者
前提準(zhǔn)備
話不多說,干就完了
1.注冊(cè)開發(fā)者賬號(hào),設(shè)置公鑰私鑰
首先進(jìn)入支付寶開發(fā)者平臺(tái):傳送門,有賬號(hào)直接登錄,沒賬號(hào)用你平時(shí)用來付款收錢的賬號(hào)登錄,然后用這個(gè)賬號(hào)激活注冊(cè)成開發(fā)者賬號(hào)就行了
登錄之后點(diǎn)開發(fā)中心:
先用沙箱測(cè)試一下,點(diǎn)研發(fā)服務(wù)
跳轉(zhuǎn)到此頁面,設(shè)置一個(gè)公鑰
先點(diǎn)查看公鑰生成:
進(jìn)入開發(fā)文檔:傳送門 根據(jù)文檔步驟下載秘鑰生成工具,根據(jù)說明文檔運(yùn)行程序
本次教程使用的是Python,所以一定要選非java,然后點(diǎn)擊生成秘鑰
再點(diǎn)打開秘鑰路徑就可以看到已經(jīng)生成了兩個(gè)文件:
用這里的公鑰和私鑰,填入剛才那個(gè)設(shè)置頁面:
如果在保存時(shí)提示什么公鑰簽名啥啥的,反正不成功的提示,利用秘鑰生成工具重新生成一次填入即可
接著按這里的文檔走就行:傳送門 需要一個(gè)安卓手機(jī)下載沙箱版的支付寶作為測(cè)試
2.安裝sdk
之前使用支付寶,都要去github上download大神寫的sdk,現(xiàn)在pyi社區(qū)已經(jīng)有官方的sdk了:傳送門 所以,直接pip install alipay-sdk-python 安裝:
但是到最后會(huì)報(bào)錯(cuò):
然后看到說是因?yàn)榘惭b支付寶的sdk時(shí),由于需要安裝這個(gè)庫(kù),而這個(gè)庫(kù)需要依賴微軟的visual c++才行,這就尷尬了,據(jù)查,要安裝visual C++最好的辦法是,下載visual Studio集成開發(fā)工具,注意不是visual studio code,兩個(gè)是不同的軟件,雖然都可以當(dāng)開發(fā)工具,我的理解就是visual studio比visual studio code多了那些需要的運(yùn)行庫(kù)
visual studio官方下載鏈接最新版 另外據(jù)網(wǎng)查,可以只安裝visual studio 2015版就行,visual studio 2015下載鏈接,總共有幾個(gè)G,解壓并安裝,只安裝這個(gè)c++ 2015
等好長(zhǎng)一會(huì)兒:
然后再安裝支付寶的sdk看看,還是不行
按報(bào)錯(cuò)提示,那個(gè)c1.exe不被認(rèn)識(shí),所以還要設(shè)置環(huán)境變量:變量名:VCINSTALLDIR,變量值就是vc的路徑
設(shè)置了環(huán)境變量后關(guān)閉cmd,重新打開cmd, 先使用命令設(shè)置:set CL=/FI"%VCINSTALLDIR%\INCLUDE\stdint.h" 這條命令是為了讓剛才我們?cè)O(shè)置的環(huán)境變量生效
然后pip 安裝,終于成功了
注:如果報(bào)錯(cuò):UnicodeDecodeError:'utf-8' codec can't decode byte... 將CMD的終端編碼用“CHCP 65001”命令改為“UTF-8”后再安裝即可
進(jìn)入支付寶支付開發(fā)
好接著進(jìn)入真正的開發(fā)階段了,本次選用【電腦頁面支付】:
然后進(jìn)到支付寶的api文檔:傳送門
選用統(tǒng)一收單下單支付接口
進(jìn)入的目錄就不展示了,里面的參數(shù)配置就是正式的支付接口,這個(gè)就根據(jù)自己實(shí)際情況配置了,本次我們只用沙箱賬號(hào)測(cè)試一下就行了,和正式的配置是一樣的,因?yàn)檎降呐渲靡J(rèn)證商家才行,所以為了方便且精簡(jiǎn)的演示,就用沙箱賬號(hào)了
在前面設(shè)置公鑰私鑰那個(gè)頁面,把安卓版的沙箱支付寶下載安裝好,使用這里的賬號(hào)密碼登錄:
準(zhǔn)備好之后,現(xiàn)在進(jìn)入真正的開發(fā)了。
1.創(chuàng)建一個(gè)django項(xiàng)目,一個(gè)簡(jiǎn)單的支付寶充值話費(fèi)的
配置文件里導(dǎo)入rest_framework app,設(shè)置url,其中pay則是一會(huì)兒要用到的測(cè)試url
html文件:
view,就按照pypi社區(qū)上支付寶給的案例調(diào)整代碼就行:
#!/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__':
"""
設(shè)置配置,包括支付寶網(wǎng)關(guān)地址、app_id、應(yīng)用私鑰、支付寶公鑰等,其他配置值可以查看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]'
"""
得到客戶端對(duì)象。
注意,一個(gè)alipay_client_config對(duì)象對(duì)應(yīng)一個(gè)DefaultAlipayClient,定義DefaultAlipayClient對(duì)象后,alipay_client_config不得修改,如果想使用不同的配置,請(qǐng)定義不同的DefaultAlipayClient。
logger參數(shù)用于打印日志,不傳則不打印,建議傳遞。
"""
client = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)
"""
系統(tǒng)接口示例:alipay.trade.pay
"""
# 對(duì)照接口文檔,構(gòu)造請(qǐng)求對(duì)象
model = AlipayTradePayModel()
model.auth_code = "282877775259787048"
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等其他公共參數(shù),放在udf_params中
# udf_params = dict()
# from alipay.aop.api.constant.ParamConstants import *
# udf_params[P_APP_AUTH_TOKEN] = "xxxxxxx"
# request.udf_params = udf_params
# 執(zhí)行請(qǐng)求,執(zhí)行過程中如果發(fā)生異常,會(huì)拋出,請(qǐng)打印異常棧
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()
# 解析響應(yīng)結(jié)果
response.parse_response_content(response_content)
print(response.body)
if response.is_success():
# 如果業(yè)務(wù)成功,則通過respnse屬性獲取需要的值
print("get response trade_no:" + response.trade_no)
else:
# 如果業(yè)務(wù)失敗,則從錯(cuò)誤碼中可以得知錯(cuò)誤情況,具體錯(cuò)誤碼信息可以查看接口文檔
print(response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg)
"""
帶文件的系統(tǒng)接口示例:alipay.offline.material.image.upload
"""
# 如果沒有找到對(duì)應(yīng)Model類,則直接使用Request類,屬性在Request類中
request = AlipayOfflineMaterialImageUploadRequest()
request.image_name = "我的店"
request.image_type = "jpg"
# 設(shè)置文件參數(shù)
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
"""
# 對(duì)照接口文檔,構(gòu)造請(qǐng)求對(duì)象
model = AlipayTradePagePayModel()
model.out_trade_no = "pay201805020000226"
model.total_amount = 50
model.subject = "測(cè)試"
model.body = "支付寶測(cè)試"
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 = "2088302300165604"
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 = "2088301300153242"
model.sub_merchant = sub_merchant
request = AlipayTradePagePayRequest(biz_model=model)
# 得到構(gòu)造的請(qǐng)求,如果http_method是GET,則是一個(gè)帶完成請(qǐng)求參數(shù)的url,如果http_method是POST,則是一段HTML表單片段
response = client.page_execute(request, http_method="GET")
print("alipay.trade.page.pay response:" + response)
"""
構(gòu)造喚起支付寶客戶端支付時(shí)傳遞的請(qǐng)求串示例:alipay.trade.app.pay
"""
model = AlipayTradeAppPayModel()
model.timeout_express = "90m"
model.total_amount = "9.00"
model.seller_id = "2088301194649043"
model.product_code = "QUICK_MSECURITY_PAY"
model.body = "Iphone6 16G"
model.subject = "iphone"
model.out_trade_no = "201800000001201"
request = AlipayTradeAppPayRequest(biz_model=model)
response = client.sdk_execute(request)
print("alipay.trade.app.pay response:" + response)
pypi社區(qū)上支付寶官方案例
#!/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():
"""
設(shè)置配置,包括支付寶網(wǎng)關(guān)地址、app_id、應(yīng)用私鑰、支付寶公鑰等,其他配置值可以查看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()
"""
得到客戶端對(duì)象。
注意,一個(gè)alipay_client_config對(duì)象對(duì)應(yīng)一個(gè)DefaultAlipayClient,定義DefaultAlipayClient對(duì)象后,alipay_client_config不得修改,如果想使用不同的配置,請(qǐng)定義不同的DefaultAlipayClient。
logger參數(shù)用于打印日志,不傳則不打印,建議傳遞。
"""
client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
return client
"""
系統(tǒng)接口示例: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('錯(cuò)誤,只能是數(shù)字')
money = int(money)
client = alipayclient()
"""
頁面接口示例:alipay.trade.page.pay
"""
# 對(duì)照接口文檔,構(gòu)造請(qǐng)求對(duì)象
model = AlipayTradePagePayModel()
import time
model.out_trade_no = 'pay201805020000226'
model.total_amount = money
model.subject = "測(cè)試"
model.body = "支付寶測(cè)試"
model.product_code = "FAST_INSTANT_TRADE_PAY"
request = AlipayTradePagePayRequest(biz_model=model)
# 得到構(gòu)造的請(qǐng)求,如果http_method是GET,則是一個(gè)帶完成請(qǐng)求參數(shù)的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>支付寶電腦端支付-話費(fèi)充值</title>
</head>
<body>
<form method="post">
{% csrf_token %}
<label for="money">金額:</label>
<input type="text" placeholder="請(qǐng)輸入充值金額" id="money" name="money">
<label for="phone">號(hào)碼:</label>
<input type="text" value="183XXXXX" id="phone" name="phone">
<input type="submit" value="立即支付寶充值">
</form>
</body>
</html>
pay.html
然后重啟項(xiàng)目:
注意,有坑,真的有坑,照這么設(shè)置啟動(dòng),點(diǎn)擊支付的時(shí)候會(huì)跳到這個(gè)頁面:
第一個(gè)坑:
反正就這兩個(gè)頁面,無效的appID,其實(shí)我用的那個(gè)ID就是支付寶沙箱給的ID號(hào),所以不可能有錯(cuò),但是注意了,我們用的是沙箱賬號(hào),而我們直接拷貝的支付寶給的案例,用的url是【openapi.alipay.com】,這個(gè)url是正式的url,并不是沙箱測(cè)試的url,所以沙箱測(cè)試的url是【https://openapi.alipaydev.com/gateway.do】
第二個(gè)坑:
修改url之后,再次提交,還是說訂單信息有錯(cuò)誤,
到底是哪里的問題呢?再檢查打碼,還是剛才的邏輯,沙箱賬號(hào)啊,那么在初始化支付寶配置時(shí),讀源碼得,需要添加這個(gè)參數(shù) 【sandbox_debug = True】:
第三個(gè)坑:
按上面的修改重啟,還是報(bào)錯(cuò):
查看支付寶給的這個(gè)錯(cuò)誤代碼解析,交易信息被篡改,好像也沒有說清楚啥問題
我試著改了下這個(gè)流水號(hào),把它改成了用事件戳隨機(jī)生成的,【model.out_trade_no = "pay" + str(time.time())】
重啟項(xiàng)目,輸入金額23
點(diǎn)擊立即支付寶充值,這頁面終于出來了
那么那個(gè)流水號(hào)有多大作用呢?我直接刪除看看:
那么就得必須帶上它了
但是支付寶官方sdk里給的案例也貌似沒有說清楚啊,只是說看著像時(shí)間戳:
但是也沒說這玩意得按時(shí)間戳來實(shí)時(shí)接收,還不能寫死了,其實(shí)呢,仔細(xì)一想,這個(gè)流水號(hào)是不是得按當(dāng)時(shí)付款時(shí)間來啊?難道以后每個(gè)時(shí)間段支付的都是同一個(gè)流水號(hào)?這不亂套了嗎?支付寶官方?jīng)]有提的原因,我猜啊,很大可能,是因?yàn)橹安皇怯写笊駥戇^sdk了嘛,那么那些參數(shù)或許在哪個(gè)大神的sdk里有解釋了,而官方的這個(gè)sdk是之后發(fā)布的,所以可能開發(fā)者以為之前這老哥都開發(fā)過了,所以這些沒必要再提了。好的,反正這里有個(gè)坑,注意就行了
接著后面的操作,打開手機(jī),用那個(gè)沙箱支付寶app登錄,掃碼支付:
2.完善代碼
可以支付了對(duì)吧,然后按個(gè)訂單流水號(hào)就是剛才設(shè)置的那個(gè)咯,但是電腦頁面還是這樣,沒有任何反饋啊,作為商家我們根本不知道買家那邊什么情況,到底支付沒有也不知道對(duì)吧,不可能一個(gè)一個(gè)的去自己賬單里查賬吧?如果人多呢,幾個(gè)人同時(shí)支付怎么辦呢?
所以,這里還得配置一下,再寫一個(gè)url來接收一下數(shù)據(jù),并返回支付結(jié)果:
url:
view:
主視圖類添加兩個(gè)url屬性
新的url對(duì)應(yīng)的識(shí)圖類:
重啟項(xiàng)目,支付查看結(jié)果:
終端打印的結(jié)果:
OK,終于完事兒了
相關(guān)代碼:
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():
"""
設(shè)置配置,包括支付寶網(wǎng)關(guān)地址、app_id、應(yīng)用私鑰、支付寶公鑰等,其他配置值可以查看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()
"""
得到客戶端對(duì)象。
注意,一個(gè)alipay_client_config對(duì)象對(duì)應(yīng)一個(gè)DefaultAlipayClient,定義DefaultAlipayClient對(duì)象后,alipay_client_config不得修改,如果想使用不同的配置,請(qǐng)定義不同的DefaultAlipayClient。
logger參數(shù)用于打印日志,不傳則不打印,建議傳遞。
"""
client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
return client
"""
系統(tǒng)接口示例: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('錯(cuò)誤,只能是數(shù)字')
money = int(money)
client = alipayclient()
"""
頁面接口示例:alipay.trade.page.pay
"""
# 對(duì)照接口文檔,構(gòu)造請(qǐng)求對(duì)象
model = AlipayTradePagePayModel()
import time
model.out_trade_no = 'pay' + str(time.time())
model.total_amount = money
model.subject = "測(cè)試"
model.body = "支付寶測(cè)試"
model.product_code = "FAST_INSTANT_TRADE_PAY"
request = AlipayTradePagePayRequest(biz_model=model)
# 得到構(gòu)造的請(qǐng)求,如果http_method是GET,則是一個(gè)帶完成請(qǐng)求參數(shù)的url,如果http_method是POST,則是一段HTML表單片段
# get請(qǐng)求 用戶支付成功后返回的頁面請(qǐng)求地址,這個(gè)可以是公網(wǎng)地址
request.return_url = "http://127.0.0.1:8002/alipay_handler"
# post請(qǐng)求 用戶支付成功通知商戶的請(qǐng)求地址
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的回調(diào)地址
print(request.data)
# 用戶支付成功之后回到哪
return HttpResponse("用戶支付成功")
def post(self, request):
print(request.data)
return HttpResponse("notify_url")
views
html文件沒變,同上
總結(jié):
就是安裝sdk的時(shí)候很煩人,如果你覺得不習(xí)慣,可以用github上那些大神寫的sdk。我個(gè)人覺得,官方的只是安裝有點(diǎn)繁瑣,后面的配置是挺簡(jiǎn)單的
在使用沙箱測(cè)試支付的時(shí)候的時(shí)候注意那三個(gè)坑就行了,因?yàn)樯诚錅y(cè)試和正式的支付還是與差距的
總結(jié)
以上是生活随笔為你收集整理的前后端分离djangorestframework—— 接入支付宝支付平台的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为阅读上线全新精品男声:语气真实自然,
- 下一篇: 英特尔锐炫 A770 8GB 与 16G