123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- from __future__ import unicode_literals
- import hashlib
- import datetime
- import requests
- import simplejson as json
- from collections import OrderedDict
- import six
- from library import to_binary, to_text
- from .exceptions import SaobeiException, SaobeiValidationError, SaobeiSignatureError
- __all__ = ('SaobeiPay')
- FAIL = '02'
- SUCCESS = '01'
- class SaobeiPay(object):
- PAY_HOST_DEV = 'http://test.lcsw.cn:8045/lcsw'
- PAY_HOST = 'https://pay.lcsw.cn/lcsw'
- def __str__(self):
- _repr = '{kclass}(merchant_no: {merchant_no}, terminal_id: {terminal_id})'.format(
- kclass = self.__class__.__name__,
- merchant_no = self.merchant_no,
- terminal_id = self.terminal_id)
- if six.PY2:
- return to_binary(_repr)
- else:
- return to_text(_repr)
- def __repr__(self):
- return str(self)
- def __init__(self, merchant_no, terminal_id, access_token, debug = False):
- self.merchant_no = merchant_no
- self.terminal_id = terminal_id
- self.access_token = access_token
- self.debug = debug
- if self.debug:
- self.host_url = SaobeiPay.PAY_HOST_DEV
- else:
- self.host_url = SaobeiPay.PAY_HOST
- 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 += '&access_token={0}'.format(self.access_token)
- return hashlib.md5(s).hexdigest()
- def check(self, data):
- sign = data.pop('key_sign')
- return sign == self.sign(data)
- def get(self, endpoint, **kwargs):
- return self._request(
- method = 'get',
- endpoint = endpoint,
- **kwargs
- )
- def post(self, endpoint, **kwargs):
- return self._request(
- method = 'post',
- endpoint = endpoint,
- **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['return_code'] == '02':
- raise SaobeiException(
- return_code = '02',
- return_msg = result['return_msg'],
- result_code = result['result_code'] if 'result_code' in result else '',
- client = self,
- request = None,
- response = None)
- return result if not result_processor else result_processor(result)
- def _request(self, method, endpoint, **kwargs):
- url = '{base}{endpoint}'.format(
- base = self.host_url,
- endpoint = endpoint
- )
- headers = {'Content-Type': 'application/json'}
- 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 SaobeiException(
- return_code = 'HTTP{}'.format(res.status_code),
- return_msg = reqe.message,
- result_code = '',
- client = self,
- request = reqe.request,
- response = reqe.response
- )
- return self._handle_result(
- res, method, url, result_processor, **kwargs
- )
- def generate_wap_pay_url(self, pay_trace, pay_time, total_fee, **data):
- """
- 统一下单
- """
- url = self.host_url + '/open/wap/110/pay'
- data.update({
- 'merchant_no': str(self.merchant_no),
- 'terminal_id': str(self.terminal_id),
- 'terminal_trace': str(pay_trace),
- 'terminal_time': str(pay_time),
- 'total_fee': str(total_fee)
- })
- data.setdefault('auto_pay', '0')
- data.setdefault('repeated_trace', '0')
- data.setdefault('key_sign', self.sign(data))
- from six.moves.urllib import parse
- return '{path}?{params}'.format(path = url, params = parse.urlencode(data))
- def api_trade_query(self, pay_type, pay_trace, pay_time):
- '''
- 查询订单
- '''
- # 查询流水号就设置为商户订单号
- data = OrderedDict()
- data['pay_ver'] = '100'
- data['pay_type'] = pay_type
- data['service_id'] = '020'
- data['merchant_no'] = self.merchant_no
- data['terminal_id'] = self.terminal_id
- data['terminal_trace'] = pay_trace
- data['terminal_time'] = str(datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
- data['out_trade_no'] = ''
- data.update({'key_sign': self.sign(data, order = False)})
- data['pay_trace'] = pay_trace
- data['pay_time'] = pay_time
- result = self.post(endpoint = '/pay/100/query', data = data)
- if self.merchant_no != result['merchant_no']:
- raise SaobeiValidationError(errmsg = u'无效的merchant_no',
- lvalue = str(self.merchant_no),
- rvalue = str(result['merchant_no']),
- client = self)
- if self.terminal_id != result['terminal_id']:
- raise SaobeiValidationError(errmsg = u'无效的terminal_id',
- lvalue = str(self.terminal_id),
- rvalue = str(result['terminal_id']),
- client = self)
- key_sign = result['key_sign']
- sign_data = OrderedDict()
- sign_data['return_code'] = result['return_code']
- sign_data['return_msg'] = result['return_msg']
- sign_data['result_code'] = result['result_code']
- sign_data['pay_type'] = result['pay_type']
- sign_data['merchant_name'] = result['merchant_name']
- sign_data['merchant_no'] = result['merchant_no']
- sign_data['terminal_id'] = result['terminal_id']
- sign_data['terminal_trace'] = result['terminal_trace']
- sign_data['terminal_time'] = result['terminal_time']
- sign_data['total_fee'] = result['total_fee']
- sign_data['end_time'] = result['end_time']
- sign_data['out_trade_no'] = result['out_trade_no']
- re_key_sign = self.sign(sign_data, order = False, token = False)
- if key_sign != re_key_sign:
- raise SaobeiSignatureError(lvalue = re_key_sign, rvalue = key_sign, client = self)
- return result
|