支付寶支付html
螞蟻沙箱環境(Beta)是協助開發者進行接口功能開發及主要功能聯調的輔助環境。沙箱環境模擬了開放平臺部分產品的主要功能和主要邏輯(當前沙箱支持產品請參考「沙箱支持產品列表」)。
在開發者應用上線審覈前,開發者能夠根據自身需求,先在沙箱環境中瞭解、組合和調試各類開放接口,進行開發調通工做,從而幫助開發者在應用上線審覈完成後,能更快速、更順利的進行線上調試和驗收工做。python
詳情,請參考連接:git
https://docs.open.alipay.com/200/105311/github
商家要使用支付寶支付功能,須要申請一個支付接口。須要提交企業的營業執照等相關信息!算法
可是對於我的開發者來講,並無這些資料。因此,爲了幫助開發者進行接口測試,我的可使用沙箱環境測試。只要你有支付寶帳號就行。裏面提供了測試帳號,錢能夠隨便充值。注意:數據是假的,金額可能會被清洗!sql
打開連接數據庫
https://openhome.alipay.com/platform/appDaily.htm?tab=infodjango
打開手機支付寶,掃描二維碼,就能夠登陸了json
登陸成功以後,點擊自研開發者windows
點擊導航的開發者中心-->研發服務
填寫相關信息,完成實名認證
再次進入沙箱應用,點擊查看應用公鑰
點擊設置應用公鑰
點擊查看祕鑰生成方法
下載windows版本的壓縮包,解壓一下,效果以下:
點擊RSA簽名驗籤工具,直接點擊生成祕鑰
不要管JAVA和非JAVA,默認的就能夠了
效果以下:
它會在當前壓縮包裏面的RSA祕鑰,生成2個txt文件
打開應用公鑰2048.txt,將裏面的內容複製一下
進入到剛纔的頁面,輸入公鑰,點擊保存。
若是保存時,提示公鑰格式不對。再生成一次公鑰,再次輸入,就能夠了!
SDK的英文全名是:software development kit,翻譯成中文的意思就是「軟件開發工具包」
通俗一點的理解,是指由第三方服務商提供的實現軟件產品某項功能的工具包。通常以集合api和文檔、範例、工具的形式出現。
一般SDK是由專業性質的公司提供專業服務的集合,好比提供安卓開發工具、或者基於硬件開發的服務等。也有針對某項軟件功能的SDK,如推送技術、圖像識別技術、移動支付技術等,同時資源優點類的公司也提供資源共享的SDK,如一些廣告SDK提供盈利渠道,分發SDK提供產品下載渠道。
注意:支付寶的SDK,採用了rsa加密算法。對於發送,返回的數據,它有必定的加密算法以及解密算法。
因此,咱們要使用支付寶服務,發送的數據,必須符合它的加密要求才行。有了SDK以後,咱們不須要關心加密算法,只須要關係業務邏輯就行!
打開支付寶提供的SDK
https://docs.open.alipay.com/54/103419/
下載連接:
https://pypi.org/project/alipay-sdk-python/
不須要下載SDK!
不須要下載SDK!
不須要下載SDK!
這裏使用的是,根據官方SDK封裝好的一個python文件,須要安裝依賴包pycryptodome
pip3 install pycryptodome
pay.py代碼以下:
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 urllib.parse import urlparse, parse_qs 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)
這個pay.py就是sdk
爲了保證交易雙方(商戶和支付寶)的身份和數據安全,開發者在調用接口前,須要配置雙方密鑰,對交易數據進行雙方校驗。
在開始集成和開發前,首先了解一下經常使用的接入方式和架構建議:
其次,爲了保證交易安全,支付寶採用了一系列的安全手段:
採用HTTPS協議傳輸交易數據,防止數據被截獲,解密。
採用RSA非對稱密鑰,明確交易雙方的身份,保證交易主體的正確性和惟一性
新建一個django項目alipay,版本爲django 2.x
在項目根目錄建立utils,將上面的pay.py複製到此目錄下
在項目根目錄建立keys
進入目錄secret_key_tools_RSA_win\RSA簽名驗籤工具windows_V1.4\RSA密鑰
將裏面的應用公鑰2048.txt,應用私鑰2048.txt這2個文件,複製到keys目錄
python代碼裏面的文件名,不要出現中文!
重命名爲英文!!!!!!!!
將這2個txt重命名爲alipay_public_2048.txt和app_private_2048.txt
注意:!!!!!!!!!!!!!!!!!!!!!!!!
alipay_public_2048.txt這個是公鑰,不能用本地的。要用網頁的!
點擊查看支付寶公鑰,不是應用公鑰!
複製裏面的公鑰
打開alopay_public_2048.txt,必須增長頭部和尾部,將網頁複製的公鑰粘貼到裏面
-----BEGIN PUBLIC KEY-----
MIIBI...
-----END PUBLIC KEY-----
效果以下:
必定要覆蓋公鑰!!!!!!!!!!!!!!!!!!!
打開app_private_2048.txt,必須增長頭部和尾部
-----BEGIN RSA PRIVATE KEY-----
MIIE...
-----END RSA PRIVATE KEY-----
效果以下:
注意:這2個txt文件,必須爲utf-8編碼,不然啓動django項目會報錯!
修改urls.py,增長路徑
from django.contrib import admin from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index), path('index/', views.index), re_path('buy/(?P<gid>\d+)/', views.buy), path('check_order/', views.check_order), re_path('show', views.show), path('order_list/', views.order_list), ]
修改models.py,增長2個表
from django.db import models # Create your models here. class Goods(models.Model): # 商品 name = models.CharField(max_length=32,verbose_name="名稱") price = models.FloatField(verbose_name="價格") class Order(models.Model): # 訂單 no = models.CharField(max_length=64,verbose_name="訂單號") goods = models.ForeignKey(to='Goods',on_delete=models.CASCADE,verbose_name="商品ID") status_choices = ( (1,'未支付'), (2,'已支付'), ) status = models.SmallIntegerField(choices=status_choices,default=1,verbose_name="支付狀態")
修改views.py,增長視圖函數
注意:appid改爲本身的,後6位我改爲xx了
from django.shortcuts import render, HttpResponse, redirect from app01 import models import uuid from utils.pay import AliPay # 導入sdk # Create your views here. def index(request): goods_list = models.Goods.objects.all() return render(request, 'index.html', {'goods_list': goods_list}) # 全局變量 alipay = AliPay( appid="2016091700xxxxxx", # 注意,改爲本身的! app_notify_url="http://127.0.0.1:8000/check_order/", # POST,發送支付狀態信息 return_url="http://127.0.0.1:8000/show/", # GET,將用戶瀏覽器地址重定向回原網站 app_private_key_path="keys/app_private_2048.txt", alipay_public_key_path="keys/alipay_public_2048.txt", debug=True, # 默認True測試環境、False正式環境 ) def buy(request, gid): """ 去購買並支付 :param request: :param gid: :return: """ obj = models.Goods.objects.get(pk=gid) # 生成訂單(未支付) no = str(uuid.uuid4()) models.Order.objects.create(no=no, goods_id=obj.id) # 根據 # APPID # 支付寶網關 # 公鑰和私鑰 # 生成要跳轉的地址 # 沙箱環境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info query_params = alipay.direct_pay( subject=obj.name, # 商品簡單描述 out_trade_no=no, # 商戶訂單號 total_amount=obj.price, # 交易金額(單位: 元 保留倆位小數) ) pay_url = "https://openapi.alipaydev.com/gateway.do?{0}".format(query_params) return redirect(pay_url) def check_order(request): """ POST請求,支付寶通知支付信息,咱們修改訂單狀態 :param request: :return: """ if request.method == 'POST': from urllib.parse import parse_qs 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] sign = post_dict.pop('sign', None) status = alipay.verify(post_dict, sign) if status: # 支付成功,獲取訂單號將訂單狀態更新 out_trade_no = post_dict['out_trade_no'] models.Order.objects.filter(no=out_trade_no).update(status=2) return HttpResponse('success') else: return HttpResponse('支持失敗') else: return HttpResponse('只支持POST請求') def show(request): """ 回到咱們頁面 :param request: :return: """ if request.method == "GET": params = request.GET.dict() sign = params.pop('sign', None) status = alipay.verify(params, sign) if status: return HttpResponse('支付成功') else: return HttpResponse('失敗') else: return HttpResponse('只支持GET請求') def order_list(request): """ 查看全部訂單狀態 :param request: :return: """ orders = models.Order.objects.all() return render(request, 'order_list.html', {'orders': orders})
增長index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for row in goods_list %} <li>{{ row.name }},價格:{{ row.price }} <a href="/buy/{{ row.id }}/">購買</a></li> {% endfor %} </ul> </body> </html>
增長order_list.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1"> {% for item in orders %} <tr> <td>{{ item.id }}</td> <td>{{ item.no }}</td> <td>{{ item.goods.name }}</td> <td>{{ item.get_status_display }}</td> </tr> {% endfor %} </table> </body> </html>
修改settings.py,關閉csrf
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
項目結構以下:
alipay/ ├── alipay │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── app01 │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20180810_1623.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── db.sqlite3 ├── keys │ ├── alipay_public_2048.txt │ └── app_private_2048.txt ├── manage.py ├── templates │ ├── index.html │ └── order_list.html └── utils └── pay.py
使用2個命令,生成表
python manage.py makemigrations
python manage.py migrate
使用navicat打開sqlite3數據庫,增長几條數據
INSERT INTO app01_goods ("id", "name", "price") VALUES (1, 'Apple iPad Pro', 5588.0); INSERT INTO app01_goods ("id", "name", "price") VALUES (2, 'i5 8500', 1599.0); INSERT INTO app01_goods ("id", "name", "price") VALUES (3, '拯救者Y7000', 7299.0); INSERT INTO app01_goods ("id", "name", "price") VALUES (4, '大白兔奶糖', 1.0);
啓動django項目,訪問首頁
點擊購買大白兔奶糖,它會自動跳轉到支付寶的支付頁面
注意:要沙箱環境的APP
它只提供了安卓版本,下載以後,進行安裝
點擊沙箱帳號
它提供了商家信息和買家信息
默認的買家信息,帳號有9萬多塊。注意:這個錢是虛擬的,不能體現!
若是錢不夠了,能夠輸入任意金額進行充值!
手機登陸買家帳號,密碼爲111111
打開掃一掃,進行支付
提示支付成功
注意:若是出現失敗,多是公鑰錯了!必定是網頁的支付寶公鑰,不是本地的公鑰!
查看買家信息,少了一塊錢
查看商家信息,多了一塊錢。那一分錢,是扣了手續費!
注意:這個是本地環境測試的。
若是須要服務器測試,請修改views.py中的全局變量,將127.0.0.1改成服務器的公網IP便可!
查看訂單狀態,這裏的狀態是未支付。爲何呢?由於支付寶要發送一個POST請求到
http://127.0.0.1:8000/check_order/ 才能修改狀態。因爲在本地,支付寶沒法訪問此地址!
注意:商品價格,必須保證最多爲小數點2位。這個是支付寶規定的!
將代碼上傳到公網服務器,修改views.py,將裏面的127.0.0.1改爲公網IP。
再次支付一次,查看訂單狀態。狀態爲已支付
完整代碼,請參數github
https://github.com/987334176/alipay
螞蟻沙箱環境(Beta)是協助開發者進行接口功能開發及主要功能聯調的輔助環境。沙箱環境模擬了開放平臺部分產品的主要功能和主要邏輯(當前沙箱支持產品請參考「沙箱支持產品列表」)。
在開發者應用上線審覈前,開發者能夠根據自身需求,先在沙箱環境中瞭解、組合和調試各類開放接口,進行開發調通工做,從而幫助開發者在應用上線審覈完成後,能更快速、更順利的進行線上調試和驗收工做。
詳情,請參考連接:
https://docs.open.alipay.com/200/105311/
商家要使用支付寶支付功能,須要申請一個支付接口。須要提交企業的營業執照等相關信息!
可是對於我的開發者來講,並無這些資料。因此,爲了幫助開發者進行接口測試,我的可使用沙箱環境測試。只要你有支付寶帳號就行。裏面提供了測試帳號,錢能夠隨便充值。注意:數據是假的,金額可能會被清洗!