# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import random import uuid from django.utils.module_loading import import_string from typing import TYPE_CHECKING, Optional, cast from apilib.systypes import IterConstant from apps.web.core import PAY_APP_MAP, APP_KEY_DELIMITER, PayAppType, BaseAppProxy if TYPE_CHECKING: from apps.web.core.models import PayAppBase from apps.web.core.payment.type_checking import PaymentGatewayT from apps.web.common.transaction.pay import RechargeRecordT class _PaymentGateway(BaseAppProxy): def __init__(self, app): super(_PaymentGateway, self).__init__(app) @property def pay_app_type(self): if hasattr(self.app, 'pay_app_type'): return getattr(self.app, 'pay_app_type') else: raise AttributeError('no __pay_app_type__ attribute') @property def gateway_type(self): if hasattr(self, '__gateway_type__'): return getattr(self, '__gateway_type__') else: raise AttributeError('no __gateway_type__ attribute') @property def gateway_key(self): if hasattr(self.app, '__gateway_key__'): key = getattr(self.app, '__gateway_key__') return APP_KEY_DELIMITER.join([self.app.pay_app_type, key]) else: raise AttributeError('no __gateway_key__ attribute') @classmethod def parse_gateway_key(cls, gateway_key, default_pay_app_type = PayAppType.WECHAT): # type: (str, str)->tuple tokens = gateway_key.split(APP_KEY_DELIMITER) if tokens[0] in PayAppType.choices(): return tokens[0], tokens[1], tokens[2:] else: return default_pay_app_type, tokens[0], tokens[1:] @classmethod def get_app_from_gateway_key(cls, gateway_key, default_pay_app_type = PayAppType.WECHAT): pay_app_type, occupant_id, tokens = cls.parse_gateway_key(gateway_key, default_pay_app_type) pay_app_name = PAY_APP_MAP[pay_app_type] app_cls = import_string(pay_app_name) factory_fun = getattr(app_cls, '__from_gateway_key__') app = factory_fun(occupant_id, tokens) # type: Optional[cast(PayAppBase)] return app class PaymentGateway(_PaymentGateway): """ 支付网关基类 """ GATEWAY_PREFIX = 'gk{}'.format(APP_KEY_DELIMITER) class TradeStatus(IterConstant): Init = 'init' Unknown = 'unknown' Success = 'success' Paying = 'paying' Closed = 'closed' Finished = 'finished' Cancel = 'cancel' Refund = 'fefund' Refunding = 'refunding' RefundFail = 'refundFail' Error = 'error' @classmethod def from_gateway_key(cls, gateway_type, gateway_key, default_pay_app_type): pay_app = cls.get_app_from_gateway_key(gateway_key = gateway_key, default_pay_app_type = default_pay_app_type) return pay_app.new_gateway(gateway_type) @classmethod def clone_from_order(cls, order): # type:(RechargeRecordT) -> PaymentGatewayT gateway_type = order.my_gateway gateway_key = order.pay_gateway_key pay_app_type = order.pay_app_type pay_app = cls.get_app_from_gateway_key( gateway_key = gateway_key, default_pay_app_type = pay_app_type ) if not pay_app.occupant: source_cls = import_string('apps.web.{}.models.{}'.format(pay_app.role, pay_app.role.capitalize())) pay_app.occupant = source_cls.objects.get(id = pay_app.occupantId) return pay_app.new_gateway(gateway_type) def api_trade_query(self, out_trade_no = None, trade_no = None): raise NotImplementedError('sub class must implement api_trade_query') def refund_to_user(self, out_trade_no, out_refund_no, refund_fee, total_fee, refund_reason, **kwargs): raise NotImplementedError('sub class must implement refund_to_user') # service 2位 18, 28 ,38 # product 4位 0001, 0002, 0003 # WF180001201712281209393393822345 @staticmethod def generate_order_no(service, product): return '%s%s%s%s%s' % ( service, product, datetime.datetime.now().strftime("%Y%m%d%H%M%S"), (str(abs(hash(uuid.uuid1())))[0:6]).rjust(6, '0'), random.randint(1000, 9999)) def withdraw_source_key(self): return self.occupant.withdraw_source_key(self.app) def api_refund_query(self, out_refund_no, out_trade_no = None): raise NotImplementedError('must implement api_refund_query.') class WithdrawGateway(_PaymentGateway): """ 提现网关基类 """ LEDGER_PREFIX = 'ledger' NO_LEDGER_PREFIX = 'noledger' def __init__(self, app): # type: (cast(PayAppBase))->None super(WithdrawGateway, self).__init__(app) @classmethod def is_ledger(cls, source_key): # type: (str)->bool return source_key.startswith(cls.LEDGER_PREFIX) @property def support_withdraw(self): return self.app.supportWithdraw @property def support_withdraw_bank(self): return self.app.supportWithdrawBank @classmethod def from_withdraw_gateway_key(cls, withdraw_gateway_key, gateway_version): pay_app = cls.get_app_from_gateway_key(gateway_key = withdraw_gateway_key, default_pay_app_type = PayAppType.WECHAT) # type: cast(PayAppBase) return pay_app.new_withdraw_gateway(gateway_version = gateway_version) @property def manual_withdraw(self): # type:()->bool return self.app.manual_withdraw def get_transfer_result_via_bank(self, order_no): """ 查询银行卡提现的返回结果 :return: """ raise NotImplementedError() def get_transfer_result_via_changes(self, order_no): """ 查询现金提现的返回结果 :return: """ raise NotImplementedError() def withdraw_via_bank(self, order_no, total_amount, bank_card, order_title = u'服务款项'): """ 提现到银行卡 :return: """ raise NotImplementedError() def withdraw_via_changes(self, amount, payOpenId, order_no, real_user_name, subject): """ 提现到现金 :return: """ raise NotImplementedError()