Python 使用支付寶支付功能

支付流程

  1. 前臺將商品價格和商品名稱發給後臺
  2. 後臺生成訂單編號並結合商品信息、前臺接口、後臺接口生成支付寶的支付連接,將支付連接返回給前臺
  3. 用戶通過後臺返回的支付連接進行支付(這一步的頁面及支付請求操作的都是支付寶後臺),支付成功後,支付寶將上一步設置的前臺接口同步回調給用戶,並異步訪問後臺接口.

補充:

第二步的 前臺接口 就是支付寶支付成功跳轉後的頁面

注意:

支付寶同步調用前臺接口,會將訂單編號,流水號,支付時間返回給該接口

支付寶異步調用後臺接口,後臺修改訂單狀態,並給返回success

使用

創建應用

登錄支付寶開發者平臺,點擊網頁&移動應用,輸入應用名等信息

這裏我們用支付寶的沙箱環境模擬出一個應用

生成支付寶公鑰

安裝支付寶的支付開發工具: https://opendocs.alipay.com/open/291/105971

  1. 在開發工具中生成應用私鑰和應用公鑰
  2. 拿着應用公鑰到沙盒應用中換取支付寶公鑰

代碼編寫

安裝支付寶開源框架: https://github.com/fzlee/alipay

 

pip install python-alipay-sdk --upgrade

普通使用:

from alipay import AliPay

# 應用私鑰
app_private_key_string = """-----BEGIN RSA PRIVATE KEY-----
MIIEp......8X68tezc=
-----END RSA PRIVATE KEY-----"""

# 支付寶公鑰
alipay_public_key_string = """-----BEGIN PUBLIC KEY-----
MIIBI......fYEmkDtYwIDAQAB
-----END PUBLIC KEY-----"""

alipay = AliPay(
    appid="2016093000631831",
    app_notify_url=None,  # 默認不用設置
    app_private_key_string=app_private_key_string,
    # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你自己的公鑰,
    alipay_public_key_string=alipay_public_key_string,
    sign_type="RSA2", # RSA 或者 RSA2  (沙盒測試時用RSA2)
    debug=True  # 默認False
)

import time
if __name__ == '__main__':
    order_string = alipay.api_alipay_trade_page_pay(
        out_trade_no=str(time.time()),  # 訂單編號
        total_amount=6.66,  # 訂單價格
        subject='小紅帽',  # 商品名稱
        return_url="http://127.0.0.1:8080",  # 支付成功後同步回調的項目前臺頁面
        notify_url="https://example.com/notify"  # 支付成功後異步回調的項目後臺接口
    )
    # 支付連接 = 支付寶網關 +  order_string
    order_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
    print(order_url)  # 得到的是支付寶的支付連接

高級使用,二次封裝

結構

libs
    ├── iPay                            # aliapy二次封裝包
    │   ├── __init__.py                 # 包文件
    │   ├── keys                        # 密鑰文件夾
    │   │   ├── alipay_public_key.pem   # 支付寶公鑰
    │   │   └── app_private_key.pem     # 應用私鑰
    └── └── settings.py                 # 應用配置  
  • setting.py
import os
# 支付寶應用APPID
APP_ID = '2016093000631831'
# 默認異步回調的地址,通常設置None就行
APP_NOTIFY_URL = None
# 應用私鑰文件路徑
APP_PRIVATE_KEY_PATH = os.path.join(os.path.dirname(__file__), 'keys', 'app_private_key.pem')
# 支付寶公鑰文件路徑
ALIPAY_PUBLIC_KEY_PATH = os.path.join(os.path.dirname(__file__), 'keys', 'alipay_public_key.pem')
# 簽名方式
SIGN_TYPE = 'RSA2'
# 是否是測試環境 - 是否是支付寶沙箱
DEBUG = True
# 支付連接(支付寶網關)
DEV_PAY_URL = 'https://openapi.alipaydev.com/gateway.do?'  # 沙盒
PROD_PAY_URL = 'https://openapi.alipay.com/gateway.do?'
  • alipay_public_key.pem
-----BEGIN PUBLIC KEY-----
支付寶公鑰
-----END PUBLIC KEY-----
  • app_private_key.pem
-----BEGIN RSA PRIVATE KEY-----
應用私鑰
-----END RSA PRIVATE KEY-----
  • __init__.py
