支付寶支付

支付寶支付html

 

1、沙箱環境介紹

螞蟻沙箱環境(Beta)是協助開發者進行接口功能開發及主要功能聯調的輔助環境。沙箱環境模擬了開放平臺部分產品的主要功能和主要邏輯(當前沙箱支持產品請參考「沙箱支持產品列表」)。
在開發者應用上線審覈前,開發者能夠根據自身需求,先在沙箱環境中瞭解、組合和調試各類開放接口,進行開發調通工做,從而幫助開發者在應用上線審覈完成後,能更快速、更順利的進行線上調試和驗收工做。python

詳情,請參考連接:git

https://docs.open.alipay.com/200/105311/github

 

商家要使用支付寶支付功能,須要申請一個支付接口。須要提交企業的營業執照等相關信息!算法

可是對於我的開發者來講,並無這些資料。因此,爲了幫助開發者進行接口測試,我的可使用沙箱環境測試。只要你有支付寶帳號就行。裏面提供了測試帳號,錢能夠隨便充值。注意:數據是假的,金額可能會被清洗!sql

2、申請帳號

打開連接數據庫

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

打開手機支付寶,掃描二維碼,就能夠登陸了json

登陸成功以後,點擊自研開發者windows

點擊導航的開發者中心-->研發服務

 

填寫相關信息,完成實名認證

再次進入沙箱應用,點擊查看應用公鑰

 

點擊設置應用公鑰

點擊查看祕鑰生成方法

 下載windows版本的壓縮包,解壓一下,效果以下:

點擊RSA簽名驗籤工具,直接點擊生成祕鑰

不要管JAVA和非JAVA,默認的就能夠了

 

 效果以下:

它會在當前壓縮包裏面的RSA祕鑰,生成2個txt文件

打開應用公鑰2048.txt,將裏面的內容複製一下

進入到剛纔的頁面,輸入公鑰,點擊保存。

若是保存時,提示公鑰格式不對。再生成一次公鑰,再次輸入,就能夠了!

3、SDK

SDK的英文全名是:software development kit,翻譯成中文的意思就是「軟件開發工具包」


通俗一點的理解,是指由第三方服務商提供的實現軟件產品某項功能的工具包。通常以集合api和文檔、範例、工具的形式出現。


一般SDK是由專業性質的公司提供專業服務的集合,好比提供安卓開發工具、或者基於硬件開發的服務等。也有針對某項軟件功能的SDK,如推送技術、圖像識別技術、移動支付技術等,同時資源優點類的公司也提供資源共享的SDK,如一些廣告SDK提供盈利渠道,分發SDK提供產品下載渠道。

 

注意:支付寶的SDK,採用了rsa加密算法。對於發送,返回的數據,它有必定的加密算法以及解密算法。

因此,咱們要使用支付寶服務,發送的數據,必須符合它的加密要求才行。有了SDK以後,咱們不須要關心加密算法,只須要關係業務邏輯就行!

 

打開支付寶提供的SDK

https://docs.open.alipay.com/54/103419/

下載連接:

https://pypi.org/project/alipay-sdk-python/

不須要下載SDK!

不須要下載SDK!

不須要下載SDK!

這裏使用的是,根據官方SDK封裝好的一個python文件,須要安裝依賴包pycryptodome

pip3 install pycryptodome

pay.py代碼以下

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

這個pay.py就是sdk 

 

4、支付寶密鑰

爲了保證交易雙方(商戶和支付寶)的身份和數據安全,開發者在調用接口前,須要配置雙方密鑰,對交易數據進行雙方校驗。

 

5、集成和開發

在開始集成和開發前,首先了解一下經常使用的接入方式和架構建議:

 

其次,爲了保證交易安全,支付寶採用了一系列的安全手段:

  1. 採用HTTPS協議傳輸交易數據,防止數據被截獲,解密。

  2. 採用RSA非對稱密鑰,明確交易雙方的身份,保證交易主體的正確性和惟一性

 6、項目部署

新建一個django項目alipay,版本爲django 2.x

在項目根目錄建立utils,將上面的pay.py複製到此目錄下

在項目根目錄建立keys

進入目錄secret_key_tools_RSA_win\RSA簽名驗籤工具windows_V1.4\RSA密鑰

將裏面的應用公鑰2048.txt,應用私鑰2048.txt這2個文件,複製到keys目錄

python代碼裏面的文件名,不要出現中文!

