《PHP教程:微信支付的開發流程詳解》要點:
本文介紹了PHP教程:微信支付的開發流程詳解,希望對您有用。如果有疑問,可以聯系我們。
PHP編程最近在公司做了微信支付的接入,這里總結下開發的一些經驗
PHP編程注意,我使用的是微信開放平臺的支付,與手機app相關,而與公眾賬號無關.
PHP編程微信支付的主要操作流程
PHP編程1.用戶瀏覽app,選定商品然后下單.
PHP編程2.服務器處理訂單邏輯,開始正式發起支付流程
PHP編程3.首先,后臺服務器向weixin服務器發起請求,獲取一個token.
PHP編程4.后臺服務器拿到token,使用和其他參數加密,再次向weixin服務器發起請求,獲取一個預支付prepayid
PHP編程5.后臺服務器將該prepayid返回給app客戶端
PHP編程6.app調用手機上的微信控件,完成付款流程.
PHP編程7.app向后臺服務器發起一個回調請求,通知服務器交易完成.
PHP編程8.weixin服務器處理完所有的流程后,向后臺服務器發起一個post請求,正式通知后臺服務器交易完畢
PHP編程上面流程的一些注意點:
PHP編程1.每次獲取的token是有時效的,默認是7200s,而且每天最多獲取200次,因此最好放到redis中緩存起來,等失效后再去重新獲取
PHP編程2.app發起的回調默認是不可靠的,后臺應該盡可能(不是必須)向微信服務器發起訂單查詢,查詢本次交易的結果.
PHP編程3.weixin服務器向后臺發起的notify,才是確保交易完成的最后屏障.后臺服務器確認后必須返回“success”,否則weixin服務器會嘗試重發請求.
PHP編程獲取token
PHP編程這步很簡單,發送一個get請求即可.只需配置正確參數.
PHP編程 ‘‘‘從微信服務器獲取token‘‘‘ def _getAccessTokenFromWeixin(self): response = requests.get(self.tokenUrl % (self.appId, self.appSecret)) if response.status_code == 200: text = response.text tokenInfo = json.loads(text) try: token = tokenInfo[‘access_token‘] expires_in = tokenInfo[‘expires_in‘] self._writeWeixinTokenLog(token, self.order_no) return token except KeyError: return None #token獲取失敗 return None #http請求失敗
PHP編程獲取prepayid
PHP編程在微信支付的開發流程中,最繁瑣的就是獲取prepayid.
PHP編程這一步我們需要組裝這樣一個參數:
PHP編程
{
"appid":"wxd930ea5d5a258f4f",
"traceid":"test_1399514976",
"noncestr":"e7d161ac8d8a76529d39d9f5b4249ccb ",
"timestamp":1399514976, "package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%
95&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_trade_ no=7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1& total_fee=1&sign=7F77B507B755B3262884291517E380F8",
"sign_method":"sha1", "app_signature":"7f77b507b755b3262884291517e380f8"
}
PHP編程組裝package
PHP編程這里的第一步就是組裝package:
PHP編程
"package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%
95&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_trade_ no=7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1& total_fee=1&sign=7F77B507B755B3262884291517E380F8",
PHP編程組裝package需要的參數如上面代碼所示,所以我們需要準備一個params,然后準備簽名,簽名流程如下:
PHP編程1.按照key的字典序,對params進行排序,然后拼接成字符串,注意這些key不包括sign
PHP編程2.在上面的字符串后面拼接key=paternerKey,然后對整個字符串進行md5簽名,然后轉換成大寫,此時我們就得到了簽名
PHP編程然后我們將所有params的value進行urlencode轉碼,然后后面拼接上sign=signValue,就得到了package字符串.
PHP編程這里創建MD5的過如下:
PHP編程
def createMD5Signature(self, signParams):
‘‘‘先排序‘‘‘
sortedParams = sorted(signParams.iteritems(), key=lambda d:d[0])
‘‘‘拼接‘‘‘
stringSignTemp = "&".join(["%s=%s" % (item[0], item[1]) for item in sortedParams if item[0] != ‘sign‘ and ‘‘ != item[1]])
#加上財付通商戶權限密鑰
stringSignTemp += ‘&key=%s‘ % (self.partnerKey)
#使用MD5進行簽名,然后轉化為大寫
stringSign = hashlib.md5(stringSignTemp).hexdigest().upper() #Upper
return stringSign
PHP編程組裝package的代碼:
PHP編程
def getPackage(self, packageParams):
‘‘‘先獲取params的sign,然后將params進行urlencode,最后拼接,加上sign‘‘‘
sign = self.createMD5Signature(packageParams)
packageParams = sorted(packageParams.iteritems(), key=lambda d:d[0])
stringParams = "&".join(["%s=%s" % (item[0], urllib.quote(str(item[1]))) for item in packageParams])
stringParams += ‘&sign=%s‘ % (sign)
return stringParams
PHP編程繼續組裝參數
PHP編程得到package后,我們繼續組裝參數:
PHP編程這里需要的參數為:
PHP編程
appid=wxd930ea5d5a258f4f
appkey=L8LrMqqeGRxST5reouB0K66CaY A WpqhA Vsq7ggKkxHCOastWksvuX1uvmvQcl xaHoYd3ElNBrNO2DHnnzgfVG9Qs473M3DTOZug5er46FhuGofumV8H2FVR9qkjSlC5K
noncestr=e7d161ac8d8a76529d39d9f5b4249ccb
package=bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%95 &fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_trade_no =7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1&tot al_fee=1&sign=7F77B507B755B3262884291517E380F8
timestamp=1399514976
PHP編程traceid=test_1399514976
PHP編程注意這里有個坑:
PHP編程參與簽名的是上面的參數,但是最后的參數中不包括appKey,簽名后要記得刪除.
PHP編程1.所有參數按照字典序排序,然后拼接
PHP編程2.進行sha1簽名,拼接到上面字符串的后面
PHP編程3.注意這里要刪除appKey,然后加上sign
PHP編程獲取sha1簽名的代碼如下:
PHP編程
def createSHA1Signature(self, params):
‘‘‘先排序,然后拼接‘‘‘
sortedParams = sorted(params.iteritems(), key=lambda d:d[0])
stringSignTemp = "&".join(["%s=%s" % (item[0], item[1]) for item in sortedParams])
stringSign = hashlib.sha1(stringSignTemp).hexdigest()
return stringSign
PHP編程隨后我們獲取到這樣的參數:
PHP編程
{
"appid":"wxd930ea5d5a258f4f",
"noncestr":"e7d161ac8d8a76529d39d9f5b4249ccb",
"package":"Sign=WXpay";
"partnerid":"1900000109"
"prepayid":"1101000000140429eb40476f8896f4c9",
"sign":"7ffecb600d7157c5aa49810d2d8f28bc2811827b",
"timestamp":"1399514976"
}
PHP編程獲取prepayid
PHP編程代碼如下:
PHP編程
‘‘‘獲取預支付prepayid‘‘‘
def gerPrepayId(self, token, requestParams):
‘‘‘將參數,包括package,進行json化,然后發起post請求‘‘‘
data = json.dumps(requestParams)
response = requests.post(self.gateUrl % (token), data=data)
if response.status_code == 200:
text = response.text
text = json.loads(text)
errcode = text[‘errcode‘]
if errcode == 0:
return text[‘prepayid‘]
return None
PHP編程我們獲取的prepayid格式應該是這樣:
PHP編程{"prepayid":"1101000000140429eb40476f8896f4c9","errcode":0,"errmsg":"Success"}?
PHP編程再次簽名
PHP編程這里采用上面sha1的簽名方式再次簽名,獲取到下面的參數:
PHP編程
{
"appid":"wxd930ea5d5a258f4f",
"noncestr":"e7d161ac8d8a76529d39d9f5b4249ccb",
"package":"Sign=WXpay";
"partnerid":"1900000109"
"prepayid":"1101000000140429eb40476f8896f4c9",
"sign":"7ffecb600d7157c5aa49810d2d8f28bc2811827b",
"timestamp":"1399514976"
}
PHP編程后臺服務器將該結果返回給app,此時app即可發起支付.
PHP編程上面的流程代碼為:
PHP編程
‘‘‘接收app的請求,返回prepayid‘‘‘
class WeixinRequirePrePaidHandler(BasicTemplateHandler):
‘‘‘這個方法在OrdersAddHandler中被調用‘‘‘
@staticmethod
def getPrePaidResult(order_no, total_pay, product_name, client_ip):
‘‘‘封裝了常用的簽名算法‘‘‘
weixinRequestHandler = WeixinRequestHandler(order_no)
‘‘‘收集訂單相關信息‘‘‘
addtion = str(random.randint(10, 100)) #產生一個兩位的數字,拼接在訂單號的后面
out_trade_no = str(order_no) + addtion
order_price = float(total_pay) #這里必須允許浮點數,后面轉化成分之后轉化為int
#order_price = 0.01 #測試
remote_addr = client_ip #客戶端的IP地址
print remote_addr
current_time = int(time.time())
order_create_time = str(current_time)
order_deadline = str(current_time + 20*60)
‘‘‘這里的一些參數供下面使用‘‘‘
noncestr = hashlib.md5(str(random.random())).hexdigest()
timestamp = str(int(time.time()))
pack = ‘Sign=WXPay‘
‘‘‘獲取token‘‘‘
access_token = weixinRequestHandler.getAccessToken()
logging.info("get token: %s" % access_token)
if access_token:
‘‘‘設置package參數‘‘‘
packageParams = {}
packageParams[‘bank_type‘] = ‘WX‘ #支付類型
packageParams[‘body‘] = product_name #商品名稱
packageParams[‘fee_type‘] = ‘1‘ #人民幣 fen
packageParams[‘input_charset‘] = ‘GBK‘ #GBK
packageParams[‘notify_url‘] = config[‘notify_url‘] #post異步消息通知
packageParams[‘out_trade_no‘] = str(out_trade_no) #訂單號
packageParams[‘partner‘] = config[‘partnerId‘] #商戶號
packageParams[‘total_fee‘] = str(int(order_price*100)) #訂單金額,單位是分
packageParams[‘spbill_create_ip‘] = remote_addr #IP
packageParams[‘time_start‘] = order_create_time #訂單生成時間
packageParams[‘time_expire‘] = order_deadline #訂單失效時間
‘‘‘獲取package‘‘‘
package = weixinRequestHandler.getPackage(packageParams)
‘‘‘設置支付參數‘‘‘
signParams = {}
signParams[‘appid‘] = config[‘appId‘]
signParams[‘appkey‘] = config[‘paySignKey‘] #delete
signParams[‘noncestr‘] = noncestr
signParams[‘package‘] = package
signParams[‘timestamp‘] = timestamp
signParams[‘traceid‘] = ‘mytraceid_001‘
‘‘‘生成支付簽名‘‘‘
app_signature = weixinRequestHandler.createSHA1Signature(signParams)
‘‘‘增加不參與簽名的額外參數‘‘‘
signParams[‘sign_method‘] = ‘sha1‘
signParams[‘app_signature‘] = app_signature
‘‘‘剔除appKey‘‘‘
del signParams[‘appkey‘]
‘‘‘獲取prepayid‘‘‘
prepayid = weixinRequestHandler.gerPrepayId(access_token, signParams)
if prepayid:
‘‘‘使用拿到的prepayid再次準備簽名‘‘‘
pack = ‘sign=WXPay‘
prepayParams = {}
prepayParams[‘appid‘] = config[‘appId‘]
prepayParams[‘appkey‘] = config[‘paySignKey‘]
prepayParams[‘noncestr‘] = noncestr
prepayParams[‘package‘] = pack
prepayParams[‘partnerid‘] = config[‘partnerId‘]
prepayParams[‘prepayid‘] = prepayid
prepayParams[‘timestamp‘] = timestamp
‘‘‘生成簽名‘‘‘
sign = weixinRequestHandler.createSHA1Signature(prepayParams)
‘‘‘準備輸出參數‘‘‘
returnParams = {}
returnParams[‘status‘] = 0
returnParams[‘retmsg‘] = ‘success‘
returnParams[‘appid‘] = config[‘appId‘]
returnParams[‘noncestr‘] = noncestr
returnParams[‘package‘] = pack
returnParams[‘prepayid‘] = prepayid
returnParams[‘timestamp‘] = timestamp
returnParams[‘sign‘] = sign
returnParams[‘partnerId‘] = config[‘partnerId‘]
returnParams[‘addtion‘] = addtion
else:
‘‘‘prepayid獲取失敗‘‘‘
returnParams = {}
returnParams[‘status‘] = -1
returnParams[‘retmsg‘] = ‘prepayid獲取失敗‘
else:
‘‘‘token獲取失敗‘‘‘
returnParams = {}
returnParams[‘status‘] = -1
returnParams[‘retmsg‘] = ‘token獲取失敗‘
‘‘‘生成json格式文本,然后返回給APP‘‘‘
return returnParams
PHP編程后臺異步通知
PHP編程微信服務器發來的notify異步通知,才是支付成功的最終標志,這一步處于安全起見,我們必須進行延簽:
PHP編程延簽代碼如下:
PHP編程
def isTenpaySign(self, params):
helper = WeixinRequestHandler()
sign = helper.createMD5Signature(params)
return params[‘sign‘] == sign
PHP編程整體流程如下:
PHP編程
‘‘‘微信服務器向后臺發送的異步通知‘‘‘
class WeixinAppNotifyHandler(BasicTemplateHandler):
def initialize(self):
self.weixinResponseHandler = WeixinResponseHandler()
def post(self):
‘‘‘解析參數‘‘‘
params = self.parseQueryString()
‘‘‘驗證是否是weixin服務器發回的消息‘‘‘
verifyWeixinSign = self.weixinResponseHandler.isTenpaySign(params)
‘‘‘處理訂單‘‘‘
if verifyWeixinSign:
‘‘‘訂單邏輯‘‘‘
order_no = str(params[‘out_trade_no‘])
order_no = order_no[0:-2]
print ‘%s paied successfully‘ % order_no
self.saveWeixinReceipt(params)
updateOrdersPaidByWeixin(order_no) #更新訂單使用狀態
consumeCouponByOrderNo(order_no) #優惠券已經使用
self.write("success")
else:
self.write("fail")
def parseQueryString(self):
‘‘‘獲取url中所有的參數‘‘‘
uri = self.request.uri
‘‘‘解析出URI中的query字符串‘‘‘
parseResult = urlparse.urlparse(uri)
query = parseResult.query
‘‘‘解析query字符串‘‘‘
params = urlparse.parse_qs(query)
for item in params:
params[item] = params[item][0].strip()
return params
PHP編程最后說明一點,用戶在手機上付完款,并不算支付成功,只有weixin服務器收到notify通知返回的success時,才算交易最終成功,此時我們的手機可以收到微信官方發來的一條消息.
PHP編程以上就是對微信支付開發流程的資料整理,后續繼續補充相關資料,謝謝大家對本站的支持!
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/3815.html