支付寶支付業務

支付寶支付業務

在支付寶開發平臺中建立項目

在咱們的業務中可能會用到須要進行支付的業務,這時咱們能夠用支付寶給咱們提供的apicss

首先登陸支付寶開發平臺https://open.alipay.com/platform/home.htm,點擊開發接入html

而後選擇支付應用linux

咱們就能夠開始建立項目了git

這是建立正式的項目,咱們這裏可使用沙箱環境進行測試github

進入後會自動幫你建立一個項目,並提供相關的信息數據庫

appid和支付寶網關都是咱們訪問支付寶支付頁面時須要的,下面的應用網關是支付後支付寶會發送post請求的地址,咱們能夠拿到post請求的信息進行驗證,確認是否支付成功,而受權回調地址是支付完成後會跳轉的地址,須要進行設置django

而後咱們須要進行RSA的公鑰設置,這裏須要下載一個生成公鑰的軟件,生成後將公鑰複製進來,會變成下圖所示json

這裏有一個應用公鑰和支付寶公鑰,須要加以區分,後面會用到bootstrap

都設置完成後,支付寶還會給咱們提供測試的帳號,可是要登陸須要下載沙箱版的支付寶,只有安卓手機能下...後端

咱們能夠登陸買家帳號進行付款,看商家帳號是否會收到

設置都完成了,那麼咱們的項目該怎麼使用它呢

使用方法

因爲要使用RSA加密,因此咱們須要下載一個模塊

Linux:
    pip3 install pycrypto
Windows:
    下載 pycryptodome.whl 
        pip3 install pycryptodome.whl

在linux中能夠直接下載,可是在window須要先下載一個pycryptodome.whl,然戶在本地進行安裝,下載地址https://pypi.org/project/pycryptodome/#files

而後咱們建立一個django項目,提供如下的url

複製代碼
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^page1/', views.page1),
    url(r'^page2/', views.page2),
    url(r'^page3/', views.page3),
]
複製代碼

通常這種提供接口的服務都會給咱們提供一個SDK供咱們使用,支付寶也提供了,可是沒有pthon的,因此咱們只能本身到github上找一個修改了使用

複製代碼
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)
複製代碼

將它拷貝到咱們的項目目錄中

而後咱們要寫咱們的視圖函數,page1給咱們提供了付款的頁面

複製代碼
from django.shortcuts import render, redirect, HttpResponse
import json
import time
from utils.pay import AliPay
from app01 import models
from django.conf import settings

def alipay_obj():
    alipay = AliPay(
        appid=settings.ALIPAY_APPID,
        app_notify_url="http://47.93.4.198:8004/page2/",
        return_url="http://47.93.4.198:8004/page3/",
        app_private_key_path="keys/app_private_2048.txt",
        alipay_public_key_path="keys/alipay_public_2048.txt",  # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你本身的公鑰
        debug=True,  # 默認False,
    )
    return alipay

def page1(request):
    if request.method == "GET":
        return render(request, 'page1.html')
    else:
        # 根據:appid、應用私鑰、支付寶公鑰、金額、訂單名稱、訂單號 生一個URL,再進行跳轉。
        # 1. 要支付的金額
        money = float(request.POST.get('money'))

        alipay = alipay_obj()

        order_num = "xxxxxxxxxxxx" + str(time.time())

        # 生成支付的url
        query_params = alipay.direct_pay(
            subject="充氣式文傑",  # 商品簡單描述
            out_trade_no=order_num,  # 商戶訂單號:ijldsddfsdfsdf
            total_amount=money,  # 交易金額(單位: 元 保留倆位小數)
        )

        # 3. query_params,根據appid、應用私鑰、支付寶公鑰、金額、訂單名稱、訂單號生成的參數。

        # 4. 拼接URL: https://openapi.alipaydev.com/gateway.do + query_params
        pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)


        # ####### 在數據庫中生成訂單 ###########
        models.Order.objects.create(num=order_num,price=money)

        return redirect(pay_url)
複製代碼

get請求訪問時,咱們能夠看到一個輸入金額和提交的頁面

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="dist/css/bootstrap.css">
</head>
<body>
    <form method="POST">
        {% csrf_token %}
        <input type="text" name="money">
        <input type="submit" value="去支付" />
    </form>


<script></script>
</body>
</html>
複製代碼

當用戶輸入了價格後,會發送post請求,後端拿到價格,而後用咱們找到的SDK中的AliPay類生成一個對象,這個對象中要包含appid,app_notify_url(支付完成後發送post請求的地址),return_url(支付完成後跳轉的地址),app_private_key_path(應用私鑰的

路徑),alipay_public_key_path(支付寶公鑰的路徑,注意不是應用公鑰)

