python調用支付寶支付接口

  • 2019 年 10 月 3 日
  • 筆記

python調用支付寶支付接口詳細示例—附帶Django demo代碼

 

項目演示:

一、輸入金額

二、跳轉到支付寶付款

 

三、支付成功

 四、跳轉回自己網站

在使用支付寶接口的前期準備:

1、支付寶公鑰

2、應用公鑰

3、應用私鑰 

4、APPID

5、Django 1.11.11 環境

 

 

1234均由阿里開放平台生成

如果你不是商戶或者你只是想測試,阿里提供了沙箱環境供測試

沙箱環境下的商戶賬號和用戶賬號、支付寶app都是沙箱版的,不能用實際賬號

這時候我們需要去阿里開放平台去生成一些1234參數

 註冊網址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

 

 點擊設置應用公鑰

 

 

 

 下載簽名生成工具,解壓後找到 RSA簽名驗簽工具.bat 文件打開

 

 

這個工具會自動保存應用公鑰和應用私鑰到 RSA密鑰 文件夾

將應用公鑰拷貝到網站窗口,點保存,在原應用公鑰按鈕旁就會自動生成支付寶公鑰

我們需要將網站窗口生成的支付寶公鑰也新建一個txt文件放到這裏面

繼續操作

修改三個txt文件的名字為英文,如:app_public.txt   app_private.txt  alipay_public.txt

修改三個txt文件內容為固定格式:

-----BEGIN PUBLIC KEY-----  # 加上這行          密鑰部分  -----END PUBLIC KEY-----   # 同上

將三個文件copy到django項目下新建文件夾中,如:

 

你也可以放在app下面,都可以,隨你自己,但你可以先按我的一模一樣配置將項目先跑通。

建議新建alipay的文件夾,配置及相關py文件可以全部放到這裏面來

接下來在settings配置文件中配置這三個txt文件的路徑

 我的配置:

ALIPAY_PUBLIC = os.path.join(BASE_DIR,'alipay_use','alipay','alipay_keys','alipay_public.txt')  APP_PUBLIC = os.path.join(BASE_DIR,'alipay_use','alipay','alipay_keys','app_public.txt')  APP_PRIVATE = os.path.join(BASE_DIR,'alipay_use','alipay','alipay_keys','app_private.txt')

 

alipay文件夾下新建alipay.py文件

alipay.py 代碼如下:

# _*_ 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請求路由  ]

 

 視圖層(app中)

views.py

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 alipay_use.alipay.alipay import AliPay      def aliPay():      obj = AliPay(          appid="2016100100642208",                              # 支付寶沙箱裏面的APPID,需要改成你自己的          app_notify_url="http://129.211.29.98/update_order/",  # 如果支付成功,支付寶會向這個地址發送POST請求(校驗是否支付已經完成),此地址要能夠在公網進行訪問,需要改成你自己的服務器地址          return_url="http://129.211.29.98/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">  </form>    </body>  </html>

 

 需要裝的模塊:

--pycryptodome  --urllib  --base64

 

 

 

支付寶接口流程:

 

項目演示:

一、輸入金額

二、跳轉到支付寶付款

 

三、支付成功

 四、跳轉回自己網站

在使用支付寶接口的前期準備:

1、支付寶公鑰

2、應用公鑰

3、應用私鑰 

4、APPID

5、Django 1.11.11 環境

 

 

1234均由阿里開放平台生成

如果你不是商戶或者你只是想測試,阿里提供了沙箱環境供測試

沙箱環境下的商戶賬號和用戶賬號、支付寶app都是沙箱版的,不能用實際賬號

這時候我們需要去阿里開放平台去生成一些1234參數

 註冊網址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

 

 點擊設置應用公鑰

 

 

 

 下載簽名生成工具,解壓後找到 RSA簽名驗簽工具.bat 文件打開

 

 

這個工具會自動保存應用公鑰和應用私鑰到 RSA密鑰 文件夾

將應用公鑰拷貝到網站窗口,點保存,在原應用公鑰按鈕旁就會自動生成支付寶公鑰

我們需要將網站窗口生成的支付寶公鑰也新建一個txt文件放到這裏面

繼續操作

修改三個txt文件的名字為英文,如:app_public.txt   app_private.txt  alipay_public.txt

修改三個txt文件內容為固定格式:

-----BEGIN PUBLIC KEY-----  # 加上這行          密鑰部分  -----END PUBLIC KEY-----   # 同上

將三個文件copy到django項目下新建文件夾中,如:

 

你也可以放在app下面,都可以,隨你自己,但你可以先按我的一模一樣配置將項目先跑通。

建議新建alipay的文件夾,配置及相關py文件可以全部放到這裏面來

接下來在settings配置文件中配置這三個txt文件的路徑

 我的配置:

ALIPAY_PUBLIC = os.path.join(BASE_DIR,'alipay_use','alipay','alipay_keys','alipay_public.txt')  APP_PUBLIC = os.path.join(BASE_DIR,'alipay_use','alipay','alipay_keys','app_public.txt')  APP_PRIVATE = os.path.join(BASE_DIR,'alipay_use','alipay','alipay_keys','app_private.txt')

 

alipay文件夾下新建alipay.py文件

alipay.py 代碼如下:

# _*_ 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請求路由  ]

 

 視圖層(app中)

views.py

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 alipay_use.alipay.alipay import AliPay      def aliPay():      obj = AliPay(          appid="2016100100642208",                              # 支付寶沙箱裏面的APPID,需要改成你自己的          app_notify_url="http://129.211.29.98/update_order/",  # 如果支付成功,支付寶會向這個地址發送POST請求(校驗是否支付已經完成),此地址要能夠在公網進行訪問,需要改成你自己的服務器地址          return_url="http://129.211.29.98/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">  </form>    </body>  </html>

 

 需要裝的模塊:

--pycryptodome  --urllib  --base64

 

 

 

支付寶接口流程: