完整項目在 :https://github.com/Mrfangyong/aliyun
在使用支付寶接口的前期準備:
1、支付寶公鑰
2、應用公鑰
3、應用私鑰
4、APPID
5、Django python 環境
1234均由阿里開放平臺生成
正式環境之前,阿里提供了沙箱環境供測試
沙箱環境下的商戶賬號和用戶賬號、支付寶app都是沙箱版的,不能用實際賬號
這時候我們需要去阿里開放平臺去生成一些1234參數
點擊設置應用公鑰
建議下載軟件,不下載也行,用openssl生成(最後附上opensll生成命令,電腦有更好,沒有需要配置環境,挺麻煩的,自行百度),只要後面保存公私鑰文件不錯就好**(這是個大坑)**
我用的是下載軟件
按照我的步驟生成 這兩個文件
第一步改名字:
應用公鑰2048.txt >>>>> app_public.txt
應用私鑰2048.txt >>>>> app_private.txt
生成支付寶公鑰
拿着 app_public.txt(應用公鑰2048.txt ) 一串代碼複製
然後生成了支付寶公鑰
新建一個alipay_public.txt文件
將生成的支付寶公鑰複製到這個文件下
坑點1. 生成的三個文件中 要以下面的格式 修改
alipay_public.txt
-----BEGIN PUBLIC KEY-----
密鑰
-----END PUBLIC KEY-----
app_public.txt
-----BEGIN PUBLIC KEY-----
密鑰
-----END PUBLIC KEY-----
app_private.txt
-----BEGIN RSA PRIVATE KEY-----
密鑰
-----END RSA PRIVATE KEY-----
現在我們獲取了 1,2,3,4
接下來我們配置環境-----------------------------大坑 如果不加 --upgrade 可能會後悔(我反正是這樣的)
- 配置服務端 sdk – 官方鏈接 :https://pypi.org/project/alipay-sdk-python/
pip install python-alipay-sdk --upgrade
- 導入加密模塊
pip install Crypto
如果導入加密模塊報錯 試試這種 pip install pycrypto
如果以上1.2.3.4.5都準備好了,接下來代碼
我的項目目錄
setting.py中添加
ALIPAY_PUBLIC = os.path.join(BASE_DIR,'keys','alipay_public.txt')
APP_PUBLIC = os.path.join(BASE_DIR,'keys','app_public.txt')
APP_PRIVATE = os.path.join(BASE_DIR,'keys','app_private.txt')
alipay文件夾下新建alipay.py文件
alipay.py 代碼如下:
'''
Created on 2020年1月9日
@author: lan
'''
# _*_ coding=utf-8 _*_
from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from base64 import decodebytes, encodebytes
import json
class AliPay(object):
"""
支付寶支付接口(PC端支付接口)
"""
def __init__(self, appid, app_notify_url, app_private_key_path,
alipay_public_key_path, return_url, debug=False):
self.appid = appid
self.app_notify_url = app_notify_url
self.app_private_key_path = app_private_key_path
self.app_private_key = None
self.return_url = return_url
with open(self.app_private_key_path) as fp:
self.app_private_key = RSA.importKey(fp.read())
self.alipay_public_key_path = alipay_public_key_path
with open(self.alipay_public_key_path) as fp:
self.alipay_public_key = RSA.importKey(fp.read())
if debug is True:
self.__gateway = "https://openapi.alipaydev.com/gateway.do"
else:
self.__gateway = "https://openapi.alipay.com/gateway.do"
def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "FAST_INSTANT_TRADE_PAY",
# "qr_pay_mode":4
}
biz_content.update(kwargs)
data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
return self.sign_data(data)
def build_body(self, method, biz_content, return_url=None):
data = {
"app_id": self.appid,
"method": method,
"charset": "utf-8",
"sign_type": "RSA2",
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"version": "1.0",
"biz_content": biz_content
}
if return_url is not None:
data["notify_url"] = self.app_notify_url
data["return_url"] = self.return_url
return data
def sign_data(self, data):
data.pop("sign", None)
# 排序後的字符串
unsigned_items = self.ordered_data(data)
unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
sign = self.sign(unsigned_string.encode("utf-8"))
# ordered_items = self.ordered_data(data)
quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
# 獲得最終的訂單信息字符串
signed_string = quoted_string + "&sign=" + quote_plus(sign)
return signed_string
def ordered_data(self, data):
complex_keys = []
for key, value in data.items():
if isinstance(value, dict):
complex_keys.append(key)
# 將字典類型的數據dump出來
for key in complex_keys:
data[key] = json.dumps(data[key], separators=(',', ':'))
return sorted([(k, v) for k, v in data.items()])
def sign(self, unsigned_string):
# 開始計算簽名
key = self.app_private_key
signer = PKCS1_v1_5.new(key)
signature = signer.sign(SHA256.new(unsigned_string))
# base64 編碼,轉換爲unicode表示並移除回車
sign = encodebytes(signature).decode("utf8").replace("\n", "")
return sign
def _verify(self, raw_content, signature):
# 開始計算簽名
key = self.alipay_public_key
signer = PKCS1_v1_5.new(key)
digest = SHA256.new()
digest.update(raw_content.encode("utf8"))
if signer.verify(digest, decodebytes(signature.encode("utf8"))):
return True
return False
def verify(self, data, signature):
if "sign_type" in data:
sign_type = data.pop("sign_type")
# 排序後的字符串
unsigned_items = self.ordered_data(data)
message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
return self._verify(message, signature)
這個Alipay類主要是用來生成一個包含訂單詳情、簽名的大字典,然後把這個大字典加密成字符串拼接到支付寶付款網關接口路徑後面
視圖中重定向到該地址,支付寶處理完成後向我們的路由發get請求攜帶詳細信息和簽名,使用這個類的一個方法來校驗簽名即可
路由層(項目中)
urls.py
from django.contrib import admin
from django.conf.urls import url
from app01 import views
urlpatterns = [
url('admin/', admin.site.urls),
url('index/', views.index),
url('result/', views.pay_result), # 支付寶處理完成後回調的get請求路由
url('update_order/', views.update_order), # 支付寶處理完成後回調的post請求路由
]
視圖層(app01中)
views.py appid="**********", # 支付寶沙箱裏面的APPID,需要改成你自己的
import time
from urllib.parse import parse_qs
from django.conf import settings
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from app01.alipay import AliPay
def aliPay():
obj = AliPay(
appid="**********", # 支付寶沙箱裏面的APPID,需要改成你自己的
app_notify_url="http://127.0.0.1:8000/update_order/", # 如果支付成功,支付寶會向這個地址發送POST請求(校驗是否支付已經完成),此地址要能夠在公網進行訪問,需要改成你自己的服務器地址
return_url="http://127.0.0.1:8000/result/", # 如果支付成功,重定向回到你的網站的地址。需要你自己改,這裏是我的服務器地址
alipay_public_key_path=settings.ALIPAY_PUBLIC, # 支付寶公鑰
app_private_key_path=settings.APP_PRIVATE, # 應用私鑰
debug=True, # 默認False,True表示使用沙箱環境測試
)
# 優化:在settings裏面的設置後使用
# obj = AliPay(
# appid=settings.APPID,
# app_notify_url=settings.NOTIFY_URL,
# return_url=settings.RETURN_URL,
# alipay_public_key_path=settings.PUB_KEY_PATH,
# app_private_key_path=settings.PRI_KEY_PATH,
# debug=True,
# )
return obj
@csrf_exempt
def index(request):
if request.method == "GET":
return render(request, 'index.html')
# 實例化SDK裏面的類AliPay
alipay = aliPay()
# 對購買的數據進行加密
money = float(request.POST.get('price')) # 保留倆位小數 前端傳回的數據
out_trade_no = "x2" + str(time.time()) # 商戶訂單號 # 訂單號可以有多中生成方式,可以百度一下
# 1. 在數據庫創建一條數據:狀態(待支付)
query_params = alipay.direct_pay(
subject="充氣式Saber", # 商品簡單描述 這裏一般是從前端傳過來的數據
out_trade_no=out_trade_no, # 商戶訂單號 這裏一般是從前端傳過來的數據
total_amount=money, # 交易金額(單位: 元 保留倆位小數) 這裏一般是從前端傳過來的數據
)
# 拼接url,轉到支付寶支付頁面
pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
return redirect(pay_url)
@csrf_exempt
def update_order(request):
"""
支付成功後,支付寶向該地址發送的POST請求(用於修改訂單狀態)
:param request:
:return:
"""
if request.method == 'POST':
body_str = request.body.decode('utf-8')
post_data = parse_qs(body_str)
post_dict = {}
for k, v in post_data.items():
post_dict[k] = v[0]
alipay = aliPay()
sign = post_dict.pop('sign', None)
status = alipay.verify(post_dict, sign)
if status:
# 1.修改訂單狀態
out_trade_no = post_dict.get('out_trade_no')
print(out_trade_no)
# 2. 根據訂單號將數據庫中的數據進行更新
return HttpResponse('支付成功')
else:
return HttpResponse('支付失敗')
return HttpResponse('')
@csrf_exempt
def pay_result(request):
"""
支付完成後,跳轉回的地址
:param request:
:return:
"""
params = request.GET.dict()
sign = params.pop('sign', None)
alipay = aliPay()
status = alipay.verify(params, sign)
if status:
return HttpResponse('支付成功')
return HttpResponse('支付失敗')
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<input type="text" name="price">
<input type="submit" value="去支付">
</form>
</body>
</html>
然後手機下載 沙盒版支付寶
手機登錄,,然後
python .\manage.py runserver
訪問
http://127.0.0.1:8000/index
打開手機沙盒版 支付寶 支付就好了,,不會扣真錢
完成這步,,你就成功了
附:openssl生成密碼命令
openssl
OpenSSL> genrsa -out app_private_key.pem 2048 # 私鑰RSA2
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 導出公鑰
OpenSSL> exit