1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Django微信公众号开发(二)公众号内微信支付

Django微信公众号开发(二)公众号内微信支付

时间:2021-02-13 04:48:40

相关推荐

Django微信公众号开发(二)公众号内微信支付

前言

微信公众号开发又一重要项就是微信支付,如何在微信公众号中或者微信自带的浏览器中实现微信支付呢?这就是本文的目的。

对于微信支付有几种分类,一种是app支付(顾名思义给予一些软件app使用的)、微信内H5支付(什么意思呢,就是微信内置的浏览器自带了一些js、css文件,当然是微信特有的,上篇博客有讲过,获取到的用户昵称带表情会乱码,在微信自带的浏览器你只要将它装换为utf-8就能正常显示,原因是内置浏览器有对应的font文件,所以在内置浏览器中也有包含了支付功能的js文件,没错在微信自带的浏览器中你不用在你的html中引入js都可以使用它的内置js,本文会讲解如何用它的js调起支付)、其它PC端浏览器的扫码支付。

开发流程

服务器

同样需要服务器,然后设置服务器的域名,比如这里还是设置域名为:

配置公众号收集信息

首先需要一个有微信支付权限和网页授权权限的公众号,其次需要一个有微信支付权限的商户号(商户号就是支付的钱到哪里)。同样,登录公众平台在开发–>基本配置–>公众号开发信息里找到公众号的开发者ID(AppID)和开发者密码(AppSecret) ,然后在微信商户平台里找到mch_id和api密钥。

注意:必须先在微信公众平台设置网页授权域名(这个域名你就填服务器的)和在微信商户平台设置您的公众号支付支付目录,设置路径:商户平台–>产品中心–>开发配置–>公众号支付–>添加(添加一个支付url,比如你的支付页面是:/payment)。公众号支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。

开发流程

微信支付不是说一开始就传订单编号、价格、商品等信息去调用支付的,在调用支付接口前我们需要先去向微信发起下单请求,只有发起下单成功后才能调用支付接口。

首先配置你的公众号、商户和回调页面信息,其它值做相应修改,参数文件如下:

# -*- coding: utf-8 -*-# ----------------------------------------------# @Time : 18-3-21 上午11:50# @Author : YYJ# @File : wechatConfig.py# @CopyRight: ZDWL# ----------------------------------------------"""微信公众号和商户平台信息配置文件"""# ----------------------------------------------微信公众号---------------------------------------------- ## 公众号idAPPID = 'appid'# 公众号AppSecretAPPSECRET = 'appscrect'# ----------------------------------------------微信商户平台---------------------------------------------- ## 商户idMCH_ID = 'mc_id'# 商户API秘钥API_KEY = 'api秘钥'# ----------------------------------------------回调页面---------------------------------------------- ## 用户授权获取code后的回调页面,如果需要实现验证登录就必须填写REDIRECT_URI = '/index'PC_LOGIN_REDIRECT_URI = '/index'defaults = {# 微信内置浏览器获取code微信接口'wechat_browser_code': 'https://open./connect/oauth2/authorize',# 微信内置浏览器获取access_token微信接口'wechat_browser_access_token': 'https://api./sns/oauth2/access_token',# 微信内置浏览器获取用户信息微信接口'wechat_browser_user_info': 'https://api./sns/userinfo',# pc获取登录二维码接口'pc_QR_code': 'https://open./connect/qrconnect',# 获取微信公众号access_token接口'mp_access_token': 'https://api./cgi-bin/token',# 设置公众号行业接口'change_industry': 'https://api./cgi-bin/template/api_set_industry',# 获取公众号行业接口'get_industry': 'https://api./cgi-bin/template/get_industry',# 发送模板信息接口'send_templates_message': 'https://api./cgi-bin/message/template/send',# 支付下单接口'order_url': 'https://api.mch./pay/unifiedorder',}SCOPE = 'snsapi_userinfo'PC_LOGIN_SCOPE = 'snsapi_login'GRANT_TYPE = 'client_credential'STATE = ''LANG = 'zh_CN'

下面就是支付下单和支付接口调用的封装代码了,其中包括了上一篇博客的授权登录代码和下一篇博客的发送模板消息的代码封装:

