微信公衆平臺開發利器-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框架。

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