def _encrypt_message(self, msg, nonce, timestamp=None, crypto_class=None): from wechatpy.replies import BaseReply xml = """<xml> <Encrypt><![CDATA[{encrypt}]]></Encrypt> <MsgSignature><![CDATA[{signature}]]></MsgSignature> <TimeStamp>{timestamp}</TimeStamp> <Nonce><![CDATA[{nonce}]]></Nonce> </xml>""" if isinstance(msg, BaseReply): msg = msg.render() timestamp = timestamp or to_binary(int(time.time())) pc = crypto_class(self.key) encrypt = to_text(pc.encrypt(msg, self._id)) signature = _get_signature(self.token, timestamp, nonce, encrypt) return to_text(xml.format( encrypt=encrypt, signature=signature, timestamp=timestamp, nonce=nonce ))
def _decrypt(self, text, _id, exception=None): text = to_binary(text) plain_text = self.cipher.decrypt(base64.b64decode(text)) padding = byte2int(plain_text[-1]) content = plain_text[16:-padding] xml_length = socket.ntohl(struct.unpack(b'I', content[:4])[0]) xml_content = to_text(content[4:xml_length + 4]) from_id = to_text(content[xml_length + 4:]) if from_id != _id: exception = exception or Exception raise exception() return xml_content
def dict_to_xml(d, sign): xml = ['<xml>\n'] for k in sorted(d): # use sorted to avoid test error on Py3k v = d[k] if isinstance(v, six.integer_types) or (isinstance(v, six.string_types) and v.isdigit()): xml.append('<{0}>{1}</{0}>\n'.format(to_text(k), to_text(v))) else: xml.append( '<{0}><![CDATA[{1}]]></{0}>\n'.format(to_text(k), to_text(v)) ) xml.append('<sign><![CDATA[{0}]]></sign>\n</xml>'.format(to_text(sign))) return ''.join(xml)
def get_jsapi_params(self, prepay_id, timestamp=None, nonce_str=None, jssdk=False): """ 获取 JSAPI 参数 :param prepay_id: 统一下单接口返回的 prepay_id 参数值 :param timestamp: 可选,时间戳,默认为当前时间戳 :param nonce_str: 可选,随机字符串,默认自动生成 :param jssdk: 前端调用方式,默认使用 WeixinJSBridge 使用 jssdk 调起支付的话,timestamp 的 s 为小写 使用 WeixinJSBridge 调起支付的话,timeStamp 的 S 为大写 :return: 参数 """ data = { 'appId': self.sub_appid or self.appid, 'timeStamp': timestamp or to_text(int(time.time())), 'nonceStr': nonce_str or random_string(32), 'signType': 'MD5', 'package': 'prepay_id={0}'.format(prepay_id), } sign = calculate_signature( data, self._client.api_key if not self._client.sandbox else self._client.sandbox_api_key ) logger.debug('JSAPI payment parameters: data = %s, sign = %s', data, sign) data['paySign'] = sign if jssdk: data['timestamp'] = data.pop('timeStamp') return data
def test_base64decode_field_to_xml(self): from wechatpy.fields import Base64DecodeField content = to_text(base64.b64encode(b"test")) field = Base64DecodeField("Content") expected = "<Content><![CDATA[test]]></Content>" self.assertEqual(expected, field.to_xml(content))
def update(self, group_id, name): """ 修改分组名 详情请参考 http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html :param group_id: 分组id,由微信分配 :param name: 分组名字(30个字符以内) :return: 返回的 JSON 数据包 使用示例:: from wechatpy import WeChatClient client = WeChatClient('appid', 'secret') res = client.group.update(1234, 'New Name') """ name = to_text(name) return self._post( 'groups/update', data={ 'group': { 'id': int(group_id), 'name': name } } )
def test_base64encode_field_to_xml(self): from wechatpy.fields import Base64EncodeField content = b"test" field = Base64EncodeField("Content") expected = "<Content><![CDATA[{content}]]></Content>".format(content=to_text(base64.b64encode(content))) self.assertEqual(expected, field.to_xml(content))
def parse_message(xml): """ 解析微信服务器推送的 XML 消息 :param xml: XML 消息 :return: 解析成功返回对应的消息或事件,否则返回 ``UnknownMessage`` """ if not xml: return message = xmltodict.parse(to_text(xml))['xml'] message_type = message['MsgType'].lower() if message_type in ('event', 'device_event'): event_type = message['Event'].lower() # special event type for device_event if message_type == 'device_event': event_type = 'device_{event}'.format(event=event_type) if event_type == 'subscribe' and message.get('EventKey'): event_key = message['EventKey'] if event_key.startswith(('scanbarcode|', 'scanimage|')): event_type = 'subscribe_scan_product' message['Event'] = event_type else: # Scan to subscribe with scene id event event_type = 'subscribe_scan' message['Event'] = event_type message['EventKey'] = event_key.replace('qrscene_', '') message_class = EVENT_TYPES.get(event_type, UnknownMessage) else: message_class = MESSAGE_TYPES.get(message_type, UnknownMessage) return message_class(message)
def get(self, key): key = self.key_name(key) value = self.mc.get(key) if not value: return None try: return json.loads(to_text(value)) except ValueError: return value
def __repr__(self): _repr = '{klass}({name})'.format( klass=self.__class__.__name__, name=repr(self.name) ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __str__(self): _repr = 'Error code: {code}, message: {msg}'.format( code=self.errcode, msg=self.errmsg ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __repr__(self): _repr = "{klass}({msg})".format( klass=self.__class__.__name__, msg=repr(self._data) ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __repr__(self): _repr = '{klass}({code}, {msg})'.format( klass=self.__class__.__name__, code=self.errcode, msg=self.errmsg ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def get_jssdk_config(self, request, debug=None): import time from wechatpy.utils import random_string, to_text from wechatpy.client.api import WeChatJSAPI jsapi = WeChatJSAPI(self.get_wechat_client()) ticket = jsapi.get_jsapi_ticket() nonce_str = random_string(32) timestamp = to_text(int(time.time())) url = request.META.get('HTTP_REFERER') signature = jsapi.get_jsapi_signature(nonce_str, ticket, timestamp, url) debug = bool(request.GET.get('debug')) if debug is None else debug return dict( debug=debug, appId=self.app_id, timestamp=timestamp, nonceStr=nonce_str, signature=signature, jsApiList=[ 'checkJsApi', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'translateVoice', 'startRecord', 'stopRecord', 'onRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard' ], )
def get_jsapi_params(self, prepay_id): """ 返回 jsapi 的付款对象 """ import time from wechatpy.pay import api from wechatpy.utils import random_string, to_text jsapi = api.WeChatJSAPI(self.wechat_pay()) return jsapi.get_jsapi_params( prepay_id=prepay_id, nonce_str=random_string(32), timestamp=to_text(int(time.time())), )
def __str__(self): if six.PY2: return to_binary('Error code: {code}, message: {msg}'.format( code=self.return_code, msg=self.return_msg )) else: return to_text('Error code: {code}, message: {msg}'.format( code=self.return_code, msg=self.return_msg ))
def parse_message(xml): if not xml: return message = xmltodict.parse(to_text(xml))['xml'] message_type = message['MsgType'].lower() if message_type == 'event': event_type = message['Event'].lower() message_class = EVENT_TYPES.get(event_type, UnknownMessage) else: message_class = MESSAGE_TYPES.get(message_type, UnknownMessage) return message_class(message)
def get_component_verify_ticket(self, msg, signature, timestamp, nonce): """ 处理 wechat server 推送的 component_verify_ticket消息 :params msg: 加密内容 :params signature: 消息签名 :params timestamp: 时间戳 :params nonce: 随机数 """ content = self.crypto.decrypt_message(msg, signature, timestamp, nonce) message = xmltodict.parse(to_text(content))['xml'] return ComponentVerifyTicketMessage(message)
def get_unauthorized(self, msg, signature, timestamp, nonce): """ 处理取消授权通知 :params msg: 加密内容 :params signature: 消息签名 :params timestamp: 时间戳 :params nonce: 随机数 """ content = self.crypto.decrypt_message(msg, signature, timestamp, nonce) message = xmltodict.parse(to_text(content))['xml'] return ComponentUnauthorizedMessage(message)
def create(self, name): """ 创建标签 :param name: 标签名(30个字符以内) :return: 返回的 JSON 数据包 """ name = to_text(name) return self._post( 'tags/create', data={'tag': {'name': name}}, result_processor=lambda x: x['tag'] )
def get_unauthorized(self, msg, signature, timestamp, nonce): """ 处理取消授权通知 :params msg: 加密内容 :params signature: 消息签名 :params timestamp: 时间戳 :params nonce: 随机数 """ warnings.warn('`get_unauthorized` method of `WeChatComponent` is deprecated,' 'Use `parse_message` instead', DeprecationWarning, stacklevel=2) content = self.crypto.decrypt_message(msg, signature, timestamp, nonce) message = xmltodict.parse(to_text(content))['xml'] return ComponentUnauthorizedMessage(message)
def preprocess_message(self, request): component = get_component() content = component.crypto.decrypt_message( request.body, request.query_params['msg_signature'], int(request.query_params['timestamp']), int(request.query_params['nonce']) ) message = xmltodict.parse(to_text(content))['xml'] cc = json.loads(json.dumps(message)) cc['CreateTime'] = int(cc['CreateTime']) cc['CreateTime'] = datetime.fromtimestamp(cc['CreateTime']) if 'MsgId' in cc: cc['MsgId'] = int(cc['MsgId']) return cc
def create(self, name): """ 创建分组 详情请参考 http://mp.weixin.qq.com/wiki/13/be5272dc4930300ba561d927aead2569.html :param name: 分组名字(30个字符以内) :return: 返回的 JSON 数据包 """ name = to_text(name) return self._post( 'groups/create', data={'group': {'name': name}} )
def cache_component_verify_ticket(self, msg, signature, timestamp, nonce): """ 处理 wechat server 推送的 component_verify_ticket消息 :params msg: 加密内容 :params signature: 消息签名 :params timestamp: 时间戳 :params nonce: 随机数 """ warnings.warn('`cache_component_verify_ticket` method of `WeChatComponent` is deprecated,' 'Use `parse_message` instead', DeprecationWarning, stacklevel=2) content = self.crypto.decrypt_message(msg, signature, timestamp, nonce) message = xmltodict.parse(to_text(content))['xml'] o = ComponentVerifyTicketMessage(message) self.session.set(o.type, o.verify_ticket)
def create(self, name): """ 创建分组 详情请参考 http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html :param name: 分组名字(30个字符以内) :return: 返回的 JSON 数据包 """ name = to_text(name) return self._post( 'groups/create', data={'group': {'name': name}} )
def _decrypt_message(self, msg, signature, timestamp, nonce, crypto_class=None): if not isinstance(msg, dict): import xmltodict msg = xmltodict.parse(to_text(msg))['xml'] encrypt = msg['Encrypt'] _signature = _get_signature(self.token, timestamp, nonce, encrypt) if _signature != signature: raise InvalidSignatureException() pc = crypto_class(self.key) return pc.decrypt(encrypt, self._id)
def get_qrcode_url(self, ticket, data=None): """ 通过 ticket 换取二维码地址 详情请参考 http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-4 :param ticket: 二维码 ticket :param data: 额外数据 :return: 二维码地址 """ url = 'http://we.qq.com/d/{ticket}'.format(ticket=ticket) if data: if isinstance(data, (dict, tuple, list)): data = urllib.urlencode(data) data = to_text(base64.b64encode(to_binary(data))) url = '{base}#{data}'.format(base=url, data=data) return url
def get_jsapi_signature(self, prepay_id, timestamp=None, nonce_str=None): """ 获取 JSAPI 签名 :param prepay_id: 统一下单接口返回的 prepay_id 参数值 :param timestamp: 可选,时间戳,默认为当前时间戳 :param nonce_str: 可选,随机字符串,默认自动生成 :return: 签名 """ data = { 'appId': self.appid, 'timeStamp': timestamp or to_text(int(time.time())), 'nonceStr': nonce_str or random_string(32), 'signType': 'MD5', 'package': 'prepay_id={0}'.format(prepay_id), } return calculate_signature(data, self._client.api_key)
def parse_message(self, msg, msg_signature, timestamp, nonce): """ 处理 wechat server 推送消息 :params msg: 加密内容 :params msg_signature: 消息签名 :params timestamp: 时间戳 :params nonce: 随机数 """ content = self.crypto.decrypt_message(msg, msg_signature, timestamp, nonce) message = xmltodict.parse(to_text(content))['xml'] message_type = message['InfoType'].lower() message_class = COMPONENT_MESSAGE_TYPES.get(message_type, ComponentUnknownMessage) msg = message_class(message) if msg.type == 'component_verify_ticket': self.session.set(msg.type, msg.verify_ticket) elif msg.type in ('authorized', 'updateauthorized'): msg.query_auth_result = self.query_auth(msg.authorization_code) return msg
def update(self, tag_id, name): """ 编辑标签 :param tag_id: 标签id,由微信分配 :param name: 标签名字(30个字符以内) :return: 返回的 JSON 数据包 """ name = to_text(name) return self._post( 'tags/update', data={ 'tag': { 'id': int(tag_id), 'name': name } } )
def get_jssdk_config(self, request, debug=None, version='1.0.0'): import time from wechatpy.utils import random_string, to_text from wechatpy.client.api import WeChatJSAPI jsapi = WeChatJSAPI(self.get_wechat_client()) ticket = jsapi.get_jsapi_ticket() nonce_str = random_string(32) timestamp = to_text(int(time.time())) url = request.META.get('HTTP_REFERER') signature = jsapi.get_jsapi_signature(nonce_str, ticket, timestamp, url) debug = bool(request.GET.get('debug')) if debug is None else debug api_list = dict( checkJsApi=dict(minv='1.0.0', maxv='^'), onMenuShareTimeline=dict(minv='1.0.0', maxv='^'), onMenuShareAppMessage=dict(minv='1.0.0', maxv='^'), onMenuShareQQ=dict(minv='1.0.0', maxv='^'), onMenuShareWeibo=dict(minv='1.0.0', maxv='^'), hideMenuItems=dict(minv='1.0.0', maxv='^'), showMenuItems=dict(minv='1.0.0', maxv='^'), hideAllNonBaseMenuItem=dict(minv='1.0.0', maxv='^'), showAllNonBaseMenuItem=dict(minv='1.0.0', maxv='^'), translateVoice=dict(minv='1.0.0', maxv='^'), startRecord=dict(minv='1.0.0', maxv='^'), stopRecord=dict(minv='1.0.0', maxv='^'), onRecordEnd=dict(minv='1.0.0', maxv='^'), playVoice=dict(minv='1.0.0', maxv='^'), pauseVoice=dict(minv='1.0.0', maxv='^'), stopVoice=dict(minv='1.0.0', maxv='^'), uploadVoice=dict(minv='1.0.0', maxv='^'), downloadVoice=dict(minv='1.0.0', maxv='^'), chooseImage=dict(minv='1.0.0', maxv='^'), previewImage=dict(minv='1.0.0', maxv='^'), uploadImage=dict(minv='1.0.0', maxv='^'), downloadImage=dict(minv='1.0.0', maxv='^'), getNetworkType=dict(minv='1.0.0', maxv='^'), openLocation=dict(minv='1.0.0', maxv='^'), getLocation=dict(minv='1.0.0', maxv='^'), hideOptionMenu=dict(minv='1.0.0', maxv='^'), showOptionMenu=dict(minv='1.0.0', maxv='^'), closeWindow=dict(minv='1.0.0', maxv='^'), scanQRCode=dict(minv='1.0.0', maxv='^'), chooseWXPay=dict(minv='1.0.0', maxv='^'), openProductSpecificView=dict(minv='1.0.0', maxv='^'), addCard=dict(minv='1.0.0', maxv='^'), chooseCard=dict(minv='1.0.0', maxv='^'), openCard=dict(minv='1.0.0', maxv='^'), # 1.4.0 added updateAppMessageShareData=dict(minv='1.4.0', maxv='^'), updateTimelineShareData=dict(minv='1.4.0', maxv='^'), onMenuShareQZone=dict(minv='1.4.0', maxv='^'), onVoiceRecordEnd=dict(minv='1.4.0', maxv='^'), onVoicePlayEnd=dict(minv='1.4.0', maxv='^'), ) return dict( debug=debug, appId=self.app_id, timestamp=timestamp, nonceStr=nonce_str, signature=signature, jsApiList=[ k for k, v in api_list.items() if v['minv'] <= version < v['maxv'] ], )
def __str__(self): if six.PY2: return to_binary(self.render()) else: return to_text(self.render())
def calculate_signature(params, api_key): url = format_url(params, api_key) logger.debug("Calculate Signature URL: %s", url) return to_text(hashlib.md5(url).hexdigest().upper())
def calculate_signature_hmac(params, api_key): url = format_url(params, api_key) sign = to_text( hmac.new(api_key.encode(), msg=url, digestmod=hashlib.sha256).hexdigest().upper()) return sign
def __base64_decode(self, text): return to_text(base64.b64decode(to_binary(text)))
def __to_text(self, value): return to_text(value)
def get(self, key, default=None): key = self.key_name(key) value = self.redis.get(key) if value is None: return default return json.loads(to_text(value))
def calculate_signature(params, api_key): url = format_url(params, api_key) return to_text(hashlib.md5(url).hexdigest().upper())