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

作者:yueguanghaidao 时间:2023-03-09 12:05:43 

本文实例讲述了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,微信公众平台
0
投稿

猜你喜欢

  • 关于生成目录树结构的类

    2007-09-13 12:19:00
  • 解决Python下imread,imwrite不支持中文的问题

    2022-11-15 06:46:23
  • asp在sql server2000中新建帐号和给帐号权限代码

    2008-01-29 13:46:00
  • 深入解析MySQL的事务隔离及其对性能产生的影响

    2024-01-27 11:17:07
  • 关于keras中卷积层Conv2D的学习记录

    2022-07-16 17:33:16
  • MySQL定时任务EVENT事件的使用方法

    2024-01-17 03:26:20
  • VSCode 配置uni-app的方法

    2024-04-29 13:18:00
  • 十一个案例带你吃透Python函数参数

    2021-07-02 08:11:12
  • Python3 pandas 操作列表实例详解

    2021-11-30 14:24:12
  • 深入理解ES6 Promise 扩展always方法

    2024-04-18 09:46:46
  • python GUI库图形界面开发之PyQt5信号与槽机制、自定义信号基础介绍

    2022-04-18 06:08:49
  • 详解Python 2.6 升级至 Python 2.7 的实践心得

    2022-01-03 09:47:09
  • vue awesome swiper异步加载数据出现的bug问题

    2024-05-03 15:10:58
  • windows10系统中安装python3.x+scrapy教程

    2022-06-03 22:27:48
  • python机器学习Sklearn实战adaboost算法示例详解

    2023-08-15 06:35:59
  • 判断浏览器是否接受 Cookie

    2009-07-28 17:52:00
  • MySQL中使用or、in与union all在查询命令下的效率对比

    2024-01-16 02:28:45
  • 对Django中static(静态)文件详解以及{% static %}标签的使用方法

    2021-03-27 20:28:21
  • MSSQL中递归SQL查询语句实例说明-

    2011-09-30 11:42:43
  • python设置环境变量的作用和实例

    2021-07-03 05:03:17
  • asp之家 网络编程 m.aspxhome.com