python實(shí)現(xiàn)銀聯(lián)支付和支付寶支付接入
本文實(shí)例為大家分享了python銀聯(lián)支付和支付寶支付接入的具體代碼,供大家參考,具體內(nèi)容如下
前置條件:需要安裝Python的OpenSSL模塊,我使用的版本是16.1.0,可以使用pip install pyopenssl來(lái)安裝
一、支付寶支付
1. 使用RSA公鑰加密系統(tǒng)進(jìn)行簽名和簽名驗(yàn)證,需要自己生成一個(gè)RSA私鑰和對(duì)應(yīng)的一個(gè)RSA公鑰(在Linux下可以使用ssh-keygen命令來(lái)生成),公鑰需要上傳至支付寶,供支付寶對(duì)開(kāi)發(fā)者發(fā)送的請(qǐng)求做簽名驗(yàn)證使用;而同時(shí)支付寶會(huì)提供一個(gè)RSA公鑰給開(kāi)發(fā)者,開(kāi)發(fā)者使用這個(gè)公鑰來(lái)驗(yàn)證支付寶的回調(diào)請(qǐng)求的合法性。
2. 整個(gè)接入過(guò)程最核心的工作就是構(gòu)建一個(gè)合法的請(qǐng)求報(bào)文,這個(gè)可以參考支付寶的相關(guān)文檔;其次是對(duì)請(qǐng)求報(bào)文的內(nèi)容進(jìn)行RSA簽名,并將簽名隨請(qǐng)求報(bào)文一并發(fā)送。
核心的簽名和報(bào)文構(gòu)建代碼如下:
import OpenSSL
import json
import time
import urllib
import base64
from django.conf import settings
def build_sign(param_map, sign_type="RSA"):
'''
Doc: https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105351&docType=1
'''
# 將篩選的參數(shù)按照第一個(gè)字符的鍵值A(chǔ)SCII碼遞增排序(字母升序排序),如果遇到相同字符則按照第二個(gè)字符的鍵值A(chǔ)SCII碼遞增排序,以此類推。
sort_param = sorted([(key, unicode(value, settings.ALIPAY_CHARSET).encode(settings.ALIPAY_CHARSET)) for key, value in param_map.iteritems()], key=lambda x: x[0])
# 將排序后的參數(shù)與其對(duì)應(yīng)值,組合成“參數(shù)=參數(shù)值”的格式,并且把這些參數(shù)用&字符連接起來(lái),此時(shí)生成的字符串為待簽名字符串。SDK中已封裝簽名方法,開(kāi)發(fā)者可直接調(diào)用,詳見(jiàn)SDK說(shuō)明。
# 如自己開(kāi)發(fā),則需將待簽名字符串和私鑰放入SHA1 RSA算法中得出簽名(sign)的值。
content = '&'.join(['='.join(x) for x in sort_param])
return base64.encodestring(OpenSSL.crypto.sign(settings.ALIPAY_APP_PRIVATE_KEY_OBJ, content, 'sha1'))
def build_params(out_trade_no, subject, body, total_amount):
'''
Doc:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.MVkRGo&treeId=193&articleId=105465&docType=1
將參數(shù)按照支付寶規(guī)定組織并簽名之后,返回
'''
params = {}
# 獲取配置文件
params['app_id'] = settings.ALIPAY_APPID
params['method'] = settings.ALIPAY_METHOD
params['format'] = settings.ALIPAY_FORMAT
params['charset'] = settings.ALIPAY_CHARSET
params['sign_type'] = settings.ALIPAY_SIGN_TYPE
params['sign_type'] = settings.ALIPAY_SIGN_TYPE
params['timestamp'] = time.strftime('%Y-%m-%d %H:%M:%S')
params['version'] = settings.ALIPAY_VERSION
params['notify_url'] = settings.ALIPAY_NOTIFY_URL
# 業(yè)務(wù)參數(shù)
params['biz_content'] = {}
params['biz_content']['body'] = body # 訂單描述、訂單詳細(xì)、訂單備注,顯示在支付寶收銀臺(tái)里的“商品描述”里
params['biz_content']['subject'] = subject # 商品的標(biāo)題/交易標(biāo)題/訂單標(biāo)題/訂單關(guān)鍵字等。
params['biz_content']['out_trade_no'] = out_trade_no # 商戶網(wǎng)站唯一訂單號(hào)
params['biz_content']['total_amount'] = '%.2f' % (float(total_amount) / 100) # 訂單總金額,單位為元,精確到小數(shù)點(diǎn)后兩位,取值范圍[0.01,100000000]
params['biz_content']['product_code'] = settings.ALIPAY_APP_PRODUCT_CODE
params['biz_content'] = json.dumps(params['biz_content'], separators=(',', ':'))
params['sign'] = build_sign(params)
return urllib.urlencode(params)
def check_sign(message, sign):
'''Doc: https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.dDRpeK&treeId=204&articleId=105301&docType=1'''
try:
OpenSSL.crypto.verify(settings.ALIPAY_PUBLIC_KEY_OBJ, sign, message, 'SHA1')
return True
except Exception as _:
return False
在讀取支付寶公鑰的時(shí)候,由于OpenSSL模塊使用的是X509格式的證書(shū),所以需要做以下的處理(用于對(duì)發(fā)起支付報(bào)文進(jìn)行簽名):
# 支付寶開(kāi)發(fā)者公鑰(支付寶生成) ALIPAY_PUBLIC_KEY = os.path.join(BASE_DIR, 'utils/paycenter/alipay/certs/alipay_public_key') _ALIPAY_PUBLIC_KEY_OBJ_PUB = OpenSSL.crypto.load_publickey(OpenSSL.crypto.FILETYPE_PEM, open(ALIPAY_PUBLIC_KEY).read()) _ALIPAY_PUBLIC_KEY_OBJ_X509 = OpenSSL.crypto.X509() _ALIPAY_PUBLIC_KEY_OBJ_X509.set_pubkey(_ALIPAY_PUBLIC_KEY_OBJ_PUB) ALIPAY_PUBLIC_KEY_OBJ = _ALIPAY_PUBLIC_KEY_OBJ_X509
載入開(kāi)發(fā)者生成的私鑰的代碼為(用于驗(yàn)證支付寶回調(diào)請(qǐng)求的合法性):
# 支付寶開(kāi)發(fā)者應(yīng)用私鑰(接入方生成) ALIPAY_APP_PRIVATE_KEY = os.path.join(BASE_DIR, 'utils/paycenter/alipay/certs/alipay_app_private_key') ALIPAY_APP_PRIVATE_KEY_OBJ = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open(ALIPAY_APP_PRIVATE_KEY).read(), None)
二、銀聯(lián)支付
1. 銀聯(lián)支付(網(wǎng)關(guān)支付)與支付寶支付基本上遵循了同樣的流程,但是在發(fā)起支付的請(qǐng)求報(bào)文和簽名、驗(yàn)證簽名等方面存在細(xì)微的差別,特別是在簽名和驗(yàn)證簽名時(shí),支付寶是直接對(duì)報(bào)文內(nèi)容進(jìn)行了RSA加密,但是銀聯(lián)卻是首先對(duì)簽名內(nèi)容取SHA1的摘要,繼而對(duì)此摘要做RSA加密,這點(diǎn)在build_sign函數(shù)中就可以很明顯地看出來(lái)。
2. 銀聯(lián)支付的證書(shū)文件申請(qǐng)流程比較繁瑣,并且其格式也和支付寶使用的不同,我們申請(qǐng)到的私鑰證書(shū)文件是以PKCS12的格式保存的,并且需要注意在從指定位置下載此證書(shū)后,導(dǎo)出證書(shū)時(shí)一定要將密碼設(shè)置為6位的整數(shù)數(shù)字,之后,還需要將PKCS12格式的證書(shū)文件上傳至指定位置并啟用才可以正常使用此證書(shū)。
核心的簽名和報(bào)文構(gòu)建代碼如下:
import time
import hashlib
import urllib, urllib2
import base64
import OpenSSL
from django.conf import settings
def build_sign(param_map, sign_type="RSA"):
'''構(gòu)建簽名'''
# 將篩選的參數(shù)按照第一個(gè)字符的鍵值A(chǔ)SCII碼遞增排序(字母升序排序),如果遇到相同字符則按照第二個(gè)字符的鍵值A(chǔ)SCII碼遞增排序,以此類推。
sort_param = sorted([(key, unicode(value, settings.UNIONPAY_ENCODING).encode(settings.UNIONPAY_ENCODING)) for key, value in param_map.iteritems()], key=lambda x: x[0])
content = '&'.join(['='.join(x) for x in sort_param])
message = hashlib.sha1(content).hexdigest()
return base64.b64encode(OpenSSL.crypto.sign(settings.UNIONPAY_PRIVATE_KEY_OBJ, message, 'sha1'))
def build_params(out_trade_no, total_amount):
params = {}
# 獲取配置信息
params['accType'] = settings.UNIONPAY_ACC_TYPE
params['accessType'] = settings.UNIONPAY_ACCESS_TYPE
params['backUrl'] = settings.UNIONPAY_BACK_URL
params['frontUrl'] = settings.UNIONPAY_FRONT_URL
params['bizType'] = settings.UNIONPAY_BIZ_TYPE
params['certId'] = settings.UNIONPAY_CERT_ID
params['channelType'] = settings.UNIONPAY_CHANNEL_TYPE
params['currencyCode'] = settings.UNIONPAY_CURRENCY_CODE
params['encoding'] = settings.UNIONPAY_ENCODING
params['merId'] = settings.UNIONPAY_MER_ID
params['signMethod'] = settings.UNIONPAY_SIGN_METHOD
params['txnType'] = settings.UNIONPAY_TXN_TYPE
params['txnSubType'] = settings.UNIONPAY_TXN_SUBTYPE
params['version'] = settings.UNIONPAY_VERSION
params['orderId'] = out_trade_no
params['txnAmt'] = '%d' % int(total_amount) # 單位為分
params['txnTime'] = time.strftime('%Y%m%d%H%M%S') #
params['signature'] = build_sign(params)
# return params
return urllib.urlencode(params)
def check_sign(message, sign):
try:
OpenSSL.crypto.verify(settings.UNIONPAY_PUBLIC_KEY_OBJ, sign, message, 'SHA1')
return True
except Exception as _:
return False
商戶私鑰證書(shū)的載入方法(用戶對(duì)發(fā)起支付的報(bào)文進(jìn)行簽名):
# 商戶私鑰證書(shū) UNIONPAY_APP_PRIVATE_KEY_CERT = os.path.join(UNIONPAY_CERTS_PATH, UNIONPAY_APP_PRIVATE_KEY_CERT_FILENAME) # PKCS12 format UNIONPAY_PRIVATE_KEYSTORE = OpenSSL.crypto.load_pkcs12(open(UNIONPAY_APP_PRIVATE_KEY_CERT).read(), UNIONPAY_APP_PRIVATE_KEY_CERT_PASSWORD) UNIONPAY_PRIVATE_KEY_OBJ = UNIONPAY_PRIVATE_KEYSTORE.get_privatekey()
銀聯(lián)公鑰證書(shū)的載入方法(用于驗(yàn)證銀聯(lián)回調(diào)的合法性):
# 銀聯(lián)公鑰證書(shū) UNIONPAY_PUBLIC_KEY_CERT = os.path.join(UNIONPAY_CERTS_PATH, UNIONPAY_PUBLIC_KEY_CERT_FILENAME) UNIONPAY_PUBLIC_KEY_OBJ = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, open(UNIONPAY_PUBLIC_KEY_CERT).read())
后記:銀聯(lián)支付接入過(guò)程中,參考過(guò)其官方提供的一個(gè)Python實(shí)現(xiàn)的接口,里面同時(shí)使用了python的rsa模塊和OpenSSL模塊,并且對(duì)證書(shū)的格式做了各種處理,但是這些似乎是沒(méi)有什么必要,只使用OpenSSL模塊就完全可以完成相關(guān)的工作。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python中base64與xml取值結(jié)合問(wèn)題
這篇文章主要介紹了Python中base64與xml取值結(jié)合問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
實(shí)例講解Python編程中@property裝飾器的用法
裝飾器中蘊(yùn)含著很多Python的進(jìn)階技巧,@property也不例外,比如文后會(huì)講到的快速進(jìn)行代碼重構(gòu)的一個(gè)例子,這里我們就來(lái)以實(shí)例講解Python編程中@property裝飾器的用法:2016-06-06
jupyter notebook 實(shí)現(xiàn)matplotlib圖動(dòng)態(tài)刷新
這篇文章主要介紹了jupyter notebook 實(shí)現(xiàn)matplotlib圖動(dòng)態(tài)刷新,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
Python爬蟲(chóng)常用庫(kù)的安裝及其環(huán)境配置
今天小編就為大家分享一篇關(guān)于python爬蟲(chóng)常用庫(kù)的安裝及其環(huán)境配置的文章,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-09-09
Python使用當(dāng)前時(shí)間、隨機(jī)數(shù)產(chǎn)生一個(gè)唯一數(shù)字的方法
這篇文章主要介紹了Python使用當(dāng)前時(shí)間、隨機(jī)數(shù)產(chǎn)生一個(gè)唯一數(shù)字的方法,涉及Python時(shí)間與隨機(jī)數(shù)相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
Selenium Webdriver元素定位的八種常用方式(小結(jié))
這篇文章主要介紹了Selenium Webdriver元素定位的八種常用方式(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
分享5個(gè)數(shù)據(jù)處理更加靈活的pandas調(diào)用函數(shù)方法
這篇文章主要介紹了分享5個(gè)數(shù)據(jù)處理更加靈活的pandas調(diào)用函數(shù)方法,文章基于python的相關(guān)內(nèi)容展開(kāi)詳細(xì)介紹,需要的小伙伴可以參考一下2022-04-04
pycharm重置設(shè)置,恢復(fù)默認(rèn)設(shè)置的方法
今天小編就為大家分享一篇pycharm重置設(shè)置,恢復(fù)默認(rèn)設(shè)置的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10