from alipay import AliPay
from .settings import *
# 對外提供支付對象
alipay = AliPay(
    appid=APP_ID,  # 支付寶應用id
    app_notify_url=APP_NOTIFY_URL,  # 默認異步回調的地址,通常設置None就行
    app_private_key_path=APP_PRIVATE_KEY_PATH,   # 應用私鑰文件路徑
    alipay_public_key_path=ALIPAY_PUBLIC_KEY_PATH,   # 支付寶公鑰文件路徑
    sign_type=SIGN_TYPE,  # 簽名方式
    debug=DEBUG  # 是否是測試環境
)
​
# 對外提供的支付鏈接(支付寶網關)前綴
pay_url = DEV_PAY_URL if DEBUG else PROD_PAY_URL
  • test.py
from libs.iPay import alipay, pay_url
import time
if __name__ == '__main__':
    order_string = alipay.api_alipay_trade_page_pay(
        out_trade_no=str(time.time()),
        total_amount=6.66,
        subject='小紅帽',
        return_url="http://127.0.0.1:8080",  # 同步回調接口
        notify_url="https://example.com/notify"  # 異步回調接口
    )
    order_url = pay_url + order_string
    print(order_url)

案例

  • dev.py
# 前後臺base_url
UP_BASE_URL = 'http://127.0.0.1:8080'
END_BASE_URL = 'http://127.0.0.1:8000'
# alipay回調接口配置
# 上線後必須換成官網地址
# 同步回調的接口(get),前後臺分離時一般設置前臺頁面url
RETURN_URL = UP_BASE_URL + '/pay/success'
# 異步回調的接口(post),一定設置爲後臺服務器接口
NOTIFY_URL = END_BASE_URL + '/order/success/'
  • views.py
from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView

from libs.ipay import alipay, pay_url
import time
from . import models
from django.conf import settings
from utils.response import APIResponse
from utils.logging import logger
from rest_framework.response import Response

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
# 支付接口需要登錄認證
class PayAPIView(APIView):
    authentication_classes = [JSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]
    def post(self, request, *args, **kwargs):
        # 1)獲取前臺信息:商品、價格、支付方式
        request_data = request.data
        subject = request_data.get('subject')
        total_amount = request_data.get('total_amount')
        pay_type = request_data.get('pay_type')
        if not (subject and total_amount and pay_type):
            return APIResponse(2, '數據有誤')

        # 2)生成訂單(訂單號,訂單表的訂單記錄)
        out_trade_no = str(time.time())
        user = request.user  # 當前登錄用戶
        try:
            models.Order.objects.create(subject=subject, total_amount=total_amount, pay_type=pay_type, out_trade_no=out_trade_no, user=user)
        except:
            return APIResponse(1, '訂單生成失敗')
        # 3)生成支付鏈接,並返回
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=out_trade_no,
            total_amount=total_amount,
            subject=subject,
            return_url=settings.RETURN_URL,
            notify_url=settings.NOTIFY_URL
        )
        order_url = pay_url + order_string
        return APIResponse(order_url=order_url)


# 支付成功的回調不需要登錄認證 - 支付寶回調不會攜帶jwt-token,但是支付回調參數需要自己做校驗
class SuccessAPIView(APIView):

    def patch(self, request, *args, **kwargs):
        # request.query_params是QueryDict類型,不能調用pop方法
        request_data = request.query_params.dict()
        signature = request_data.pop("sign")
        success = alipay.verify(request_data, signature)
        if success:  # 校驗通過
            print("通過")
            # 一般不在該處修改訂單狀態
            return APIResponse()
        return APIResponse(1, '校驗失敗')

    # 支付寶異步回調
    def post(self, request, *args, **kwargs):
        # 默認是QueryDict類型,不能使用pop方法
        request_data = request.data.dict()
        # 必須將 sign、sign_type(內部有安全處理) 從數據中取出,拿sign與剩下的數據進行校驗
        sign = request_data.pop('sign')
        result = alipay.verify(request_data, sign)
        # 異步回調:修改訂單狀態
        if result and request_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
            out_trade_no = request_data.get('out_trade_no')
            logger.critical('%s支付成功' % out_trade_no)
            try:
                order = models.Order.objects.get(out_trade_no=out_trade_no)
                if order.order_status != 1:
                    order.order_status = 1
                    order.save()
                    return Response('success')  # 必須返回success字符串,8次異步回調機制
            except:
                pass
        return Response('failed')
  • urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
    # 支付接口 - 訂單信息換支付鏈接
    path('pay/', views.PayAPIView.as_view()),
    # 支付成功結果 - 修改訂單狀態
    path('success/', views.SuccessAPIView.as_view())
]

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章