這裏要注意,支付完成後發送post請求的地址和跳轉地址應該是公網ip,否則是發送不成功的

公鑰和私鑰

須要注意,文件的內容要加BEGIN和END

複製代碼
公鑰:
    -----BEGIN PUBLIC KEY-----
    公鑰
    -----END PUBLIC KEY-----
私鑰:
    -----BEGIN RSA PRIVATE KEY-----
    私鑰
    -----END RSA PRIVATE KEY-----
複製代碼

拿到這個alipay對象後,使用它的alipay.direct_pay方法生成url的參數,這裏要傳一些參數,包括商品描述,訂單號和金額

# 生成支付的url
query_params = alipay.direct_pay(
    subject="充氣式文傑",  # 商品簡單描述
    out_trade_no=order_num,  # 商戶訂單號:ijldsddfsdfsdf
    total_amount=money,  # 交易金額(單位: 元 保留倆位小數)
)

最後將這個參數拼接到支付寶的網關後面,最後跳轉這個頁面

# 4. 拼接URL: https://openapi.alipaydev.com/gateway.do + query_params
pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

完成了上面的步驟說明已經有一條訂單生成了,只是還未完成付款,因此咱們要在訂單表中添加一條數據

訂單表

複製代碼
from django.db import models

class Order(models.Model):
    num = models.CharField(max_length=32)
    price = models.FloatField()
    status_choices = (
        (1,'未支付'),
        (2,'已支付')
    )
    status = models.IntegerField(choices=status_choices,default=1)
複製代碼

添加數據

# ####### 在數據庫中生成訂單 ###########
models.Order.objects.create(num=order_num,price=money)

最後跳轉頁面

return redirect(pay_url)

這裏跳轉到的就是支付寶的付款頁面

咱們能夠用沙箱版的支付版進行付款,付款完成後會發送兩個請求,一個post請求,一個get請求

post請求咱們發送到了page2的url上,用來進行驗證是否付款成功

複製代碼
def page2(request):
    """
    支付寶支付成功後,支付寶主動向個人網站發送:post請求,用於通知我支付成功,我來修改訂單狀態
    PS: 檢驗數據是否合法
    :param request:
    :return:
    """
    alipay = alipay_obj()

    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)
        # 使用sign+支付寶發來的數據,進行校驗
        status = alipay.verify(post_dict, sign)
        if status:
            order_num = post_dict.get('out_trade_no')
            models.Order.objects.filter(num=order_num).update(status=2)

        return HttpResponse('POST返回')
複製代碼

在page2中首先咱們從request.body中獲取返回的數據,再將數據轉換成字典的格式,而後咱們從字典中獲取鍵爲sign的值,再用alipay對象的verify方法獲得一個status狀態,若是爲true說明支付成功了,那麼咱們從字典中獲取訂單號

 

 order_num = post_dict.get('out_trade_no'),再將數據庫中該訂單的狀態改成已支付

get請求發送到page3中

複製代碼
def page3(request):
    alipay = alipay_obj()
    params = request.GET.dict()
    sign = params.pop('sign', None)
    status = alipay.verify(params, sign)
    if status:
        return HttpResponse('支付成功')
    else:
        return HttpResponse('支付失敗')
複製代碼

在這裏咱們也要驗證一下是否支付成功,而後來進行相應的跳轉

 到這裏支付功能就實現了

總結

總體的流程

複製代碼
- 申請帳號
- 申請appid
- 下載SDK
- URL接口

沙箱環境測試:
         APPID: 2016082500309412
        支付寶網關:https://openapi.alipaydev.com/gateway.do
      數據加密:
                應用公鑰
                應用私鑰
            
      1. 拷貝 pay.py
      2. 在支付寶填入:應用公鑰 -> 支付寶公鑰
      3. 拷貝我本身的應用私鑰
         PS: 支付寶公鑰+應用私鑰
         公鑰:
            -----BEGIN PUBLIC KEY-----
            公鑰
            -----END PUBLIC KEY-----
         私鑰:
            -----BEGIN RSA PRIVATE KEY-----
            私鑰
            -----END RSA PRIVATE KEY-----
      4. 環境 
         Linux:
            pip3 install pycrypto
         Windows:
            下載 pycryptodome.whl 
                pip3 install pycryptodome.whl 
                pip3 install django 
複製代碼
相關文章
相關標籤/搜索