Python开发微信公众平台的方法详解【基于weixin-knife】


Posted in Python onJuly 08, 2017

本文实例讲述了Python开发微信公众平台的方法。分享给大家供大家参考,具体如下:

这两天将之前基于微信公众平台的代码重构了下,基础功能以库的方式提供,提供了demo使用的是django,看着之前为赶进度写的代码真的惨不忍睹,所以weixin-knife产生了,正如其名,提供的是必要的功能,而不是完整的应用。weixin-knife可以很方便的处理关注,取关注事件,处理文本消息,回复用户信息,jssdk处理,oauth认证,以及微信支付。

github地址:https://github.com/Skycrab/weixin-knife。

首先看看怎么用

from .weixin import handler as HD
@HD.subscribe
def subscribe(xml):
  return "welcome to brain"
@HD.unsubscribe
def subscribe(xml):
  print "leave"
  return "leave brain"

上面处理了关注和取关事件,通过装饰器处理的还算透明。

处理文本消息,回复图文消息如下:

@HD.text
def text(xml):
  content = xml.Content
  if content == "111":
    return {"Title":"美女", "Description":"比基尼美女", "PicUrl":"http://9smv.com/static/mm/uploads/150411/2-150411115450247.jpg", "Url":"http://9smv.com/beauty/list?category=5"}
  elif content == "222":
    return [
      ["比基尼美女", "比基尼美女", "http://9smv.com/static/mm/uploads/150411/2-150411115450247.jpg", "http://9smv.com/beauty/list?category=5"],
      ["长腿美女", "长腿美女", "http://9smv.com/static/mm/uploads/150506/2-150506111A9648.jpg", "http://9smv.com/beauty/list?category=8"]
    ]
  elif content == "push":
    Helper.send_text_message(xml.FromUserName, "推送消息测试")
    return "push ok"
  return "hello world"

如何文本是111或222,我们回复图文消息,如何使push,我们使用客服接口推送消息,其它返回“hello world"

一般我们会使用oauth网页授权获取用户的openid,如果是多个链接都需要通过oauth处理,代码会很难看,通过装饰器可以很好的处理这个问题。

def sns_userinfo_callback(callback=None):
  """网页授权获取用户信息装饰器
  callback(openid, userinfo):
    return user
  """
  def wrap(func):
    @wraps(func)
    def inner(*args, **kwargs):
      request = args[0] #django第一个参数request
      openid = request.COOKIES.get('openid')
      userinfo = None
      if not openid:
        code = request.GET.get("code")
        if not code:
          current = "http://"+ request.get_host() + request.get_full_path()
          return redirect(WeixinHelper.oauth2(current))
        else:
          data = json.loads(WeixinHelper.getAccessTokenByCode(code))
          access_token, openid, refresh_token = data["access_token"], data["openid"], data["refresh_token"]
          #WeixinHelper.refreshAccessToken(refresh_token)
          userinfo = json.loads(WeixinHelper.getSnsapiUserInfo(access_token, openid))
      else:
        ok, openid = Helper.check_cookie(openid)
        if not ok:
          return redirect("/")
      request.openid = openid
      if callable(callback):
        request.user = callback(openid, userinfo)
      response = func(request)
      return response
    return inner
  return wrap
sns_userinfo = sns_userinfo_callback()

在所有需要用户openid的函数前使用sns_userinfo装饰器就可以了,callback函数接收openid,userinfo,返回用户实例,这样就可以使用request.user获取当前用户

@sns_userinfo
def oauth(request):
  """网页授权获取用户信息"""
  resp = HttpResponse(request.openid)
  resp.set_cookie("openid", Helper.sign_cookie(request.openid))
  return resp

使用oauth需要保存cookie,不然每次用户请求都需要授权,需要走一遍完整的oauth流程,拖慢整体响应。

weixin-knife提供了微信支付支持,稍微修改我之前移植的官方PHP版本,https://github.com/Skycrab/wzhifuSDK

@sns_userinfo
def pay(request):
  response = render_to_response("pay.html")
  response.set_cookie("openid", Helper.sign_cookie(request.openid))
  return response
