1. 程式人生 > >Django框架中使用支付寶在線支付API

Django框架中使用支付寶在線支付API

mode method 使用 amount 單位 pop item 接口 ems

一.螞蟻金服開發平臺申請測試賬號

  a. 登陸螞蟻金服開放平臺https://open.alipay.com/platform/manageHome.htm,在“開發中心”—“研發服務”下拉處選擇沙箱作為測試環境。

  技術分享圖片

  b. 填寫相關信息,成功申請沙箱賬號後進入沙箱環境頁面

  技術分享圖片

  c.下載沙箱錢包(目前僅支持安卓手機)

  技術分享圖片

  d. 使用沙箱賬號處的買家賬號登陸下載好的沙箱錢包

  技術分享圖片

二.制作公鑰和密鑰

  a.制作應用私鑰和公鑰

    在沙箱應用處點擊查看應用公鑰

    技術分享圖片

    在顯示的應用公鑰處點擊修改

    技術分享圖片

     在點擊查看密鑰生成方法

    技術分享圖片

    

    根據教程生成密鑰和公鑰

    技術分享圖片

    

    得到一個RSA密鑰文件夾,裏面有兩個文件

    技術分享圖片

  b. 制作支付寶公鑰

    將應用公鑰復制上傳,點擊保存會自動生成支付寶公鑰

    技術分享圖片

      

    查看支付寶公鑰

      技術分享圖片

三.Django程序開發

  a.創建一個django項目

    技術分享圖片

  b.配置支付寶公鑰應用私鑰

    在私鑰和公鑰頭圍加上標記便於python識別

    技術分享圖片

  c.代碼編寫

    首先安裝依賴  pip3 install pycryptodome

    SDK官方加密代碼,可以在github上找到

技術分享圖片
#
_*_ 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.py

技術分享圖片
from django.contrib import admin
from django.urls import path
from api import views


urlpatterns = [
    path(admin/, admin.site.urls),
    path(index/, views.index),
    path(result/, views.pay_result),
    path(update_order/, views.update_order),


]
urls.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 utils.alipay import AliPay


def aliPay():
    obj = AliPay(
        appid="2016092400581835",                              # 支付寶沙箱裏面的APPID
        app_notify_url="http://127.0.0.1:8800/update_order/",  # 如果支付成功,支付寶會向這個地址發送POST請求(校驗是否支付已經完成),此地址要能夠在公網進行訪問
        return_url="http://127.0.0.1:8800/result/",            # 如果支付成功,重定向回到你的網站的地址。
        alipay_public_key_path="keys/alipay_public_2048.txt",  # 支付寶公鑰
        app_private_key_path="keys/app_private_2048.txt",      # 應用私鑰
        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(支付失敗)
views.py

  d.進行測試

    啟動項目,訪問index頁面,輸入支付金額,點擊支付

    技術分享圖片

    使用下載的沙箱錢包掃描付款

    技術分享圖片

 

   付款成功

  技術分享圖片

  

  最後會重定向到return_url

    技術分享圖片

註意:配置文件信息可以在setting中設置,要完成全部測試需要有一個公網IP和服務器,來測試支付寶向app_notify_url發送的POST請求

ALLOWED_HOSTS = [*, ]
# 支付相關配置
APPID = "***6082500309412"
NOTIFY_URL = "http://127.0.0.1/update_order/"
RETURN_URL = "http://127.0.0.1/pay_result/"
PRI_KEY_PATH = "keys/app_private_2048.txt"
PUB_KEY_PATH = "keys/alipay_public_2048.txt"

  

  

  

  

Django框架中使用支付寶在線支付API