Django1.11配合uni-app发起微信支付!
Django1.11配合uni-app发起微信支付!
经过三天的断断续续的奋战,我终于是干动了微信支付。为了以后不忘记,现在来一篇教程,来来来,开干!!!
一、准备阶段
1、准备阶段我们需要去微信官网申请一个小程序或者公众号。获得AppID和AppSecret。
2、去微信商户平台 成为商家,开通JSAPI用来获得商户号和自己配置的钥匙。然后再商户平台上面绑定小程序appid。
(点击下面图片进入官方链接!)
在配置里面配置一个自己的key,需要记住后台开发的时候需要!
关联后即可在小程序管理页面开通微信支付!
到此,准备阶段完成!
二、流程梳理
在这里我大概写一下流程:首先我们在前端发起微信登陆,此时微信会给我们返回一个openid,这个openid一定要留存在某一个位置。然后前段发起微信支付,向后端发送数据请求,后端对结合前段的数据向微信方面发送一个请求,请求相关数据,得到相关数据之后把数据发送给前段,前段收到数据,利用微信接口再向微信指定连接发送请求,微信返回请求,即可!这个就是全流程,很多人肯定已经懵了。没事,咱一步一步来,别步子跨大了——扯到蛋了!
以上就是数据处理阶段大概流程!
三、代码实现
0、用户登录根据用户code获取openid
1 uni.login({ 2 provider: 'weixin', 3 success: function(loginRes) { 4 let code = loginRes.code; 5 if (!_this.isCanUse) { 6 //非第一次授权获取用户信息 7 uni.getUserInfo({ 8 provider: 'weixin', 9 success: function(infoRes) { 10 //获取用户信息后向调用信息更新方法 11 _this.nickName = infoRes.userInfo.nickName; //昵称 12 _this.avatarUrl = infoRes.userInfo.avatarUrl; //头像 13 _this.updateUserInfo();//调用更新信息方法 14 } 15 }); 16 } 17 18 //2.将用户登录code传递到后台置换用户SessionKey、OpenId等信息 19 uni.request({ 20 url: 'http://127.0.0.1:8000/users/', 21 data: { 22 code: code, 23 }, 24 method: 'GET', 25 header: { 26 'content-type': 'application/json' 27 }, 28 success: (res) => { 29 console.log(res.data) 30 if ( res.data.state== 1001) { 31 console.log("新注册的用户!") 32 _this.OpenId = res.data.openid; 33 } else{ 34 _this.OpenId = res.data.openid; 35 console.log("注册过的用户!开始设置本地缓存!") 36 console.log(res.data[0].id) 37 if ( res.data[0].id ) { 38 //这里获得登陆状态,然后根据登陆状态来改变用户按钮信息!!!! 39 } else{ 40 41 }; 42 _this.user_id = res.data[0].id 43 uni.setStorage({ 44 key: 'user', 45 data: res.data, 46 success: function () { 47 console.log('设置缓存成功'); 48 } 49 }); 50 // _this.gotoshopping() 51 // uni.reLaunch({//信息更新成功后跳转到小程序首页 52 // url: '/pages/shopping/shopping' 53 // }); 54 } 55 //openId、或SessionKdy存储//隐藏loading 56 uni.hideLoading(); 57 } 58 }); 59 }, 60 });
1 if request.GET.get("code"): 2 ret = {"state": 1000} 3 code = request.GET.get("code") 4 5 url = "https://api.weixin.qq.com/sns/jscode2session" 6 appid = "xxxxxxxxxxxxx" 7 secret = "xxxxxxxxxxxxxxxxxxxxx" 8 9 # url一定要拼接,不可用传参方式 10 url = url + "?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code" 11 import requests 12 r = requests.get(url) 13 print("======", r.json()) 14 openid = r.json()['openid'] 15 user = users.objects.filter(openid=openid).all() 16 if not user: 17 ret["state"] = 1001 18 ret["msg"] = "用户第一次登陆" 19 ret["openid"] = openid 20 return Response(ret) 21 else: 22 serializer = self.get_serializer(user, many=True) 23 return Response(serializer.data)
1、首先需要创建一个confige.py的配置文件!然后写路由,让前端找到“门”在哪里!
# 微信支付的配置参数 client_appid = 'xxxxxxxxxxxxxx' # 小程序appid client_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx' # 小程序secret Mch_id = 'xxxxxxxxxxx' # 商户号 Mch_key = 'xxxxxxxxxxxxxxxxxxx' # 商户Key order_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 订单地址
1 router = routers.DefaultRouter() 2 router.register("users", views.UsersViewSet) 3 router.register("goods", views.GoodsViewSet) 4 router.register("comments", views.CommentsViewSet) 5 router.register("payOrder", views.OrdersViewSet) #这个就是微信支付的接口 6 7 8 urlpatterns = [ 9 url(r'^admin/', admin.site.urls), 10 url(r'', include(router.urls)), 11 12 ]+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 13 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
1 class OrdersViewSet(viewsets.ModelViewSet): 2 queryset = Order.objects.all() 3 serializer_class = OrderModelSerializer 4 5 def create(self, request, *args, **kwargs): 6 if request.data.get("user_id"): 7 from goods.wxpay.wxpay import payOrder 8 data = payOrder(request) 9 print(data) 10 return Response(data) 11 else: 12 serializer = self.get_serializer(data=request.data) 13 serializer.is_valid(raise_exception=True) 14 self.perform_create(serializer) 15 headers = self.get_success_headers(serializer.data) 16 return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) 17 18 def perform_create(self, serializer): 19 serializer.save() 20 21 def get_success_headers(self, data): 22 try: 23 return {'Location': str(data[api_settings.URL_FIELD_NAME])} 24 except (TypeError, KeyError): 25 return {}
2、然后创建逻辑文件,获取数据请求数据返回数据!
# -*- coding: utf-8 -*- from .config import client_appid, client_secret, Mch_id, Mch_key, order_url import hashlib import datetime import xml.etree.ElementTree as ET import requests from ..models import users # 生成签名的函数 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' } print(ret) # 处理函数,对参数按照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() print(sign.upper()) return sign.upper() # 生成随机字符串 def getNonceStr(): import random data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP" nonce_str = ''.join(random.sample(data, 30)) return nonce_str # 生成商品订单号 def getWxPayOrdrID(): date = datetime.datetime.now() # 根据当前系统时间来生成商品订单号。时间精确到微秒 payOrdrID = date.strftime("%Y%m%d%H%M%S%f") return payOrdrID # 获取全部参数信息,封装成xml def get_bodyData(openid, client_ip, price): body = 'Mytest' # 商品描述 notify_url = 'https://127.0.0.1:8000/payOrder/' # 支付成功的回调地址 可访问 不带参数 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 = '<xml>' bodyData += '<appid>' + client_appid + '</appid>' # 小程序ID bodyData += '<body>' + body + '</body>' # 商品描述 bodyData += '<mch_id>' + Mch_id + '</mch_id>' # 商户号 bodyData += '<nonce_str>' + nonce_str + '</nonce_str>' # 随机字符串 bodyData += '<notify_url>' + notify_url + '</notify_url>' # 支付成功的回调地址 bodyData += '<openid>' + openid + '</openid>' # 用户标识 bodyData += '<out_trade_no>' + out_trade_no + '</out_trade_no>' # 商户订单号 bodyData += '<spbill_create_ip>' + client_ip + '</spbill_create_ip>' # 客户端终端IP bodyData += '<total_fee>' + total_fee + '</total_fee>' # 总金额 单位为分 bodyData += '<trade_type>JSAPI</trade_type>' # 交易类型 小程序取值如下:JSAPI bodyData += '<sign>' + sign + '</sign>' bodyData += '</xml>' return bodyData def xml_to_dict(xml_data): ''' xml to dict :param xml_data: :return: ''' xml_dict = {} root = ET.fromstring(xml_data) for child in root: xml_dict[child.tag] = child.text return xml_dict def dict_to_xml(dict_data): ''' dict to xml :param dict_data: :return: ''' xml = ["<xml>"] for k, v in dict_data.iteritems(): xml.append("<{0}>{1}</{0}>".format(k, v)) xml.append("</xml>") return "".join(xml) # 获取返回给小程序的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() # 统一下单支付接口 def payOrder(request): import time # 获取价格,和用户是谁 price = request.data.get("price") user_id = request.data.get("user_id") # 获取客户端ip client_ip, port = request.get_host().split(":") # 获取小程序openid openid = users.objects.get(id=user_id).openid # 请求微信的url url = order_url # 拿到封装好的xml数据 body_data = 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 = xml_to_dict(respone.content) print(content) # 返回给调用函数的数据 ret = {"state": 1000} if content["return_code"] == 'SUCCESS': # 获取预支付交易会话标识 prepay_id = content.get("prepay_id") # 获取随机字符串 nonceStr = content.get("nonce_str") # 获取paySign签名,这个需要我们根据拿到的prepay_id和nonceStr进行计算签名 paySign = get_paysign(prepay_id, timeStamp, nonceStr) # 封装返回给前端的数据 data = {"prepay_id": prepay_id, "nonceStr": nonceStr, "paySign": paySign, "timeStamp": timeStamp} print('=========',data) ret["msg"] = "成功" return data else: ret["state"] = 1001 ret["msg"] = "失败" return ret
3、前段获取后端返回的数据给微信再次发送数据请求!(包含点击的时候往后端发送数据处理请求)
1 pay(){ 2 uni.request({ 3 url: 'http://127.0.0.1:8000/payOrder/', 4 method: 'POST', 5 header: { 6 'content-type': 'application/json' 7 }, 8 data: { 9 user_id:this.user_id, 10 price:128 11 }, 12 success: res => { 13 console.log("success") 14 console.log(res.data) 15 16 uni.requestPayment({ 17 provider: 'wxpay', 18 19 timeStamp: res.data.timeStamp, 20 nonceStr: res.data.nonceStr, 21 package: 'prepay_id='+String(res.data.prepay_id), 22 signType: 'MD5', 23 paySign: res.data.paySign, 24 25 success: function (res) { 26 console.log('success:' + JSON.stringify(res)); 27 // 支付成功,给后台发送数据,保存订单 28 29 }, 30 fail: function (err) { 31 console.log('fail:' + JSON.stringify(err)); 32 // 支付失败,给后台发送数据,保存订单 33 } 34 }); 35 36 37 38 }, 39 fail: (res) => { 40 console.log("fail") 41 console.log(res) 42 }, 43 complete: () => {} 44 }); 45 46 47 }
至此相信大家也就会了。
附上我的目录结构
最后,人生苦短,我用Python!
醉不成欢惨将别,别时茫茫江浸月!