@sns_userinfo
@catch
def paydetail(request):
  """获取支付信息"""
  openid = request.openid
  money = request.POST.get("money") or "0.01"
  money = int(float(money)*100)
  jsApi = JsApi_pub()
  unifiedOrder = UnifiedOrder_pub()
  unifiedOrder.setParameter("openid",openid) #商品描述
  unifiedOrder.setParameter("body","充值测试") #商品描述
  timeStamp = time.time()
  out_trade_no = "{0}{1}".format(WxPayConf_pub.APPID, int(timeStamp*100))
  unifiedOrder.setParameter("out_trade_no", out_trade_no) #商户订单号
  unifiedOrder.setParameter("total_fee", str(money)) #总金额
  unifiedOrder.setParameter("notify_url", WxPayConf_pub.NOTIFY_URL) #通知地址
  unifiedOrder.setParameter("trade_type", "JSAPI") #交易类型
  unifiedOrder.setParameter("attach", "6666") #附件数据,可分辨不同商家(string(127))
  try:
    prepay_id = unifiedOrder.getPrepayId()
    jsApi.setPrepayId(prepay_id)
    jsApiParameters = jsApi.getParameters()
  except Exception as e:
    print(e)
  else:
    print jsApiParameters
    return HttpResponse(jsApiParameters)
FAIL, SUCCESS = "FAIL", "SUCCESS"
@catch
def payback(request):
  """支付回调"""
  xml = request.raw_post_data
  #使用通用通知接口
  notify = Notify_pub()
  notify.saveData(xml)
  print xml
  #验证签名,并回应微信。
  #对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,
  #微信会通过一定的策略(如30分钟共8次)定期重新发起通知,
  #尽可能提高通知的成功率,但微信不保证通知最终能成功
  if not notify.checkSign():
    notify.setReturnParameter("return_code", FAIL) #返回状态码
    notify.setReturnParameter("return_msg", "签名失败") #返回信息
  else:
    result = notify.getData()
    if result["return_code"] == FAIL:
      notify.setReturnParameter("return_code", FAIL)
      notify.setReturnParameter("return_msg", "通信错误")
    elif result["result_code"] == FAIL:
      notify.setReturnParameter("return_code", FAIL)
      notify.setReturnParameter("return_msg", result["err_code_des"])
    else:
      notify.setReturnParameter("return_code", SUCCESS)
      out_trade_no = result["out_trade_no"] #商户系统的订单号,与请求一致。
      ###检查订单号是否已存在,以及业务代码
  return HttpResponse(notify.returnXml())

pay.html就是使用WeixinJSBridge.invode调用

$.post("/paydetail",{
  money: $momey
  },function(data){
   if(data){
    var jsonobj = eval('('+data+')');
    WeixinJSBridge.invoke('getBrandWCPayRequest', {
       "appId" : jsonobj.appId, //公众号名称,由商户传入
       "timeStamp" : jsonobj.timeStamp, //时间戳
       "nonceStr" : jsonobj.nonceStr, //随机串
       "package" : jsonobj.package,//扩展包
       "signType" : "MD5", //微信签名方式:1.sha1
       "paySign" : jsonobj.paySign //微信签名
       });
   }
  }
 );

由于access_token, jsapi_ticket需要缓存,而缓存方式又依赖于具体环境,所以提供了一个Helper类,使用了django 的cache缓存。

class Helper(object):
  """微信具体逻辑帮组类"""
  @class_property
  def access_token(cls):
    key = "ACCESS_TOKEN"
    token = cache.get(key)
    if not token:
      data = json.loads(WeixinHelper.getAccessToken())
      token, expire = data["access_token"], data["expires_in"]
      cache.set(key, token, expire-300)
    return token
  @class_property
  def jsapi_ticket(cls):
    key = "JSAPI_TICKET"
    ticket = cache.get(key)
    if not ticket:
      data = json.loads(WeixinHelper.getJsapiTicket(cls.access_token))
      ticket, expire = data["ticket"], data["expires_in"]
      cache.set(key, ticket, expire-300)
    return ticket

class_property提供了类级别的property,当然实例也是可以用的。

class class_property(object):
  """ A property can decorator class or instance
  class Foo(object):
    @class_property
    def foo(cls):
      return 42
  print(Foo.foo)
  print(Foo().foo)
  """
  def __init__(self, func, name=None, doc=None):
    self.__name__ = name or func.__name__
    self.__module__ = func.__module__
    self.__doc__ = doc or func.__doc__
    self.func = func
  def __get__(self, obj, type=None):
    value = self.func(type)
    return value