重命名爲英文!!!!!!!!

將這2個txt重命名爲alipay_public_2048.txt和app_private_2048.txt

注意:!!!!!!!!!!!!!!!!!!!!!!!!

alipay_public_2048.txt這個是公鑰,不能用本地的。要用網頁的!

點擊查看支付寶公鑰,不是應用公鑰!

複製裏面的公鑰

打開alopay_public_2048.txt,必須增長頭部和尾部,將網頁複製的公鑰粘貼到裏面

-----BEGIN PUBLIC KEY-----
MIIBI...
-----END PUBLIC KEY-----
View Code

效果以下:

必定要覆蓋公鑰!!!!!!!!!!!!!!!!!!!

 

打開app_private_2048.txt,必須增長頭部和尾部

 

-----BEGIN RSA PRIVATE KEY-----
MIIE...
-----END RSA PRIVATE KEY-----
View Code

 

效果以下:

 注意:這2個txt文件,必須爲utf-8編碼,不然啓動django項目會報錯!

 

修改urls.py,增長路徑

from django.contrib import admin
from django.urls import path,re_path

from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
    path('index/', views.index),
    re_path('buy/(?P<gid>\d+)/', views.buy),
    path('check_order/', views.check_order),
    re_path('show', views.show),
    path('order_list/', views.order_list),
]
View Code

修改models.py,增長2個表

from django.db import models

# Create your models here.
class Goods(models.Model):  # 商品
    name = models.CharField(max_length=32,verbose_name="名稱")
    price = models.FloatField(verbose_name="價格")


class Order(models.Model):  # 訂單
    no = models.CharField(max_length=64,verbose_name="訂單號")
    goods = models.ForeignKey(to='Goods',on_delete=models.CASCADE,verbose_name="商品ID")
    status_choices = (
        (1,'未支付'),
        (2,'已支付'),
    )
    status = models.SmallIntegerField(choices=status_choices,default=1,verbose_name="支付狀態")
View Code

修改views.py,增長視圖函數

注意:appid改爲本身的,後6位我改爲xx了

from django.shortcuts import render, HttpResponse, redirect
from app01 import models
import uuid
from utils.pay import AliPay  # 導入sdk


# Create your views here.
def index(request):
    goods_list = models.Goods.objects.all()

    return render(request, 'index.html', {'goods_list': goods_list})


# 全局變量
alipay = AliPay(
    appid="2016091700xxxxxx",  # 注意,改爲本身的!
    app_notify_url="http://127.0.0.1:8000/check_order/",  # POST,發送支付狀態信息
    return_url="http://127.0.0.1:8000/show/",  # GET,將用戶瀏覽器地址重定向回原網站
    app_private_key_path="keys/app_private_2048.txt",
    alipay_public_key_path="keys/alipay_public_2048.txt",
    debug=True,  # 默認True測試環境、False正式環境
)


def buy(request, gid):
    """
    去購買並支付
    :param request:
    :param gid:
    :return:
    """
    obj = models.Goods.objects.get(pk=gid)

    # 生成訂單(未支付)
    no = str(uuid.uuid4())
    models.Order.objects.create(no=no, goods_id=obj.id)

    # 根據
    #   APPID
    #   支付寶網關
    #   公鑰和私鑰
    # 生成要跳轉的地址
    # 沙箱環境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

    query_params = alipay.direct_pay(
        subject=obj.name,  # 商品簡單描述
        out_trade_no=no,  # 商戶訂單號
        total_amount=obj.price,  # 交易金額(單位: 元 保留倆位小數)
    )

    pay_url = "https://openapi.alipaydev.com/gateway.do?{0}".format(query_params)

    return redirect(pay_url)


def check_order(request):
    """
    POST請求,支付寶通知支付信息,咱們修改訂單狀態
    :param request:
    :return:
    """
    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)
        status = alipay.verify(post_dict, sign)
        if status:
            # 支付成功,獲取訂單號將訂單狀態更新
            out_trade_no = post_dict['out_trade_no']
            models.Order.objects.filter(no=out_trade_no).update(status=2)
            return HttpResponse('success')
        else:
            return HttpResponse('支持失敗')
    else:
        return HttpResponse('只支持POST請求')


def show(request):
    """
    回到咱們頁面
    :param request:
    :return:
    """

    if request.method == "GET":
        params = request.GET.dict()
        sign = params.pop('sign', None)
        status = alipay.verify(params, sign)
        if status:
            return HttpResponse('支付成功')
        else:
            return HttpResponse('失敗')
    else:
        return HttpResponse('只支持GET請求')


