ymao 4 年 前
コミット
4674e0db5b
3 ファイル変更120 行追加3 行削除
  1. 1 1
      authen/wxlogin/__init__.py
  2. 118 1
      authen/wxlogin/wx.py
  3. 1 1
      setup.py

+ 1 - 1
authen/wxlogin/__init__.py

@@ -6,7 +6,7 @@ import json
 from flask import Blueprint, url_for
 
 from .login import bp
-from .wx import WeixinLogin
+from .wx import WeixinLogin, WeixinMP
 
 
 class WxLogin(WeixinLogin):

+ 118 - 1
authen/wxlogin/wx.py

@@ -1,9 +1,14 @@
 from __future__ import unicode_literals
 
+import time
 import json
 import requests
+import redis
+import string
+import random
+import hashlib
 
-__all__ = ("wx_login")
+__all__ = ("wx_login", "WeixinMP", )
 
 try:
     unicode = unicode
@@ -65,12 +70,123 @@ class Map(dict):
         super(Map, self).__delitem__(key)
         del self.__dict__[key]
 
+
 class WeixinLoginError(WeixinError):
 
     def __init__(self, msg):
         super(WeixinLoginError, self).__init__(msg)
 
 
+class WeixinMP(object):
+    api_uri = "https://api.weixin.qq.com/cgi-bin"
+
+    def __init__(self, app_id=None, app_secret=None, redis_url=None):
+        self.app_id = app_id
+        self.app_secret = app_secret
+        self.session = requests.Session()
+
+        if redis_url is not None:
+            self.redis_store = redis.StrictRedis.from_url(redis_url)
+        else:
+            self.redis_store = None
+
+    def init_app(self, app):
+        """
+        flask config
+        """
+        app.config.setdefault("WXMP_REDIS_URL", None)
+        app.config.setdefault("WXMP_APP_ID", None)
+        app.config.setdefault("WXMP_APP_SECRET", None)
+
+        self.app_id = app.config.get("WXMP_APP_ID")
+        self.app_secret = app.config.get("WXMP_APP_SECRET")
+        redis_url = app.config.get("WXMP_REDIS_URL")
+
+        assert self.app_id is not None, "APP_ID IS NULL"
+        assert self.app_secret is not None, "APP_SECRET IS NULL"
+        # assert redis_url is not None, "WXMP_REDIS_URL IS NULL"
+        if redis_url:
+            self.redis_store = redis.StrictRedis.from_url(redis_url)
+
+    def fetch(self, method, url, params=None, data=None, headers=None):
+        req = requests.Request(method, url, params=params,
+                               data=data, headers=headers)
+        prepped = req.prepare()
+        resp = self.session.send(prepped, timeout=20)
+        data = Map(resp.json())
+        if data.errcode:
+            msg = "%(errcode)d %(errmsg)s" % data
+            raise WeixinError(msg)
+        return data
+
+    def get(self, path, params=None, token=True):
+        url = "{0}{1}".format(self.api_uri, path)
+        params = {} if not params else params
+        token and params.setdefault("access_token", self.access_token)
+        return self.fetch("GET", url, params)
+
+    def gen_token(self):
+        params = dict()
+        params.setdefault("grant_type", "client_credential")
+        params.setdefault("appid", self.app_id)
+        params.setdefault("secret", self.app_secret)
+        data = self.get("/token", params, False)
+        print("-" * 20, data, params)
+        return data.access_token
+
+    @property
+    def access_token(self):
+        """
+        获取服务端凭证
+        """
+        if self.redis_store:
+            ac_key = "access_token:%s" % self.app_id
+
+            access_token = self.redis_store.get(ac_key)
+            if not access_token:
+                access_token = self.gen_token()
+                self.redis_store.setex(ac_key, 2 * 60 * 60, access_token)
+                return access_token
+            return access_token
+        else:
+            return self.gen_token()
+
+    @property
+    def jsapi_ticket(self):
+        """
+        获取jsapi ticket
+        """
+        ticket_key = "jsapi_ticket:%s" % self.app_id
+        ticket = self.redis_store.get(ticket_key)
+        if not ticket:
+            params = dict()
+            params.setdefault("type", "jsapi")
+            data = self.get("/ticket/getticket", params, True)
+            self.redis_store.setex(ticket_key, 2 * 60 * 60, data.ticket)
+            return data.ticket
+        return ticket
+
+    @property
+    def nonce_str(self):
+        char = string.ascii_letters + string.digits
+        return "".join(random.choice(char) for _ in range(32))
+
+    def jsapi_sign(self, **kwargs):
+        """
+        生成签名给js使用
+        """
+        timestamp = str(int(time.time()))
+        nonce_str = self.nonce_str
+        kwargs.setdefault("jsapi_ticket", self.jsapi_ticket)
+        kwargs.setdefault("timestamp", timestamp)
+        kwargs.setdefault("noncestr", nonce_str)
+        raw = [(k, kwargs[k]) for k in sorted(kwargs.keys())]
+        s = "&".join("=".join(kv) for kv in raw if kv[1])
+        print(s)
+        sign = hashlib.sha1(s.encode("utf-8")).hexdigest().lower()
+        return Map(sign=sign, timestamp=timestamp, noncestr=nonce_str)
+
+
 class WeixinLogin(object):
 
     def __init__(self, app=None, prefix=""):
@@ -184,4 +300,5 @@ class WeixinLogin(object):
         """
         return self.userinfo(access_token, openid)
 
+
 wx_login = WeixinLogin()

+ 1 - 1
setup.py

@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
 
 setup(
     name='authen.wxlogin',
-    version=0.51,
+    version=0.52,
     url='http://git.trops-global.com/authen/trops_wxlogin.git',
     license='GPL',
     author='authen',