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中声明只包含一个元素的元组数据方法
Aug 25 Python
python基础教程之序列详解
Aug 29 Python
python通过colorama模块在控制台输出彩色文字的方法
Mar 19 Python
数据清洗--DataFrame中的空值处理方法
Jul 03 Python
使用Python向DataFrame中指定位置添加一列或多列的方法
Jan 29 Python
Python爬虫抓取技术的一些经验
Jul 12 Python
对Django 中request.get和request.post的区别详解
Aug 12 Python
python 多进程共享全局变量之Manager()详解
Aug 15 Python
python文字转语音的实例代码分析
Nov 12 Python
用openCV和Python 实现图片对比,并标识出不同点的方式
Dec 19 Python
Pytorch 实现focal_loss 多类别和二分类示例
Jan 14 Python
Python中npy和mat文件的保存与读取
Apr 24 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 5.0对象模型深度探索之属性和方法
2008/03/27 PHP
浅谈php中mysql与mysqli的区别分析
2013/06/10 PHP
php函数指定默认值方法的小例子
2013/12/04 PHP
php+ajax实时输入自动搜索匹配的方法
2014/12/26 PHP
PHP中把对象数组转换成普通数组的方法
2015/07/10 PHP
PHP+jQuery+Ajax实现分页效果 jPaginate插件的应用
2015/10/09 PHP
PHP上传图片到数据库并显示的实例代码
2019/12/20 PHP
Laravel 5+ .env环境配置文件详解
2020/04/06 PHP
项目实践之javascript技巧
2007/12/06 Javascript
Javascript的匿名函数小结
2009/12/31 Javascript
分享十五个最佳jQuery 幻灯插件和教程
2010/03/27 Javascript
editable.js 基于jquery的表格的编辑插件
2011/10/24 Javascript
Javascript前端UI框架Kit使用指南之Kitjs简介
2014/11/28 Javascript
JavaScript中的继承方式详解
2015/02/11 Javascript
javascript实现的图片切割多块效果实例
2015/05/07 Javascript
js和jquery分别验证单选框、复选框、下拉框
2015/12/17 Javascript
学习JavaScript设计模式之模板方法模式
2016/01/20 Javascript
原生js实现百叶窗效果及原理介绍
2016/04/12 Javascript
JS调用Android、Ios原生控件
2017/01/06 Javascript
JS实现的小火箭发射动画效果示例
2018/12/08 Javascript
解决VantUI popup 弹窗不弹出或无蒙层的问题
2020/11/03 Javascript
原生JavaScript实现幻灯片效果
2021/02/19 Javascript
Python的Django框架中的数据库配置指南
2015/07/17 Python
Python使用Pickle模块进行数据保存和读取的讲解
2019/04/09 Python
python logging 日志的级别调整方式
2020/02/21 Python
美的官方商城:Midea
2016/09/14 全球购物
俄罗斯街头服装品牌:Black Star Wear
2017/03/01 全球购物
马来西亚最大的在线隐形眼镜商店:MrLens
2019/03/27 全球购物
商铺消防安全责任书
2014/07/29 职场文书
2014乡镇领导班子四风对照检查材料思想汇报
2014/10/05 职场文书
思想作风整顿个人剖析材料
2014/10/06 职场文书
亮剑观后感
2015/06/05 职场文书
详解Mysql和Oracle之间的误区
2021/05/18 MySQL
详解Spring事件发布与监听机制
2021/06/30 Java/Android
关于对TypeScript泛型参数的默认值理解
2022/07/15 Javascript
基于redis+lua进行限流的方法
2022/07/23 Redis