# -*- coding: utf-8 -*- # !/usr/bin/env python import logging import dicttoxml import six from django.utils.module_loading import import_string from typing import TYPE_CHECKING, Optional, cast, Union from apilib.systypes import IterConstant from apps.web.constant import AppPlatformType from library.jdopen import JDOpenErrorCode, BankType, JdOpenException from library.jd.pay import PiType from library.ys.base import PayOpt if TYPE_CHECKING: from apps.web.core.models import WechatPayApp, PayAppBase, EmbeddedApp, WechatMiniApp from apps.web.common.models import CapitalUser logger = logging.getLogger(__name__) class PayAppType(IterConstant): # 资金池模式只使用ALIPAY,WECHAT,JD_AGGR三种支付方式 ALIPAY = 'alipay' WECHAT = 'wechat' # 蓝牙特殊处理, 使用小程序绑定的支付方式, 后续可以迁移到京东聚合支付 WECHAT_MINI = 'wechat_mini' # 各经销商可使用的支付方式(包括JD_AGGR以及ALIPAY+WECHAT) PLATFORM_PROMOTION = "platform_promotion" PLATFORM_WALLET = 'platform_wallet' PLATFORM_RECONCILE ='platform_reconcile' MANUAL = 'manual' SWAP = 'swap' LEDGER_CONSUME = "ledgerConsume" PAY_APP_TYPE_TRANSLATION = { AppPlatformType.ALIPAY: u'支付宝', AppPlatformType.WECHAT: u'微信', PayAppType.WECHAT_MINI: u'微信小程序', } PAY_APP_MAP = { PayAppType.ALIPAY: 'apps.web.core.models.AliApp', PayAppType.WECHAT: 'apps.web.core.models.WechatPayApp', PayAppType.WECHAT_MINI: 'apps.web.core.models.WechatMiniApp' PayAppType.PLATFORM_PROMOTION: 'apps.web.core.models.PlatformPromotionApp', PayAppType.PLATFORM_WALLET: 'app.web.core.models.PlatformWalletApp', PayAppType.PLATFORM_RECONCILE: 'app.web.core.models.PlatformReconcileApp', PayAppType.MANUAL: 'apps.web.core.models.ManualPayApp' } APP_KEY_DELIMITER = '-' wechat_bound_openid_key = lambda appid: APP_KEY_DELIMITER.join([AppPlatformType.WECHAT, appid]) class BaseAppProxy(object): def __init__(self, app): # type: (Optional[EmbeddedApp, WechatPayApp])->None self._app = app # type: Optional[EmbeddedApp, WechatPayApp] @property def app(self): return self._app @property def client(self): if hasattr(self, '__client__'): return getattr(self, '__client__') else: raise AttributeError('no __client__ attribute') @property def occupantId(self): return self._app.occupantId @property def occupant(self): return self._app.occupant @property def bound_openid_key(self): if not hasattr(self, '__bound_openid_key__'): raise AttributeError('no __bound_openid_key__ attribute') return getattr(self, '__bound_openid_key__') @property def gateway_type(self): if not hasattr(self, '__gateway_type__'): raise AttributeError('no __gateway_type__ attribute') return getattr(self, '__gateway_type__') @property def debug(self): return self._app.debug @property def enable(self): return self._app.enable @property def valid(self): return self._app.valid class _BaseMixin(object): @property def __app_for_inner__(self): # type: ()->Union[cast(PayAppBase), EmbeddedApp] if not hasattr(self, 'app'): raise AttributeError('no app attribute') return getattr(self, 'app') @property def __client_for_inner__(self): if not hasattr(self, 'client'): raise AttributeError('no client attribute') return getattr(self, 'client') @property def __bound_openid_key__(self): raise AttributeError('no __bound_openid_key__ attribute') class WechatMixin(_BaseMixin): @property def appid(self): return self.__app_for_inner__.appid @property def secret(self): return self.__app_for_inner__.secret @classmethod def reply(cls, msg, ok): # type: (basestring, bool)->str response = { "return_code": "SUCCESS" if ok else "FAIL", "return_msg": msg } return cls.serialize_wechat_xml(response) def check(self, data, order = True, token = True): return self.__client_for_inner__.check_signature(data) @classmethod def serialize_wechat_xml(cls, payload): # type:(dict)->str return dicttoxml.dicttoxml(payload, attr_type = False, custom_root = 'xml', cdata = True) @property def __bound_openid_key__(self): return wechat_bound_openid_key(self.appid) # @property # def __gateway_type__(self): # return AppPlatformType.WECHAT class DlbMixin(_BaseMixin): @property def merchant_no(self): return self.__app_for_inner__.merchant_no @property def shop_no(self): return self.__app_for_inner__.shop_no @property def machine_no(self): return self.__app_for_inner__.machine_no @property def access_key(self): return self.__app_for_inner__.access_key @property def secret_key(self): return self.__app_for_inner__.secret_key @property def __bound_openid_key__(self): # 哆啦宝不需要获取openid raise AttributeError("no __bound_openid_key__ attribute") class YsMixin(_BaseMixin): GATEWAY_TYPE_TO_OPT = { AppPlatformType.WECHAT: PayOpt.wxPreOrder, AppPlatformType.ALIPAY: PayOpt.apPreOrder } @property def channel_id(self): return self.__app_for_inner__.channel_id @property def mer_id(self): return self.__app_for_inner__.mer_id @property def term_id(self): return self.__app_for_inner__.term_id @property def work_key(self): return self.__app_for_inner__.work_key @property def __bound_openid_key__(self): # 接口调用的时候获取openid raise AttributeError("no __bound_openid_key__ attribute") class AlipayMixin(_BaseMixin): """ Alipay 支付网关,扩展原库没有的接口 ``alipay.trade.create`` """ @property def appid(self): return self.__app_for_inner__.appid @property def app_private_key_string(self): return self.__app_for_inner__.app_private_key_string @property def public_key_string(self): return self.__app_for_inner__.public_key_string @property def public_key_cert_string(self): return self.__app_for_inner__.public_key_cert_string @property def root_cert_string(self): return self.__app_for_inner__.root_cert_string @property def app_public_key_cert_string(self): return self.__app_for_inner__.app_public_key_cert_string @property def aes_encrypt_key(self): return self.__app_for_inner__.aes_encrypt_key @property def signKeyType(self): return self.__app_for_inner__.signKeyType @property def __gateway_type__(self): return AppPlatformType.ALIPAY @property def __bound_openid_key__(self): # 支付宝用户ID是唯一的,直接以平台类型区分 return AppPlatformType.ALIPAY @property def __client__(self): return self.__app_for_inner__.client def check(self, data, pop_sign_type = True): # type:(dict, bool)->bool sign = data.pop('sign') if not sign: logger.error('has no sign') return False return self.__client__.verify(data, sign, pop_sign_type) class ROLEMetaClass(type): def __setattr__(self, name, value): raise NotImplementedError() def __getattr__(self, item): if item in self.choices(): return item else: raise KeyError() class ROLE(six.with_metaclass(ROLEMetaClass)): __role_map = { 'myuser': { 'module': 'apps.web.user.models.MyUser', 'sub_type': 'U' }, 'dealer': { 'module': 'apps.web.dealer.models.Dealer', 'sub_type': 'D' }, 'subaccount': { 'module': 'apps.web.dealer.models.SubAccount', 'sub_type': 'B' }, 'agent': { 'module': 'apps.web.agent.models.Agent', 'sub_type': 'A' }, 'manager': { 'module': 'apps.web.management.models.Manager', 'sub_type': 'M' }, 'supermanager': { 'module': 'apps.web.superamdin.models.SuperManager', 'sub_type': 'S' }, 'advertiser': { 'module': 'apps.web.ad.models.Advertiser', 'sub_type': 'V' }, 'tester': { 'module': 'apps.web.test.models.Tester', 'sub_type': 'T' }, 'advertisement': { 'module': 'apps.web.ad.models.Advertisement', 'sub_type': 'T' }, 'anonymoususer': { 'module': 'django.contrib.auth.models.AnonymousUser', 'sub_type': 'N' }, 'manufacturer': { 'module': 'apps.web.management.models.Manager', 'sub_type': 'M' } } def __init__(self): raise NotImplementedError() @classmethod def choices(cls): return cls.__role_map.keys() @classmethod def from_role_id(cls, role, id): if role == cls.anonymoususer: return import_string(cls.__role_map[role]['module'])() else: return import_string(cls.__role_map[role]['module']).objects(id = id).get() @classmethod def sub_type(cls, role): return cls.__role_map[role]['sub_type'] @classmethod def sub_type_list(cls): return [item['sub_type'] for item in cls.__role_map.values()]