123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- # -*- 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.monetary import RMB
- 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.jdopen.exceptions import JdOpenException, JDOpenValidationError
- from taskmanager.mediator import task_caller
- if TYPE_CHECKING:
- from apps.web.common.transaction.pay import RechargeRecordT
- from apps.web.core.payment.jdopen import JDOpenPaymentGateway
- logger = logging.getLogger(__name__)
- def update_record(record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- import arrow
- finished_time = arrow.get(payload['completeTime'], 'YYYY-MM-DD HH:mm:ss', tzinfo=settings.TIME_ZONE).naive
- jdOrderNo = payload['orderNum'] # 京东系统订单号. 京东系统查单
- if 'bankOutTradeNum' in payload and payload['bankOutTradeNum']:
- transactionId = payload['bankOutTradeNum']
- elif 'bankRequestNum' in payload and payload['bankRequestNum']:
- transactionId = payload['bankRequestNum']
- else:
- transactionId = jdOrderNo
- _payload = {
- 'extraInfo.bankRequestNum': payload.get('bankRequestNum', ''),
- 'extraInfo.bankOutTradeNum': payload.get('bankOutTradeNum', ''),
- 'extraInfo.payload': payload,
- 'extraInfo.notify_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
- if 'ledgerStatus' in payload and payload['ledgerStatus']:
- _payload.update({
- 'extraInfo.ledgerStatus': payload['ledgerStatus']
- })
- return record.succeed(wxOrderNo=jdOrderNo,
- transactionId=transactionId,
- finishedTime=finished_time,
- **_payload)
- class JDOpenPayReordPoller(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: (JDOpenPaymentGateway, 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, {
- 'completeTime': result['completeTime'],
- 'orderNum': result['orderNum'],
- 'bankRequestNum': result['payRecordList'][0]['bankRequestNum'],
- 'bankOutTradeNum': result['payRecordList'][0]['bankOutTradeNum']
- }
- if result['payStatus'] in ['FAIL', 'REFUND', 'CANCEL', 'CLOSE']:
- return PayNotifyAction.NoHandle, result
- return PayNotifyAction.Unknown, result
- except JdOpenException as e:
- logger.error(str(e))
- return PayNotifyAction.Unknown, {}
- except Exception, e:
- logger.exception(e)
- return PayNotifyAction.Unknown, {}
- class JDOpenPayNotifier(PayNotifier):
- def __init__(self, request, record_cls_factory):
- super(JDOpenPayNotifier, self).__init__(request=request, record_cls_factory=record_cls_factory)
- self.body = request.body
- def parse_request(self, request):
- _head = {
- 'accessKey': request.META['HTTP_ACCESSKEY'],
- 'version': request.META['HTTP_VERSION'],
- 'timestamp': request.META['HTTP_TIMESTAMP'],
- 'token': request.META['HTTP_TOKEN']
- }
- _body = json.loads(request.body) # type: Dict
- logger.debug('received jdopen pay notify: body = {}; heads = {}'.format(_body, _head))
- return {
- 'body': _body,
- 'head': _head
- }
- @property
- def out_trade_no(self):
- return self.payload['body']['requestNum']
- def verify(self, payment_gateway, record, payload):
- # type: (JDOpenPaymentGateway, RechargeRecordT, Dict)->None
- notifier_yuan = RMB(payload['body']['orderAmount'])
- if record.money != notifier_yuan:
- raise JDOpenValidationError(
- tips=u'无效的total_fee',
- lvalue=str(record.money),
- rvalue=str(notifier_yuan),
- client=payment_gateway.client)
- def update_record(self, record, **payload):
- # type: (RechargeRecordT, Dict)->bool
- return update_record(record, **payload)
- def reply(self):
- return HttpResponse('ok')
- def check_and_update_ledger_status(self, record):
- if 'ledgerStatus' in self.payload['body'] and self.payload['body']['ledgerStatus']:
- old_ledger_status = record.extraInfo.get('ledgerStatus', '')
- if old_ledger_status != self.payload['body']['ledgerStatus']:
- record.update(extraInfo__ledgerStatus = self.payload['body']['ledgerStatus'])
- def do(self, post_pay):
- # type: (callable)->HttpResponse
- try:
- order_no = self.out_trade_no
- 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 self.payload['body']['status'] != 'SUCCESS':
- logger.error('status of record<orderNo={}> is not success.'.format(order_no))
- return self.reply()
- if record.is_success():
- logger.error(
- 'record<id={},orderNo={}> has been finished.'.format(str(record.id), order_no))
- self.check_and_update_ledger_status(record)
- return self.reply()
- pay_gateway = PaymentGateway.from_gateway_key(
- record.my_gateway,
- record.pay_gateway_key,
- record.pay_app_type) # type: JDOpenPaymentGateway
- pay_gateway.check_token(self.body, self.payload['head']['timestamp'], self.payload['head']['token'])
- self.verify(pay_gateway, record, self.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)))
- self.check_and_update_ledger_status(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, **(self.payload['body']))
- if not modified:
- # 如果没有任何记录被更新则说明已经处理了
- logger.debug('{} has been done because db in notify'.format(repr(record)))
- self.check_and_update_ledger_status(record)
- return self.reply()
- logger.info('{} successfully confirmed'.format(repr(record)))
- async_operation(post_pay, record=record)
- return self.reply()
- except JdOpenException as e:
- logger.exception(e)
- return self.reply()
- except Exception as e:
- logger.exception(e)
- class JDOpenPullUp(PayPullUp):
- def do(self): # type: ()->HttpResponse
- gateway_type = self.payment_gateway.gateway_type
- if gateway_type not in [AppPlatformType.ALIPAY, AppPlatformType.WECHAT, AppPlatformType.JD]:
- raise UserServerException(u'不支持该支付类型')
- OrderCacheMgr(self.record).initial()
- try:
- ledger_type, ledger_fee_assume, ledger_list = self.payment_gateway.bill_split_rule(
- self.record.partition_map)
- _payload = {
- 'out_trade_no': self.record.orderNo,
- 'money': self.record.money,
- 'notify_url': self.payload['notifyUrl'],
- 'subject': self.record.subject,
- 'attach': {'dealerId': self.record.ownerId},
- 'openId': self.openId
- }
- if ledger_list:
- _payload.update({
- 'ledgerRule': {
- 'ledgerType': ledger_type,
- 'ledgerFeeAssume': ledger_fee_assume,
- 'list': ledger_list
- }})
- pay_info = self.payment_gateway.unified_order(**_payload)
- except JdOpenException as e:
- logger.exception(e)
- self.record.fail(description = e.errMsg)
- raise UserServerException(e.errMsg)
- except Exception as e:
- logger.exception(e)
- self.record.fail(description = u'拉起支付发生异常', extraInfo__traceback = traceback.format_exc())
- raise UserServerException(u'拉起支付发生异常')
- else:
- logger.debug('jdopen unified order pay info = {}'.format(str(pay_info)))
- self.record.update(
- wxOrderNo = pay_info['data']['bankRequestNum'], extraInfo__jdOrderNo = pay_info['data']['orderNum'])
- gateway_type = self.payment_gateway.gateway_type
- if gateway_type == AppPlatformType.ALIPAY:
- response = JsonOkResponse(payload = {
- 'tradeNO': pay_info['data']['bankRequest']['TRADENO'],
- 'outTradeNo': self.record.orderNo,
- 'adShow': self.payload.get('showAd')
- })
- elif gateway_type == AppPlatformType.WECHAT:
- response = JsonOkResponse(payload = {
- 'outTradeNo': self.record.orderNo,
- 'golden': True,
- 'adShow': self.payload.get('showAd'),
- 'appId': pay_info['data']['bankRequest']['APPID'],
- 'timeStamp': pay_info['data']['bankRequest']['TIMESTAMP'],
- 'nonceStr': pay_info['data']['bankRequest']['NONCESTR'],
- 'signType': pay_info['data']['bankRequest']['SIBGTYPE'], # 小程序和公众号返回的值不是一个
- 'package': pay_info['data']['bankRequest']['PACKAGE'],
- 'paySign': pay_info['data']['bankRequest']['PAYSIGN']
- })
- else: # gateway_type == AppPlatformType.JD
- response = JsonResponse({
- 'result': 1,
- 'description': 'SUCCESS',
- 'payload': {
- 'payUrl': pay_info['data']['bankRequest']['PAY_URL']
- }})
- 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
|