需求:
微信打開商品列表頁面-> 點擊商品后直接顯示付款頁面-> 點擊付款調用微信支付
說明
微信支付需要你申請了公眾號(appid, key - 用於簽名), 商戶號(mch_id, AppSecret - 用於獲取openid, 獲取code)
調起微信支付的頁面需要配置授權, 如你的頁面是http://www.shazuihuo.com/goods/index.html. 那么你需要配置為: http://www.shazuihuo.com/goods/ 即可, 這個是在公眾號中配置
簽名算法和校驗工具
坑
- 簽名校驗通過時還是提示簽名錯誤, 可能時候商戶號KEY配置的問題了, 重置一下KEY, 你可以繼續使用原來的KEY來重置
- 公眾號變更時記得修改后台和前台代碼中的APPID
需要的ID和KEY
# 微信配置基礎數據
WPC = {
'APPID': 'wx53c1xxxxad626eb8',
'APPSECRET': 'fdd177a7xxxxxxxxxxxxx856eeeb187c',
'MCHID': '14222000000',
'KEY': 'd7810713e1exxxxxxxxxxadc9617d0a6',
'GOODDESC': '商戶號中的公司簡稱或全稱-無要求的商品名字',
'NOTIFY_URL': 'https://www.xxxx.com/service/applesson/wechatordernotice',
}
流程簡介
- 網頁內調起微信支付需要一個微信統一下單生成的訂單號(prepay_id)
- 調用微信的統一下單接口需要一個用戶在商戶下的唯一標示(openid)
- 獲取openid需要code參數加上AppID和AppSecret等,通過API換取access_token(openid)
- 其中code又需要通過頁面跳轉來獲取, 需要appid和重定向url(可以帶有你自己的參數, 會原樣返回)
那么開發思路便是一步步回朔了.
1. 獲取code
用戶點擊按鈕跳轉到微信授權頁, 微信處理完后重定向到redirect_uri, 並給我們加上code=xxx的參數, 這個code就是我們需要的
$('#buy').click(function() {
var param = {
appid: 'wx53c1xxxxad626eb8',
redirect_uri: 'https://www.xxxxx.com/wcpay/pay.html',
response_type: 'code',
scope: 'snsapi_base',
state: '1'
}
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?' + $.param(param);
})
2. 獲取openid
這個在后台完成, WPC中配置了你的APPSECRET, 這個不能泄露, 接口調用成功會拿到一個openid, 這里都不會有什么問題
@classmethod
def getOpenID(cls, kwargs):
param = {
'code': kwargs['code'],
'appid': WPC['APPID'],
'secret': WPC['APPSECRET'],
'grant_type': 'authorization_code',
}
# 通過code獲取access_token
openIdUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token'
resp = requests.get(openIdUrl, params=param)
# {openid, accss_token, refresh_token, openid, scope, expires_in}
# openId = json.loads(resp.text)['openid']
return resp.text
3. 微信統一下單
統一下單 時參數傳遞需要簽名(微信用我們設定的密匙對參數進行MD5加密, 通過雙方的簽名判斷請求是否被篡改)
簽名算法
@classmethod
def getSign(cls, kwargs):
# 計算簽名
keys, paras = sorted(kwargs), []
paras = ['{}={}'.format(key, kwargs[key]) for key in keys if key != 'appkey'] # and kwargs[key] != '']
stringA = '&'.join(paras)
stringSignTemp = stringA + '&key=' + WPC['KEY']
sign = MD5(stringSignTemp).upper()
return sign
MD5函數
import hashlib
# 獲取MD5
def MD5(str):
md5 = hashlib.md5()
md5.update(str.encode('utf-8'))
return md5.hexdigest()
參數轉xml
@classmethod
def getxml(cls, kwargs):
kwargs['sign'] = Utility.getSign(kwargs)
# 生成xml
xml = ''
for key, value in kwargs.items():
xml += '<{0}>{1}</{0}>'.format(key, value)
xml = '<xml>{0}</xml>'.format(xml)
# print(xml)
return xml
統一下單代碼
code = self.POST.get('code')
openidresp = Utility.getOpenID({'code': code})
openid = json.loads(openidresp).get('openid')
UnifieOrderRequest = {
'appid': 'wx53c1xxxxad626eb8', # 公眾賬號ID
'body': '公司名稱-商品', # 商品描述
'mch_id': '1397xxxxxx8', # 商戶號:深圳市澤慧文化傳播有限公司
'nonce_str': '', # 隨機字符串
'notify_url': 'https://service.xxxx.com/service/applesson/wechatordernotice', # 微信支付結果異步通知地址
'openid': '', # trade_type為JSAPI時,openid為必填參數!此參數為微信用戶在商戶對應appid下的唯一標識, 統一支付接口中,缺少必填參數openid!
'out_trade_no': '', # 商戶訂單號
'spbill_create_ip': '', # 終端IP
'total_fee': '', # 標價金額
'trade_type': 'JSAPI', # 交易類型
}
UnifieOrderRequest['nonce_str'] = Utility.getnoncestr()
UnifieOrderRequest['openid'] = openid
UnifieOrderRequest['out_trade_no'] = UnifieOrderRequest['mch_id'] + str(order.id) # 內部訂單號碼
UnifieOrderRequest['spbill_create_ip'] = self.request.remote_ip
UnifieOrderRequest['total_fee'] = int(lesson.price * 100)
# 簽名並生成xml
xml = Utility.getxml(UnifieOrderRequest)
resp = requests.post("https://api.mch.weixin.qq.com/pay/unifiedorder", data=xml.encode('utf-8'), headers={'Content-Type': 'text/xml'})
msg = resp.text.encode('ISO-8859-1').decode('utf-8')
xmlresp = xmltodict.parse(msg)
prepay_id = ''
if xmlresp['xml']['return_code'] == 'SUCCESS':
if xmlresp['xml']['result_code'] == 'SUCCESS':
prepay_id = xmlresp['xml']['prepay_id']
timestamp = str(int(time.time()))
data = {
"appId": xmlresp['xml']['appid'],
"nonceStr": Utility.getnoncestr(),
"package": "prepay_id=" + xmlresp['xml']['prepay_id'],
"signType": "MD5",
"timeStamp": timestamp
}
data['paySign'] = Utility.getSign(data)
data['orderid'] = order.id # 付款后操作的訂單
# 簽名后返回給前端做支付參數
return JsonResponse(self, '000', data=data)
else:
msg = xmlresp['xml']['err_code_des']
return JsonResponse(self, '002', msg=msg)
else:
msg = xmlresp['xml']['return_msg']
return JsonResponse(self, '002', msg=msg)
統一下單成功返回后直接調用微信支付, 顯示支付界面, 其中的paySign是我們自己的簽名
try {
// statements
// 微信統一訂單, 返回預支付信息
var code = query('code'),
origin = query('groupid');
// alert(code);
$.post({
url: orderurl,
data: {
origin: origin,
mobile: phone,
code: code
}
}).then(function(resp) {
if (resp.code && resp.code == "000") {
// 后台返回訂單信息
var wepaydata = {
appId: resp.data.appId,
nonceStr: resp.data.nonceStr,
package: resp.data.package,
paySign: resp.data.paySign,
signType: "MD5",
timeStamp: resp.data.timeStamp
};
var orderid = resp.data.orderid || 0;
window.jsApiCall = function() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
wepaydata,
function(res) {
WeixinJSBridge.log(res.err_msg);
// alert(res.err_code + res.err_desc + res.err_msg);
// alert(res.err_msg)
if (res.err_msg == 'get_brand_wcpay_request:ok') {
$.get(orderurl, { orderid: orderid }, function(resp) {
if (resp.code == '000') {
window.location.href = window.location.href.replace('pay.html', 'success.html');
} else {
alert(resp.msg);
// 一個code只能請求一次, 重新進入index
if (resp.code == '002') {
window.location.href = window.location.href.replace('pay.html', 'index.html');
}
}
});
} else {
// 其他支付異常微信有顯示消息
// alert(res.err_msg);
}
}
);
}
window.callpay = function() {
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
} else {
jsApiCall();
}
}
// 發起支付
window.callpay();
} else {
alert(resp.msg);
// alert(JSON.stringify(resp) + resp.msg);
}
}, function(resp) {
alert(resp)
alert(JSON.stringify(resp))
// alert('請求失敗, 請重試');
});
} catch (e) {
// statements
alert(e)
}
4. 訂單查詢
訂單查詢 是為了確認我們的支付是成功的
# 查詢微信付款情況
orderid = self.GET.get('orderid')
orderquery = {
'appid': WPC['APPID'],
'mch_id': WPC['MCHID'],
'nonce_str': Utility.getnoncestr(),
'out_trade_no': WPC['MCHID'] + orderid
}
xml = Utility.getxml(orderquery)
print(xml)
resp = requests.post("https://api.mch.weixin.qq.com/pay/orderquery", data=xml.encode('utf-8'), headers={'Content-Type': 'text/xml'})
msg = resp.text.encode('ISO-8859-1').decode('utf-8')
xmlresp = xmltodict.parse(msg)
print(xmlresp)
orderPaid = 0
if xmlresp['xml']['return_code'] == 'SUCCESS':
if xmlresp['xml']['result_code'] == 'SUCCESS':
if xmlresp['xml']['trade_state'] == 'SUCCESS':
orderPaid = 1
else:
msg = xmlresp['xml']['trade_state_desc']
return JsonResponse(self, '001', msg=smg)
else:
msg = xmlresp['xml']['err_code_des']
return JsonResponse(self, '001', msg=msg)
else:
msg = xmlresp['xml']['return_msg']
return JsonResponse(self, '001', msg=msg)
官方Demo
SDK與DEMO下載, 用python就需要自己碼代碼, 當時看的是PHP的