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之yield表达式学习
Sep 02 Python
python常见数制转换实例分析
May 09 Python
python处理大数字的方法
May 27 Python
Python输出带颜色的字符串实例
Oct 10 Python
python爬虫中get和post方法介绍以及cookie作用
Feb 08 Python
PyQt5固定窗口大小的方法
Jun 18 Python
python UDP(udp)协议发送和接收的实例
Jul 22 Python
利用pandas合并多个excel的方法示例
Oct 10 Python
将tensorflow模型打包成PB文件及PB文件读取方式
Jan 23 Python
使用Python三角函数公式计算三角形的夹角案例
Apr 15 Python
Python偏函数Partial function使用方法实例详解
Jun 17 Python
Python爬虫之用Xpath获取关键标签实现自动评论盖楼抽奖(二)
Jun 07 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完整的日历类(CLASS)
2006/11/27 PHP
PHP字符串的编码问题的详细介绍
2013/04/27 PHP
CodeIgniter生成网站sitemap地图的方法
2013/11/13 PHP
CodeIgniter安全相关设置汇总
2014/07/03 PHP
php为字符串前后添加指定数量字符的方法
2015/05/04 PHP
PHP验证类的封装与使用方法详解
2019/01/10 PHP
使用jQuery.wechat构建微信WEB应用
2014/10/09 Javascript
JavaScript分秒倒计时器实现方法
2015/02/02 Javascript
javascript实现博客园页面右下角返回顶部按钮
2015/02/22 Javascript
javascript实现类似于新浪微博搜索框弹出效果的方法
2015/07/27 Javascript
JavaScript获取图片像素颜色并转换为box-shadow显示
2016/03/11 Javascript
Jquery把获取到的input值转换成json
2017/05/15 jQuery
node版本管理工具n包使用教程详解
2018/11/09 Javascript
ES6小技巧之代替lodash
2019/06/07 Javascript
微信小程序实现吸顶效果
2020/01/08 Javascript
JS表格的动态操作完整示例
2020/01/13 Javascript
你准备好迎接vue3.0了吗
2020/04/28 Javascript
实例讲解python函数式编程
2014/06/09 Python
Python中zip()函数用法实例教程
2014/07/31 Python
详解Django中的ifequal和ifnotequal标签使用
2015/07/16 Python
mac PyCharm添加Python解释器及添加package路径的方法
2018/10/29 Python
Pandas:Series和DataFrame删除指定轴上数据的方法
2018/11/10 Python
python+selenium实现简历自动刷新的示例代码
2019/05/20 Python
python遍历文件目录、批量处理同类文件
2019/08/31 Python
Python print不能立即打印的解决方式
2020/02/19 Python
Python 基于jwt实现认证机制流程解析
2020/06/22 Python
常用的HTML5列表标签
2017/06/20 HTML / CSS
美国首屈一指的高品质珠宝设计师和零售商:Allurez
2018/01/23 全球购物
艺术专业大学生自我评价
2013/09/22 职场文书
总裁秘书岗位职责
2013/12/04 职场文书
2014年学习厉行节约反对浪费思想汇报
2014/09/10 职场文书
民政局标准版离婚协议书
2014/12/01 职场文书
支行行长岗位职责
2015/02/15 职场文书
妈妈别哭观后感
2015/06/08 职场文书
用Python爬取各大高校并可视化帮弟弟选大学,弟弟直呼牛X
2021/06/11 Python
使用compose函数优化代码提高可读性及扩展性
2022/06/16 Javascript