微信公众平台开发利器-weixin-knife(Python版)

转自 http://blog.csdn.net/yueguanghaidao/article/details/45748793 


这两天将之前基于微信公众平台的代码重构了下,基础功能以库的方式提供,提供了demo使用的是django,看着之前为赶

进度写的代码真的惨不忍睹,所以weixin-knife产生了,正如其名,提供的是必要的功能,而不是完整的应用。weixin-knife可

以很方便的处理关注,取关注事件,处理文本消息,回复用户信息,jssdk处理,oauth认证,以及微信支付。

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


首先看看怎么用

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. from .weixin import handler as HD  
  2. @HD.subscribe  
  3. def subscribe(xml):  
  4.     return "welcome to brain"  
  5.  
  6. @HD.unsubscribe  
  7. def subscribe(xml):  
  8.     print "leave"  
  9.     return "leave  brain"  
上面处理了关注和取关事件,通过装饰器处理的还算透明。

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

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @HD.text  
  2. def text(xml):  
  3.     content = xml.Content  
  4.     if content == "111":  
  5.         return {"Title":"美女""Description":"比基尼美女""PicUrl":"http://9smv.com/static/mm/uploads/150411/2-150411115450247.jpg""Url":"http://9smv.com/beauty/list?category=5"}  
  6.     elif content == "222":  
  7.         return [  
  8.             ["比基尼美女""比基尼美女""http://9smv.com/static/mm/uploads/150411/2-150411115450247.jpg""http://9smv.com/beauty/list?category=5"],  
  9.             ["长腿美女""长腿美女""http://9smv.com/static/mm/uploads/150506/2-150506111A9648.jpg""http://9smv.com/beauty/list?category=8"]  
  10.         ]  
  11.     elif content == "push":  
  12.         Helper.send_text_message(xml.FromUserName, "推送消息测试")  
  13.         return "push ok"  
  14.   
  15.     return "hello world"  
如何文本是111或222,我们回复图文消息,如何使push,我们使用客服接口推送消息,其它返回“hello world"


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

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. def sns_userinfo_callback(callback=None):  
  2.     """网页授权获取用户信息装饰器 
  3.     callback(openid, userinfo): 
  4.         return user 
  5.     """  
  6.     def wrap(func):  
  7.         @wraps(func)  
  8.         def inner(*args, **kwargs):  
  9.             request = args[0]  #django第一个参数request  
  10.             openid = request.COOKIES.get('openid')  
  11.             userinfo = None  
  12.             if not openid:  
  13.                 code = request.GET.get("code")  
  14.                 if not code:  
  15.                     current = "http://"+ request.get_host() + request.get_full_path()  
  16.                     return redirect(WeixinHelper.oauth2(current))  
  17.                 else:  
  18.                     data = json.loads(WeixinHelper.getAccessTokenByCode(code))  
  19.                     access_token, openid, refresh_token = data["access_token"], data["openid"], data["refresh_token"]  
  20.                     #WeixinHelper.refreshAccessToken(refresh_token)  
  21.                     userinfo = json.loads(WeixinHelper.getSnsapiUserInfo(access_token, openid))  
  22.             else:  
  23.                 ok, openid = Helper.check_cookie(openid)  
  24.                 if not ok:  
  25.                     return redirect("/")  
  26.             request.openid = openid  
  27.             if callable(callback):  
  28.                 request.user = callback(openid, userinfo)  
  29.             response = func(request)  
  30.             return response  
  31.         return inner  
  32.     return wrap  
  33.   
  34. sns_userinfo = sns_userinfo_callback()  
在所有需要用户openid的函数前使用sns_userinfo装饰器就可以了,callback函数接收openid,userinfo,返回用户实例,这样

就可以使用request.user获取当前用户

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @sns_userinfo  
  2. def oauth(request):  
  3.     """网页授权获取用户信息"""  
  4.     resp = HttpResponse(request.openid)  
  5.     resp.set_cookie("openid", Helper.sign_cookie(request.openid))  
  6.     return resp  
