Пример #1
0
    def _request(self, method, url_or_endpoint, **kwargs):
        if not url_or_endpoint.startswith(('http://', 'https://')):
            url = '{base}{endpoint}'.format(
                base=self.API_BASE_URL,
                endpoint=url_or_endpoint
            )
        else:
            url = url_or_endpoint

        if isinstance(kwargs.get('data', ''), dict):
            body = json.dumps(kwargs['data'], ensure_ascii=False)
            body = body.encode('utf-8')
            kwargs['data'] = body

        res = self._http.request(
            method=method,
            url=url,
            **kwargs
        )
        try:
            res.raise_for_status()
        except requests.RequestException as reqe:
            raise WeChatOAuthException(
                errcode=None,
                errmsg=None,
                client=self,
                request=reqe.request,
                response=reqe.response
            )

        return self._handle_result(res, method=method, url=url, **kwargs)
Пример #2
0
    def _request(self, method, url_or_endpoint, **kwargs):
        if not url_or_endpoint.startswith(('http://', 'https://')):
            api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
            url = '{base}{endpoint}'.format(base=api_base_url,
                                            endpoint=url_or_endpoint)
        else:
            url = url_or_endpoint

        if 'params' not in kwargs:
            kwargs['params'] = {}
        if isinstance(kwargs['params'], dict) and \
                'component_access_token' not in kwargs['params']:
            kwargs['params']['component_access_token'] = self.access_token
        if isinstance(kwargs['data'], dict):
            kwargs['data'] = json.dumps(kwargs['data'])

        res = self._http.request(method=method, url=url, **kwargs)
        try:
            res.raise_for_status()
        except requests.RequestException as reqe:
            raise WeChatClientException(errcode=None,
                                        errmsg=None,
                                        client=self,
                                        request=reqe.request,
                                        response=reqe.response)

        return self._handle_result(res, method, url, **kwargs)
Пример #3
0
    def add(self, media_type, media_file, title=None, introduction=None):
        """
        新增其它类型永久素材
        详情请参考
        http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html

        :param media_type: 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
        :param media_file: 要上传的文件,一个 File-object
        :param title: 视频素材标题,仅上传视频素材时需要
        :param introduction: 视频素材简介,仅上传视频素材时需要
        :return: 返回的 JSON 数据包
        """
        params = {
            'access_token': self.access_token,
            'type': media_type
        }
        if media_type == 'video':
            assert title, 'Video title must be set'
            assert introduction, 'Video introduction must be set'
            description = {
                'title': title,
                'introduction': introduction
            }
            params['description'] = json.dumps(description)
        return self._post(
            'material/add_material',
            params=params,
            files={
                'media': media_file
            }
        )
Пример #4
0
    def _request(self, method, url_or_endpoint, **kwargs):
        if not url_or_endpoint.startswith(('http://', 'https://')):
            url = '{base}{endpoint}'.format(base=self.API_BASE_URL,
                                            endpoint=url_or_endpoint)
        else:
            url = url_or_endpoint

        if isinstance(kwargs.get('data', ''), dict):
            body = json.dumps(kwargs['data'], ensure_ascii=False)
            body = body.encode('utf-8')
            kwargs['data'] = body

        res = self._http.request(method=method, url=url, **kwargs)
        try:
            res.raise_for_status()
        except requests.RequestException as reqe:
            raise WeChatOAuthException(errcode=None,
                                       errmsg=None,
                                       client=self,
                                       request=reqe.request,
                                       response=reqe.response)
        result = json.loads(res.content.decode('utf-8', 'ignore'),
                            strict=False)

        if 'errcode' in result and result['errcode'] != 0:
            errcode = result['errcode']
            errmsg = result['errmsg']
            raise WeChatOAuthException(errcode,
                                       errmsg,
                                       client=self,
                                       request=res.request,
                                       response=res)

        return result
Пример #5
0
    def _request(self, method, url_or_endpoint, **kwargs):
        if not url_or_endpoint.startswith(('http://', 'https://')):
            api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
            url = '{base}{endpoint}'.format(base=api_base_url,
                                            endpoint=url_or_endpoint)
        else:
            url = url_or_endpoint

        if 'params' not in kwargs:
            kwargs['params'] = {}
        if isinstance(kwargs['params'], dict) and \
                'access_token' not in kwargs['params']:
            kwargs['params']['access_token'] = self.access_token
        if isinstance(kwargs.get('data', ''), dict):
            body = json.dumps(kwargs['data'], ensure_ascii=False)
            body = body.encode('utf-8')
            kwargs['data'] = body

        kwargs['timeout'] = kwargs.get('timeout', self.timeout)
        result_processor = kwargs.pop('result_processor', None)
        res = self._http.request(method=method, url=url, **kwargs)
        try:
            res.raise_for_status()
        except requests.RequestException as reqe:
            raise WeChatClientException(errcode=None,
                                        errmsg=None,
                                        client=self,
                                        request=reqe.request,
                                        response=reqe.response)

        return self._handle_result(res, method, url, result_processor,
                                   **kwargs)
