支付寶支付環境搭建

1、設置沙箱帳戶css

  一、經過支付寶登錄到螞蟻金服開放平臺(https://openhome.alipay.com/platform/appDaily.htm?tab=info)html

    註釋:只要登錄上後沙箱應用上面就有本身的APPID和支付寶網關,支付寶網關就是支付寶給咱們的api接口,而RSA2(SHA256)密鑰是須要本身設置德。python

  二、設置密鑰django

    

    下載支付寶自動生成密鑰軟件,將本身的應用私鑰保存在本地,應用公鑰發送給支付寶點擊確認後就會獲得支付寶發送給咱們的支付寶公鑰json

  三、下載支付寶錢包bootstrap

    經過手機掃碼下載支付寶沙箱錢包,沙箱錢包的用戶名和密碼在在沙箱帳戶中查找api

 2、設置Alipay服務器

  一、因爲支付寶不提供python版本的sdk,因此須要手動編寫python版本sdk。app

  二、編寫python版本sdk框架

    一、項目目錄結構

      

    二、settings配置(主要寫一些必須的配置項)

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

class AliPayConfig(object):
    # 商戶app_id,即沙箱的APPID
    app_id = "2016081500252288"

    # 商戶私鑰路徑
    merchant_private_key_path = os.path.join(BASE_DIR, "keys", "app_private_2048.txt")

    # 支付寶公鑰路徑
    alipay_public_key_path = os.path.join(BASE_DIR, "keys", "alipay_public_2048.txt")

    # 服務器異步通知頁面路徑 需http: // 格式的完整路徑,不能加?id = 123 這類自定義參數,必須外網能夠正常訪問
    # 向支付寶支付成功後,支付寶會執行回調函數來通知服務器的支付狀態,即支付寶發送post請求是支付寶支付成功後調用服務器的驗證函數,服務器來判斷是不是真的支付成功
    notify_url = "http://47.94.172.250:8804/api/v1/trade/alipay/"

    # 頁面跳轉同步通知頁面路徑 需http: // 格式的完整路徑,不能加?id = 123 這類自定義參數,必須外網能夠正常訪問
    # 向支付寶支付成功後,支付寶會執行回調函數來通知服務器的支付狀態,即支付寶發送get請求是支付寶支付成功後調用服務器的支付成功的頁面信息
    return_url = "http://47.94.172.250:8804/api/v1/trade/alipay/"

    # 簽名方式(當前只支持RSA和RSA2)
    sign_type = "RSA2"

    # 字符編碼格式
    charset = "utf-8"

    # 支付寶網關(若是是線上環境的話, dev 這三個字去掉便可)
    gateway_url = "https://openapi.alipaydev.com/gateway.do"

    # 異步通知參數DOC(支付寶會主動發起POST請求)
    notify_doc = "https://docs.open.alipay.com/270/105902/"
View Code

 

     三、支付寶公鑰和應用私鑰配置

      註釋:一、主要是存放在keys文件中,alipay_public_2048.txt中保存的是支付寶公鑰,app_private_2048.txt中保存的是應用私鑰。

          二、不管是支付寶公鑰仍是應用私鑰在文件中的寫法必須遵循以下格式:

            -----BEGIN RSA PRIVATE KEY-----
              中間寫支付寶公鑰或是應用私鑰
            -----END RSA PRIVATE KEY-----

    四、alipay文件配置

    註釋:配置以前必須先安裝Crypto和pycryptodome倆模塊

 

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
from config.settings import AliPayConfig
import json

# 文檔連接(支付成功回調通知參數等相關, 支付寶交易狀態是 TRADE_SUCCESS 會主動發起異步通知)
# https://docs.open.alipay.com/270/105902/

# 沙箱測試環境可以使用沙箱支付寶支付
# 帳號: rjoegs7144@sandbox.com
# 密碼: 111111
# 支付密碼: 111111

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:  #alipaydev是測試環境,alipay是生產環境
            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)

#上面的類就是支付寶的api接口,如下只須要調實例化產生api接口類對象後
if __name__ == "__main__":
#支付寶的api接口類實例化
    alipay = AliPay(
        appid=AliPayConfig.app_id,      #APPDI
        app_notify_url=AliPayConfig.notify_url,   #支付成功後的post請求
        return_url=AliPayConfig.return_url,     #支付成功後的get請求
        app_private_key_path=AliPayConfig.merchant_private_key_path,   #應用私鑰
        alipay_public_key_path=AliPayConfig.alipay_public_key_path,  # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你本身的公鑰
        debug=True,  # 默認False,是不是測試環境仍是生產環境
    )

    #調用實例化對象下的direct_pay方法,生成支付的url
  import time
    query_params = alipay.direct_pay(
        subject="luffycity",  # 商品簡單描述,可自定義哦
        out_trade_no="%s"time.time(),  # 商戶訂單號,可自定義可是商品訂單號必須是惟一的,不然發送給支付寶不會被處理
        total_amount=100.00,  # 交易金額(單位: 元 保留倆位小數)
    )

    pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)#這就是生成的支付寶api接口
    print(pay_url)

 # 這一部分是驗證url的簽名,即支付成功後支付寶經過post請求返回給服務器的驗證數據
    return_url = 'http://47.92.87.172:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2Fvhd
            DYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdp
            VQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6
            Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&meth
            od=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0' #這一部分就是支付寶返回給服務器的數據
    o = urlparse(return_url)  #路徑解析,就至關因而取request.body中的數據
    query = parse_qs(o.query)  #將數據轉換成字典
    processed_query = {}
    ali_sign = query.pop("sign")[0]
  for key, value in query.items(): 
    processed_query[key] = value[0] # 驗證簽名校驗的結果,就是支付寶發的post請求驗證
  print(alipay.verify(processed_query, ali_sign))  #調用alipay對象下的verify認證方法,若是返回True就證實支付成功,不然失敗
