1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import hashlib
- import json
- import logging
- import time
- import traceback
- from collections import defaultdict
- 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, Q
- from typing import TYPE_CHECKING, Optional, 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
- 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
- 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.wechat 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, \
- 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, \
- 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 django.core.handlers.wsgi import WSGIRequest
- from apps.web.core import PayAppBase
- 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<openId={},groupId={}> 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<openId={}> has not enough coins to pay for {}. something is wrong.'.format(openId, devNo))
- ExceptionLog.log(
- user = '{}<openId={}>'.format(MyUser.__class__.__name__, openId),
- exception = e.message,
- extra = {'devNo': devNo, 'deduct': deduct_list})
- except Exception as e:
- logger.exception(e)
- ExceptionLog.log(
- user = '{}<openId={}>'.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)
- 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<id={}> not find'.format(cardId))
- raise ServiceException({'result': 0, 'description': u'该卡不存在,请联系平台客服处理(1001)'})
- if not card.dealerId or not card.groupId:
- logger.error('card<id={}> 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()
|