Пример #6
0
 def set(self, key, value, ttl=None):
     if value is None:
         return
     with open('%s-%s' % (self.file_dir, key), 'w') as f:
         value = json.dumps({
             'val':
             value,
             'expires_at':
             ttl and int(time.time()) + ttl or 0
         })
         f.write(value)
Пример #7
0
    def _request(self, method, url_or_endpoint, **kwargs):
        if not url_or_endpoint.startswith(('http://', 'https://')):
            api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
            url = '{base}{endpoint}'.format(
                base=api_base_url,
                endpoint=url_or_endpoint
            )
        else:
            url = url_or_endpoint

        # 群发消息上传视频接口地址 HTTPS 证书错误,暂时忽略证书验证
        if url.startswith('https://file.api.weixin.qq.com'):
            kwargs['verify'] = False

        if 'params' not in kwargs:
            kwargs['params'] = {}
        if isinstance(kwargs['params'], dict) and \
                'access_token' not in kwargs['params']:
            kwargs['params']['access_token'] = self.access_token
        if isinstance(kwargs.get('data', ''), dict):
            body = json.dumps(kwargs['data'], ensure_ascii=False)
            body = body.encode('utf-8')
            kwargs['data'] = body

        kwargs['timeout'] = kwargs.get('timeout', self.timeout)
        result_processor = kwargs.pop('result_processor', None)
        res = requests.request(
            method=method,
            url=url,
            **kwargs
        )
        try:
            res.raise_for_status()
        except requests.RequestException as reqe:
            raise WeChatClientException(
                errcode=None,
                errmsg=None,
                client=self,
                request=reqe.request,
                response=reqe.response
            )

        return self._handle_result(
            res, method, url, result_processor, **kwargs
        )
Пример #8
0
    def fetch_access_token(self):
        """
        获取 component_access_token
        详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list\
        &t=resource/res_list&verify=1&id=open1419318587&token=&lang=zh_CN

        :return: 返回的 JSON 数据包
        """
        url = '{0}{1}'.format(self.API_BASE_URL,
                              '/component/api_component_token')
        return self._fetch_access_token(url=url,
                                        data=json.dumps({
                                            'component_appid':
                                            self.component_appid,
                                            'component_appsecret':
                                            self.component_appsecret,
                                            'component_verify_ticket':
                                            self.component_verify_ticket
                                        }))
Пример #9
0
    def fetch_access_token(self):
        """
        获取 component_access_token
        详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list\
        &t=resource/res_list&verify=1&id=open1419318587&token=&lang=zh_CN

        :return: 返回的 JSON 数据包
        """
        url = '{0}{1}'.format(
            self.API_BASE_URL,
            '/component/api_component_token'
        )
        return self._fetch_access_token(
            url=url,
            data=json.dumps({
                'component_appid': self.component_appid,
                'component_appsecret': self.component_appsecret,
                'component_verify_ticket': self.component_verify_ticket
            })
        )
Пример #10
0
    def add(self, media_type, media_file, title=None, introduction=None):
        """
        新增其它类型永久素材
        详情请参考
        https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html

        :param media_type: 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
        :param media_file: 要上传的文件,一个 File-object
        :param title: 视频素材标题,仅上传视频素材时需要
        :param introduction: 视频素材简介,仅上传视频素材时需要
        :return: 返回的 JSON 数据包
        """
        params = {'access_token': self.access_token, 'type': media_type}
        if media_type == 'video':
            assert title, 'Video title must be set'
            assert introduction, 'Video introduction must be set'
            description = {'title': title, 'introduction': introduction}
            params['description'] = json.dumps(description)
        return self._post('material/add_material',
                          params=params,
                          files={'media': media_file})
