123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import traceback
- from django.conf import settings
- from django.http import QueryDict, HttpResponse
- from typing import TYPE_CHECKING, Dict, Optional
- 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.dlb import DlbPayException, DlbErrorCode, DlbValidationError
- from apps.web.core.payment import PaymentGateway
- from taskmanager.mediator import task_caller
- if TYPE_CHECKING:
- from apps.web.common.transaction.pay import RechargeRecordT
- from typing import Dict
- from apps.web.core.payment import PaymentGatewayT
- from apps.web.core.payment.dlb import DlbPaymentGateway
- logger = logging.getLogger(__name__)
- def update_record(record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- if 'completeTime' in payload:
- import arrow
- finished_time = arrow.get(payload['completeTime'], 'YYYY-MM-DD HH:mm:ss', tzinfo = settings.TIME_ZONE).naive
- else:
- finished_time = datetime.datetime.now()
- return record.succeed(wxOrderNo = payload['orderNum'],
- transactionId = payload['bankOutTradeNum'],
- finishedTime = finished_time,
- **{
- 'extraInfo.bankTradeNum': payload['bankTradeNum'],
- 'extraInfo.notify_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- })
- class DlbPayReordPoller(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: (DlbPaymentGateway, RechargeRecordT)->(PayNotifyAction, str)
- try:
- result = payment_gateway.api_trade_query(out_trade_no = record.orderNo)
- if 'status' not in result:
- return PayNotifyAction.Unknown, result
- if result['status'] == 'SUCCESS':
- return PayNotifyAction.NeedHandle, {
- 'orderNum': result['orderNum'],
- 'bankTradeNum': result['payRecordList'][0]['bankTradeNum'],
- 'bankOutTradeNum': result['payRecordList'][0]['bankOutTradeNum']
- }
- if result['status'] == 'FAIL':
- return PayNotifyAction.NoHandle, result
- if result['status'] == 'INIT':
- if len(result['payRecordList']) > 0 and result['payRecordList'][0]['payStatus'] == 'FAIL':
- return PayNotifyAction.NoHandle, result
- else:
- return PayNotifyAction.Unknown, result
- if result['status'] in ['REFUND', 'CLOSE', 'CANCLE']:
- return PayNotifyAction.NoHandle, result
- return PayNotifyAction.Unknown, result
- except DlbPayException as e:
- logger.error(str(e))
- return PayNotifyAction.Unknown, {}
- except Exception, e:
- logger.exception(e)
- return PayNotifyAction.Unknown, {}
- class DlbPayNotifier(PayNotifier):
- def __init__(self, request, record_cls_factory):
- self.timestamp = request.META.get('HTTP_TIMESTAMP')
- self.token = request.META.get('HTTP_TOKEN')
- super(DlbPayNotifier, self).__init__(request = request, record_cls_factory = record_cls_factory)
- def parse_request(self, request):
- return request.GET # type: QueryDict
- @property
- def out_trade_no(self):
- return self.payload['requestNum']
- def verify(self, payment_gateway, record, payload):
- # type: (PaymentGatewayT, RechargeRecordT, Dict)->None
- notifier_fen = int(RMB(payload['orderAmount']) * 100)
- if record.fen_total_fee != notifier_fen:
- raise DlbValidationError(
- errorMsg = u'invalid orderAmount',
- lvalue = record.fen_total_fee,
- rvalue = notifier_fen,
- client = payment_gateway._app)
- def update_record(self, record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- return update_record(record, **payload)
- def reply(self):
- return JsonResponse(data = {})
- def verify_sign(self, gateway, data, token):
- # type: (DlbPaymentGateway, dict, str)->bool
- return gateway.check(data, token)
- def do(self, post_pay):
- # type: (callable)->JsonResponse
- try:
- payload = self.payload
- logger.debug('received dlb pay notify: %s' % str(payload))
- if payload['status'] != 'SUCCESS':
- return self.reply()
- order_no = payload['requestNum']
- record = self.record_cls.get_record(order_no = order_no) # type: Optional[RechargeRecordT]
- if not record:
- logger.error('no such record<orderNo={}>'.format(order_no))
- return self.reply()
- if record.is_success():
- logger.error(
- 'record<id={},orderNo={}> has been finished.'.format(str(record.id), order_no))
- return self.reply()
- payment_gateway = PaymentGateway.from_gateway_key(
- record.my_gateway,
- record.pay_gateway_key,
- record.pay_app_type) # type: DlbPaymentGateway
- if not self.verify_sign(gateway = payment_gateway,
- data = {
- 'timestamp': self.timestamp
- },
- token = self.token):
- raise DlbPayException(errorCode = DlbErrorCode.MY_ERROR_SIGNATURE,
- errorMsg = u'TOKEN校验错误',
- client = payment_gateway._app)
- self.verify(payment_gateway, record, payload)
- order_cache_mgr = OrderCacheMgr(record)
- try:
- if order_cache_mgr.check_and_set_done():
- logger.debug('{} has been done because cache in notify.'.format(repr(record)))
- return self.reply()
- except Exception as e:
- logger.error(
- 'cache key is not exist or exception. exception = {}, key = {}'.format(str(e), order_cache_mgr.key))
- modified = self.update_record(record, **payload)
- if not modified:
- # 如果没有任何记录被更新则说明已经处理了
- logger.debug('{} has been done because db in notify'.format(repr(record)))
- return self.reply()
- logger.info('{} successfully confirmed'.format(repr(record)))
- async_operation(post_pay, record = record)
- return self.reply()
- except DlbPayException as e:
- logger.error(str(e))
- return self.reply()
- except Exception as e:
- logger.exception(e)
- return self.reply()
- class DlbPullUp(PayPullUp):
- """
- 哆啦宝支付获取支付参数。目前只作为商户支付
- """
- def do(self): # type: ()->HttpResponse
- OrderCacheMgr(self.record).initial()
- try:
- pay_url = self.payment_gateway.create_pay_url(
- order_no = self.record.orderNo,
- money = self.record.money,
- attach = {'dealerId': self.record.ownerId},
- notify_url = self.payload.get('notifyUrl'),
- front_url = self.payload.get('front_url'))
- except Exception as e:
- logger.exception(e)
- self.record.fail(description = u'拉起支付发生异常', extraInfo__traceback = traceback.format_exc())
- raise UserServerException(u'拉起支付发生异常')
- else:
- if not pay_url:
- self.record.fail(description = u'调起支付失败,请刷新后重试')
- raise UserServerException(u'调起支付失败,请刷新后重试')
- else:
- 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)
- return JsonResponse({
- 'result': 1,
- 'description': 'SUCCESS',
- 'payload': {
- 'payType': self.payment_gateway.pay_app_type,
- 'payUrl': pay_url
- }})
|