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)
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)
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 } )
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
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)
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)
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 )
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 }))
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 }) )
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})
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)
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)
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)
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)
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)
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)
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)