Пример #11
0
    def _request(self, method, url_or_endpoint, **kwargs):
        if not url_or_endpoint.startswith(('http://', 'https://')):
            api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
            url = '{base}{endpoint}'.format(
                base=api_base_url,
                endpoint=url_or_endpoint
            )
        else:
            url = url_or_endpoint

        if 'params' not in kwargs:
            kwargs['params'] = {}
        if isinstance(kwargs['params'], dict) and \
                'component_access_token' not in kwargs['params']:
            kwargs['params'][
                'component_access_token'] = self.access_token
        if isinstance(kwargs['data'], dict):
            kwargs['data'] = json.dumps(kwargs['data'])

        res = requests.request(
            method=method,
            url=url,
            **kwargs
        )
        try:
            res.raise_for_status()
        except requests.RequestException as reqe:
            raise WeChatClientException(
                errcode=None,
                errmsg=None,
                client=self,
                request=reqe.request,
                response=reqe.response
            )

        return self._handle_result(res, method, url, **kwargs)
Пример #12
0
    def create(self,
               trade_type,
               body,
               total_fee,
               notify_url,
               client_ip=None,
               user_id=None,
               out_trade_no=None,
               detail=None,
               attach=None,
               fee_type='CNY',
               time_start=None,
               time_expire=None,
               goods_tag=None,
               product_id=None,
               device_info=None,
               limit_pay=None,
               scene_info=None):
        """
        统一下单接口

        :param trade_type: 交易类型,取值如下:JSAPI,NATIVE,APP,WAP, MWEB
        :param body: 商品描述
        :param total_fee: 总金额,单位分
        :param notify_url: 接收微信支付异步通知回调地址
        :param client_ip: 可选,APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
        :param user_id: 可选,用户在商户appid下的唯一标识。trade_type=JSAPI,此参数必传
        :param out_trade_no: 可选,商户订单号,默认自动生成
        :param detail: 可选,商品详情
        :param attach: 可选,附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
        :param fee_type: 可选,符合ISO 4217标准的三位字母代码,默认人民币:CNY
        :param time_start: 可选,订单生成时间,默认为当前时间
        :param time_expire: 可选,订单失效时间,默认为订单生成时间后两小时
        :param goods_tag: 可选,商品标记,代金券或立减优惠功能的参数
        :param product_id: 可选,trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义
        :param device_info: 可选,终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
        :param limit_pay: 可选,指定支付方式,no_credit--指定不能使用信用卡支付
        :param scene_info: 可选,上报支付的场景信息
        :type scene_info: dict
        :return: 返回的结果数据
        """
        now = datetime.fromtimestamp(time.time(), tz=timezone('Asia/Shanghai'))
        hours_later = now + timedelta(hours=2)
        if time_start is None:
            time_start = now
        if time_expire is None:
            time_expire = hours_later
        if not out_trade_no:
            out_trade_no = '{0}{1}{2}'.format(self.mch_id,
                                              now.strftime('%Y%m%d%H%M%S'),
                                              random.randint(1000, 10000))
        if scene_info is not None:
            scene_info = json.dumps(scene_info, ensure_ascii=False)
        data = {
            'appid': self.appid,
            'device_info': device_info,
            'body': body,
            'detail': detail,
            'attach': attach,
            'out_trade_no': out_trade_no,
            'fee_type': fee_type,
            'total_fee': total_fee,
            'spbill_create_ip': client_ip or get_external_ip(),
            'time_start': time_start.strftime('%Y%m%d%H%M%S'),
            'time_expire': time_expire.strftime('%Y%m%d%H%M%S'),
            'goods_tag': goods_tag,
            'notify_url': notify_url,
            'trade_type': trade_type,
            'limit_pay': limit_pay,
            'product_id': product_id,
            'openid': user_id,
            'scene_info': scene_info,
        }
        return self._post('pay/unifiedorder', data=data)
Пример #13
0
 def set(self, key, value, ttl=None):
     if value is None:
         return
     key = self.key_name(key)
     value = json.dumps(value)
     self.mc.set(key, value)