def order_list(request):
    """
    查看全部訂單狀態
    :param request:
    :return:
    """
    orders = models.Order.objects.all()
    return render(request, 'order_list.html', {'orders': orders})
View Code

增長index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {% for row in goods_list %}
            <li>{{ row.name }},價格:{{ row.price }}   <a href="/buy/{{ row.id }}/">購買</a></li>
        {% endfor %}
    </ul>
</body>
</html>
View Code

增長order_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table border="1">
        {% for item in orders %}
        <tr>
            <td>{{ item.id }}</td>
            <td>{{ item.no }}</td>
            <td>{{ item.goods.name }}</td>
            <td>{{ item.get_status_display }}</td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>
View Code

修改settings.py,關閉csrf

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
View Code

項目結構以下:

alipay/
├── alipay
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── app01
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── 0002_auto_20180810_1623.py
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── keys
│   ├── alipay_public_2048.txt
│   └── app_private_2048.txt
├── manage.py
├── templates
│   ├── index.html
│   └── order_list.html
└── utils
    └── pay.py
View Code

使用2個命令,生成表

python manage.py makemigrations
python manage.py migrate

使用navicat打開sqlite3數據庫,增長几條數據

INSERT INTO app01_goods ("id", "name", "price") VALUES (1, 'Apple iPad Pro', 5588.0);
INSERT INTO app01_goods ("id", "name", "price") VALUES (2, 'i5 8500', 1599.0);
INSERT INTO app01_goods ("id", "name", "price") VALUES (3, '拯救者Y7000', 7299.0);
INSERT INTO app01_goods ("id", "name", "price") VALUES (4, '大白兔奶糖', 1.0);

啓動django項目,訪問首頁

 

7、本地測試支付

 

點擊購買大白兔奶糖,它會自動跳轉到支付寶的支付頁面

注意:要沙箱環境的APP

它只提供了安卓版本,下載以後,進行安裝

點擊沙箱帳號

它提供了商家信息和買家信息

默認的買家信息,帳號有9萬多塊。注意:這個錢是虛擬的,不能體現!

若是錢不夠了,能夠輸入任意金額進行充值!

手機登陸買家帳號,密碼爲111111

打開掃一掃,進行支付

 提示支付成功

 

注意:若是出現失敗,多是公鑰錯了!必定是網頁的支付寶公鑰,不是本地的公鑰!

 

 查看買家信息,少了一塊錢

 

查看商家信息,多了一塊錢。那一分錢,是扣了手續費!

注意:這個是本地環境測試的。

若是須要服務器測試,請修改views.py中的全局變量,將127.0.0.1改成服務器的公網IP便可!

 

查看訂單狀態,這裏的狀態是未支付。爲何呢?由於支付寶要發送一個POST請求到

http://127.0.0.1:8000/check_order/ 才能修改狀態。因爲在本地,支付寶沒法訪問此地址!

注意:商品價格,必須保證最多爲小數點2位。這個是支付寶規定的!

8、線上測試支付

將代碼上傳到公網服務器,修改views.py,將裏面的127.0.0.1改爲公網IP。

再次支付一次,查看訂單狀態。狀態爲已支付

 

完整代碼,請參數github

https://github.com/987334176/alipay

 

 

螞蟻沙箱環境(Beta)是協助開發者進行接口功能開發及主要功能聯調的輔助環境。沙箱環境模擬了開放平臺部分產品的主要功能和主要邏輯(當前沙箱支持產品請參考「沙箱支持產品列表」)。
在開發者應用上線審覈前,開發者能夠根據自身需求,先在沙箱環境中瞭解、組合和調試各類開放接口,進行開發調通工做,從而幫助開發者在應用上線審覈完成後,能更快速、更順利的進行線上調試和驗收工做。

詳情,請參考連接:

https://docs.open.alipay.com/200/105311/

 

商家要使用支付寶支付功能,須要申請一個支付接口。須要提交企業的營業執照等相關信息!

可是對於我的開發者來講,並無這些資料。因此,爲了幫助開發者進行接口測試,我的可使用沙箱環境測試。只要你有支付寶帳號就行。裏面提供了測試帳號,錢能夠隨便充值。注意:數據是假的,金額可能會被清洗!

相關文章
相關標籤/搜索