123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import traceback
- from decimal import Decimal
- import xmltodict
- from django.conf import settings
- from pyexpat import ExpatError
- from django.http import HttpResponse
- from typing import TYPE_CHECKING, Dict
- from apilib.monetary import RMB
- from apilib.utils_json import JsonResponse
- from apps.web.common.transaction.pay import PayNotifyAction, PayRecordPoller, OrderCacheMgr, PayNotifier, PayPullUp
- from apps.web.constant import PollRecordDefine
- from apps.web.core.utils import async_operation
- from apps.web.exceptions import UserServerException
- from library.wechatpy.constants import WeChatErrorCode
- from library.wechatbase.exceptions import WeChatException, WechatValidationError, InvalidSignatureException
- import simplejson as json
- from apps.web.core.payment.wechat import WechatPaymentGateway
- from taskmanager.mediator import task_caller
- if TYPE_CHECKING:
- from apps.web.common.transaction.pay import RechargeRecordT
- logger = logging.getLogger(__name__)
- def update_record(record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- if 'time_end' in payload:
- import arrow
- finished_time = arrow.get(payload["time_end"], 'YYYYMMDDHHmmss', tzinfo = settings.TIME_ZONE).naive
- else:
- finished_time = datetime.datetime.now()
- return record.succeed(wxOrderNo = payload['transaction_id'],
- transactionId = payload['transaction_id'],
- finishedTime = finished_time,
- **{
- 'extraInfo.notify_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- })
- class WechatPayRecordPoller(PayRecordPoller):
- def update_record(self, record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- return update_record(record, **payload)
- def action_of_pay(self, payment_gateway, record):
- # type: (WechatPaymentGateway, RechargeRecordT)->(PayNotifyAction, str)
- TradeMap = {
- 'SUCCESS': PayNotifyAction.NeedHandle,
- 'PAYERROR': PayNotifyAction.NoHandle,
- 'NOTPAY': PayNotifyAction.Unknown,
- 'CLOSED': PayNotifyAction.NoHandle,
- 'REFUND': PayNotifyAction.NoHandle
- }
- try:
- result = payment_gateway.api_trade_query(out_trade_no = record.orderNo)
- if result['trade_state'] not in TradeMap:
- return PayNotifyAction.Unknown, result
- action = TradeMap[result['trade_state']]
- return action, result
- except Exception as e:
- logger.exception(e)
- return PayNotifyAction.Unknown, {}
- class WechatPayNotifier(PayNotifier):
- def __init__(self, request, record_cls_factory):
- super(WechatPayNotifier, self).__init__(request = request, record_cls_factory = record_cls_factory)
- def parse_request(self, request):
- return xmltodict.parse(request.body)['xml'] # type: Dict
- @property
- def out_trade_no(self):
- return self.payload['out_trade_no']
- def verify(self, payment_gateway, record, payload):
- # type: (WechatPaymentGateway, RechargeRecordT, Dict)->None
- notifier_money = int(payload['total_fee'])
- if record.fen_total_fee != notifier_money:
- raise WechatValidationError(errmsg = u"invalid money",
- lvalue = str(record.fen_total_fee),
- rvalue = str(notifier_money),
- client = payment_gateway.client)
- def update_record(self, record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- return update_record(record, **payload)
- @classmethod
- def reply(cls, msg, ok):
- # type:(basestring, bool)->HttpResponse
- return HttpResponse(WechatPaymentGateway.reply(msg, ok))
- def do(self, post_pay):
- # type: (callable)->HttpResponse
- try:
- raw = self.payload
- logger.debug('received wechat pay notify: %s' % json.dumps(raw))
- return_code = raw['return_code']
- if return_code != 'SUCCESS':
- logger.error('return_code = %s' % return_code)
- return self.reply('OK', True)
- out_trade_no = raw['out_trade_no']
- money = RMB(raw['total_fee']) * Decimal('0.01')
- wx_order_no = raw['transaction_id']
- pay_open_id = raw['openid']
- logger.debug(
- 'processing payment notify. payOpenId = {}, orderNo = {}, wxOrderNo = {}, money = {}'.format(
- pay_open_id, out_trade_no, wx_order_no, money))
- # TODO 数据库分片修改
- # dealerId = json.loads(raw['attach'])['dealerId']
- # record = self.record_cls.get_record(order_no = out_trade_no, dealer_id = dealerId)
- record = self.record_cls.get_record(order_no = out_trade_no) # type: RechargeRecordT
- if not record:
- logger.error('no such record. orderNo = {}'.format(out_trade_no))
- return self.reply("OK", True)
- if record.is_success:
- logger.info('recharge record has done. orderNo = {}'.format(out_trade_no))
- return self.reply("OK", True)
- payment_gateway = WechatPaymentGateway.from_gateway_key(
- record.my_gateway,
- record.pay_gateway_key,
- record.pay_app_type) # type: WechatPaymentGateway
- if not payment_gateway.check(raw):
- raise InvalidSignatureException(
- errCode = WeChatErrorCode.MY_ERROR_SIGNATURE,
- errMsg = u'支付通知签名错误',
- client = payment_gateway.client
- )
- if 'payOpenId' in record.attachParas and record.attachParas['payOpenId'] and \
- pay_open_id != record.attachParas['payOpenId']:
- raise WechatValidationError(errmsg = 'invalid openid',
- lvalue = record.attachParas['payOpenId'],
- rvalue = pay_open_id,
- client = payment_gateway.client)
- if 'payOpenId' in record.extraInfo and record.extraInfo['payOpenId'] and \
- pay_open_id != record.extraInfo['payOpenId']:
- raise WechatValidationError(errmsg = 'invalid openid',
- lvalue = record.extraInfo['payOpenId'],
- rvalue = pay_open_id,
- client = payment_gateway.client)
- self.verify(payment_gateway, record, raw)
- result_code = raw['result_code']
- if result_code != 'SUCCESS':
- try:
- errorMsg = u'%s(%s)' % (raw.get('err_code_des', u'微信支付返回错误'), result_code)
- logger.debug('%s failure confirmed. message = %s' % (repr(record), errorMsg))
- record.fail(wxOrderNo = wx_order_no, finishedTime = datetime.datetime.now(), desciprtion = errorMsg)
- return self.reply('OK', True)
- except Exception as e:
- logger.exception('save recharge record failure. code = %s; error = %s; record = %s' % (
- result_code, e, repr(record)))
- return self.reply(u'数据记录失败', False)
- order_cache_mgr = OrderCacheMgr(record)
- try:
- if OrderCacheMgr(record).check_and_set_done():
- logger.debug('%s has been done because cache in notify.' % repr(record))
- return self.reply('OK', True)
- except Exception as e:
- logger.error(
- 'cache key is not exist or exception. key = %s' % order_cache_mgr.key)
- modified = self.update_record(record, **raw)
- if not modified:
- # 如果没有任何记录被更新则说明已经处理了
- logger.debug('%s has been done because db in notify' % repr(record))
- return self.reply('OK', True)
- async_operation(post_pay, record = record)
- logger.info('[pay notifier]%s successfully confirmed' % repr(record))
- return self.reply('OK', True)
- except WeChatException as e:
- logger.error(repr(e))
- return self.reply('ERROR', False)
- except ExpatError as e:
- logger.error('parse xml error. may be XXE. e = %s' % (e,))
- return WechatPayNotifier.reply('invalid xml', False)
- except Exception as e:
- logger.exception(e)
- return self.reply('UNKNOWN', False)
- class WechatPullUp(PayPullUp):
- """
- 微信支付获取支付参数。可能作为商户和资金池商户
- """
- def do(self): # type: ()->HttpResponse
- OrderCacheMgr(self.record).initial()
- try:
- data = self.payment_gateway.generate_js_payment_params(
- payOpenId = self.openId,
- out_trade_no = self.record.orderNo,
- notify_url = self.payload['notifyUrl'],
- money = self.record.money,
- body = self.record.subject,
- attach = {'dealerId': self.record.ownerId})
- except Exception as e:
- logger.exception(e)
- self.record.fail(description = u'拉起支付发生异常', extraInfo__traceback = traceback.format_exc())
- raise UserServerException(u'拉起支付发生异常')
- else:
- logger.debug('wechat payment signature: %s' % str(data))
- task_caller('poll_user_recharge_record',
- delay = PollRecordDefine.DELAY_BEFORE,
- expires = PollRecordDefine.TASK_EXPIRES,
- pay_app_type = self.payment_gateway.pay_app_type,
- record_id = str(self.record.id),
- interval = PollRecordDefine.WAIT_EACH_ROUND,
- total_count = PollRecordDefine.TOTAL_ROUNDS)
- data.update({
- 'outTradeNo': self.record.orderNo, 'adShow': self.payload.get('showAd', False)
- })
- return JsonResponse({'result': 1, 'description': 'SUCCESS', 'payload': data})
|