View Code

 

   三、根據步驟2中的講解,本身編寫支付寶支付框架

    一、如圖所示:

      

    二、pay.py文件(該文件內就只寫了個類,該類就是支付寶api的接口類)

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)
View Code

 

      三、page1.html文件

<!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>
View Code

 

       四、url.py文件

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),
]
View Code

 

      五、key文件夾

         一、pri文件存放的是應用私鑰

         二、pub文件存放的是支付寶公鑰

    六、views.py文件

from django.shortcuts import render, redirect, HttpResponse
from utils.pay import AliPay
import json
import time

#將實例化的api接口和參數存放子啊函數中
def ali():
    #APPID
    app_id = "2016082500309412"

    # POST請求,支付寶回調函數,用於支付成功後的驗證
    notify_url = "http://www.dabo.com:8009/page2/"

    # GET請求,支付寶回調函數,用於支付成功後返回給用戶的頁面信息
    return_url = "http://www.dabo.com:8009/page2/"

    merchant_private_key_path = "keys/pri"  #應用私鑰
    alipay_public_key_path = "keys/pub"  #支付寶公鑰

    #實例化api接口類
    alipay = AliPay(
        appid=app_id,
        app_notify_url=notify_url,
        return_url=return_url,
        app_private_key_path=merchant_private_key_path,
        alipay_public_key_path=alipay_public_key_path,
        debug=True,  # 默認False,若是是True則爲測試環境,若是爲Flase則爲生成環境
    )
    return alipay


def page1(request):
    if request.method == "GET":
        return render(request, 'page1.html')
    else:
        money = float(request.POST.get('money'))
        alipay = ali()          # 生成支付的url
        query_params = alipay.direct_pay(    #調用支付寶對象下的direct_pay方法
            subject="充氣式韓紅",  # 商品簡單描述
            out_trade_no="x2" + str(time.time()),  # 商戶訂單號,有切必須惟一
            total_amount=money,  # 交易金額(單位: 元 保留倆位小數)
        )
        #支付寶api完整路徑
        pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

        return redirect(pay_url)


def page2(request):
    alipay = ali()
    if request.method == "POST":
        # 檢測是否支付成功
        # 去請求體中獲取全部返回的數據:狀態/訂單號
        from urllib.parse import parse_qs


        """
        # request.body                  => 字節類型
        # request.body.decode('utf-8')  => 字符串類型
        {"k1":["v1"],"k2":["v1"]}
        k1=[v1]&k2=[v2]
        """
        body_str = request.body.decode('utf-8')
        post_data = parse_qs(body_str)#parse_qs是將字符串直接json成字典的形式
        # {k1:[v1,],k2:[v2,]},這是支付寶返回給咱們的json數據,可是不是咱們想要的數據,

        post_dict = {}
        for k, v in post_data.items():#因此經過這種方式能夠轉換成咱們想要的{k1:v1}格式的數據
            post_dict[k] = v[0]


        print(post_dict)
        """
        {'gmt_create': '2017-11-24 14:53:41', 'charset': 'utf-8', 'gmt_payment': '2017-11-24 14:53:48', 'notify_time': '2017-11-24 14:57:05', 'subject': '充氣式韓紅', 'sign': 'YwkPI9BObXZyhq4LM8//MixPdsVDcZu4BGPjB0qnq2zQj0SutGVU0guneuONfBoTsj4XUMRlQsPTHvETerjvrudGdsFoA9ZxIp/FsZDNgqn9i20IPaNTXOtQGhy5QUetMO11Lo10lnK15VYhraHkQTohho2R4q2U6xR/N4SB1OovKlUQ5arbiknUxR+3cXmRi090db9aWSq4+wLuqhpVOhnDTY83yKD9Ky8KDC9dQDgh4p0Ut6c+PpD2sbabooJBrDnOHqmE02TIRiipULVrRcAAtB72NBgVBebd4VTtxSZTxGvlnS/VCRbpN8lSr5p1Ou72I2nFhfrCuqmGRILwqw==', 'buyer_id': '2088102174924590', 'invoice_amount': '666.00', 'version': '1.0', 'notify_id': '11aab5323df78d1b3dba3e5aaf7636dkjy', 'fund_bill_list': '[{"amount":"666.00","fundChannel":"ALIPAYACCOUNT"}]', 'notify_type': 'trade_status_sync', 'out_trade_no': 'x21511506412.4733646', 'total_amount': '666.00', 'trade_status': 'TRADE_SUCCESS', 'trade_no': '2017112421001004590200343962', 'auth_app_id': '2016082500309412', 'receipt_amount': '666.00', 'point_amount': '0.00', 'app_id': '2016082500309412', 'buyer_pay_amount': '666.00', 'sign_type': 'RSA2', 'seller_id': '2088102172939262'}
        {'stade_status': "trade_success",'order':'x2123123123123'}
        """
        sign = post_dict.pop('sign', None)

        status = alipay.verify(post_dict, sign)#把數據和簽名放在一塊兒進行進行校驗,驗證是否支付成功
        if status:
            #print(post_dict['stade_status'])#支付寶系統的交易狀態,stade_status這個寫的有點問題,單詞好像寫錯了
            print(post_dict['out_trade_no'])#商品網站惟一訂單號

        return HttpResponse('POST返回')
    else:
        # QueryDict = {'k':[1],'k1':[11,22,3]}
        params = request.GET.dict()
        sign = params.pop('sign', None)
        status = alipay.verify(params, sign)
        print('GET驗證', status)
        return HttpResponse('支付成功')
View Code
相關文章
相關標籤/搜索