# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import hashlib import json import logging import os import string import time import traceback import urllib import urlparse from collections import defaultdict, OrderedDict from decimal import Decimal from functools import wraps, partial import requests from aliyunsdkcore.http.method_type import GET from django.conf import settings from django.core.cache import cache from django.utils.module_loading import import_string from mongoengine import NotUniqueError, DoesNotExist, MultipleObjectsReturned, Q from requests.exceptions import ConnectTimeout from typing import TYPE_CHECKING, Optional, Union, Dict from apilib.monetary import VirtualCoin, RMB, Ratio from apilib.utils import fix_dict from apilib.utils_json import JsonResponse from apilib.utils_string import cn, get_random_str from apilib.utils_url import before_frag_add_query from apps import serviceCache from apps.web.agent.models import Agent, MoniApp from apps.web.common.models import ExceptionLog, MonthlyPackageTemp from apps.web.common.proxy import ClientConsumeModelProxy from apps.web.common.transaction import OrderNoMaker, OrderMainType, UserPaySubType from apps.web.constant import Const, ErrorCode, USER_RECHARGE_TYPE, APP_TYPE, AppPlatformType from apps.web.core import PayAppType, ROLE from apps.web.core.accounting import Accounting from apps.web.core.auth.wechat import WechatAuthBridge from apps.web.core.bridge import WechatClientProxy from apps.web.core.exceptions import ServiceException, InvalidParameter, InterceptException from apps.web.core.helpers import ActionDeviceBuilder from apps.web.core.models import WechatPayApp, WechatAuthApp from apps.web.dealer.models import Dealer, VirtualCard from apps.web.device.define import DeviceChannelType from apps.web.device.models import Device, Group from apps.web.exceptions import UserServerException from apps.web.helpers import get_wechat_auth_bridge, get_wechat_user_manager_mp_proxy, get_app, get_user_manager_agent, \ DealerCustomizedBrandVisitor, AgentCustomizedBrandVisitor, get_platform_promotion_pay_gateway from apps.web.report.ledger import Ledger from apps.web.user import Cookies, UserAuthState from apps.web.user.conf import USER_AUTH_REDIRECT_URL from apps.web.user.models import MyUser, Card, RechargeRecord, UserVirtualCard, \ ServiceProgress, ConsumeRecord, MoniUser, BlackListUsers, Redpack, MyUserDetail from apps.web.user.payments import gen_quick_pay_purchase_subject, gen_recharge_purchase_subject from apps.web.user.utils2 import ConsumeOrderStateEngine from apps.web.utils import supported_app, detect_wechat_client, detect_alipay_client, detect_jdpay_client, \ detect_jdjr_client, NotSupportedPlatformResponseRedirect, \ UserRechargeResponseRedirect, UserCenterResponseRedirect, FollowGZHResponseRedirect, \ CardRechargeResponseRedirect, BtDeviceResponseRedirect, NetDeviceResponseRedirect, HuopoDoorResponseRedirect, \ ExternalResponseRedirect, CountDownResponseRedirect, \ MoniGZHResponseRedirect, OneCardGateResponseRedirect, concat_front_end_url logger = logging.getLogger(__name__) if TYPE_CHECKING: from apps.web.device.models import GroupDict, DeviceDict from django.http.response import HttpResponse, HttpResponseRedirect from apps.web.core.payment import PaymentGateway, WechatMiniPaymentGatewayT from django.core.handlers.wsgi import WSGIRequest from apps.web.core import PayAppBase from apps.web.dealer.models import OnSale def get_homepage_response(platform_type, user, dev, chargeIndex, product_agent): # type:(str, MyUser, DeviceDict, str, Agent)->HttpResponseRedirect def _set_recent_cookies(response): # type: (HttpResponse)->None response.set_cookie( key = Cookies.Recent_DevNo, value = dev.devNo, max_age = 24 * 3600, domain = settings.COOKIE_DOMAIN) if platform_type == AppPlatformType.WECHAT: moniFollowEnable = dev.owner.supports("moniAPPEnable") if moniFollowEnable: wechat_mp_proxy = get_wechat_user_manager_mp_proxy(product_agent) bound_open_id = user.get_bound_pay_openid(wechat_mp_proxy.bound_openid_key) if not bound_open_id: logger.error('no bound open id.') moniFollowEnable = False else: moniUser = MoniUser.objects(moniAppId = wechat_mp_proxy.appid, moniOpenId = bound_open_id).first() if moniUser: if (datetime.datetime.now() - moniUser.checkTime).total_seconds() > 24 * 60 * 60 * 30: moniFollowEnable = True else: moniFollowEnable = False if moniFollowEnable: # 再查找一次moniApp inhouseMoniApp = MoniApp.get_inhouse_app() moniAuthProxy = WechatClientProxy( WechatAuthApp(appid = inhouseMoniApp.appid, secret = inhouseMoniApp.secret)) boundOpenId = user.get_bound_pay_openid(moniAuthProxy.bound_openid_key) # 找到openId 进行判断 如果不存在boundOpenId 那么需要进行鉴权 if not boundOpenId: moniAuthBridge = WechatAuthBridge(moniAuthProxy.app) userState = UserAuthState.by_dev( devNo = dev.devNo, productAgentId = str(product_agent.id), agentId = str(dev.owner.agentId), chargeIndex = chargeIndex, uid = str(user.id), appid = inhouseMoniApp.appid ) return ExternalResponseRedirect( moniAuthBridge.generate_auth_url_base_scope( redirect_uri = USER_AUTH_REDIRECT_URL.MONI_BASE, payload = userState.encode() ) ) # 存在 boundOpenId 的情况下 去判断下用户到底有没有关注 moniUser = MoniUser.get_or_create( moniOpenId = boundOpenId, moniAppId = moniAuthProxy.appid, openId = user.openId, subLogicalCode = dev.logicalCode, subDealerId = dev.ownerId, subAgentId = dev.owner.agentId, subPoint = "scabCode" ) if moniUser and moniUser.isSubscribe: moniSubscribed = True else: moniSubscribed = moniAuthProxy.is_subscribe_gzh(boundOpenId) # 没有订阅的 需要关注去订阅了 订阅的二维码由appId生成 if not moniSubscribed: logicalCode = dev.logicalCode if dev else "" productAgentId = str(product_agent.id) openId = user.openId chargeIndex = chargeIndex or "" # 通过参数生成二维码 sceneStr = "{}:{}:{}:{}".format(productAgentId, openId, logicalCode, chargeIndex) ticket = moniAuthProxy.get_qr_str_scene(sceneStr) if ticket: return MoniGZHResponseRedirect(ticket) # 霍珀首页适配 if dev["devType"]["code"] == Const.DEVICE_TYPE_CODE_HP_GATE: response = HuopoDoorResponseRedirect(l = dev.logicalCode, port = chargeIndex) _set_recent_cookies(response) return response if dev["devType"]["code"] == Const.DEVICE_TYPE_CODE_ONE_CARD: response = OneCardGateResponseRedirect(l = dev.logicalCode, port = chargeIndex) _set_recent_cookies(response) return response if dev["devType"]["code"] == Const.DEVICE_TYPE_CODE_AHZT: response = CardRechargeResponseRedirect(l = dev.logicalCode) _set_recent_cookies(response) return response # 久恒门禁 if dev['devType']['code'] == Const.DEVICE_TYPE_CODE_JH_GATE: balance = user.calc_currency_balance(dev.owner, dev.group) if balance > VirtualCoin(0): smart_box = ActionDeviceBuilder.create_action_device(dev) try: smart_box.start(None, user.openId, None) except ServiceException as e: logger.error(str(e)) response = UserCenterResponseRedirect() else: response = UserRechargeResponseRedirect(dev.logicalCode) _set_recent_cookies(response) return response # 支付宝蓝牙流程 if dev.channelType == DeviceChannelType.Channel_BT: response = BtDeviceResponseRedirect(l = dev.logicalCode, port = chargeIndex) _set_recent_cookies(response) return response # 支持倒计时页面 smart_box = ActionDeviceBuilder.create_action_device(dev) if smart_box.support_count_down(user.openId, chargeIndex) and dev['status'] in [Const.DEV_WORK_STATUS_WORKING, Const.DEV_WORK_STATUS_PAUSE]: response = CountDownResponseRedirect(devNo = dev.devNo, port = chargeIndex) _set_recent_cookies(response) return response response = NetDeviceResponseRedirect(l = dev.logicalCode, port = chargeIndex) _set_recent_cookies(response) return response def auth_wechat_pay_app(user, dealer, product_agent, state, redirect_uri): # type:(MyUser, Dealer, Agent, UserAuthState, str)->Optional[HttpResponseRedirect] app = get_app(source = product_agent, app_type = APP_TYPE.WECHAT_ENV_PAY, role = ROLE.myuser) # type: PayAppBase if isinstance(app, WechatPayApp): auth_bridge = WechatAuthBridge(app) # type: WechatAuthBridge bound_open_id = user.get_bound_pay_openid(auth_bridge.bound_openid_key) if bound_open_id: logger.debug('auth bridge<{}> has openid<{}>'.format(repr(auth_bridge), bound_open_id)) return None else: logger.debug('auth bridge<{}> need auth to get openid'.format(repr(auth_bridge))) return ExternalResponseRedirect( auth_bridge.generate_auth_url_base_scope(redirect_uri = redirect_uri, payload = state.encode())) def auth_wechat_manager_app(my_user, product_agent, state, dev): # type:(MyUser, Agent, UserAuthState, DeviceDict)->Optional[HttpResponseRedirect] auth_bridge = get_wechat_auth_bridge(product_agent, APP_TYPE.WECHAT_AUTH) manager_auth_bridge = get_wechat_auth_bridge(product_agent, APP_TYPE.WECHAT_USER_MANAGER) manager_openid = my_user.get_bound_pay_openid(manager_auth_bridge.bound_openid_key) if manager_openid and (my_user.managerialOpenId != manager_openid): my_user.managerialAppId = manager_auth_bridge.appid my_user.managerialOpenId = manager_openid my_user.save() if manager_openid: force_follow_gzh = dev.owner.force_follow_gzh if force_follow_gzh: wechat_mp_proxy = get_wechat_user_manager_mp_proxy(product_agent) if not wechat_mp_proxy.is_subscribe_gzh(manager_openid): logger.debug( 'user need to subscribe gzh.'.format(my_user.openId, my_user.groupId)) MoniUser.get_or_create( moniOpenId = manager_openid, moniAppId = wechat_mp_proxy.appid, openId = my_user.openId, subLogicalCode = dev.logicalCode, subDealerId = dev.ownerId, subAgentId = dev.owner.agentId, subPoint = "scabCode", checkTime = datetime.datetime.now()) return FollowGZHResponseRedirect(agentId = str(product_agent.id)) if manager_openid and my_user.nickname: return None if auth_bridge.appid == manager_auth_bridge.appid: if dev.owner.show_auth_window: return ExternalResponseRedirect( manager_auth_bridge.generate_auth_url_user_scope( redirect_uri = USER_AUTH_REDIRECT_URL.WECHAT_MANAGER_AUTH_USER, payload = state.encode())) else: return None else: if manager_openid: if dev.owner.show_auth_window: return ExternalResponseRedirect( manager_auth_bridge.generate_auth_url_user_scope( redirect_uri = USER_AUTH_REDIRECT_URL.WECHAT_MANAGER_AUTH_USER, payload = state.encode())) else: return None else: return ExternalResponseRedirect( manager_auth_bridge.generate_auth_url_base_scope( redirect_uri = USER_AUTH_REDIRECT_URL.WECHAT_MANAGER_AUTH_BASE, payload = state.encode())) def login_required(func = None, error_response = None): if func is None: return partial(login_required, error_response = error_response) @wraps(func) def wrapper(request, *args, **kwargs): if not supported_app(request): return NotSupportedPlatformResponseRedirect() is_right_role = lambda r: r == request.user.__class__.__name__ if hasattr(request, 'user') and request.user: if (request.user.is_authenticated()) and (any(map(is_right_role, (ROLE.myuser)))): return func(request, *args, **kwargs) if error_response is None: return JsonResponse({"payload": {}}, status = 401) else: return error_response() if callable(error_response) else error_response return wrapper def user_balance_cache(openId): # type:(str)->str return 'balance_{openId}'.format(openId = openId) def user_last_time_use_ended_cache(openId, logicalCode): # type:(str, str)->str return 'last_time_use_{openId}_{logicalCode}'.format(openId = openId, logicalCode = logicalCode) def moyaya_half_price_cache(openId, logicalCode): # type:(str, str)->str return 'half_price_{openId}_{logicalCode}'.format(openId = openId, logicalCode = logicalCode) def parse_auth_payload(request): # type: (WSGIRequest)->UserAuthState state_str = request.GET.get('payload', None) if state_str is None: raise InvalidParameter(u'所传参数为空') state = UserAuthState.decode(state_str) if not state.is_valid(): raise InvalidParameter(u'网络开小差了, 重新扫码试试喔(%s)' % ErrorCode.USER_STATE_IS_NOT_VALID) return state def calc_real_package(openId, logicalCode, package): coins = VirtualCoin(package['coins']) money = RMB(package['price']) if VirtualCoin(serviceCache.get(moyaya_half_price_cache(openId, logicalCode), -1)) == coins: money = money * Decimal('0.50') return coins, money class OrderBuilderContext(object): def __init__(self, user, device, group, pay_gateway): self.user = user self.device = device self.group = group self.pay_gateway = pay_gateway self.dealer = self.device_or_group.owner @property def device_or_group(self): return self.device or self.group class RechargeRecordBuilder(object): @staticmethod def _new_recharge_record(context, out_trade_no, via, money, coins, subject, attachParas, **kwargs): # type: (OrderBuilderContext, str, str, RMB, VirtualCoin, basestring, Dict, Dict)->RechargeRecord extra_info = kwargs.pop('extraInfo', {}) extra_info.update({ 'dealerId': context.device_or_group.ownerId, 'agentId': context.user.agentId }) if money <= RMB(0): raise ServiceException({'result': 0, 'description': u'支付金额不得小于或等于0'}) if context.pay_gateway: gateway_type = context.pay_gateway.gateway_type assert gateway_type in AppPlatformType.choices(), 'invalid pay gateway type' pay_app_type = context.pay_gateway.pay_app_type assert pay_app_type in PayAppType.choices(), 'invalid pay app type' payload = { 'orderNo': out_trade_no, 'ownerId': context.device_or_group.ownerId, 'money': money, 'coins': coins, 'wxOrderNo': '', 'result': RechargeRecord.PayResult.UNPAY, 'via': via, 'subject': subject, 'attachParas': attachParas, 'extraInfo': extra_info } payload.update(context.device_or_group.identity_info) payload.update(**kwargs) try: return RechargeRecord.issue_pay_record(context=context, **payload) except NotUniqueError: raise ServiceException({'result': 0, 'description': u'您已发起支付'}) @staticmethod def new_recharge_record(payParam): # type: (PayParam)->RechargeRecord user = payParam._payer device = payParam._device group = payParam._group ruleId = payParam._ruleId attachParas = payParam._attachParas pay_gateway = payParam._payGateway if not group.rule_is_accepted(ruleId): raise ServiceException({'result': 0, 'description': u'组内未找到所提供ruleId'}) money = RMB(ruleId) coins = VirtualCoin(group['ruleDict'][ruleId]) if coins <= VirtualCoin(0): raise ServiceException({'result': 0, 'description': u'充值金币数额不得小于或等于0'}) logger.info( 'make new recharge order. gateway = %s; user = %s; ruleId = %s, money = %s, attachParas = %s' % (pay_gateway.gateway_type, repr(user), ruleId, money, attachParas)) order_no = OrderNoMaker.make_order_no_32( identifier = device.logicalCode, main_type = OrderMainType.PAY, sub_type = UserPaySubType.RECHARGE) context = OrderBuilderContext(user = user, device = device, group = group, pay_gateway = pay_gateway) return RechargeRecordBuilder._new_recharge_record(context=context, out_trade_no = order_no, via = USER_RECHARGE_TYPE.RECHARGE, money = money, coins = coins, subject = payParam.subject, attachParas = attachParas, **{ 'isQuickPay': False, 'selectedQuickPayPackageId': None }) @staticmethod def new_start_device_record(payParam): consumeOrder = payParam._order # type: ConsumeRecord pay_gateway = payParam._payGateway attachParas = payParam._attachParas order_no = OrderNoMaker.make_order_no_32( identifier=consumeOrder.logicalCode, main_type=OrderMainType.PAY, sub_type=UserPaySubType.QUICK_PAY ) context = OrderBuilderContext( user=consumeOrder.user, device=consumeOrder.device, group=consumeOrder.group, pay_gateway=pay_gateway ) return RechargeRecordBuilder._new_recharge_record( context=context, out_trade_no=order_no, via=USER_RECHARGE_TYPE.START_DEVICE, money=consumeOrder.price, coins=consumeOrder.price, subject=payParam.subject, attachParas=attachParas, **{ 'isQuickPay': True, }) @staticmethod def new_consume_order_recharge_record(payParam): # type: (PayParam)->RechargeRecord consumeRecord = payParam._consumeRecord user = payParam._payer device = payParam._device group = payParam._group attachParas = payParam._attachParas pay_gateway = payParam._payGateway money = RMB(consumeRecord.price) coins = VirtualCoin(consumeRecord.price) logger.info('make charge consume order. user = %s; money = %s' % (repr(user), money)) order_no = OrderNoMaker.make_order_no_32( identifier=device.logicalCode, main_type=OrderMainType.PAY, sub_type=UserPaySubType.CASH_PAY ) attachParas.update({"orderNo": consumeRecord.orderNo}) context = OrderBuilderContext(user=user, device=device, group=group, pay_gateway=pay_gateway) return RechargeRecordBuilder._new_recharge_record( context=context, out_trade_no=order_no, via=USER_RECHARGE_TYPE.RECHARGE_CASH, money=money, coins=coins, subject=payParam.subject, attachParas=attachParas ) @staticmethod def new_card_recharge_record(payParam): card = payParam._card user = payParam._payer device = payParam._device group = payParam._group attachParas = payParam._attachParas pay_gateway = payParam._payGateway attachParas.update({ 'cardNo': card.cardNo }) if card.id: # 直接设备充值的时候构造了一个内存Card, id为空 attachParas.update({ 'cardId': str(card.id), }) money = RMB(attachParas.get('price', 0)) coins = VirtualCoin(attachParas.get('coins')) logger.info('make charge card order. user = %s; money = %s' % (repr(user), money)) order_no = OrderNoMaker.make_order_no_32(identifier=str(attachParas['cardNo']), main_type=OrderMainType.PAY, sub_type=UserPaySubType.CARD_RECHARGE) context = OrderBuilderContext(user=user, device=device, group=group, pay_gateway=pay_gateway) return RechargeRecordBuilder._new_recharge_record(context=context, out_trade_no=order_no, via=USER_RECHARGE_TYPE.RECHARGE_CARD, money=money, coins=coins, subject=payParam.subject, attachParas=attachParas) class BatteryInfo(object): TOKEN_URL = "http://litapi.gmiot.net/1/auth/access_token?" TRACK_URL = "http://litapi.gmiot.net/1/devices/tracking?" ACCOUNT = "15595278025" PW = 'aqkj168' TOKEN_FIELD_NAME = "access_token" STATUS_FIELD_NAME = "status" TOKEN_CACHE_PRE = "AQ_API_TOKEN" TOKEN_EXPIRE_TIME = 3600 @staticmethod def myMd5(data): return hashlib.md5(data.encode("utf-8")).hexdigest().lower() @staticmethod def getTokenSign(ntime, pw): return BatteryInfo.myMd5(BatteryInfo.myMd5(pw) + ntime) @staticmethod def sendRequest(url, timeout = 3): try: result = requests.post(url = url, timeout = timeout) if result.status_code != 200: raise Exception(u"response status is not equal 200") except Exception as e: logger.info("api request error, reason is %s" % e) raise e jsonData = result.json() if jsonData.get("ret") != 0: logger.error("api request return error ret code ret is %s and msg is %s" % ( jsonData.get("ret"), jsonData.get("msg"))) return jsonData @staticmethod def joinUrl(url, **kwargs): from six.moves.urllib import parse query_params = parse.urlencode(kwargs) return "{}{}".format(url, query_params) @staticmethod def decodeStatus(status): # 第七到八个字节是电池电压信息 单位0.01v return int(status[12: 14], 16) + (int(status[14:16], 16) * 0.01) @property def token(self): # 缓存获取token token = serviceCache.get(self.TOKEN_CACHE_PRE) if not token: ntime = str(int(time.time())) url = self.joinUrl(self.TOKEN_URL, account = self.ACCOUNT, time = ntime, signature = self.getTokenSign(ntime, self.PW)) data = self.sendRequest(url) token = data.get(self.TOKEN_FIELD_NAME) serviceCache.set(self.TOKEN_CACHE_PRE, token, 3600) return token def getBatteryInfo(self, imeis): """ 获取电池状态 """ url = self.joinUrl(self.TRACK_URL, access_token = self.token, imeis = imeis) result = dict() data = self.sendRequest(url = url).get("data") if not data: return dict() for item in data: # 添加电压解析信息 item.update({"voltage": self.decodeStatus(item.get(self.STATUS_FIELD_NAME))}) result.update( { item.get("imei"): item } ) return result def __call__(self, imeis): if isinstance(imeis, list): imeis = ",".join(imeis) return self.getBatteryInfo(imeis) batteryInfo = BatteryInfo() class IsNeedRenew(object): """ 虚拟卡是否即将过期 """ def __init__(self, userCard = None): self._userCard = userCard def check_renew(self): if not self._userCard or not self._userCard.cardTypeId or self._userCard.continueTime: return False, -1 cardType = self.cardType # 有的暂停发售的卡也需要能够续卡 if cardType is None or (not cardType.renewIgnoreStatus and cardType.status != 1) or ( cardType.renewIgnoreStatus and cardType.status not in [0, 1]): return False, -1 nowTime = datetime.datetime.now() leftDays = (self._userCard.expiredTime - nowTime).days if -cardType.needRenewMax < leftDays < cardType.needRenewMin: return True, leftDays return False, -1 @property def cardType(self): """ 快速查询, 考虑是否需要用缓存实现 :return: """ # TODO 缓存实现 cardTypeId = self._userCard.cardTypeId _cardType = VirtualCard.objects.filter(id = cardTypeId).first() return _cardType def __call__(self, userCard): self._userCard = userCard return self.check_renew() is_need_renew = IsNeedRenew() def check_user_tel(user): return False if user.phone else True # 校验用户是否绑定了校园卡 def check_user_bind_card(user): return False if user.extra.get('campus_user_num', None) else True def check_black_user(dealerId, openId): return BlackListUsers.check_black(openId = openId, dealerId = dealerId) def user_pay_coin(openId, devNo, groupId, ownerId, payCount): """ 对用户的金币进行扣款 逐个地址进行扣除 """ deduct_list = [] try: Accounting.recordNetPayCoinCount(devNo) first_user = MyUser.objects(openId = openId, groupId = groupId).get() first_user.update(inc__total_consumed = payCount) user_list = [first_user] group_list = [str(_.id) for _ in Group.objects(ownerId = ownerId, id__ne = groupId).only('id')] last_users = MyUser.objects(openId = openId, balance__gt = 0.0, groupId__in = group_list) user_list += list(last_users) should_pay = VirtualCoin(payCount) for user in user_list: # type: MyUser min_pay = min(VirtualCoin(user.balance), VirtualCoin(should_pay)) deduct_list.append({ 'id': str(user.id), 'field': 'balance', 'before': VirtualCoin(user.balance).mongo_amount, 'coins': min_pay.mongo_amount }) user.pay(coins=min_pay) should_pay -= min_pay if should_pay <= VirtualCoin(0): break # 当前经销商下的所有余额都扣完了,还不够,就要扣代理商下的了(实际也只有代理商支持代理商扣费,才可能到此分支) # TODO 这里明显隐含了关系,需要显示出来 if should_pay > VirtualCoin(0): my_users = MyUser.objects.filter(openId = openId, balance__gt = 0.0) agentDict = defaultdict() for user in my_users: if user.is_product_user(): # TODO 目前自定义公众号进入构成的用户并不记录任何金额信息, 所以直接跳过 continue tempGroup = Group.get_group(user.groupId) if tempGroup: groupDealer = Dealer.objects(id = tempGroup['ownerId']).get() if tempGroup['ownerId'] in agentDict: groupAgentId = agentDict[tempGroup['ownerId']] if groupDealer.agentId != groupAgentId: continue else: groupAgentId = groupDealer.agentId agentDict[tempGroup['ownerId']] = groupDealer.agentId min_pay = min(user.balance, should_pay) # type: VirtualCoin deduct_list.append({ 'id': str(user.id), 'field': 'balance', 'before': VirtualCoin(user.balance).mongo_amount, 'coins': min_pay.mongo_amount }) user.pay(coins = min_pay) should_pay -= min_pay if should_pay <= VirtualCoin(0): break if should_pay > VirtualCoin(0) or should_pay < VirtualCoin(0): raise UserServerException(u'支付扣除金币异常') except UserServerException, e: logger.error('user has not enough coins to pay for {}. something is wrong.'.format(openId, devNo)) ExceptionLog.log( user = '{}'.format(MyUser.__class__.__name__, openId), exception = e.message, extra = {'devNo': devNo, 'deduct': deduct_list}) except Exception as e: logger.exception(e) ExceptionLog.log( user = '{}'.format(MyUser.__class__.__name__, openId), exception = traceback.format_exc(), extra = {'devNo': devNo, 'deduct': deduct_list}) def freeze_user_balance(device, group, order, virtual_card = None): # type: (DeviceDict, GroupDict, ConsumeRecord, UserVirtualCard)->bool if order.paymentInfo['via'] == 'free': return True elif order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: MyUser.freeze_balance(str(order.id), order.paymentInfo['deduct']) elif order.paymentInfo['via'] == 'virtualCard': if not virtual_card: virtual_card = UserVirtualCard.objects(id = str(order.paymentInfo['itemId'])).first() # type: UserVirtualCard if virtual_card: virtual_card.freeze_quota(order.package, str(order.id)) else: raise ServiceException({'result': 0, 'description': u"不支持的支付类型"}) def recover_user_frozen_balance(device, group, order, virtual_card = None): # type: (DeviceDict, GroupDict, ConsumeRecord, UserVirtualCard)->bool if order.paymentInfo['via'] == 'free': return True elif order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: MyUser.recover_frozen_balance(str(order.id), order.paymentInfo['deduct']) elif order.paymentInfo['via'] == 'virtualCard': if not virtual_card: virtual_card = UserVirtualCard.objects( id = str(order.paymentInfo['itemId'])).first() # type: UserVirtualCard if virtual_card: virtual_card.recover_frozen_quota(str(order.id)) else: raise ServiceException({'result': 0, 'description': u"不支持的支付类型"}) def clear_frozen_user_balance(device, order, usedTime, spendElec, backCoins, user = None, virtual_card = None): # type: (DeviceDict, ConsumeRecord, float, float, VirtualCoin, MyUser, UserVirtualCard)->bool if order.paymentInfo['via'] == 'free': return True elif order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: if not user: user = MyUser.objects(openId = order.openId, groupId = order.groupId).first() # type: MyUser if user: user.clear_frozen_balance(str(order.id), order.paymentInfo['deduct'], backCoins, order.coin) elif order.paymentInfo['via'] == 'virtualCard': if not virtual_card: virtual_card = UserVirtualCard.objects( id = str(order.paymentInfo['itemId'])).first() # type: UserVirtualCard if virtual_card: virtual_card.clear_frozen_quota(str(order.id), usedTime, spendElec, backCoins) else: raise ServiceException({'result': 0, 'description': u"不支持的支付类型"}) def vCard_pay_coin(order, vCard, dev, group, package, attachParas, user = None): # type:(ConsumeRecord, UserVirtualCard, DeviceDict, GroupDict, dict, dict, MyUser)->None devNo = dev.devNo groupId = group['groupId'] if not user: try: user = MyUser.objects(openId = order.openId, groupId = groupId).get() except DoesNotExist as e: logger.error('cannot find user openId=%s, groupId=%s' % (order.openId, groupId)) raise e nickname = user.nickname # 扣除卡的额度,并把缓存记录到缓存中 consumeRcd = vCard.consume(openId = order.openId, group = group, dev = dev, package = package, attachParas = attachParas, nickname = nickname) if not consumeRcd: raise ServiceException({'result': 2, 'description': u"扣除虚拟卡额度的时候,出现异常,请重试"}) else: # TODO 巨大的错误 对于冻结的方式来说 ,itemId 指的是卡Id 对于pay 方式来说 这个id又指的是 消费id 指代不一致 容易导致问题 # TODO 这个地方重新修改 统一规定为 卡的ID 将代码全部检查一遍 之前定义为rcd id的全部重写逻辑 order.paymentInfo = { 'itemId': str(vCard.id), 'via': "virtualCard", "money": RMB(order.money).mongo_amount, "coins": VirtualCoin(order.coin).mongo_amount, "rcdId": str(consumeRcd.id), } order.save() cInfo = Device.get_dev_control_cache(devNo) if attachParas.has_key('chargeIndex'): port = attachParas.get('chargeIndex') if cInfo.has_key(port): cInfo[port].update({'consumeRcdId': str(consumeRcd.id)}) Device.update_dev_control_cache(devNo, cInfo) return consumeRcd BUILDER_MAP = { "RECHARGE": RechargeRecordBuilder.new_recharge_record, "START_DEVICE": RechargeRecordBuilder.new_start_device_record, "RECHARGE_ORDER": RechargeRecordBuilder.new_consume_order_recharge_record, "RECHARGE_CARD": RechargeRecordBuilder.new_card_recharge_record, } class PayParam(object): """ 用户进入 payGateway 的时候 对于前台入参的解析校验 所有的拉起支付,必定需要传入logicalCode或者groupId以及type参数 """ def __init__(self, request): # type: (WSGIRequest)->None self._request = request self._clientName = self._get_client_env(request) self._ua = request.META.get('HTTP_USER_AGENT') self._payer = request.user self._payload = self._parse_payload(request) self._recordBuilderKey = None self._consumeRecord = None self._vCard = None self._card = None self._order = None self._payType = self._payload.get("type", None) self._attachParas = self._fix_attach_paras(self.payload.get('attachParas', dict())) if self._payload.get('logicalCode', None): self._device = Device.get_dev_by_l(self._payload.get('logicalCode')) # type: DeviceDict self._group = self._device.group # type: GroupDict self._dealer = self._device.owner # type: Dealer elif self._payload.get('groupId', None): self._device = None # type: Optional[DeviceDict] self._group = Group.get_group(self._payload.get('groupId')) # type: GroupDict self._dealer = self._group.owner # type: Dealer elif self._attachParas.get("orderNo"): order = get_consume_order(self._attachParas["orderNo"]) # type: ConsumeRecord self._device = order.device self._group = order.group self._dealer = order.group.owner else: raise ServiceException({'result': 0, 'description': u'参数错误,请刷新重试(1001)'}) self.common_check() self._payGateway = None self._subject = None self._source = None self._money = None self._coins = None self._discountInfo = None self._monthlyPackage = None if self._payType == 'startDevice': self.handle_param_with_start_device() elif self._payType in ['charge', 'forceCharge']: self.handle_param_with_recharge() elif self._payType == "orderPay": self.handle_param_with_order() elif self._payType == "chargeCard": self.handle_param_with_card() else: logger.error( "error pay type, payload is <{}>, attachParas is <{}>".format(self._payload, self._attachParas) ) raise ServiceException({'result': 0, 'description': u'设备投放地址不属于该平台,请选择其他地址。'}) product_agent = get_user_manager_agent(self._dealer) if self._payer.productAgentId != str(product_agent.id): logger.debug('product of {} is {}. is not belong to {}'.format(repr(self._dealer), repr(product_agent), repr(self._payer))) raise ServiceException({'result': 0, 'description': u'参数错误,请刷新重试(1002)'}) self._product_agent = product_agent # type: Agent # 获取支付网关 self.load_pay_gateway() @property def UA(self): return self._ua @property def clientName(self): return self._clientName @property def _ruleId(self): return self._payload.get('ruleId', None) or self._attachParas.get('ruleId', None) @staticmethod def _fix_attach_paras(attachParas): # type: (dict)->dict return fix_dict(attachParas, ['chargeIndex']) def common_check(self): """ 支付前的公共参数校验 通用的校验都放在这个里面 目前校验的 1. 是否是拉黑用户 拉黑用户一律不允许充值 2. 如果用户是个人中心的充值行为 校验是否是该平台下的用户 :return: """ # 经销商状态异常 if not self._dealer.normal: raise ServiceException({"result": 0, "description": u"设备经销商账号异常,暂时不能支付。"}) # 对于用户拉黑的处理 if check_black_user(dealerId = str(self._dealer.id), openId = self._payer.openId): raise ServiceException({"result": 0, "description": u"暂不支持支付,请联系设备经销商解决。"}) if not self._group: raise ServiceException({'result': 0, 'description': u'参数错误,请刷新重试(1002)'}) # 校验组owner是否一致 if self._group.ownerId != str(self._dealer.id): raise ServiceException({"result": 0, "description": u"设备参数错误,无法支付,请联系设备经销商解决(1001)。"}) # 校验设备owner是否一致 if self._device and self._device.ownerId != str(self._dealer.id): raise ServiceException({"result": 0, "description": u"设备参数错误,无法支付,请联系设备经销商解决(1002)。"}) if not self._payType: raise ServiceException({'result': 0, 'description': u'参数错误,请刷新重试(1002)'}) def call_check(self): """ 拉起支付检查. 支付作为唯一检测点 :return: """ if self._dealer.supports("support_collect_room_info"): myUserDetail = MyUserDetail.objects(openId = str(self._payer.openId), dealerId = str(self._dealer.id)).first() if not (myUserDetail and myUserDetail.userName and myUserDetail.userPhone and myUserDetail.userUnit and myUserDetail.userFloor and myUserDetail.userRoom): raise InterceptException(redirect = before_frag_add_query( concat_front_end_url(uri = '/user/index.html#/user/roomVerify'), added_query = { 'ownerId': str(self._dealer.id), 'userName': myUserDetail and myUserDetail.userName or '', 'userPhone': myUserDetail and myUserDetail.userPhone or '', 'userUnit': myUserDetail and myUserDetail.userUnit or '', 'userFloor': myUserDetail and myUserDetail.userFloor or '', 'userRoom': myUserDetail and myUserDetail.userRoom or '', })) @staticmethod def _get_client_env(request): if detect_wechat_client(request): return "wechat" elif detect_alipay_client(request): return "alipay" elif detect_jdpay_client(request) or detect_jdjr_client(request): return "jd" else: raise ServiceException({"result": 0, "description": u"不支持的客户端"}) def _check_big_package(self, package): if RMB(package['price']) > self._dealer.maxPackagePrice: raise ServiceException({'result': 0, 'description': u'套餐支付金额超限'}) def _check_big_charge(self, money): agent = Agent.objects.get(id = self._dealer.agentId) # type: Agent max_pay_limit = int(agent.maxPayLimit) if money > RMB(max_pay_limit): raise ServiceException({'result': 0, 'description': u'支付金额超限(%d)' % ErrorCode.TOO_BIG_RECHARGE}) def handle_param_with_recharge(self): """ 用户直接充值余额的参数处理 对于用户的充值 :return: """ self._recordBuilderKey = "RECHARGE" ruleId = self._ruleId if not self._group.rule_is_accepted(ruleId): raise ServiceException({'result': 0, 'description': u'组内未找到所提供ruleId'}) money = RMB(ruleId) coins = VirtualCoin(self._group['ruleDict'][ruleId]) self._check_big_charge(money) if coins <= VirtualCoin(0): raise ServiceException({'result': 0, 'description': u'充值金币数额必须大于0金币'}) if money <= RMB(0): raise ServiceException({'result': 0, 'description': u'支付金额必须大于0元'}) self._money = money self._coins = coins self._subject = gen_recharge_purchase_subject( major_type=self._device.majorDeviceType[0:8], logicalCode=self._device.logicalCode ) if self._payType == 'charge' and 'startKey' in self._attachParas: try: actionBox = ActionDeviceBuilder.create_action_device(self._device) actionBox.check_dev_status(self._attachParas) except ServiceException as e: logger.info('device(devNo=%s)(via %s)recharge failure. response==%s' % ( self._device.devNo, self._payGateway.gateway_type, e.result['description'].encode('utf-8'),)) raise ServiceException({'result': 103, 'description': e.result['description'].encode('utf-8')}) def handle_param_with_order(self): """ 订单的后支付流程 这个地方其实可以和start_device统一 :return: """ consumeRecordId = self._attachParas.get("consumeRecordId", "") consumeRecord = ConsumeRecord.objects.filter(id=consumeRecordId).first() if not consumeRecord: raise ServiceException({"result": 0, "description": u"查询消费记录失败,请刷新页面后再尝试支付"}) if consumeRecord.status != consumeRecord.Status.END: raise ServiceException({"result": 0, "description": u"当前订单状态不允许支付"}) if self._payer.openId != consumeRecord.openId: raise ServiceException({'result': 0, 'description': u'不是您消费的订单,无需为此支付费用'}) if self._group.groupId != consumeRecord.groupId: raise ServiceException({'result': 0, 'description': u'支付失败,请联系经销商或客服'}) self._consumeRecord = consumeRecord # 将运行结束的订单 切换为 等待支付的状态 ConsumeOrderStateEngine(consumeRecord).to_wait_pay() self._recordBuilderKey = "RECHARGE_ORDER" self._subject = gen_quick_pay_purchase_subject( major_type = self._device.majorDeviceType[0:8], logicalCode = self._device.logicalCode, port = self.selected_port) def handle_param_with_start_device(self): self._recordBuilderKey = "START_DEVICE" orderNo = self.attachParas.get("orderNo") order = get_consume_order(orderNo) # type: ConsumeRecord if not order: raise ServiceException({'result': 0, 'description': u'消费订单查询失败'}) if order.status != order.Status.WAIT_CONF: raise ServiceException({"result": 2, "description": u"订单状态异常,请前往个人中心查看订单"}) self._order = order self._check_big_charge(order.price) if self._order.price <= RMB(0): raise ServiceException({'result': 0, 'description': u'支付金额必须大于0元'}) self._subject = gen_recharge_purchase_subject( major_type=order.device.majorDeviceType[0:8], logicalCode=order.logicalCode ) def handle_param_with_card(self): self._recordBuilderKey = "RECHARGE_CARD" self._money = RMB(self._attachParas["price"]) self._coins = VirtualCoin(self._attachParas["coins"]) cardId = self._attachParas.get("cardId") if not cardId: raise ServiceException({"result": 0, "description": u"该卡不存在,请联系平台客服处理(1000)"}) card = Card.objects.filter(id=str(cardId)).first() if not card: logger.error('card not find'.format(cardId)) raise ServiceException({'result': 0, 'description': u'该卡不存在,请联系平台客服处理(1001)'}) if not card.dealerId or not card.groupId: logger.error('card not bind dealer or group'.format(cardId)) raise ServiceException({ 'result': ErrorCode.CARD_NEED_BIND_GROUP, 'description': u'该卡未绑定开卡地址', 'payload': { 'cardId': str(card.id), 'cardName': card.cardName, 'phone': card.phone, 'cardNo': card.cardNo }}) if self._group.ownerId != card.dealerId: raise ServiceException({'result': 0, 'description': u'该卡不存在,请联系平台客服(1002)'}) self._card = card self._subject = cn(u"实体卡充值 {}".format(card.cardNo)) def load_pay_gateway(self): """ 加载支付网关 目前不支持商户的模式 """ source = self._product_agent visitor = AgentCustomizedBrandVisitor self._source = source funName = "get_{}_env_pay_gateway".format(self._clientName) get_env_pay_gateway_func = import_string("apps.web.helpers.{}".format(funName)) payGateway = get_env_pay_gateway_func(source, role = ROLE.myuser, vistor = visitor) self._payGateway = payGateway def to_dict(self): return { "user": self._payer, "consumeRecord": self._consumeRecord, "vCard": self._vCard, "card": self._card, "device": self._device, "group": self._group, "dealer": self._dealer, "agent": self._product_agent, "gateWay": self._payGateway } def __str__(self): return "pay info is <{}>".format(self.to_dict()) @property def payGateway(self): assert self._payGateway.pay_app_type in PayAppType.choices(), 'invalid pay app type' return self._payGateway @property def recordBuilderKey(self): assert self._recordBuilderKey in BUILDER_MAP.keys(), 'invalid build key' return self._recordBuilderKey @property def curUser(self): return self._payer # type: MyUser @property def subject(self): return self._subject or "" @property def payload(self): return self._payload @property def attachParas(self): return self._attachParas @property def isQuickPay(self): return True if self._payload.get("quickPay") else False @property def source(self): return self._source @property def goodsInfo(self): info = {} info.update({ "goodsName": cn(self._subject), "payPrice": str(self._money), "goodsPrice": str(self._money), "discountInfo": "" }) return info @staticmethod def _parse_payload(request): """ 解析支付的参数 历史遗留原因 之前的支付参数使用的是query传参 参数键为params 现在的预下单接口 使用的是body-json传参 """ if request.method == GET: return json.loads(request.GET.get("params", '{}')) else: return json.loads(request.body) @property def showAd(self): return self._dealer.ad_show @property def selected_port(self): if not self._attachParas: return None if 'chargeIndex' in self._attachParas and \ self._attachParas['chargeIndex'] and \ self.attachParas['chargeIndex'] not in ['None', 'null', -1, '-1']: return str(self._attachParas['chargeIndex']) else: return None class RedpackBuilder(object): @staticmethod def redpack_to_ledger(order): # type:(ConsumeRecord) -> None pay_app_type = order.user.gateway payGateway = get_platform_promotion_pay_gateway(pay_app_type = pay_app_type) # 红包订单建立 并分账 redpackId = order.attachParas.get('redpackId') redpack = Redpack.get_one(redpackId) if not redpack: logger.error("redpack is null, is not valid record.") return redpack_attachParas = order.attachParas redpack_attachParas.update({'masterId': order.rechargeRcdId, 'startKey': order.startKey}) dev = Device.get_dev(order.devNo) # type: DeviceDict subject = gen_quick_pay_purchase_subject( major_type = dev.majorDeviceType[0:8], logicalCode = order.logicalCode, port = order.used_port) context = OrderBuilderContext(user = order.user, device = dev, group = dev.group, pay_gateway = payGateway) redpack_recharge_record = RechargeRecordBuilder._new_recharge_record( context = context, out_trade_no = OrderNoMaker.make_order_no_32( identifier = order.logicalCode, main_type = OrderMainType.PAY, sub_type = UserPaySubType.REDPACK ), via = USER_RECHARGE_TYPE.RECHARGE_REDPACK, money = RMB(redpack['redpackMoney']), coins = VirtualCoin(redpack['redpackCoins']), subject = subject, attachParas = redpack_attachParas, **{ 'isQuickPay': True, 'selectedQuickPayPackageId': order.attachParas.get('packageId'), 'result': RechargeRecord.PayResult.SUCCESS, 'finishedTime': datetime.datetime.now(), 'description': '平台红包', }) ledger = Ledger(USER_RECHARGE_TYPE.RECHARGE_REDPACK, redpack_recharge_record) ledger.execute(journal = False, stats = True, check = False) @staticmethod def user_pay_coin_with_redpack(order): if Redpack.use_redpack(order.attachParas['redpackId'], str(order.id), order.package): redpack = Redpack.get_one(order.attachParas['redpackId']) deduct_coins = VirtualCoin(redpack.get('redpackCoins', 0)) pay_count = round(VirtualCoin(order.package['coins']) - deduct_coins, 2) if pay_count > 0: user_pay_coin(order.openId, order.devNo, order.groupId, order.ownerId, pay_count) try: RedpackBuilder.redpack_to_ledger(order) except: import traceback logger.error(traceback.format_exc()) @staticmethod def bt_user_pay_coin_with_redpack(order): if Redpack.use_redpack(order.attachParas['redpackId'], str(order.id), order.package): redpack = Redpack.get_one(order.attachParas['redpackId']) deduct_coins = VirtualCoin(redpack.get('redpackCoins', 0)) pay_count = VirtualCoin(order.package['coins']) - deduct_coins if pay_count > VirtualCoin(0): device = Device.get_dev_by_l(order.logicalCode) errCode, errMsg = order.user.deduct_fee_for_bt(device, pay_count) if errCode != 1: raise ServiceException({'result': errCode, 'description': errMsg}) try: RedpackBuilder.redpack_to_ledger(order) except: import traceback logger.error(traceback.format_exc()) @staticmethod def get_alipay_cpa_by_ruhui(openId, logicalCode, urlId='', showType='', **kw): if not urlId: urlId = ConsumeRecord.make_no() kw.update({ 'delayPay': 'true', 'urlId': urlId, 'logicalCode': logicalCode }) dev = Device.get_dev_by_l(logicalCode) from apps.thirdparties.aliyun import AliRuHui RH = AliRuHui() extra = RedpackBuilder._connection_string(kw) params = { 'urlId': urlId, # 'channelId': settings.ALIPAY_RUHUI_CHANNEL_ID, 'alipayOpenId': openId, 'outerCode': logicalCode, 'extra': extra, 'optionType': '1' } popUpQueryRequest = RH.PopUpQueryRequest(params) # Type: Dict if popUpQueryRequest.get('Status') == True: money = RedpackBuilder.calc_by_amount(RMB(popUpQueryRequest.get('UnionAmount', 0))) if money == RMB(0): return {} else: import urlparse taskId = dict(urlparse.parse_qsl(popUpQueryRequest.get('Url'))).get('taskId') redpack = Redpack.create_redpack_by_ruhui( taskId=taskId, openId=openId, urlId=urlId, money=money, leastPayMoney=RMB(0), gateway='alipay', logicalCode=logicalCode, devNo=dev.devNo, extra={'ApiPopUpQueryRequest': popUpQueryRequest}, showType=showType) return {'url': popUpQueryRequest.get('Url'), 'money': redpack.money.mongo_amount, 'redpackId': str(redpack.id), 'showType': showType, } else: return {} @staticmethod def get_alipay_cpa_by_ruhui_v3(openId, logicalCode, showType='', **kw): from apps.thirdparties.aliyun import AlipayYunMaV3 RH = AlipayYunMaV3() dev = Device.get_dev_by_logicalCode(logicalCode) result = RH.get_cpa_ruhui_body(openId, logicalCode) logger.info(result) if result.body.success == True and len(result.body.result.seatbid[0].bid[0].ads): seatb = result.body.result.seatbid[0] urlId = result.body.result.id money = RMB.fen_to_yuan(seatb.bid[0].ads[0].price) money = RedpackBuilder.calc_by_amount(money) if money == RMB(0): return {} else: import urlparse url = seatb.bid[0].ads[0].crurl taskId = seatb.bid[0].ads[0].id redpack = Redpack.create_redpack_by_ruhui( taskId=taskId, openId=openId, urlId=urlId, money=money, leastPayMoney=RMB(0), gateway='alipay', logicalCode=logicalCode, devNo=dev.devNo, extra={'ApiRequest': json.dumps(result.body.result.to_map(), ensure_ascii=False)}, showType=showType) return {'url': url, 'money': redpack.money.mongo_amount, 'redpackId': str(redpack.id), 'showType': showType, 'urlId': urlId} else: return {} @staticmethod def _connection_string(kw): # type: (dict) -> unicode """ 拼接字符串 :param kw: :return: """ return "&".join("{}={}".format(*_) for _ in kw.items()) @classmethod def calc_by_amount(cls, unionAmount): # type: (RMB) -> Optional[RMB, bool] """ 随机金额 """ random_list = [0.1, 0.2, 0.2, 0.3, 0.3, 0.3, 0.4, 0.4, 0.5] import random radio = Ratio(random.choice(random_list)) return unionAmount * radio @classmethod def get_alipay_cpa_by_laxin(cls, openId, channelId=None): TASKMAPNAME={ '1': '关注天猫店铺领红包', '2': '关注天猫店铺领红包', '3': '关注淘宝店铺领红包', '4': '关注淘宝店铺领红包', '5': '浏览商品任务领红包', '6': '浏览商品任务领红包', '7': '看直播领红包', '8': '看直播领红包', } from apps.thirdparties.aliyun import AliLaXin ali = AliLaXin() resp = ali.QueryUnionPromotionRequest({'alipayOpenId': openId, 'channelId': channelId}) code = resp.get('ErrorCode') dataList = [] if code == 0: dataList = resp.get('Result', []) for item in dataList: item['alipayOpenId'] = openId item['channel'] = channelId or settings.ALIPAY_LAXIN_CHANNEL_ID item['taskName'] = TASKMAPNAME.get(item.get('taskType', ''), '') return dataList @classmethod def query_cpa_laxin_task_status(cls, taskId, openId): from apps.thirdparties.aliyun import AliLaXin ali = AliLaXin() resp = ali.GetUnionTaskStatusRequest({'taskId': taskId, 'alipayOpenId': openId}) code = resp.get('ErrorCode') result = resp.get('Result') success = resp.get('Success') if code == 0 and result == True and success == True: return True else: return False @classmethod def create_cpa_laxin_redpack(cls, dev, taskId, openId, taskType): effectTime = datetime.datetime.now() expiredTime = datetime.datetime.now() + datetime.timedelta(days=30 * 3) return Redpack.create_redpack_by_laxin(factoryCode=taskId, openId=openId, money=RMB(0.01), leastPayMoney=RMB(0), effectTime=effectTime, expiredTime=expiredTime, gateway='alipay', logicalCode=dev.logicalCode, devNo=dev.devNo, extra={'taskType':taskType}) @staticmethod def _set_alipay_key(openId, taskId, urlId, money, showType): cache.set('alipayAd{}'.format(openId), {'taskId': taskId, 'urlId': urlId, 'money': money, 'showType': showType}) @staticmethod def _pop_alipay_key(openId): result = cache.get('alipayAd{}'.format(openId)) if result: cache.delete('alipayAd{}'.format(openId)) return result def get_consume_order(orderNo): # type: (str) -> Optional[ConsumeRecord, None] return ConsumeRecord.objects.filter(Q(orderNo=orderNo) | Q(sequenceNo=orderNo)).first()