123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import traceback
- import simplejson as json
- from django.conf import settings
- from django.http import HttpResponse
- from typing import TYPE_CHECKING, Dict, Optional
- from apilib.utils_json import JsonResponse
- from apps.web.common.transaction.pay import PayNotifier, OrderCacheMgr, PayRecordPoller, PayNotifyAction, PayPullUp
- from apps.web.constant import AppPlatformType, PollRecordDefine
- from apps.web.core import PayAppType
- from apps.web.core.payment import PaymentGateway
- from apps.web.core.utils import async_operation, JsonOkResponse
- from apps.web.exceptions import UserServerException
- from library.ys import YsException
- from taskmanager.mediator import task_caller
- if TYPE_CHECKING:
- from apps.web.common.transaction.pay import RechargeRecordT
- from apps.web.core.payment.ys import YsPaymentGateway
- logger = logging.getLogger(__name__)
- def update_record(record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- if 'wxtimeend' in payload:
- import arrow
- finished_time = arrow.get(payload['wxtimeend'], 'YYYYMMDDHHmmss', tzinfo = settings.TIME_ZONE).naive
- else:
- finished_time = datetime.datetime.now()
- return record.succeed(wxOrderNo = payload['wtorderid'],
- transactionId = payload['wxtransactionid'],
- finishedTime = finished_time,
- **{
- 'extraInfo.wxopenid': payload['wxopenid'],
- 'extraInfo.acctype': payload['acctype']
- })
- class YsReordPoller(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: (YsPaymentGateway, RechargeRecordT)->(PayNotifyAction, str)
- '''
- 00 - 成功;98 - 未确定;25 - 找不到原笔交易;AA-待支付;其他 - 失败
- :param payment_gateway:
- :param record:
- :return:
- '''
- try:
- result = payment_gateway.api_trade_query(out_trade_no = record.orderNo)
- if 'resultcode' not in result:
- return PayNotifyAction.Unknown, result
- if result['resultcode'] == '00':
- return PayNotifyAction.NeedHandle, result
- if result['resultcode'] in ['98', 'AA']:
- return PayNotifyAction.Unknown, result
- if result['resultcode'] == '25':
- return PayNotifyAction.NoHandle, result
- # 其他说明交易失败, 不处理
- return PayNotifyAction.NoHandle, result
- except YsException as e:
- logger.error(str(e))
- return PayNotifyAction.Unknown, {}
- except Exception, e:
- logger.exception(e)
- return PayNotifyAction.Unknown, {}
- class YsNotifier(PayNotifier):
- def __init__(self, request, record_cls_factory):
- super(YsNotifier, self).__init__(request = request, record_cls_factory = record_cls_factory)
- def parse_request(self, request):
- return json.loads(request.body) # type: Dict
- @property
- def out_trade_no(self):
- return self.payload['tradetrace']
- def verify(self, payment_gateway, record, payload):
- # type: (YsPaymentGateway, RechargeRecordT, Dict)->None
- pass
- def update_record(self, record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- return update_record(record, **payload)
- def reply(self, retry = False):
- if retry:
- return JsonResponse({'resultcode': ''})
- else:
- return JsonResponse({'resultcode': '00'})
- def verify_sign(self, gateway, data):
- # type: (YsPaymentGateway, dict)->bool
- sign = data.pop('sign')
- return gateway.client.sign(data) == sign
- def do(self, post_pay):
- # type: (callable)->JsonResponse
- try:
- payload = self.payload
- logger.debug('received ys pay notify: {}'.format(str(payload)))
- order_no = payload['tradetrace']
- 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(True)
- if record.is_success():
- logger.error('record has been finished. orderNo = {}'.format(order_no))
- return self.reply(True)
- payment_gateway = PaymentGateway.from_gateway_key(
- record.my_gateway,
- record.pay_gateway_key,
- record.pay_app_type) # type: YsPaymentGateway
- # 校验签名
- self.verify_sign(payment_gateway, payload)
- # 校验参数
- 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 = 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(True)
- except YsException as e:
- logger.error(str(e))
- return self.reply()
- except Exception as e:
- logger.exception(e)
- return self.reply(retry = True)
- class YsPullUp(PayPullUp):
- """
- 易生获取支付参数。只用作商户,所以GOLDEN为True
- """
- def do(self): # type: ()->HttpResponse
- OrderCacheMgr(self.record).initial()
- try:
- wtorderid, pay_info = self.payment_gateway.unified_order(
- out_trade_no = self.record.orderNo,
- money = self.record.money,
- notify_url = self.payload['notifyUrl'],
- subject = self.record.subject,
- openId = self.openId)
- except Exception as e:
- logger.exception(e)
- self.record.fail(description = u'拉起支付发生异常', extraInfo__traceback = traceback.format_exc())
- raise UserServerException(u'拉起支付发生异常')
- else:
- logger.debug("wt order id is {}. pay info = {}".format(wtorderid, pay_info))
- response = None
- if self.payment_gateway.gateway_type == AppPlatformType.ALIPAY:
- response = JsonOkResponse(payload = {
- 'tradeNO': pay_info['tradeNO'],
- 'outTradeNo': self.record.orderNo,
- 'adShow': self.payload.get('showAd', False)
- })
- elif self.payment_gateway.gateway_type == AppPlatformType.WECHAT:
- pay_info.update({
- 'outTradeNo': self.record.orderNo,
- 'golden': True,
- 'adShow': self.payload.get('showAd', False)
- })
- response = JsonOkResponse(payload = pay_info)
- if not response:
- self.record.fail(description = u'不支持该支付类型')
- raise UserServerException(u'不支持该支付类型')
- 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 response
|