Django 支付寶付款接口的使用


  我們在開發的過程中經常會碰到調用微信或者支付寶接口進行付款,付款完成之后,如果用戶綁定了我的賬號,我只要有活動了,就要給這個關注我的用戶推動消息,讓用戶知道,比如說,我們經常會關注一些公眾號,然后這些公眾號只要有了消息就會自動給我推送,我們以后也會遇到這種推送的需求,那么具體如何使用我們的代碼來實現這種需求呢?還有就是用戶在付款的時候,我如何給他調用支付寶的接口呢?下面我們就來具體的學習

一、支付寶付款

  目前我們的付款方式有支付寶支付,微信支付,銀聯支付,然后使用支付寶支付需要有一個商戶號,這個需要自己公司去申請(需要企業營業執照),申請完之后會有一個商戶號,我們這邊主要是使用一種沙箱測試環境,沙箱環境地址如下

https://openhome.alipay.com/platform/appDaily.htm?tab=info

 進入以后,我們就可以申請個人的商戶號了,如下圖所示

第一步、進入沙箱測試環境

第二步、生成商戶公鑰和支付寶公鑰

第三步、下載沙箱測試應用

我們在使用的時候,調用支付寶接口和支付寶返回給我們數據的時候,傳輸過程中數據都是加密的,那么簡單的復習一下加密方式

對稱加密:所謂的對稱加密就是指加密密鑰和解密密鑰使用的是同一個,這樣只要我們保證了這個私鑰不泄露,數據就是安全的

非對稱加密:加密和解密的密鑰是不同的,使用公鑰加密,然后使用私鑰進行解密,公鑰可以隨意分發給任何人,但是私鑰一定不能泄露,比如支付寶的公鑰就是公開的,用戶使用支付寶的公鑰進行數據的加密,傳輸給支付寶,然后支付寶通過自己的私鑰進行解密,這樣就可以達到數據的傳輸,而且在傳輸的過程中,就算被別人截取了,他由於不知道支付寶的私鑰,所以對於我們來說這個數據也是安全的。

  我們在使用加密的時候,需要使用第三方的模塊,安裝如下

pip3 install pycryptodome

二、支付寶支付流程

1、項目結構

 2、項目流程介紹

  1.拿到商戶號,回調地址,支付寶公鑰,商戶私鑰生成一個alipay對象

  2.對象點direct_pay,傳支付金額,支付商品描述,支付訂單號,然后返回一個加密的串,這個加密串包含了第一步的信息

  3.將拿到的串拼接到支付寶網關(測試/正式)的后面pay_url="https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

  4.向生成的地址發送get請求,會跳轉到支付寶的支付頁面

  5.用戶付款完成(此時錢已經進入了商戶號),支付寶收到付款,向我們配置的兩個回調地址發送請求(get,post)

  6.一般get請求用戶展示,post請求用於修改訂單狀態,我們在拿到請求的時候一定要驗簽(防止仿造支付寶向我們發送請求)

    -get請求一般用於頁面展示

    -post請求用於修改訂單狀態

項目中需要配置的東西如下:

  1.公鑰私鑰生成方式:https://docs.open.alipay.com/291/105971

    支付寶公鑰:商戶號中輸入的應用公鑰,會自動生成一個支付寶公鑰,直接黏貼過來就好

    應用私鑰(用戶私鑰)

  2.支付寶回調返回數據中會有

    singn:必須驗證簽名,驗證通過以后才能進行后續的修改

    訂單id:根據訂單id修改訂單狀態

 可能遇到的問題處理:

  用戶付款了,但正在這時,服務掛掉,收不到支付寶的回調數據,如何處理?

    如果服務掛掉了,支付寶會過一段時間自動在回調,在24h內,隔一段時間就會回調,這個時候我們只需要重啟服務,就可以 接收到了!

3、代碼展示

  首先是路由層,我們需要用到的是兩個接口,一個用來向支付寶發起付款,一個用來接收支付寶收款成功之后返回給我們數據的接口,這里我們使用的是page1,page2

