180 lines
No EOL
5.8 KiB
Python
180 lines
No EOL
5.8 KiB
Python
from core.services.networking.httpx import connect
|
|
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
|
|
|
|
from core.models.invoice.Invoice import Invoice
|
|
from core.models.invoice.PaymentMethod import PaymentMethod
|
|
from core.models.session.SessionProfile import SessionProfile
|
|
from core.models.system.SystemProfile import SystemProfile
|
|
from core.Errors import MissingSubscriptionError, InvalidSubscriptionError
|
|
# from core.services.WebServiceApiService import WebServiceApiService
|
|
from core.controllers.ConnectionController import ConnectionController
|
|
from core.observers.ConnectionObserver import ConnectionObserver
|
|
from core.Constants import Constants
|
|
from core.models.Subscription import Subscription
|
|
from core.models.SubscriptionPlan import SubscriptionPlan
|
|
from core.errors.logger import logger
|
|
|
|
from typing import Union, Optional
|
|
|
|
|
|
def activate_subscription(
|
|
profile: Union[SessionProfile, SystemProfile],
|
|
connection_observer: Optional[ConnectionObserver] = None
|
|
) -> bool:
|
|
"""
|
|
Purpose:
|
|
Ensure subscription is ready.
|
|
|
|
Features:
|
|
Idempotent.
|
|
Checks local first
|
|
|
|
Confusion:
|
|
This returns True both if was already active or just activated.
|
|
|
|
Returns:
|
|
Returns True if subscription was already active.
|
|
Returns True if subscription was just activated.
|
|
|
|
Errors:
|
|
True.
|
|
Raises if subscription is missing or invalid. (not false)
|
|
"""
|
|
|
|
if not profile.has_subscription():
|
|
raise MissingSubscriptionError()
|
|
|
|
# Already activated—nothing to do
|
|
if profile.subscription.has_been_activated():
|
|
return True
|
|
|
|
# ==================================================
|
|
# ENCRYPTED PROXY
|
|
# ==================================================
|
|
if profile.connection.code in ("vless", "hysteria2"):
|
|
logger.info("Getting an Encrypted proxy subscription code")
|
|
subscription = get_encrypted_proxy_billing_code(
|
|
location_id=profile.location.id,
|
|
subscription_plan_id=profile.subscription.id,
|
|
connection_observer=connection_observer
|
|
)
|
|
|
|
# ==================================================
|
|
# WIREGUARD AND GENERIC SOCKS5 PROXY
|
|
# ==================================================
|
|
else:
|
|
logger.info("Getting a Wireguard or regular proxy subscription DATED EXPIRED")
|
|
# Fetch and activate
|
|
subscription = get_subscription(
|
|
billing_code=profile.subscription.billing_code,
|
|
connection_observer=connection_observer
|
|
)
|
|
|
|
# legacy
|
|
# subscription = ConnectionController.with_preferred_connection(
|
|
# profile.subscription.billing_code,
|
|
# task=WebServiceApiService.get_subscription,
|
|
# connection_observer=connection_observer
|
|
# )
|
|
|
|
if subscription is None:
|
|
raise InvalidSubscriptionError()
|
|
|
|
profile.subscription = subscription
|
|
profile.save()
|
|
return True
|
|
|
|
|
|
def is_subscription_ready(profile: Union[SessionProfile, SystemProfile]) -> bool:
|
|
"""Check if subscription exists and is activated (no side effects)."""
|
|
return (
|
|
profile.has_subscription()
|
|
and profile.subscription.has_been_activated()
|
|
)
|
|
|
|
|
|
def get_subscription(billing_code: str, connection_observer: ConnectionObserver) -> Subscription:
|
|
|
|
billing_code = billing_code.replace('-', '').upper()
|
|
billing_code_fragments = re.findall('....?', billing_code)
|
|
billing_code = '-'.join(billing_code_fragments)
|
|
|
|
url = f'{Constants.SP_API_BASE_URL}/subscriptions/current'
|
|
|
|
api_response = connect.single_endpoint(
|
|
method="get",
|
|
url=url,
|
|
observer=connection_observer,
|
|
payload=None,
|
|
billing_code=billing_code
|
|
)
|
|
|
|
if api_response.valid:
|
|
raw_json = api_response.data
|
|
subscription = raw_json['data']
|
|
return Subscription(billing_code, Subscription.from_iso_format(subscription['expires_at']))
|
|
else:
|
|
logger.error(f"API Reply of {api_response.error_type}")
|
|
return None
|
|
|
|
|
|
def get_invoice(billing_code: str, connection_observer: ConnectionObserver) -> Invoice:
|
|
|
|
url = f'{Constants.SP_API_BASE_URL}/invoices/current'
|
|
|
|
api_response = connect.single_endpoint(
|
|
method="get",
|
|
url=url,
|
|
observer=connection_observer,
|
|
payload=None,
|
|
billing_code=billing_code
|
|
)
|
|
|
|
if api_response.valid:
|
|
raw_json = api_response.data
|
|
response_data = raw_json['data']
|
|
|
|
invoice = {
|
|
'status': response_data['status'],
|
|
'expires_at': response_data['expires_at']
|
|
}
|
|
|
|
payment_methods = []
|
|
|
|
for payment_method in response_data['payment_methods']:
|
|
payment_methods.append(PaymentMethod(payment_method['code'], payment_method['name'], payment_method['address'], payment_method['payment_link'], payment_method['rate'], payment_method['amount'], payment_method['due']))
|
|
|
|
return Invoice(billing_code, invoice['status'], invoice['expires_at'], tuple[PaymentMethod](payment_methods))
|
|
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_encrypted_proxy_billing_code(
|
|
location_id: int,
|
|
subscription_plan_id: int,
|
|
connection_observer: ConnectionObserver
|
|
) -> str:
|
|
|
|
url = f'{Constants.SP_API_BASE_URL}/api/v1/subscriptions'
|
|
payload = {
|
|
"subscription_plan_id": subscription_plan_id,
|
|
"location_id": location_id
|
|
}
|
|
|
|
api_response = connect.single_endpoint(
|
|
method="post",
|
|
url=url,
|
|
observer=connection_observer,
|
|
payload=payload,
|
|
billing_code=None
|
|
)
|
|
|
|
if api_response.valid:
|
|
raw_reply = api_response.data
|
|
data = raw_reply['data']
|
|
billing_code = data.get('billing_code', None)
|
|
return billing_code
|
|
else:
|
|
logger.error(f"API Error: {api_response.error_type}")
|
|
return None |