支付寶支付功能接入(PC)


  在使用支付寶支付功能開發的階段, 可以在沙箱環境下進行開發, 開發完成之后, 到線上再進行相關參數的替換即可

一. 登陸支付寶開放平台(https://open.alipay.com/platform/home.htm)

進入沙盒環境:

 

 

 

二. 設置加密方式 RSA 或者RSA2(推薦)

  1. 生成RSA秘鑰

    可以使用支付寶提供的生成工具,具體說明見官網說明(https://opendocs.alipay.com/open/291/105971#LDsXr)

    使用工具生成秘鑰對

 

  應用公鑰: 需要將公鑰填寫到RSA2秘鑰的設置中

  應用私鑰:是生成簽名需要使用的

  支付寶公鑰: 在設置中點擊查看支付寶公鑰即可看到

  

 

 

  2. 秘鑰文件的修改

    在秘鑰文件中的內容的頭部和尾部加上兩行字符串, 注意所有的秘鑰都要添加

-----BEGIN PRIVATE KEY-----
# 秘鑰內容....
-----END PRIVATE KEY-----

    這樣可以方便的直接讀取文件

 

三. 集成支付寶到項目

  基本流程:

1\. 接收商品名稱信息、訂單金額、訂單號
2\. 請求支付寶預付訂單創建接口
3\. 根據返回的URL返回給客戶端
4\. 用戶支付
5\. 用戶支付完成之后,客戶端會跳轉到一個頁面(服務器預置頁面)
6\. 用戶支付完成之后,支付寶會調用回調(通知服務器)

  安裝模塊

# 需要模塊加密方面的模塊
pip install pycryptodome   

 

 

封裝類

# __init__.py文件(直接拷貝)
from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import b64encode, b64decode
from urllib.parse import quote_plus
from urllib.parse import urlparse, parse_qs
from urllib.request import urlopen
from base64 import decodebytes, encodebytes
import json

# 支付寶封裝(不需要做任何修改)
class AliPay(object):
    """
    支付寶支付接口
    """
    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.import_key(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 ordered_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)

# 初始化操作
# 設置秘鑰公鑰的存放路徑
app_private_key_path = os.path.join(BASE_DIR, 'alipay/ying_yong_si_yao.txt')
alipay_public_key_path = os.path.join(BASE_DIR, 'alipay/zhi_fu_bao_gong_yao.txt')
# 根據自己申請的進行設置 
alipay = AliPay(
    appid="2016091800542542",  # 設置簽約的appid
    app_notify_url="http://112.74.55.3/notify/",   # 異步支付通知url
    app_private_key_path=app_private_key_path,  # 設置應用私鑰
    alipay_public_key_path=alipay_public_key_path,  # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你自己的公鑰,
    debug=True,  # 默認False,            # 設置是否是沙箱環境,True是沙箱環境
    return_url="http://112.74.55.3/result/",   # 同步支付通知url,在這個頁面可以展示給用戶看,只有付款成功后才會跳轉
)

  

備注: return_url 和 notify_url的區別  
    return_url: 
        如果設置了return_url,買家付款成功后會跳到return_url所在頁面;
        在這個頁面可以展示給用戶看,只有付款成功后才會跳轉;

    notify_url:
        服務器后台通知,這個頁面是程序后台運行的(買家和賣家都看不到);
        買家付完款后,支付寶會調用notify_url這個頁面所在的頁面並把相應的參數傳遞到這個頁面;
        這個頁面根據支付寶傳遞過來的參數修改網站訂單的狀態,更新完訂單后需要在頁面上;
        打印出一個success給支付寶,如果反饋給支付寶的不是success,支付寶會繼續調用這個頁面;

  

應用 公鑰:

-----BEGIN PRIVATE KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5X3ezQUT2Cg5ujqoz+dfEBTGzavJRxG8CsXMzs5ualReWI0i3Itn15rG9mqYQUqIsfte2gAHc3+rNgMR8oEjcVrgY0qcPl/D3rj+/h0vYHl7lT5g93+n7/CVlk9KOhCm217kFV3gAJYusIXk6jWtpFXl2nXq1BlNhDgrBli4dozJ2k99OSUfNndrpuGfAVIOGIG4dsff598k+gb92eXwT151DMrqoV++387vRRnNnnBqbnYldBodBJXNyEsUCaf+CtacJy72tL6IwRVErD2MVuGjMWQ25Y1qWa/kNXI6ElIDljZylpWFDfMySMFkyOQra3RePlda7pDeKLmPwbwHpQIDAQAB
-----END PRIVATE KEY-----

 

應用私鑰:

 

-----BEGIN PRIVATE KEY-----
MIIEpQIBAAKCAQEA5X3ezQUT2Cg5ujqoz+dfEBTGzavJRxG8CsXMzs5ualReWI0i3Itn15rG9mqYQUqIsfte2gAHc3+rNgMR8oEjcVrgY0qcPl/D3rj+/h0vYHl7lT5g93+n7/CVlk9KOhCm217kFV3gAJYusIXk6jWtpFXl2nXq1BlNhDgrBli4dozJ2k99OSUfNndrpuGfAVIOGIG4dsff598k+gb92eXwT151DMrqoV++387vRRnNnnBqbnYldBodBJXNyEsUCaf+CtacJy72tL6IwRVErD2MVuGjMWQ25Y1qWa/kNXI6ElIDljZylpWFDfMySMFkyOQra3RePlda7pDeKLmPwbwHpQIDAQABAoIBAQDIh24SE+e9L5kRw4QS9we4E4+L1BnywepgN2q5IuSUgNhX5CucLywz14kXdRlJpAlCDHZbYz8fi2Vdst0R3AnOWSxNVrqkPiX3DguynGnzkPON+43bdm6WWNE9IFep2TcT5RzEFt1PZeuyT60xgmoSeQR6GiNx5hI4AKSHzSqSWteL9xd/IUbHrA4YBXhX/oqJdrQr7Ri+380qoHHLiz5gNkXzVGFKrqVwsvVOBGdQ75S+wSTmJJ6hYm9vF4T7FQk8Ar22/22ZFuNLa1IMDXEe8Oh5NVCoXQqMFyhAYf7oN4AzZrBSvLmd85wfQSmvbthmL2JdOynEytYVa7Q2F3zZAoGBAPp6RDO16jEZySrX4T/eKukCD1Cq2g1LAPsq8dxjfdSgNxtz9qt24vuflZu2RbW/VoLZxDZJEIGhY7x0ClhioEwixBCHuPcXLFRd4mvxawzpJBRg9z0Mquf1cret6CBqsML+hgqlu2WwAbgX6hJ3ZJ8Sz3zICoG3K1NgzQkAbsybAoGBAOqNJ/r56jtB42XeGrNRE9bmpOnNDMN56oKiGDIMMe/FDjwYJw/dX7Ih3k0AAGgQWgdq03s+6ji3sI65mcqUrwBf6gSufDWrOvolbKIIfiC1dn/h81HsfkC5y02hTjfmfYg/nCXF5WUsVahrYRgTZL6iHrav2EighyxZCHDlWiC/AoGBAKdkiqvcSTSjPPOq7lgWi6rIu593bt4vX+p7WsOkofU1VV69cv5IJ41tqzgy6gd6ZcBw9FYrIiLfzp2lFzzWPLZlDY7WhWBsVi3TyHvHDXnFXV1ZKCHek2ENgasLR/RSGOtX2hDHjaN5qsxy/vV0YQSGN6/2qD7kQMoqCRyY83M9AoGBAJrvUViVG2e3V1nkBCktRnivdIJIg8TgVMuj/W2Z5qamuYdywOLW7a7CdpDQjnQvUPkTVM1VuuxqUoOFEJTj8FHECda716qwCE5SyFakW4SJudQx0kPsf12MHUKTahK8+3Ez65Z8CdLyHUcaX1HDBcpzVuyAbdNTzta5nTzOE+ILAoGAF0dMOdqnprsr82MudHqjIZMSanfd+QHgARNT0IQVxIFBIsV4PkGmHKU2EZdcB604mZYqAKaCJy+LZIE6VGpgGydRZ98RSK/jaNprge5cm9KPy9nGdfVKBhPkrU3Lkcix0gcvRWC3HVVaYMiZlS1i1tpwgI8z0xtoA9EoLJGxuYc=
-----END PRIVATE KEY-----

 

 

支付寶公鑰:

-----BEGIN PRIVATE KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtaQiWU7tUcDzqbTT5cWF6PS9jn6sMlpG4wR1YFhUo8TDqW1FI5wi/dfylOqJSS2lV8OOuzMk80Ajq9wbyQ0oB+dmrgcZIwF8bLkHvGyO83SAmmNmZ8bcnlD91EnS2c+vIF02ADpE/NKaX+5gjXj9FMTr5z6cqrpfjxWYB7FCp77fd1hd6TbzkMaW70RzBkWGTVMG91p7uZexVKkURPl5Sb8KRnXSmTkjTTWBFZOsARFVu7nsbMjd+q3RYni3BvhfKyZIW/PtQ9HUrX3tHbGThdj2D4qgO3mWB51IdH0gM9OOTyFYvm9TMJGeJ6KdeELlGLNVGhRAbK4NJjPlnuVd2wIDAQAB
-----END PRIVATE KEY-----

 

 

支付示例:

# 定義資源
class Pay(Resource):
    def post(self):
        # 獲取訂單號,根據訂單生成 支付訂單
        # 支付訂單包括: 訂單號、支付金額、訂單名稱

        # 傳遞參數執行支付類里的direct_pay方法,返回簽名后的支付參數,
        url = alipay.direct_pay(
            subject="測試訂單",  # 訂單名稱
            # 訂單號生成,一般是當前時間(精確到秒)+用戶ID+隨機數
            out_trade_no="201810021221",  # 訂單號
            total_amount=100,  # 支付金額
            return_url="http://112.74.55.3/result/"  # 支付成功后,跳轉url 【客戶端顯示】
        )

        # 將前面后的支付參數,拼接到支付網關
        # 注意:下面支付網關是沙箱環境,最終進行簽名后組合成支付寶的url請求
        re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)

        # 返回的是支付寶的支付地址
        return {'re_url': re_url}

# 添加資源
api.add_resource(Pay, '/paytest/')  # 支付接口

  

支付寶同步數據:

# 支付寶返回的數據
data = {
    'seller_id': '2088102176233911',
    'trade_no': '2018101822001438720500654194', # 訂單號
    'timestamp': '2018-10-18 00:12:54',
    'app_id': '2016091800542542',
    'version': '1.0',
    'sign_type': 'RSA2',
    'out_trade_no': '6823789339978248',
    'auth_app_id': '2016091800542542',
    'total_amount': '123.00',
    'charset': 'utf-8',
    'method': 'alipay.trade.page.pay.return'
    'sign': 'cjok55ELAfbGc9RfwsS93pplaFNUIv0hAsfzbklDU+5sdnq0lc9K1a+IRO8MJZpwCX9OjBLXqY64LEa2cM5hv0bly2HnEmbNTta8e15VdvXFH//zMWT05HeNmXu2+H3Kq2RzQbFs2LIdFD2HmYDnbkwhTyAxnhQWEn7yZdAeD/7vNjGoREW2Xbh3aV5ukSFBihzwTGJNMNxyKei6KxQwJRJKDyV0fdZyH6i39xoyp8aMfQGZqRC9HbKKBW6BsFg/At1hVsMJQ4Ib/6jK2NMNMckUIf1gXLrY4Gv/LaqVSYuZpwNQoEwgHYHiC9KUa0BhsgAlJDEpARliLnvyUUOJKA=='
}

 

 

 支付寶異步數據:

# 支付寶返回的數據
data = {
     "subject": "測試訂單",
     "gmt_payment": "2016-11-16 11:42:19",
     "charset": "utf-8",
     "seller_id": "xxxx",
     "trade_status": "TRADE_SUCCESS",
     "buyer_id": "xxxx",
     "auth_app_id": "xxxx",
     "buyer_pay_amount": "0.01",
     "version": "1.0",
     "gmt_create": "2016-11-16 11:42:18",
     "trade_no": "xxxx",    # 訂單號
     "fund_bill_list": "[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
     "app_id": "xxxx",
     "notify_time": "2016-11-16 11:42:19",
     "point_amount": "0.00",
     "total_amount": "0.01",
     "notify_type": "trade_status_sync",
     "out_trade_no": "xxxx",
     "buyer_logon_id": "xxxx",
     "notify_id": "xxxx",
     "seller_email": "xxxx",
     "receipt_amount": "0.01",
     "invoice_amount": "0.01",
     "sign": "xxx"
}

 

注意點:

  1. 測試的時候, 使用沙盒環境的支付寶

 

   平台提供了沙盒賬號,進行登陸與支付的時候使用

 

  2. 參數說明

appid :2016091700535495(填寫沙箱應用的appid)
return_url: 對於PC網站支付的交易,在用戶支付完成之后,支付寶會根據API中商戶傳入的return_url參數,通過GET請求的形式將部分支付結果參數通知到商戶系統, 即同步返回地址,即支付成功跳轉的頁面url。同步通知詳情
sign 簽名字段,最重要的字段,后面會講,詳見簽名
notify_url: 對於PC網站支付的交易,在用戶支付完成之后,支付寶會根據API中商戶傳入的notify_url,通過POST請求的形式將支付結果作為參數通知到商戶系統。異步通知詳情
biz_content:除公共參數以外的其他必要參數


請求參數這些字段是最后放入公共字段里的biz_content,着重關注必填項

 

 

 

 

  3. 生成簽名與簽名驗證

import json

from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import b64encode, b64decode
from urllib.parse import urlparse, parse_qs, quote_plus


class Alipay(object):
    def __init__(self, app_id, app_private_key_path, alipay_public_key_path, debug=True, return_url=None, notify_url=None):
        self.app_id = app_id
        self.debug = debug
        self.return_url = return_url
        self.notify_url = notify_url

        with open(app_private_key_path) as fp:
            self.app_private_key = RSA.importKey(fp.read())
        with open(alipay_public_key_path) as fp:
            self.alipay_public_key = RSA.importKey(fp.read())

        if debug:
            self.__gateway = "https://openapi.alipaydev.com/gateway.do"
        else:
            self.__gateway = "https://openapi.alipay.com/gateway.do"

    def build_url(self, biz_content):
        """
        :param biz_content: 請求參數部分
        :return: 完整的訪問地址url字符串
        """
        # 初始化必填公共字段,sign除外
        data = {
            "app_id": self.app_id,
            "method": "alipay.trade.page.pay",
            "charset": "utf-8",
            "sign_type": "RSA2",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "version": "1.0",
            "biz_content": biz_content
        }
        if self.notify_url:
            data["notify_url"] = self.notify_url
        if self.return_url:
            data["return_url"] = self.return_url

        # 對待簽名字符串進行RSA簽名
        sign_str = self.get_request_url_str(data, flag=False)
        sign = self.rsa_sign(sign_str.encode("utf-8"))

        # 將簽好名的sign字段拼接到請求字符串最后,字符串需對斜線進行編碼
        request_url_str = self.get_request_url_str(data)
        request_url_str += "&sign={}".format(quote_plus(sign))
        print(self.__gateway + '?' + request_url_str)
        return self.__gateway + '?' + request_url_str

    def translate_url(self, returl_url):
        """
        :param returl_url:返回字符串
        :return: 解析出請求參數字符串及簽名
        """
        o = urlparse(return_url)
        query = parse_qs(o.query)
        processed_query = {}

        ali_sign = query.pop("sign")[0] if "sign" in query else None

        if "sign_type" in query:
            sign_type = query.pop("sign_type")[0]
        for key, value in query.items():
            processed_query[key] = value[0]
        request_url_str = self.get_request_url_str(processed_query, flag=False).encode("utf-8")
        # 將解析后的request_url_str字符串和簽名參數拼接成元祖
        data = ([request_url_str, ali_sign])
        return data


    def get_biz_content(self, out_trade_no, total_amount, subject, **kwargs):
        """
        獲取請求參數
        """
        biz_content = {
            "out_trade_no": out_trade_no,
            "product_code": "FAST_INSTANT_TRADE_PAY",
            "total_amount": total_amount,
            "subject": subject,
        }
        biz_content.update(kwargs)
        return biz_content

    def get_request_url_str(self, data, flag=True):
        """
        :param data: 獲取的字典型請求參數
        :param flag: 是否對字典的值進行編碼斜線
        :return:排好序並用&進行拼接的url字符串
        """
        #對鍵值為字典的轉化為str
        for k, v in data.items():
            if isinstance(v, dict):
                data[k] = json.dumps(v)
        # 按key值字母升序排序為列表,方便拼接
        data_lst = sorted([(k, v) for k, v in data.items()])
        if flag:
            request_url_str = "&".join(["{k}={v}".format(k=k, v=quote_plus(v)) for k, v in data_lst])
        else:
            request_url_str = "&".join(["{k}={v}".format(k=k, v=v) for k, v in data_lst])
        return request_url_str

    def rsa_sign(self, data):
        """
        利用商戶私鑰對待簽名字符串進行簽名
        :param data: 待簽名data,格式bytes
        :return: 生成的簽名字符串
        """
        private_key = self.app_private_key
        hash_obj = SHA256.new(data)
        signer = PKCS1_v1_5.new(private_key)
        signature = signer.sign(hash_obj)
        sign = b64encode(signature).decode("utf-8")
        return sign

    def rsa_verify(self, raw_data, signature):
        """
        使用RSA2的驗簽方法,通過簽名字符串、簽名參數(經過base64解碼)及支付寶公鑰驗證簽名,
        根據返回結果判定是否驗簽通過
        :param raw_data: 原簽名字符串data,格式bytes
        :param signature: 簽名參數
        :return: 驗簽是否通過
        """
        public_key = self.alipay_public_key
        hash_obj = SHA256.new(raw_data)
        verifier = PKCS1_v1_5.new(public_key)

        return verifier.verify(hash_obj, b64decode(signature.encode("utf-8")))


if __name__ == "__main__":
    alipay = Alipay(
        app_id="2016091700535495",
        return_url="http://101.200.32.20:8000/",
        notify_url="http://projectsedus.com/",
        app_private_key_path="../trade/keys/private_2048.txt",
        alipay_public_key_path="../trade/keys/alipay_keys_2048.txt",
    )
    content = alipay.get_biz_content(out_trade_no="201802021232", total_amount=1, subject="測試支付")
    alipay.build_url(content)

    # 根據return_url獲取請求參數
    return_url = "http://101.200.32.20:8000/?charset=utf-8&out_trade_no=201702021230&method=alipay.trade.page.pay.return&total_amount=1.00&sign=gvXy%2FXOSOUQupNTn%2Fi%2BdkpqqcDc0Ia8R5lZ2kQRl5UcVb6w83pJqadDPzjdCTLq7%2F4aoIEHhAR6%2FOZwXmsEGWGd2DNynjiGF2mzIpKjDIaGLhdlP9DMIVy%2BIuCfLB7tHC9n1%2BPYoH1YYVWIqO%2B5FLcTh77ucwP66Glq5gFBKpD0b6fiPYlAjxO%2FAIEiyRxSaZS3fj%2BBOhB%2Fo31rjMLa5EOHgvdDeP8OTa01VVJ3wAMBDHyGslj5cShZQwdALyuxjGTFOb0PHFiWEjS828j7ZiTkWiODZ2QFClLE9NHwR6VrlnT0Ttss9Hfu8zwIBMsK3SWt9f32TnmC2wq7hkvMsyg%3D%3D&trade_no=2018082021001004950200556565&auth_app_id=2016091700535495&version=1.0&app_id=2016091700535495&sign_type=RSA2&seller_id=2088102176097894&timestamp=2018-08-20+23%3A23%3A12"
    data = alipay.translate_url(return_url)
    print(alipay.rsa_verify(data[0], data[1]))

 

 

遇到的問題:

  1. 使用支付提供的驗證簽名工具一直都是簽名失敗, 后台發現是它工具版本問題, 導致不能驗證成功

    下載了1.4版本就可以成功了, 最新版提供了在線工具:https://miniu.alipay.com/keytool/

 

   驗證過程:

一、驗簽准備
1.原始報文:
【gmt_create=2020-03-25 10:59:32&charset=utf-8&gmt_payment=2020-03-25 10:59:38&notify_time=2020-03-25 10:59:38&subject=仙玉小袋&sign=zs+snU7B3l4Cnec/JWBcXWkFUNWrW1ttEeMZI4X2mtuSNQ9LL9Mri3b6QK0xTc/KMs/Cafk0Ln7iRKQZfnuBvvuaOwVxSgjuWT0ygGktDok0HITE2wKeT8j4SNX4ScIGHLqHXkMojXIZxE6bsfD9kjUb356aFItinf3vURu3FuPSkbJR1KZXDm41hz2rQ9o6Sc3Xs2cr7AbCSVgyGjv5c6G6OiqaXcdgM+FxHtxkhfqB/3awRjXMGmLmWr2m5VyFIfNMOuNCdRN7fNkUBIX3h35KI6av/x6BDYKwTW2zrJHVy91C0h3mrVoDNuRrJ4fPMW2ZH88j7Q3exmPTP0gqMA==&buyer_id=2088002582656381&body=仙玉小袋&invoice_amount=0.01&version=1.0&notify_id=2020032500222105938056380529774436&fund_bill_list=[{"amount":"0.01","fundChannel":"PCREDIT"}]&notify_type=trade_status_sync&out_trade_no=TEST171&total_amount=0.01&trade_status=TRADE_SUCCESS&trade_no=2020032522001456380542497258&auth_app_id=2021001144680325&receipt_amount=0.01&point_amount=0.00&buyer_pay_amount=0.01&app_id=2021001144680325&sign_type=RSA2&seller_id=2088031940831575】
 
2.驗簽公鑰:
【MIIBIjANBgktf9owIDAQAB】
 
3.從報文取出簽名值sign:
【zs+snU7B3l4Cnec/JWBcXWkFUNWrW1ttEeMZI4X2mtuSNQ9LL9Mri3b6QK0xTc/KMs/Cafk0Ln7iRKQZfnuBvvuaOwVxSgjuWT0ygGktDok0HITE2wKeT8j4SNX4ScIGHLqHXkMojXIZxE6bsfD9kjUb356aFItinf3vURu3FuPSkbJR1KZXDm41hz2rQ9o6Sc3Xs2cr7AbCSVgyGjv5c6G6OiqaXcdgM+FxHtxkhfqB/3awRjXMGmLmWr2m5VyFIfNMOuNCdRN7fNkUBIX3h35KI6av/x6BDYKwTW2zrJHVy91C0h3mrVoDNuRrJ4fPMW2ZH88j7Q3exmPTP0gqMA==】
 
二、組成待驗簽串:
1.剔除空值參數:
【gmt_create=2020-03-25 10:59:32&charset=utf-8&gmt_payment=2020-03-25 10:59:38&notify_time=2020-03-25 10:59:38&subject=仙玉小袋&sign=zs+snU7B3l4Cnec/JWBcXWkFUNWrW1ttEeMZI4X2mtuSNQ9LL9Mri3b6QK0xTc/KMs/Cafk0Ln7iRKQZfnuBvvuaOwVxSgjuWT0ygGktDok0HITE2wKeT8j4SNX4ScIGHLqHXkMojXIZxE6bsfD9kjUb356aFItinf3vURu3FuPSkbJR1KZXDm41hz2rQ9o6Sc3Xs2cr7AbCSVgyGjv5c6G6OiqaXcdgM+FxHtxkhfqB/3awRjXMGmLmWr2m5VyFIfNMOuNCdRN7fNkUBIX3h35KI6av/x6BDYKwTW2zrJHVy91C0h3mrVoDNuRrJ4fPMW2ZH88j7Q3exmPTP0gqMA==&buyer_id=2088002582656381&body=仙玉小袋&invoice_amount=0.01&version=1.0&notify_id=2020032500222105938056380529774436&fund_bill_list=[{"amount":"0.01","fundChannel":"PCREDIT"}]&notify_type=trade_status_sync&out_trade_no=TEST171&total_amount=0.01&trade_status=TRADE_SUCCESS&trade_no=2020032522001456380542497258&auth_app_id=2021001144680325&receipt_amount=0.01&point_amount=0.00&buyer_pay_amount=0.01&app_id=2021001144680325&sign_type=RSA2&seller_id=2088031940831575】
 
2.剔除sign、sign_type參數:
【gmt_create=2020-03-25 10:59:32&charset=utf-8&gmt_payment=2020-03-25 10:59:38&notify_time=2020-03-25 10:59:38&subject=仙玉小袋&buyer_id=2088002582656381&body=仙玉小袋&invoice_amount=0.01&version=1.0&notify_id=2020032500222105938056380529774436&fund_bill_list=[{"amount":"0.01","fundChannel":"PCREDIT"}]&notify_type=trade_status_sync&out_trade_no=TEST171&total_amount=0.01&trade_status=TRADE_SUCCESS&trade_no=2020032522001456380542497258&auth_app_id=2021001144680325&receipt_amount=0.01&point_amount=0.00&buyer_pay_amount=0.01&app_id=2021001144680325&seller_id=2088031940831575】
 
3、排序:
【app_id=2021001144680325&auth_app_id=2021001144680325&body=仙玉小袋&buyer_id=2088002582656381&buyer_pay_amount=0.01&charset=utf-8&fund_bill_list=[{"amount":"0.01","fundChannel":"PCREDIT"}]&gmt_create=2020-03-25 10:59:32&gmt_payment=2020-03-25 10:59:38&invoice_amount=0.01&notify_id=2020032500222105938056380529774436&notify_time=2020-03-25 10:59:38&notify_type=trade_status_sync&out_trade_no=TEST171&point_amount=0.00&receipt_amount=0.01&seller_id=2088031940831575&subject=仙玉小袋&total_amount=0.01&trade_no=2020032522001456380542497258&trade_status=TRADE_SUCCESS&version=1.0】
 
三、驗簽結果:
【驗簽成功】

  校驗demo:

from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
import Crypto.Hash.SHA512
from Crypto.Hash import SHA, SHA256
 
import base64
 
 
 
def rsa2_verify(sign, plaintext, keypath):
    print('*' * 100)
    print('sign =', sign)
    with open(keypath) as f:
        key = f.read()
    print('plaintext =', plaintext)
    hash_value = SHA256.new(plaintext.encode('utf-8'))
    print('hash_value =', hash_value)
    verifier = PKCS1_v1_5.new(RSA.importKey(key))
    # 驗證,通過返回true,失敗返回false
    print('*' * 100)
    return verifier.verify(hash_value, base64.b64decode(sign))
 
if __name__ == '__main__':
    message = 'app_id=2021001144680325&auth_app_id=2021001144680325&trade_status=TRADE_SUCCESS&version=1.0'
 
    publickeyPath = r"H:\alipay\xn\alipay_public.txt" # alipay的公鑰 不是app公鑰
    result = rsa2_verify(signature, message, publickeyPath)
 
    print('result =', result)

 


免責聲明!

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



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