# -*- coding: utf-8 -*- # !/usr/bin/env python from __future__ import unicode_literals import hashlib import requests import simplejson as json import six from library import to_binary, to_text from library.ys.base import YsErrorCode, PayOpt, TradeType from .exceptions import YsPayException, YsCommuException class YsPay(object): PAY_HOST_DEV = 'https://open.eycard.cn:8443/WorthTech_Access_AppPaySystemV2/apppayacc' PAY_HOST = 'https://open.eycard.cn:8443/WorthTech_Access_AppPaySystemV2/apppayacc' def __str__(self): _repr = '{kclass}(channelId: {channelId}, merId: {merId})'.format( kclass = self.__class__.__name__, channelId = self.channelId, merId = self.merId) if six.PY2: return to_binary(_repr) else: return to_text(_repr) def __repr__(self): return str(self) def __init__(self, channelId, merId, termId, key, debug = False): self.channelId = channelId self.merId = merId self.termId = termId self.key = key self.debug = debug if self.debug: self.host_url = YsPay.PAY_HOST_DEV else: self.host_url = YsPay.PAY_HOST def get(self, **kwargs): return self._request( url = self.host_url, method = 'get', **kwargs ) def post(self, **kwargs): return self._request( url = self.host_url, method = 'post', **kwargs ) def _decode_result(self, res): try: result = json.loads(res.content.decode('utf-8', 'ignore'), strict = False) except (TypeError, ValueError): return res return result def _handle_result(self, res, method = None, url = None, result_processor = None, **kwargs): if not isinstance(res, dict): result = self._decode_result(res) else: result = res if result['resultcode'] not in ['00', '98', 'AA']: raise YsPayException( errCode = result['resultcode'], errMsg = result.get('returnmsg', 'unknown'), client = self) return result if not result_processor else result_processor(result) def _request(self, url, method, **kwargs): headers = {'Content-Type': 'application/json;charset=utf-8'} 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', 15) result_processor = kwargs.pop('result_processor', None) with requests.sessions.Session() as session: res = session.request( url = url, method = method, headers = headers, **kwargs ) try: res.raise_for_status() except requests.RequestException as reqe: raise YsCommuException( errCode = 'HTTP{}'.format(res.status_code), errMsg = reqe.message, result_code = '', client = self, request = reqe.request, response = reqe.response) return self._handle_result( res, method, self.host_url, result_processor, **kwargs ) def sign(self, raw, order = True, token = True): if order: raw = [(k, str(raw[k])) for k in sorted(raw.keys())] else: raw = [(k, str(raw[k])) for k in raw.keys()] s = '&'.join('='.join(kv) for kv in raw) if token: s += '&key={0}'.format(self.key) return hashlib.md5(s).hexdigest().upper() def unified_order(self, pay_opt, openId, out_trade_no, total_fee, subject, notify_url, **data): """ 统一下单 """ if pay_opt not in [PayOpt.wxPreOrder, PayOpt.apPreOrder]: raise YsPayException(errCode = YsErrorCode.MY_INVALID_PARAMETER, errMsg = u'不支持的支付类型') if not openId: raise YsPayException(errCode = YsErrorCode.MY_INVALID_PARAMETER, errMsg = u'缺少openId参数') data = { 'channelid': self.channelId, 'merid': self.merId, 'termid': self.termId, 'opt': pay_opt, 'tradetype': TradeType.JSAPI, 'tradetrace': out_trade_no, 'tradeamt': total_fee, 'body': subject, 'openid': openId, 'notifyurl': notify_url } sign = self.sign(data) data.update({ 'sign': sign }) from six.moves.urllib import parse params = parse.urlencode(data) result = self.get(**{ 'params': params }) if pay_opt == PayOpt.wxPreOrder: return result['wtorderid'], json.loads(result['prepayid']) if pay_opt == PayOpt.apPreOrder: return result['wtorderid'], {'tradeNO': str(result['prepayid'])} assert True, 'can not execute this line.' def api_trade_query(self, out_trade_no = None, trade_no = None): assert out_trade_no, 'out trade no must not be empty' data = { 'channelid': self.channelId, 'merid': self.merId, 'termid': self.termId, 'opt': PayOpt.tradeQuery, 'tradetrace': out_trade_no } sign = self.sign(data) data.update({ 'sign': sign }) from six.moves.urllib import parse params = parse.urlencode(data) result = self.get(**{ 'params': params }) return result