def get_subscribe_authorize_url(self, scene, template_id, redirect_url, reserved=None): """ 构造请求用户授权的url 详情请参阅: https://mp.weixin.qq.com/wiki?id=mp1500374289_66bvB :param scene: 订阅场景值,开发者可以填0-10000的整形值,用来标识订阅场景值 :type scene: int :param template_id: 订阅消息模板ID,登录公众平台后台,在接口权限列表处可查看订阅模板ID :param redirect_url: 授权后重定向的回调地址 :param reserved: 用于保持请求和回调的状态,授权请后原样带回给第三方。该参数可用于防止csrf攻击。若不指定则随机生成。 """ if reserved is None: reserved = random_string() base_url = 'https://mp.weixin.qq.com/mp/subscribemsg' params = [ ('action', 'get_confirm'), ('appid', self.appid), ('scene', scene), ('template_id', template_id), ('redirect_url', redirect_url), ('reserved', reserved), ] encoded_params = six.moves.urllib.parse.urlencode(params) url = '{base}?{params}#wechat_redirect'.format(base=base_url, params=encoded_params) return url
async 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 await 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
async 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) if self.sandbox: api_base_url = '{url}sandboxnew/'.format(url=api_base_url) url = '{base}{endpoint}'.format( base=api_base_url, endpoint=url_or_endpoint ) else: url = url_or_endpoint params = kwargs.pop('params', {}) params = urlencode(dict((k, to_binary(v)) for k, v in params.items())) url = '{0}?{1}'.format(url, params) data = kwargs.pop('data', {}) if isinstance(data, dict): if 'mchid' not in data: data.setdefault('mch_id', self.mch_id) data.setdefault('sub_mch_id', self.sub_mch_id) data.setdefault('nonce_str', random_string(32)) data = optionaldict(data) if data.get('sign_type', 'MD5') == 'HMAC-SHA256': sign = calculate_signature_hmac(data, await self.sandbox_api_key() if self.sandbox else self.api_key) else: sign = calculate_signature(data, await self.sandbox_api_key() if self.sandbox else self.api_key) body = dict_to_xml(data, sign) body = body.encode('utf-8') kwargs['body'] = body # 商户证书 if self.mch_cert and self.mch_key: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(self.mch_cert, self.mch_key) kwargs['ssl_options'] = ssl_ctx kwargs['request_timeout'] = kwargs.pop('timeout') if 'timeout' in kwargs else self.timeout logger.debug('Request to WeChat API: %s %s\n%s', method, url, kwargs) req = HTTPRequest( url, method=method, **kwargs ) res = await self._http.fetch(req) if res.error is not None: raise WeChatPayException( return_code=None, client=self, request=req, response=res ) return self._handle_result(res)
async def _fetch_sandbox_api_key(self): nonce_str = random_string(32) sign = calculate_signature({'mch_id': self.mch_id, 'nonce_str': nonce_str}, self.api_key) payload = dict_to_xml({ 'mch_id': self.mch_id, 'nonce_str': nonce_str, }, sign=sign) headers = {'Content-Type': 'text/xml'} api_url = '{base}sandboxnew/pay/getsignkey'.format(base=self.API_BASE_URL) request = HTTPRequest(api_url, method='POST', body=payload, headers=headers) response = await self._http.fetch(request) return xmltodict.parse(response.body.decode('utf-8'))['xml'].get('sandbox_signkey')
def get_appapi_params(self, prepay_id, timestamp=None, nonce_str=None): """ 获取 APP 支付参数 :param prepay_id: 统一下单接口返回的 prepay_id 参数值 :param timestamp: 可选,时间戳,默认为当前时间戳 :param nonce_str: 可选,随机字符串,默认自动生成 :return: 签名 """ data = { 'appid': self.appid, 'partnerid': self.mch_id, 'prepayid': prepay_id, 'package': 'Sign=WXPay', 'timestamp': timestamp or to_text(int(time.time())), 'noncestr': nonce_str or random_string(32) } sign = calculate_signature(data, self._client.api_key) data['sign'] = sign return data
def get_jsapi_card_params(self, card_ticket, card_type, **kwargs): """ 参数意义见微信文档地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 :param card_ticket: 用于卡券的微信 api_ticket :param card_type: :param kwargs: 非必须参数:noncestr, timestamp, code, openid, fixed_begintimestamp, outer_str :return: 包含调用jssdk所有所需参数的 dict """ card_signature_dict = { 'card_type': card_type, 'noncestr': kwargs.get('noncestr', random_string()), 'api_ticket': card_ticket, 'appid': self.appid, 'timestamp': kwargs.get('timestamp', str(int(time.time()))), } list_before_sign = sorted( [str(x) for x in card_signature_dict.values()]) str_to_sign = "".join(list_before_sign).encode() card_signature_dict['sign'] = hashlib.sha1(str_to_sign).hexdigest() return card_signature_dict
async 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.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), } return calculate_signature( data, self._client.api_key if not self._client.sandbox else await self._client.sandbox_api_key())
def get_random_string(self): return random_string(16)