使用oauth需要保存cookie,不然每次用户请求都需要授权,需要走一遍完整的oauth流程,拖慢整体响应。


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

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @sns_userinfo  
  2. def pay(request):  
  3.     response = render_to_response("pay.html")  
  4.     response.set_cookie("openid", Helper.sign_cookie(request.openid))  
  5.     return response  
  6.   
  7. @sns_userinfo  
  8. @catch  
  9. def paydetail(request):  
  10.     """获取支付信息"""  
  11.     openid = request.openid  
  12.     money = request.POST.get("money") or "0.01"  
  13.     money = int(float(money)*100)  
  14.   
  15.     jsApi = JsApi_pub()  
  16.     unifiedOrder = UnifiedOrder_pub()  
  17.     unifiedOrder.setParameter("openid",openid) #商品描述  
  18.     unifiedOrder.setParameter("body","充值测试") #商品描述  
  19.     timeStamp = time.time()  
  20.     out_trade_no = "{0}{1}".format(WxPayConf_pub.APPID, int(timeStamp*100))  
  21.     unifiedOrder.setParameter("out_trade_no", out_trade_no) #商户订单号  
  22.     unifiedOrder.setParameter("total_fee", str(money)) #总金额  
  23.     unifiedOrder.setParameter("notify_url", WxPayConf_pub.NOTIFY_URL) #通知地址   
  24.     unifiedOrder.setParameter("trade_type", "JSAPI") #交易类型  
  25.     unifiedOrder.setParameter("attach", "6666") #附件数据,可分辨不同商家(string(127))  
  26.     try:  
  27.         prepay_id = unifiedOrder.getPrepayId()  
  28.         jsApi.setPrepayId(prepay_id)  
  29.         jsApiParameters = jsApi.getParameters()  
  30.     except Exception as e:  
  31.         print(e)  
  32.     else:  
  33.         print jsApiParameters  
  34.         return HttpResponse(jsApiParameters)  
  35.   
  36.   
  37. FAIL, SUCCESS = "FAIL", "SUCCESS"  
  38. @catch  
  39. def payback(request):  
  40.     """支付回调"""  
  41.     xml = request.raw_post_data  
  42.     #使用通用通知接口  
  43.     notify = Notify_pub()  
  44.     notify.saveData(xml)  
  45.     print xml  
  46.     #验证签名,并回应微信。  
  47.     #对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,  
  48.     #微信会通过一定的策略(如30分钟共8次)定期重新发起通知,  
  49.     #尽可能提高通知的成功率,但微信不保证通知最终能成功  
  50.     if not notify.checkSign():  
  51.         notify.setReturnParameter("return_code", FAIL) #返回状态码  
  52.         notify.setReturnParameter("return_msg", "签名失败") #返回信息  
  53.     else:  
  54.         result = notify.getData()  
  55.   
  56.         if result["return_code"] == FAIL:  
  57.             notify.setReturnParameter("return_code", FAIL)  
  58.             notify.setReturnParameter("return_msg", "通信错误")  
  59.         elif result["result_code"] == FAIL:  
  60.             notify.setReturnParameter("return_code", FAIL)  
  61.             notify.setReturnParameter("return_msg", result["err_code_des"])  
  62.         else:  
  63.             notify.setReturnParameter("return_code", SUCCESS)  
  64.             out_trade_no = result["out_trade_no"] #商户系统的订单号,与请求一致。  
  65.             ###检查订单号是否已存在,以及业务代码  
  66.   
  67.     return  HttpResponse(notify.returnXml())  

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

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. $.post("/paydetail",{  
  2.    money: $momey  
  3.    },function(data){  
  4.      if(data){  
  5.        var jsonobj = eval('('+data+')');  
  6.        WeixinJSBridge.invoke('getBrandWCPayRequest', {  
  7.               "appId" : jsonobj.appId, //公众号名称,由商户传入  
  8.               "timeStamp" : jsonobj.timeStamp, //时间戳  
  9.               "nonceStr" : jsonobj.nonceStr, //随机串  
  10.               "package" : jsonobj.package,//扩展包  
  11.               "signType" : "MD5"//微信签名方式:1.sha1  
  12.               "paySign" : jsonobj.paySign //微信签名  
  13.               });  
  14.      }  
  15.    }  
  16.  );  


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

缓存。

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Helper(object):  
  2.     """微信具体逻辑帮组类"""  
  3.  
  4.     @class_property  
  5.     def access_token(cls):  
  6.         key = "ACCESS_TOKEN"  
  7.         token = cache.get(key)  
  8.         if not token:  
  9.             data = json.loads(WeixinHelper.getAccessToken())  
  10.             token, expire = data["access_token"], data["expires_in"]  
  11.             cache.set(key, token, expire-300)  
  12.         return token  
  13.  
  14.     @class_property  
  15.     def jsapi_ticket(cls):  
  16.         key = "JSAPI_TICKET"  
  17.         ticket = cache.get(key)  
  18.         if not ticket:  
  19.             data = json.loads(WeixinHelper.getJsapiTicket(cls.access_token))  
  20.             ticket, expire = data["ticket"], data["expires_in"]  
  21.             cache.set(key, ticket, expire-300)  
  22.         return ticket  

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

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class class_property(object):  
  2.     """ A property can decorator class or instance 
  3.  
  4.     class Foo(object): 
  5.         @class_property 
  6.         def foo(cls): 
  7.             return 42 
  8.  
  9.  
  10.     print(Foo.foo) 
  11.     print(Foo().foo) 
  12.  
  13.     """  
  14.     def __init__(self, func, name=None, doc=None):  
  15.         self.__name__ = name or func.__name__  
  16.         self.__module__ = func.__module__  
  17.         self.__doc__ = doc or func.__doc__  
  18.         self.func = func  
  19.   
  20.     def __get__(self, obj, type=None):  
  21.         value = self.func(type)  
  22.         return value  


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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章