復制代碼
from django.contrib import admin

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url('admin/', admin.site.urls),
    url(r'^page1/', views.page1),
    url(r'^page2/', views.page2),
]
復制代碼

視圖函數展示

Views

在這里我們用到了alipay對象,現在展示alipay對象的代碼,它里面做的具體操作如下

支付寶alipay的代碼展示

三. Django項目中的使用

  首先附一張支付寶支付流程圖:

  在項目中注冊一個APP用於支付寶接口的調用,同時將支付寶接口使用需要的一些代碼與公鑰等文件放入一個文件夾下,目錄如下:

  其實只要將其中的代碼復制修改即可,payinit中需要提供app_id及回調地址等信息,里面有注釋,同時還需要提供應用私鑰與支付寶公鑰。

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)
pay.py
from .pay import AliPay
import os


def ali_init():
    # o_你自己的應用ID
    app_id = "*************"
    # 支付寶收到用戶的支付,會向商戶發兩個請求,一個get請求,一個post請求
    # o_你自己公網服務器處理支付寶回調的POST請求,驗證訂單
    notify_url = "http://39.100.155.49:8000/order/aliback/"
    # # o_你自己公網服務器處理支付寶回調的GET請求,將訂單結果展現給用戶
    return_url = "http://39.100.155.49:8000/order/aliback/"
    # o_你自己的私鑰
    alipay_private_key_path = os.path.join(os.path.dirname(__file__), 'alipay_private_2048.txt')
    # o_你自己的公鑰
    alipay_public_key_path = os.path.join(os.path.dirname(__file__), 'alipay_public_2048.txt')

    alipay = AliPay(
        appid=app_id,
        app_notify_url=notify_url,
        return_url=return_url,
        app_private_key_path=alipay_private_key_path,
        alipay_public_key_path=alipay_public_key_path,  # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你自己的公鑰
        debug=True,  # 默認False,
    )
    return alipay
payinit.py
from rest_framework.views import APIView

from luffyapi.libs.alipay import payinit
import time, hashlib
from django.core.cache import cache
from rest_framework.response import Response


class AliPayAPIView(APIView):
    def post(self, request, *args, **kwargs):
        # 獲取前台商品的信息
        subject = request.data.get('subject')
        money = request.data.get('money')

        # 初始化alipay
        alipay = payinit.ali_init()

        # 生成訂單號
        info = str(time.time()) + subject + money
        pay_num = hashlib.md5(info.encode('utf-8')).hexdigest()

        # redis存儲
        cache.set(pay_num, False, 300)

        try:
            # 生成訂單鏈接
            query_params = alipay.direct_pay(
                subject=subject,  # 商品名描述
                out_trade_no=pay_num,  # 商戶訂單號
                total_amount=money,  # 交易金額(單位: 元 保留倆位小數)
            )
            pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
        except:
            return Response({})

        return Response({
            'pay_num': pay_num,
            'pay_url': pay_url
        })


from rest_framework.viewsets import ViewSet


class ApiBackViewSet(ViewSet):
    def get(self, request, *args, **kwargs):
        # 初始化alipay
        alipay = payinit.ali_init()
        params = request.GET.dict()
        sign = params.pop('sign', None)
        status = alipay.verify(params, sign)
        print('GET驗證', status)
        if status:
            # 獲取訂單狀態,顯示給用戶
            return Response('支付成功')

    def post(self, request, *args, **kwargs):
        # 初始化alipay
        alipay = payinit.ali_init()
        # 檢測是否支付成功
        # 去請求體中獲取所有返回的數據:狀態/訂單號
        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]
        print(post_dict)

        sign = post_dict.pop('sign', None)
        # 通過調用alipay的verify方法去二次認證
        status = alipay.verify(post_dict, sign)
        if status:
            # 修改訂單狀態
            pass
        return Response('驗證成功')
調用支付寶接口對應的views.py

  這里需要注意一點,如果你的項目本地測試接口,支付成功之后支付寶是無法朝回調地址成功發送請求的,你需要將項目上線。

  該篇博客來自https://www.cnblogs.com/mcc61/p/11178444.html,我只是大自然的搬運工。

 

  

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM