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

来源:互联网 发布:淘宝店铺的主营在哪里 编辑:程序博客网 时间:2024/05/15 23:52

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

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

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

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


首先看看怎么用

from .weixin import handler as HD@HD.subscribedef subscribe(xml):    return "welcome to brain"@HD.unsubscribedef subscribe(xml):    print "leave"    return "leave  brain"
上面处理了关注和取关事件,通过装饰器处理的还算透明。

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

@HD.textdef 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 wrapsns_userinfo = sns_userinfo_callback()
在所有需要用户openid的函数前使用sns_userinfo装饰器就可以了,callback函数接收openid,userinfo,返回用户实例,这样

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

@sns_userinfodef 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_userinfodef pay(request):    response = render_to_response("pay.html")    response.set_cookie("openid", Helper.sign_cookie(request.openid))    return response@sns_userinfo@catchdef 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"@catchdef 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框架。






8 0