微信支付有多种模式,刷卡支付,扫码支付,公众号支付,APP支付,小程序支付,这里我们只讨论小程序支付的实现,其他实现方式可以自行百度。
首先我们必须先申请好商户号等信息,详情看https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_11&index=2,这里我们获取到商户号(Mch_id)和商户Key(Mch_key)两个信息,后面会用到。注意,我们需要在商户平台上绑定我们小程序的appid。
流程:
第一步:
在自己的后台服务器上访问微信提供的接口,需要携带一系列参数,这些参数的生成就是挺麻烦的事。这里先贴出微信的开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1。我们可以看到里面有些参数是必填的,有些不是,这里我们想快速完成,只要填好必填项就可以了。里面两个参数是需要我们计算的,下面逐个分析下:
生成随机字符串:
#方式一:
def getNonceStr():
import random
data="123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP"
nonce_str = ''.join(random.sample(data , 30))
return nonce_str
#方式二:
def get_nonce_str():
import uuid
return str(uuid.uuid4()).replace('-', '')
生成签名:
#生成签名的函数
def paysign(appid,body,mch_id,nonce_str,notify_url,openid,out_trade_no,spbill_create_ip,total_fee):
ret= {
"appid": appid,
"body": body,
"mch_id": mch_id,
"nonce_str": nonce_str,
"notify_url":notify_url,
"openid":openid,
"out_trade_no":out_trade_no,
"spbill_create_ip":spbill_create_ip,
"total_fee":total_fee,
"trade_type": 'JSAPI'
}
#处理函数,对参数按照key=value的格式,并按照参数名ASCII字典序排序
stringA = '&'.join(["{0}={1}".format(k, ret.get(k))for k in sorted(ret)])
stringSignTemp = '{0}&key={1}'.format(stringA,Mch_key)
sign = hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest()
return sign.upper()
生成订单号:
#生成商品订单号,方式一:
def getWxPayOrdrID():
import datetime
date=datetime.datetime.now()
#根据当前系统时间来生成商品订单号。时间精确到微秒
payOrdrID=date.strftime("%Y%m%d%H%M%S%f")
#方式二:
def getWxPayOrdrID():
import time
return str(int(time.time()))
生成paySign ,返回给小程序的参数之一:
#获取返回给小程序的paySign
def get_paysign(prepay_id,timeStamp,nonceStr):
pay_data={
'appId': client_appid,
'nonceStr': nonceStr,
'package': "prepay_id="+prepay_id,
'signType': 'MD5',
'timeStamp':timeStamp
}
stringA = '&'.join(["{0}={1}".format(k, pay_data.get(k))for k in sorted(pay_data)])
stringSignTemp = '{0}&key={1}'.format(stringA,Mch_key)
sign = hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest()
return sign.upper()
接下来我们写个函数来封装所有的参数信息:
#获取全部参数信息,封装成xml,传递过来的openid和客户端ip,和价格需要我们自己获取传递进来
def get_bodyData(openid,client_ip,price):
body = 'Mytest' #商品描述
notify_url = 'https:/.../' #填写支付成功的回调地址,微信确认支付成功会访问这个接口
nonce_str =getNonceStr() #随机字符串
out_trade_no =getWxPayOrdrID() #商户订单号
total_fee =str(price) #订单价格,单位是 分
#获取签名
sign=paysign(client_appid,body,Mch_id,nonce_str,notify_url,openid,out_trade_no,client_ip,total_fee)
bodyData = ''
bodyData += '' + client_appid + ' ' # 小程序ID
bodyData += '' + body + '' #商品描述
bodyData += '' + Mch_id + ' ' #商户号
bodyData += '' + nonce_str + ' ' #随机字符串
bodyData += '' + notify_url + ' ' #支付成功的回调地址
bodyData += '' + openid + ' ' #用户标识
bodyData += '' + out_trade_no + ' '#商户订单号
bodyData += '' + client_ip + ' '#客户端终端IP
bodyData += '' + total_fee + ' ' #总金额 单位为分
bodyData += 'JSAPI ' #交易类型 小程序取值如下:JSAPI
bodyData += '' + sign + ' '
bodyData += ' '
return bodyData
请求微信接口并返回拿到的数据给小程序:
#统一下单支付接口
def payOrder(request,user_id):
import time
if request.method == 'POST':
#获取价格
price=request.POST.get("price")
#获取客户端ip
client_ip,port=request.get_host().split(":")
#获取小程序openid
openid=MyUser.objects.get(id=user_id).openid
#请求微信的url
url=configuration.order_url
#拿到封装好的xml数据
body_data=pay.get_bodyData(openid,client_ip,price)
#获取时间戳
timeStamp=str(int(time.time()))
#请求微信接口下单
respone=requests.post(url,body_data.encode("utf-8"),headers={'Content-Type': 'application/xml'})
#回复数据为xml,将其转为字典
content=pay.xml_to_dict(respone.content)
if content["return_code"]=='SUCCESS':
#获取预支付交易会话标识
prepay_id =content.get("prepay_id")
#获取随机字符串
nonceStr =content.get("nonce_str")
#获取paySign签名,这个需要我们根据拿到的prepay_id和nonceStr进行计算签名
paySign=pay.get_paysign(prepay_id,timeStamp,nonceStr)
#封装返回给前端的数据
data={"prepay_id":prepay_id,"nonceStr":nonceStr,"paySign":paySign,"timeStamp":timeStamp}
return HttpResponse(packaging_list(data))
else:
return HttpResponse("请求支付失败")
else:
return HttpResponse(request_code())
第二步
小程序调起接口:
// 支付按钮点击事件
payTap: function(){
var self = this;
wx.request({
url: 'https://127.0.0.1:8000/payOrder/',
data: {
openid: self.data.openid // 这里正常项目不会只有openid一个参数
},
success: function(res){
if(res.data.status == 100){
var payModel = res.data;
wx.requestPayment({
'timeStamp': payModel.timestamp,
'nonceStr': payModel.nonceStr,
'package': payModel.package,
'signType': 'MD5',
'paySign': payModel.paySign,
'success': function (res) {
wx.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
})
},
'fail': function (res) {
}
})
}
},
fail: function(){
}
})
},
github源码地址:https://github.com/xiaoyuan199/wechatPay