# -*- coding: utf-8 -*-# ----------------------------------------------# @Time : 18-3-21 下午1:36# @Author : YYJ# @File : WechatAPI.py# @CopyRight: ZDWL# ----------------------------------------------import hashlibimport randomimport timefrom urllib import parsefrom xml.etree.ElementTree import fromstringimport requestsfrom src.beauty.main.wechat.config import wechatConfigclass WechatAPI(object):def __init__(self):self.config = wechatConfigself._access_token = Noneself._openid = Noneself.config = wechatConfigself.dic = {}@staticmethoddef process_response_login(rsp):"""解析微信登录返回的json数据,返回相对应的dict, 错误信息"""if 200 != rsp.status_code:return None, {'code': rsp.status_code, 'msg': 'http error'}try:content = rsp.json()except Exception as e:return None, {'code': 9999, 'msg': e}if 'errcode' in content and content['errcode'] != 0:return None, {'code': content['errcode'], 'msg': content['errmsg']}return content, Nonedef process_response_pay(self, rsp):"""解析微信支付下单返回的json数据,返回相对应的dict, 错误信息"""rsp = self.xml_to_array(rsp)if 'SUCCESS' != rsp['return_code']:return None, {'code': '9999', 'msg': rsp['return_msg']}if 'prepay_id' in rsp:return {'prepay_id': rsp['prepay_id']}, Nonereturn rsp, None@staticmethoddef create_time_stamp():"""产生时间戳"""now = time.time()return int(now)@staticmethoddef create_nonce_str(length=32):"""产生随机字符串,不长于32位"""chars = "abcdefghijklmnopqrstuvwxyz0123456789"strs = []for x in range(length):strs.append(chars[random.randrange(0, len(chars))])return "".join(strs)@staticmethoddef xml_to_array(xml):"""将xml转为array"""array_data = {}root = fromstring(xml)for child in root:value = child.textarray_data[child.tag] = valuereturn array_datadef get_sign(self):"""生成签名"""# 签名步骤一:按字典序排序参数key = sorted(self.dic.keys())buffer = []for k in key:buffer.append("{0}={1}".format(k, self.dic[k]))# self.dic["paySign"] = self.get_sign(jsApiObj)parm = "&".join(buffer)# 签名步骤二:在string后加入KEYparm = "{0}&key={1}".format(parm, self.config.API_KEY).encode('utf-8')# 签名步骤三:MD5加密signature = hashlib.md5(parm).hexdigest()# 签名步骤四:所有字符转为大写result_ = signature.upper()return result_def array_to_xml(self, sign_name=None):"""array转xml"""if sign_name is not None:self.dic[sign_name] = self.get_sign()xml = ["<xml>"]for k in self.dic.keys():xml.append("<{0}>{1}</{0}>".format(k, self.dic[k]))xml.append("</xml>")return "".join(xml)class WechatLogin(WechatAPI):def get_code_url(self):"""微信内置浏览器获取网页授权code的url"""url = self.config.defaults.get('wechat_browser_code') + ('?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect' %(self.config.APPID, parse.quote(self.config.REDIRECT_URI),self.config.SCOPE, self.config.STATE if self.config.STATE else ''))return urldef get_code_url_pc(self):"""pc浏览器获取网页授权code的url"""url = self.config.defaults.get('pc_QR_code') + ('?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect' %(self.config.APPID, parse.quote(self.config.REDIRECT_URI), self.config.PC_LOGIN_SCOPE,self.config.STATE if self.config.STATE else ''))return urldef get_access_token(self, code):"""获取access_token"""params = {'appid': self.config.APPID,'secret': self.config.APPSECRET,'code': code,'grant_type': 'authorization_code'}token, err = self.process_response_login(requests.get(self.config.defaults.get('wechat_browser_access_token'),params=params))if not err:self._access_token = token['access_token']self._openid = token['openid']return self._access_token, self._openiddef get_user_info(self, access_token, openid):"""获取用户信息"""params = {'access_token': access_token,'openid': openid,'lang': self.config.LANG}return self.process_response_login(requests.get(self.config.defaults.get('wechat_browser_user_info'), params=params))class WechatTemplates(WechatAPI):def __init__(self):super().__init__()self.mp_access_token = Noneself.mp_expires_in = Nonedef get_mp_access_token(self):"""获取公众号的access_token"""# err_code = {#'-1': '系统繁忙,请稍候再试',#'0': '请求成功',#'40001': 'AppSecret错误或者AppSecret不属于这个公众号,请开发者确认AppSecret的正确性',#'40002': '请确保grant_type字段值为client_credential',#'40164': '调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置',# }url = self.config.defaults.get('mp_access_token') + ('?grant_type=%s&appid=%s&secret=%s' %(self.config.GRANT_TYPE, self.config.APPID,self.config.APPSECRET))token_data = eval(requests.get(url).content)if 'access_token' not in token_data:return token_data['errcode'], token_data['errmsg'], Falseelse:self.mp_access_token = token_data['access_token']self.mp_expires_in = token_data['expires_in']return self.mp_access_token, self.mp_expires_in, True# 以下功能暂不使用# def change_industry(self):#"""设置所属行业,每月可修改行业1次"""#url = self.config.defaults.get('change_industry') + (# '?access_token=%s' % self.mp_access_token)#prams = {# "industry_id1": "23",# "industry_id2": "31"#}#data = requests.post(url, prams)## def get_industry(self):#"""获取行业信息"""#if self.mp_access_token is None:# _, msg, success = self.get_mp_access_token()# if not success:# return msg, False#url = self.config.defaults.get('get_industry') + (# '?access_token=%s' % self.mp_access_token)#industry_data = requests.get(url)#if 'primary_industry' in industry_data:# primary_industry = industry_data['primary_industry']# secondary_industry = industry_data['secondary_industry']# return primary_industry, secondary_industry, True#else:# return '', '获取行业信息错误', False## def get_templates_id(self):#pass#def send_templates_message(self, touser, template_id, data, url=None, miniprogram=None):post_data = {"touser": touser,"template_id": template_id,"data": data}if url is not None:post_data['url'] = urlif miniprogram is not None:post_data['miniprogram'] = miniprogramurl = self.config.defaults.get('send_templates_message') + ('?access_token=%s' % self.mp_access_token)back_data = requests.post(url, json=post_data)print(back_data)if "errcode" in back_data and back_data["errcode"] == 0:return Trueelse:return Falseclass WechatPayAPI(WechatAPI):def __init__(self, package, sign_type=None):super().__init__()self.appId = self.config.APPIDself.timeStamp = self.create_time_stamp()self.nonceStr = self.create_nonce_str()self.package = packageself.signType = sign_typeself.dic = {"appId": self.appId, "timeStamp": "{0}".format(self.create_time_stamp()),"nonceStr": self.create_nonce_str(), "package": "prepay_id={0}".format(self.package)}if sign_type is not None:self.dic["signType"] = sign_typeelse:self.dic["signType"] = "MD5"def get_dic(self):self.dic['paySign'] = self.get_sign()return self.dicclass WechatOrder(WechatAPI):def __init__(self, body, trade_type, out_trade_no, total_fee, spbill_create_ip, notify_url, device_info=None,sign_type=None, attach=None, fee_type=None, time_start=None, time_expire=None, goods_tag=None,product_id=None, detail=None, limit_pay=None, openid=None, scene_info=None):super().__init__()self.device_info = device_info #self.nonce_str = self.create_nonce_str()self.sign_type = sign_type #self.detail = detail #self.body = bodyself.attach = attach #self.out_trade_no = out_trade_noself.fee_type = fee_type #self.total_fee = total_feeself.spbill_create_ip = spbill_create_ipself.time_start = time_start #self.time_expire = time_expire #self.goods_tag = goods_tag #self.notify_url = notify_urlself.trade_type = trade_typeself.product_id = product_id #self.limit_pay = limit_pay #self.openid = openid #self.scene_info = scene_info #self.dic = {"appid": self.config.APPID, "mch_id": self.config.MCH_ID,"nonce_str": self.nonce_str, "body": self.body,'out_trade_no': out_trade_no,'openid': self.openid,"total_fee": self.total_fee, "spbill_create_ip": self.spbill_create_ip,"notify_url": self.notify_url,"trade_type": self.trade_type}if self.device_info is not None:self.dic["device_info"] = self.device_infoif self.sign_type is not None:self.dic["sign_type"] = self.sign_typeif self.detail is not None:self.dic["detail"] = self.detailif self.attach is not None:self.dic["attach"] = self.attachif self.fee_type is not None:self.dic["fee_type"] = self.fee_typeif self.time_start is not None:self.dic["time_start"] = self.time_startif self.time_expire is not None:self.dic["time_expire"] = self.time_expireif self.goods_tag is not None:self.dic["goods_tag"] = self.goods_tagif self.product_id is not None:self.dic["product_id"] = self.product_idif self.limit_pay is not None:self.dic["limit_pay"] = self.limit_payif self.openid is not None:self.dic["openid"] = self.openidif self.scene_info is not None:self.dic["scene_info"] = self.scene_infodef order_post(self):if self.config.APPID is None:return None, Truexml_ = self.array_to_xml('sign')data = requests.post(self.config.defaults['order_url'], data=xml_.encode('utf-8'),headers={'Content-Type': 'text/xml'})return self.process_response_pay(data.content)