Пример #14
0
    def _request(self, method, url_or_endpoint, **kwargs):
        http_client = AsyncHTTPClient()
        if not url_or_endpoint.startswith(('http://', 'https://')):
            api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
            url = '{base}{endpoint}'.format(base=api_base_url,
                                            endpoint=url_or_endpoint)
        else:
            url = url_or_endpoint

        headers = {}
        params = kwargs.pop('params', {})
        if 'access_token' not in params:
            # 这里需要针对 tornado 特殊处理
            access_token = self.access_token
            if access_token:
                if not self.expires_at:
                    # user provided access_token, just return it
                    access_token = self.access_token
                else:
                    timestamp = time.time()
                    if self.expires_at - timestamp > 60:
                        access_token = self.access_token
            else:
                # fetch access
                yield self.fetch_access_token()

            params['access_token'] = self.access_token

        params = urlencode(dict((k, to_binary(v)) for k, v in params.items()))
        url = '{0}?{1}'.format(url, params)

        data = kwargs.get('data', {})
        files = kwargs.get('files')
        if files:
            from requests.models import RequestEncodingMixin
            from requests.utils import super_len

            body, content_type = RequestEncodingMixin._encode_files(
                files, data)
            headers['Content-Type'] = content_type
            headers['Content-Length'] = super_len(body)
        else:
            if isinstance(data, dict):
                body = json.dumps(data, ensure_ascii=False)
                body = body.encode('utf-8')
            else:
                body = data

        result_processor = kwargs.pop('result_processor', None)
        timeout = kwargs.get('timeout', self.timeout)
        req = HTTPRequest(url=url,
                          method=method.upper(),
                          headers=headers,
                          body=body if method.upper() != "GET" else None,
                          request_timeout=timeout)
        res = yield http_client.fetch(req)
        if res.error is not None:
            raise WeChatClientException(errcode=None,
                                        errmsg=None,
                                        client=self,
                                        request=req,
                                        response=res)

        result = self._handle_result(res, method, url, result_processor,
                                     **kwargs)
        raise Return(result)
Пример #15
0
 def set(self, key, value, ttl=None):
     if value is None:
         return
     key = self.key_name(key)
     value = json.dumps(value)
     self.redis.set(key, value, ex=ttl)
Пример #16
0
    def create(self, trade_type, body, total_fee, notify_url, client_ip=None,
               user_id=None, out_trade_no=None, detail=None, attach=None,
               fee_type='CNY', time_start=None, time_expire=None, goods_tag=None,
               product_id=None, device_info=None, limit_pay=None, scene_info=None, sub_user_id=None):
        """
        统一下单接口

        :param trade_type: 交易类型,取值如下:JSAPI,NATIVE,APP,WAP, MWEB
        :param body: 商品描述
        :param total_fee: 总金额,单位分
        :param notify_url: 接收微信支付异步通知回调地址
        :param client_ip: 可选,APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
        :param user_id: 可选,用户在商户appid下的唯一标识。trade_type=JSAPI和appid已设定,此参数必传
        :param sub_user_id: 可选,小程序appid下的唯一标识。trade_type=JSAPI和sub_appid已设定,此参数必传
        :param out_trade_no: 可选,商户订单号,默认自动生成
        :param detail: 可选,商品详情
        :param attach: 可选,附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
        :param fee_type: 可选,符合ISO 4217标准的三位字母代码,默认人民币:CNY
        :param time_start: 可选,订单生成时间,默认为当前时间
        :param time_expire: 可选,订单失效时间,默认为订单生成时间后两小时
        :param goods_tag: 可选,商品标记,代金券或立减优惠功能的参数
        :param product_id: 可选,trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义
        :param device_info: 可选,终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
        :param limit_pay: 可选,指定支付方式,no_credit--指定不能使用信用卡支付
        :param scene_info: 可选,上报支付的场景信息
        :type scene_info: dict
        :return: 返回的结果数据
        """
        now = datetime.fromtimestamp(time.time(), tz=timezone('Asia/Shanghai'))
        hours_later = now + timedelta(hours=2)
        if time_start is None:
            time_start = now
        if time_expire is None:
            time_expire = hours_later
        if not out_trade_no:
            out_trade_no = '{0}{1}{2}'.format(
                self.mch_id,
                now.strftime('%Y%m%d%H%M%S'),
                random.randint(1000, 10000)
            )
        if scene_info is not None:
            scene_info = json.dumps(scene_info, ensure_ascii=False)
        data = {
            'appid': self.appid,
            'sub_appid': self.sub_appid,
            'device_info': device_info,
            'body': body,
            'detail': detail,
            'attach': attach,
            'out_trade_no': out_trade_no,
            'fee_type': fee_type,
            'total_fee': total_fee,
            'spbill_create_ip': client_ip or get_external_ip(),
            'time_start': time_start.strftime('%Y%m%d%H%M%S'),
            'time_expire': time_expire.strftime('%Y%m%d%H%M%S'),
            'goods_tag': goods_tag,
            'notify_url': notify_url,
            'trade_type': trade_type,
            'limit_pay': limit_pay,
            'product_id': product_id,
            'openid': user_id,
            'sub_openid': sub_user_id,
            'scene_info': scene_info,
        }
        return self._post('pay/unifiedorder', data=data)
Пример #17
0
 def set(self, key, value, ttl=0):
     if value is None:
         return
     key = self.key_name(key)
     value = json.dumps(value)
     self.mc.set(key, value, ttl)