使用weixin-knife助力公众平台开发,你完全可以稍加修改用于flask等其它web框架。

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python连接DB2数据库
Aug 27 Python
Python的时间模块datetime详解
Apr 17 Python
Python探索之爬取电商售卖信息代码示例
Oct 27 Python
详解如何利用Cython为Python代码加速
Jan 27 Python
Python基于多线程实现抓取数据存入数据库的方法
Jun 22 Python
Python 多线程搜索txt文件的内容,并写入搜到的内容(Lock)方法
Aug 23 Python
python爬虫 2019中国好声音评论爬取过程解析
Aug 26 Python
python用requests实现http请求代码实例
Oct 31 Python
python的time模块和datetime模块实例解析
Nov 29 Python
python 进程池pool使用详解
Oct 15 Python
详解python的super()的作用和原理
Oct 29 Python
python 办公自动化——基于pyqt5和openpyxl统计符合要求的名单
May 25 Python
python 中random模块的常用方法总结
Jul 08 #Python
Python调用微信公众平台接口操作示例
Jul 08 #Python
HTML中使用python屏蔽一些基本功能的方法
Jul 07 #Python
CentOS 7下安装Python 3.5并与Python2.7兼容并存详解
Jul 07 #Python
浅谈Python中的可变对象和不可变对象
Jul 07 #Python
Python MySQL数据库连接池组件pymysqlpool详解
Jul 07 #Python
Python用Pillow(PIL)进行简单的图像操作方法
Jul 07 #Python
You might like
《PHP编程最快明白》第七讲:php图片验证码与缩略图
2010/11/01 PHP
PHP获取Exif缩略图的方法
2015/07/13 PHP
PHP 枚举类型的管理与设计知识点总结
2020/02/13 PHP
如何用js 实现依赖注入的思想,后端框架思想搬到前端来
2015/08/03 Javascript
jQuery+css3实现Ajax点击后动态删除功能的方法
2015/08/10 Javascript
JS弹出层遮罩,隐藏背景页面滚动条细节优化分析
2016/04/29 Javascript
JavaScript中对象的不同创建方法
2016/08/12 Javascript
Vue.js创建Calendar日历效果
2016/11/03 Javascript
localStorage的黑科技-js和css缓存机制
2017/02/06 Javascript
如何使用Bootstrap 按钮实例详解
2017/03/29 Javascript
浅谈React和Redux的连接react-redux
2017/12/04 Javascript
基于node搭建服务器,写接口,调接口,跨域的实例
2018/05/13 Javascript
Vue父子组件双向绑定传值的实现方法
2018/07/31 Javascript
vue-rx的初步使用教程
2018/09/21 Javascript
vue项目打包上传github并制作预览链接(pages)
2019/04/19 Javascript
vue 集成jTopo 处理方法
2019/08/07 Javascript
原生js实现无缝轮播图
2020/01/11 Javascript
JS数组方法concat()用法实例分析
2020/01/18 Javascript
vue和小程序项目中使用iconfont的方法
2020/05/19 Javascript
Python的IDEL增加清屏功能实例
2017/06/19 Python
opencv改变imshow窗口大小,窗口位置的方法
2018/04/02 Python
Python 确定多项式拟合/回归的阶数实例
2018/12/29 Python
python的sorted用法详解
2019/06/25 Python
django和vue实现数据交互的方法
2019/08/21 Python
浅谈tensorflow中Dataset图片的批量读取及维度的操作详解
2020/01/20 Python
Python with语句用法原理详解
2020/07/03 Python
详解Django ORM引发的数据库N+1性能问题
2020/10/12 Python
使用CSS3的rem属性制作响应式页面布局的要点解析
2016/05/24 HTML / CSS
html5 Canvas画图教程(1)—画图的基本常识
2013/01/09 HTML / CSS
个人求职自荐信范文
2014/06/20 职场文书
借名购房协议书范本
2014/10/06 职场文书
悬空寺导游词
2015/02/05 职场文书
2015年领导干部廉洁自律工作总结
2015/05/26 职场文书
《画家和牧童》教学反思
2016/02/17 职场文书
求职信如何撰写?
2019/05/22 职场文书
九大龙王魂骨,山龙王留下躯干骨,榜首死的最憋屈(被捏碎)
2022/03/18 国漫