上面的WechatOrder类就是支付下单,WechatPayAPI类是支付请求,你看官方文档的支付接口,可能刚开始你会问怎么调用这个接口不传商品信息和价格信息啊,其实这些信息是在支付下单的时候传过去的,下单需要的参数如下(根据你的需要填写非必须的字段):

接着是我的urls.py文件中加一个下单支付请求url和支付结果返回回调url:

from django.conf.urls import urlfrom src.beauty.main.wechat.apps.index.views import AuthView, GetInfoView, WechatPayurlpatterns = [url(r'^$', views.home),# 支付下单及请求url(r'^wechatPay$', WechatPay.as_view()),# 授权请求url(r'^auth/$', AuthView.as_view()),# 之前的授权回调页面url(r'^index$', GetInfoView.as_view()),# 调起支付后返回结果的回调页面url(r'^success$', views.success),# 这里我省掉了我的其它页面]

然后是我的views.py文件,我只展示支付和结果返回的view:

from django.contrib.auth import logout, authenticate, loginfrom django.contrib.auth.backends import ModelBackend# from django.core import serializersimport jsonimport requestsimport base64import randomimport timefrom datetime import datetime, datefrom django.core.serializers.json import DjangoJSONEncoderfrom django.db.models import Qfrom django.http import HttpResponse, HttpResponseServerErrorfrom django.shortcuts import render, redirect# from src.beauty.main.wechat.utils.wechatAPI import WechatAPIfrom src.beauty.main.wechat.utils.WechatAPI import WechatLogin, WechatTemplates, WechatOrder, WechatPayAPIfrom django.views.generic import Viewfrom django.conf import settingsfrom django.http import HttpResponseRedirectclass WechatPay(View):@staticmethoddef post(request):# 这个if判断是我传入的订单的id,测试的时候没有传入,你可以测试的时候去掉这个判断if 'order' in request.POST:# order = request.POST['order']# order = Order.objects.filter(is_effective=True).filter(uuid=order).first()body = 'JSP支付测试'trade_type = 'JSAPI'import randomrand = random.randint(0, 100)out_trade_no = 'HSTY3JMKFHGA325' + str(rand)total_fee = 1spbill_create_ip = '127.0.0.1'notify_url = '/success'order = WechatOrder(body=body,trade_type=trade_type,out_trade_no=out_trade_no,openid=request.session['openid'],total_fee=total_fee,spbill_create_ip=spbill_create_ip,notify_url=notify_url)datas, error = order.order_post()if error:return HttpResponseServerError('get access_token error')order_data = datas['prepay_id'].encode('iso8859-1').decode('utf-8'),pay = WechatPayAPI(package=order_data[0])dic = pay.get_dic()dic["package"] = "prepay_id=" + order_data[0]return HttpResponse(json.dumps(dic), content_type="application/json")def success(request):# 这里写支付结果的操作,重定向return redirect('/')

最后在你的需要支付页面的html中添加如下:

<script>function onBridgeReady(data){WeixinJSBridge.invoke('getBrandWCPayRequest', {"appId": data.appId,//公众号名称,由商户传入"timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数"nonceStr": data.nonceStr, //随机串"package": data.package, //订单id,这是微信下单微信生成的订单id不是你自己的"signType":"MD5", //微信签名方式:"paySign": data.paySign //微信签名 }, function(res){if(res.err_msg == "get_brand_wcpay_request:ok" ) {# 支付成功的跳转页面,我这里跳到了首页window.location.href = '/';}// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。});}# 点击支付的响应函数,调起下单请求,并返回支付需要上传的数据(都在data里)$('#wechatPay').click(function(){var order = $(this).data('parm');$.ajaxSetup({data: {csrfmiddlewaretoken: '{{ csrf_token }}'},});$.ajax({type: 'POST',url: '/wechatPay',data: {'order': order},dataType: 'json',success: function (data) {if (typeof WeixinJSBridge == "undefined"){if( document.addEventListener ){document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);}else if (document.attachEvent){document.attachEvent('WeixinJSBridgeReady', onBridgeReady);document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);}}else{onBridgeReady(data);}},error: function () {}});});</script>

结语

中间配置有问题欢迎留言,这是我的调用结果:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。