diff --git a/app/billing/__init__.py b/app/billing/__init__.py index bf3f06f..1fa389a 100644 --- a/app/billing/__init__.py +++ b/app/billing/__init__.py @@ -1,4 +1,23 @@ -"""Billing subsystem. +"""billing — DEPRECATED shim. Use app.domains.billing. -Phase 3B of AUDIT-2026-Q3.md. +Phase 4.1 of AUDIT-2026-Q3.md moved app/billing/ to app/domains/billing/. +This shim re-exports the canonical surface and aliases submodules so that +`from app.billing.x402.router import router` keeps working. + +MIGRATION: replace `from app.billing.x402 import ...` with +`from app.domains.billing.x402 import ...`. """ +import sys as _sys +from app.domains.billing import x402 as _x402 +from app.domains.billing.x402 import router as _router + +# Public re-exports +from app.domains.billing.x402 import ( # noqa: F401 + X402Enforcer, + TOOL_PRICES, + CHAIN_USDC, +) + +# Alias submodules so `from app.billing.x402.router import router` works +_sys.modules["app.billing.x402"] = _x402 +_sys.modules["app.billing.x402.router"] = _router diff --git a/app/domains/billing/__init__.py b/app/domains/billing/__init__.py new file mode 100644 index 0000000..bf3f06f --- /dev/null +++ b/app/domains/billing/__init__.py @@ -0,0 +1,4 @@ +"""Billing subsystem. + +Phase 3B of AUDIT-2026-Q3.md. +""" diff --git a/app/domains/billing/facilitators/__init__.py b/app/domains/billing/facilitators/__init__.py new file mode 100644 index 0000000..767b93c --- /dev/null +++ b/app/domains/billing/facilitators/__init__.py @@ -0,0 +1,44 @@ +""" +x402 Multi-Facilitator Registry +================================ +Pluggable facilitator modules for payment verification and settlement. +Each facilitator implements the base.Facilitator interface. + +Architecture: + Payment Request → Smart Router → Best Facilitator → Verify/Settle + ↓ (fallback) + Next Facilitator + +Supported Facilitators: + Hosted: + - Coinbase CDP (Base, instant settlement) + - PayAI (Base, Solana, deferred) + - Cloudflare x402 (Base, Ethereum) + - AsterPay (EUR/SEPA, European) + - Primev FastRPC (Ethereum, fee-free, mev-commit) + Self-Hosted: + - x402-rs (multi-chain Rust facilitator) + Universal: + - EIP-7702 (all EVM chains, all tokens, all native coins) + OFFLINE (dead - DNS NXDOMAIN): + - BNB Pieverse (api.pieverse.xyz) - OFFLINE + - MERX x402 (api.merx.finance) - OFFLINE + - Satoshi (api.satoshi.dev) - OFFLINE +""" + +from app.domains.billing.facilitators.base import ( + Facilitator, + FacilitatorRegistry, + SettlementResult, + VerificationResult, +) +from app.domains.billing.facilitators.router import FacilitatorRouter, get_facilitator_router + +__all__ = [ + "Facilitator", + "FacilitatorRegistry", + "FacilitatorRouter", + "SettlementResult", + "VerificationResult", + "get_facilitator_router", +] diff --git a/app/facilitators/asterpay.py b/app/domains/billing/facilitators/asterpay.py similarity index 98% rename from app/facilitators/asterpay.py rename to app/domains/billing/facilitators/asterpay.py index 5b1316f..6603c57 100644 --- a/app/facilitators/asterpay.py +++ b/app/domains/billing/facilitators/asterpay.py @@ -13,7 +13,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -21,7 +21,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.asterpay") diff --git a/app/facilitators/base.py b/app/domains/billing/facilitators/base.py similarity index 100% rename from app/facilitators/base.py rename to app/domains/billing/facilitators/base.py diff --git a/app/facilitators/bitcoin_selfverify.py b/app/domains/billing/facilitators/bitcoin_selfverify.py similarity index 98% rename from app/facilitators/bitcoin_selfverify.py rename to app/domains/billing/facilitators/bitcoin_selfverify.py index e7599d0..0375772 100644 --- a/app/facilitators/bitcoin_selfverify.py +++ b/app/domains/billing/facilitators/bitcoin_selfverify.py @@ -26,7 +26,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -34,7 +34,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.bitcoin_selfverify") diff --git a/app/facilitators/cloudflare_x402.py b/app/domains/billing/facilitators/cloudflare_x402.py similarity index 98% rename from app/facilitators/cloudflare_x402.py rename to app/domains/billing/facilitators/cloudflare_x402.py index b40359b..36295d1 100644 --- a/app/facilitators/cloudflare_x402.py +++ b/app/domains/billing/facilitators/cloudflare_x402.py @@ -12,7 +12,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -20,7 +20,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.cloudflare_x402") diff --git a/app/facilitators/coinbase_cdp.py b/app/domains/billing/facilitators/coinbase_cdp.py similarity index 98% rename from app/facilitators/coinbase_cdp.py rename to app/domains/billing/facilitators/coinbase_cdp.py index 171a7ab..2d41a79 100644 --- a/app/facilitators/coinbase_cdp.py +++ b/app/domains/billing/facilitators/coinbase_cdp.py @@ -17,7 +17,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -25,7 +25,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import get_config +from app.domains.billing.facilitators.config import get_config logger = logging.getLogger("facilitator.coinbase_cdp") diff --git a/app/facilitators/config.py b/app/domains/billing/facilitators/config.py similarity index 99% rename from app/facilitators/config.py rename to app/domains/billing/facilitators/config.py index 5ae779f..c3e7479 100644 --- a/app/facilitators/config.py +++ b/app/domains/billing/facilitators/config.py @@ -5,7 +5,7 @@ Environment variable mapping and configuration for all facilitators. Each facilitator loads its config from environment variables. Usage: - from app.facilitators.config import FacilitatorConfig + from app.domains.billing.facilitators.config import FacilitatorConfig cfg = FacilitatorConfig() cdp_url = cfg.coinbase_cdp_url diff --git a/app/facilitators/eip7702.py b/app/domains/billing/facilitators/eip7702.py similarity index 98% rename from app/facilitators/eip7702.py rename to app/domains/billing/facilitators/eip7702.py index 83ca985..920998c 100644 --- a/app/facilitators/eip7702.py +++ b/app/domains/billing/facilitators/eip7702.py @@ -27,7 +27,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -35,7 +35,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.eip7702") diff --git a/app/facilitators/merx_tron.py b/app/domains/billing/facilitators/merx_tron.py similarity index 98% rename from app/facilitators/merx_tron.py rename to app/domains/billing/facilitators/merx_tron.py index cc70835..7c76529 100644 --- a/app/facilitators/merx_tron.py +++ b/app/domains/billing/facilitators/merx_tron.py @@ -13,7 +13,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -21,7 +21,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.merx_tron") diff --git a/app/facilitators/payai.py b/app/domains/billing/facilitators/payai.py similarity index 98% rename from app/facilitators/payai.py rename to app/domains/billing/facilitators/payai.py index f57da14..4dd9cb8 100644 --- a/app/facilitators/payai.py +++ b/app/domains/billing/facilitators/payai.py @@ -15,14 +15,14 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, SettlementType, VerificationResult, ) -from app.facilitators.config import get_config +from app.domains.billing.facilitators.config import get_config logger = logging.getLogger("facilitator.payai") diff --git a/app/facilitators/pieverse.py b/app/domains/billing/facilitators/pieverse.py similarity index 97% rename from app/facilitators/pieverse.py rename to app/domains/billing/facilitators/pieverse.py index 1932233..c2b16be 100644 --- a/app/facilitators/pieverse.py +++ b/app/domains/billing/facilitators/pieverse.py @@ -12,7 +12,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -20,7 +20,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.pieverse") diff --git a/app/facilitators/primev.py b/app/domains/billing/facilitators/primev.py similarity index 98% rename from app/facilitators/primev.py rename to app/domains/billing/facilitators/primev.py index 697175f..2dd78cd 100644 --- a/app/facilitators/primev.py +++ b/app/domains/billing/facilitators/primev.py @@ -11,7 +11,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -19,7 +19,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.primev") diff --git a/app/facilitators/router.py b/app/domains/billing/facilitators/router.py similarity index 98% rename from app/facilitators/router.py rename to app/domains/billing/facilitators/router.py index 1df72fc..1862575 100644 --- a/app/facilitators/router.py +++ b/app/domains/billing/facilitators/router.py @@ -10,7 +10,7 @@ Routes payments to the best facilitator based on: 6. Settlement speed - prefer instant > deferred Usage: - from app.facilitators.router import get_facilitator_router + from app.domains.billing.facilitators.router import get_facilitator_router router = get_facilitator_router() result = await router.verify(payload, chain="base", token="USDC") @@ -21,7 +21,7 @@ import time from collections import defaultdict from typing import Any -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorRegistry, SettlementResult, @@ -120,7 +120,7 @@ class FacilitatorRouter: # 4. Settlement type preference st = facilitator.settlement_type - from app.facilitators.base import SettlementType + from app.domains.billing.facilitators.base import SettlementType if st == SettlementType.FEE_FREE: score -= 100 # Strong preference for free diff --git a/app/facilitators/satoshi.py b/app/domains/billing/facilitators/satoshi.py similarity index 98% rename from app/facilitators/satoshi.py rename to app/domains/billing/facilitators/satoshi.py index 4f8524c..68c4906 100644 --- a/app/facilitators/satoshi.py +++ b/app/domains/billing/facilitators/satoshi.py @@ -13,7 +13,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -21,7 +21,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.satoshi") diff --git a/app/facilitators/startup.py b/app/domains/billing/facilitators/startup.py similarity index 82% rename from app/facilitators/startup.py rename to app/domains/billing/facilitators/startup.py index aa2a8b0..e5051c5 100644 --- a/app/facilitators/startup.py +++ b/app/domains/billing/facilitators/startup.py @@ -5,7 +5,7 @@ Registers all facilitators with the global registry at app startup. Called from main.py app startup handler. Usage (in main.py): - from app.facilitators.startup import register_all_facilitators + from app.domains.billing.facilitators.startup import register_all_facilitators await register_all_facilitators() """ @@ -21,15 +21,15 @@ async def register_all_facilitators() -> None: Called once at app startup. """ - from app.facilitators.base import get_registry - from app.facilitators.config import get_config + from app.domains.billing.facilitators.base import get_registry + from app.domains.billing.facilitators.config import get_config registry = get_registry() get_config() # ── 1. Primev FastRPC (fee-free, highest priority for Ethereum) ── try: - from app.facilitators.primev import PrimevFacilitator + from app.domains.billing.facilitators.primev import PrimevFacilitator registry.register(PrimevFacilitator()) logger.info("Registered: Primev FastRPC (fee-free Ethereum)") @@ -39,7 +39,7 @@ async def register_all_facilitators() -> None: # ── 2. Coinbase CDP (highest priority for Base) ── # Always register - health check will mark unavailable if no API key try: - from app.facilitators.coinbase_cdp import CoinbaseCDPFacilitator + from app.domains.billing.facilitators.coinbase_cdp import CoinbaseCDPFacilitator registry.register(CoinbaseCDPFacilitator()) logger.info("Registered: Coinbase CDP (Base + Solana, fee-free)") @@ -49,7 +49,7 @@ async def register_all_facilitators() -> None: # ── 3. BNB Pieverse (BNB Chain) - OFFLINE: api.pieverse.xyz NXDOMAIN ── # Skipped: dead facilitator, DNS does not resolve # try: - # from app.facilitators.pieverse import PieverseFacilitator + # from app.domains.billing.facilitators.pieverse import PieverseFacilitator # registry.register(PieverseFacilitator()) # logger.info("Registered: BNB Pieverse (BNB Chain, instant)") # except Exception as e: @@ -59,7 +59,7 @@ async def register_all_facilitators() -> None: # ── 4. MERX TRON - OFFLINE: api.merx.finance NXDOMAIN ── # Skipped: dead facilitator, DNS does not resolve # try: - # from app.facilitators.merx_tron import MerxTronFacilitator + # from app.domains.billing.facilitators.merx_tron import MerxTronFacilitator # registry.register(MerxTronFacilitator()) # logger.info("Registered: MERX x402 for TRON (USDT/USDC/USDD, sub-3s)") # except Exception as e: @@ -68,7 +68,7 @@ async def register_all_facilitators() -> None: # ── 4b. TRON Self-Verify (replaces dead MERX - uses TronGrid API) ── try: - from app.facilitators.tron_selfverify import TronSelfVerifyFacilitator + from app.domains.billing.facilitators.tron_selfverify import TronSelfVerifyFacilitator tron_fac = TronSelfVerifyFacilitator() if tron_fac._tron_pay_to: @@ -81,7 +81,7 @@ async def register_all_facilitators() -> None: # ── 5. PayAI (Base + Solana, always available - no key needed) ── try: - from app.facilitators.payai import PayAIFacilitator + from app.domains.billing.facilitators.payai import PayAIFacilitator registry.register(PayAIFacilitator()) logger.info("Registered: PayAI (Base + Solana, deferred)") @@ -91,7 +91,7 @@ async def register_all_facilitators() -> None: # ── 6. AsterPay (EUR/SEPA Europe) ── # Always register - health check marks unavailable if no key try: - from app.facilitators.asterpay import AsterPayFacilitator + from app.domains.billing.facilitators.asterpay import AsterPayFacilitator registry.register(AsterPayFacilitator()) logger.info("Registered: AsterPay (European, EUR/SEPA off-ramp)") @@ -100,7 +100,7 @@ async def register_all_facilitators() -> None: # ── 7. Cloudflare x402 (Base Sepolia fallback) ── try: - from app.facilitators.cloudflare_x402 import CloudflareX402Facilitator + from app.domains.billing.facilitators.cloudflare_x402 import CloudflareX402Facilitator registry.register(CloudflareX402Facilitator()) logger.info("Registered: Cloudflare x402 (Base Sepolia/Ethereum, deferred)") @@ -110,7 +110,7 @@ async def register_all_facilitators() -> None: # ── 8. Satoshi (Bitcoin) - OFFLINE: api.satoshi.dev NXDOMAIN ── # Skipped: dead facilitator, DNS does not resolve # try: - # from app.facilitators.satoshi import SatoshiFacilitator + # from app.domains.billing.facilitators.satoshi import SatoshiFacilitator # registry.register(SatoshiFacilitator()) # logger.info("Registered: Satoshi Facilitator (Bitcoin → Base/Solana)") # except Exception as e: @@ -119,7 +119,7 @@ async def register_all_facilitators() -> None: # ── 8b. Bitcoin Self-Verify (replaces dead Satoshi - uses Mempool.space API) ── try: - from app.facilitators.bitcoin_selfverify import BitcoinSelfVerifyFacilitator + from app.domains.billing.facilitators.bitcoin_selfverify import BitcoinSelfVerifyFacilitator btc_fac = BitcoinSelfVerifyFacilitator() if btc_fac._btc_pay_to: @@ -132,7 +132,7 @@ async def register_all_facilitators() -> None: # ── 9. Self-hosted x402-rs (optional, check if container is running) ── try: - from app.facilitators.x402_rs import X402RsFacilitator + from app.domains.billing.facilitators.x402_rs import X402RsFacilitator facilitator = X402RsFacilitator() # Only register if container is reachable @@ -147,7 +147,7 @@ async def register_all_facilitators() -> None: # ── 10. EIP-7702 Universal EVM (always available - no key needed) ── try: - from app.facilitators.eip7702 import EIP7702Facilitator + from app.domains.billing.facilitators.eip7702 import EIP7702Facilitator registry.register(EIP7702Facilitator()) logger.info("Registered: EIP-7702 Universal EVM (all chains, all tokens)") diff --git a/app/facilitators/tron_selfverify.py b/app/domains/billing/facilitators/tron_selfverify.py similarity index 99% rename from app/facilitators/tron_selfverify.py rename to app/domains/billing/facilitators/tron_selfverify.py index ebbf240..6ec74ee 100644 --- a/app/facilitators/tron_selfverify.py +++ b/app/domains/billing/facilitators/tron_selfverify.py @@ -24,7 +24,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -32,7 +32,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.tron_selfverify") diff --git a/app/facilitators/x402_rs.py b/app/domains/billing/facilitators/x402_rs.py similarity index 98% rename from app/facilitators/x402_rs.py rename to app/domains/billing/facilitators/x402_rs.py index 1d569bf..329f806 100644 --- a/app/facilitators/x402_rs.py +++ b/app/domains/billing/facilitators/x402_rs.py @@ -13,7 +13,7 @@ from typing import Any import aiohttp -from app.facilitators.base import ( +from app.domains.billing.facilitators.base import ( Facilitator, FacilitatorType, NetworkSupport, @@ -21,7 +21,7 @@ from app.facilitators.base import ( SettlementType, VerificationResult, ) -from app.facilitators.config import FacilitatorConfig, get_config +from app.domains.billing.facilitators.config import FacilitatorConfig, get_config logger = logging.getLogger("facilitator.x402_rs") diff --git a/app/billing/x402/__init__.py b/app/domains/billing/x402/__init__.py similarity index 91% rename from app/billing/x402/__init__.py rename to app/domains/billing/x402/__init__.py index bdba2de..729d3ec 100644 --- a/app/billing/x402/__init__.py +++ b/app/domains/billing/x402/__init__.py @@ -6,7 +6,7 @@ Re-exports the canonical public API of the x402 payment enforcement middleware. Implementation lives in app.billing.x402.enforcement (moved verbatim from app.routers.x402_enforcement on 2026-07-07). """ -from app.billing.x402.enforcement import ( # noqa: F401 +from app.domains.billing.x402.enforcement import ( # noqa: F401 CHAIN_USDC, EVM_PAY_TO, SECURITY_HEADERS, diff --git a/app/billing/x402/enforcement.py b/app/domains/billing/x402/enforcement.py similarity index 99% rename from app/billing/x402/enforcement.py rename to app/domains/billing/x402/enforcement.py index 0c8dc6a..4bdedab 100755 --- a/app/billing/x402/enforcement.py +++ b/app/domains/billing/x402/enforcement.py @@ -1127,7 +1127,7 @@ async def verify_payment_via_router(payload: dict) -> dict: # Try the smart router first try: - from app.facilitators.router import get_facilitator_router + from app.domains.billing.facilitators.router import get_facilitator_router router = get_facilitator_router() diff --git a/app/billing/x402/router.py b/app/domains/billing/x402/router.py similarity index 67% rename from app/billing/x402/router.py rename to app/domains/billing/x402/router.py index f00f9d1..1c076a6 100644 --- a/app/billing/x402/router.py +++ b/app/domains/billing/x402/router.py @@ -16,15 +16,15 @@ from __future__ import annotations from fastapi import APIRouter -from app.billing.x402.tools.analysis_tools import sub_router as analysis_tools -from app.billing.x402.tools.deployer_tools import sub_router as deployer_tools -from app.billing.x402.tools.evidence_tools import sub_router as evidence_tools -from app.billing.x402.tools.integration import sub_router as integration_tools -from app.billing.x402.tools.label_tools import sub_router as label_tools -from app.billing.x402.tools.market_tools import sub_router as market_tools -from app.billing.x402.tools.report_tools import sub_router as report_tools -from app.billing.x402.tools.token_tools import sub_router as token_tools -from app.billing.x402.tools.wallet_tools import sub_router as wallet_tools +from app.domains.billing.x402.tools.analysis_tools import sub_router as analysis_tools +from app.domains.billing.x402.tools.deployer_tools import sub_router as deployer_tools +from app.domains.billing.x402.tools.evidence_tools import sub_router as evidence_tools +from app.domains.billing.x402.tools.integration import sub_router as integration_tools +from app.domains.billing.x402.tools.label_tools import sub_router as label_tools +from app.domains.billing.x402.tools.market_tools import sub_router as market_tools +from app.domains.billing.x402.tools.report_tools import sub_router as report_tools +from app.domains.billing.x402.tools.token_tools import sub_router as token_tools +from app.domains.billing.x402.tools.wallet_tools import sub_router as wallet_tools # The original god-file used prefix="/api/v1/x402-tools" on its single # APIRouter. After the P3A split, every sub-tool module exposes a diff --git a/app/billing/x402/shared.py b/app/domains/billing/x402/shared.py similarity index 99% rename from app/billing/x402/shared.py rename to app/domains/billing/x402/shared.py index 4c11951..bf255d5 100644 --- a/app/billing/x402/shared.py +++ b/app/domains/billing/x402/shared.py @@ -444,7 +444,7 @@ async def record_x402_payment(tool_id: str, price_usd: str, payer: str) -> None: debug message so receipts flow to logs even though the formal revenue table is owned by app/billing/x402/enforcement.py. - Replace with `from app.billing.x402.enforcement import record_x402_payment` + Replace with `from app.domains.billing.x402.enforcement import record_x402_payment` once the canonical recording path is wired. """ logger.debug( diff --git a/app/billing/x402/tools/__init__.py b/app/domains/billing/x402/tools/__init__.py similarity index 100% rename from app/billing/x402/tools/__init__.py rename to app/domains/billing/x402/tools/__init__.py diff --git a/app/billing/x402/tools/analysis_tools.py b/app/domains/billing/x402/tools/analysis_tools.py similarity index 99% rename from app/billing/x402/tools/analysis_tools.py rename to app/domains/billing/x402/tools/analysis_tools.py index 9bc61b3..f9981ab 100644 --- a/app/billing/x402/tools/analysis_tools.py +++ b/app/domains/billing/x402/tools/analysis_tools.py @@ -19,7 +19,7 @@ from urllib.parse import quote from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( GenericRequest, SentimentRequest, URLRequest, diff --git a/app/billing/x402/tools/deployer_tools.py b/app/domains/billing/x402/tools/deployer_tools.py similarity index 99% rename from app/billing/x402/tools/deployer_tools.py rename to app/domains/billing/x402/tools/deployer_tools.py index 4842f33..f07d7f3 100644 --- a/app/billing/x402/tools/deployer_tools.py +++ b/app/domains/billing/x402/tools/deployer_tools.py @@ -16,7 +16,7 @@ from datetime import datetime from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( GenericRequest, InsiderRequest, fetch_with_fallback, diff --git a/app/billing/x402/tools/evidence_tools.py b/app/domains/billing/x402/tools/evidence_tools.py similarity index 99% rename from app/billing/x402/tools/evidence_tools.py rename to app/domains/billing/x402/tools/evidence_tools.py index 28fb18d..7c410ed 100644 --- a/app/billing/x402/tools/evidence_tools.py +++ b/app/domains/billing/x402/tools/evidence_tools.py @@ -26,7 +26,7 @@ from datetime import datetime from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( SentinelModuleRequest, SentinelScanRequest, record_x402_payment, diff --git a/app/billing/x402/tools/integration.py b/app/domains/billing/x402/tools/integration.py similarity index 99% rename from app/billing/x402/tools/integration.py rename to app/domains/billing/x402/tools/integration.py index bcabfd0..59b1689 100644 --- a/app/billing/x402/tools/integration.py +++ b/app/domains/billing/x402/tools/integration.py @@ -40,7 +40,7 @@ import aiohttp from fastapi import APIRouter, HTTPException, Request from fastapi.responses import JSONResponse -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( HUMAN_PAY_TO, HUMAN_PAYMENT_TOKENS, AuditRequest, @@ -768,7 +768,7 @@ async def meme_vibe_score(req: MemeVibeRequest): # Bundles aggregate multiple tools at a discount vs individual calls. # Each bundle runs its component tools in parallel and returns a unified result. -from app.billing.x402.shared import BUNDLES as _BUNDLES # noqa: E402 +from app.domains.billing.x402.shared import BUNDLES as _BUNDLES # noqa: E402 @sub_router.get("/bundles") @@ -1085,7 +1085,7 @@ async def _record_payment(tool_id: str, price_usd: str, payer: str) -> None: Reuses the P3A stub via the shared module; canonical recording lives in app/billing/x402/enforcement.py and is wired in P3B+. """ - from app.billing.x402.shared import record_x402_payment + from app.domains.billing.x402.shared import record_x402_payment await record_x402_payment(tool_id, price_usd, payer) @@ -1126,7 +1126,7 @@ async def human_execute(req: Any): All payments go to your wallets - no middleman. """ # Re-import the typed request model for runtime validation - from app.billing.x402.shared import HumanPaymentRequest + from app.domains.billing.x402.shared import HumanPaymentRequest req = HumanPaymentRequest.model_validate(req) if isinstance(req, dict) else req @@ -1150,7 +1150,7 @@ async def human_execute(req: Any): try: # Try facilitator router first (same as bot payments) - from app.facilitators.router import get_facilitator_router + from app.domains.billing.facilitators.router import get_facilitator_router router = get_facilitator_router() @@ -1376,7 +1376,7 @@ async def tool_alias_dispatcher_get(tool_id: str, request: Request): instead of POST + JSON body. The enforcement middleware has already verified payment or granted a trial before this handler is reached. """ - from app.billing.x402.shared import TOOL_ALIASES + from app.domains.billing.x402.shared import TOOL_ALIASES # Read query params as the request body query_params = dict(request.query_params) @@ -1514,7 +1514,7 @@ async def tool_alias_dispatcher(tool_id: str, request: Request): 3. Expanded tools (flash_loan_detect → closest real handler) Returns 404 for truly unknown tools.""" - from app.billing.x402.shared import TOOL_ALIASES + from app.domains.billing.x402.shared import TOOL_ALIASES # ── Rate limiting ── if not await _check_rate_limit(request): diff --git a/app/billing/x402/tools/label_tools.py b/app/domains/billing/x402/tools/label_tools.py similarity index 97% rename from app/billing/x402/tools/label_tools.py rename to app/domains/billing/x402/tools/label_tools.py index d87f0c1..b2f0589 100644 --- a/app/billing/x402/tools/label_tools.py +++ b/app/domains/billing/x402/tools/label_tools.py @@ -16,7 +16,7 @@ from datetime import datetime from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( SentinelModuleRequest, record_x402_payment, ) diff --git a/app/billing/x402/tools/market_tools.py b/app/domains/billing/x402/tools/market_tools.py similarity index 99% rename from app/billing/x402/tools/market_tools.py rename to app/domains/billing/x402/tools/market_tools.py index 3f1648e..de6de0e 100644 --- a/app/billing/x402/tools/market_tools.py +++ b/app/domains/billing/x402/tools/market_tools.py @@ -21,7 +21,7 @@ from datetime import datetime import aiohttp from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( FREE_RPCS, GenericRequest, fetch_with_fallback, diff --git a/app/billing/x402/tools/report_tools.py b/app/domains/billing/x402/tools/report_tools.py similarity index 99% rename from app/billing/x402/tools/report_tools.py rename to app/domains/billing/x402/tools/report_tools.py index 26af571..6df7e81 100644 --- a/app/billing/x402/tools/report_tools.py +++ b/app/domains/billing/x402/tools/report_tools.py @@ -24,7 +24,7 @@ from datetime import datetime from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( SentinelModuleRequest, record_x402_payment, ) diff --git a/app/billing/x402/tools/token_tools.py b/app/domains/billing/x402/tools/token_tools.py similarity index 99% rename from app/billing/x402/tools/token_tools.py rename to app/domains/billing/x402/tools/token_tools.py index 450ad3f..7b149e5 100644 --- a/app/billing/x402/tools/token_tools.py +++ b/app/domains/billing/x402/tools/token_tools.py @@ -20,7 +20,7 @@ from datetime import datetime from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( GenericRequest, MultiTokenRequest, TokenRequest, diff --git a/app/billing/x402/tools/wallet_tools.py b/app/domains/billing/x402/tools/wallet_tools.py similarity index 99% rename from app/billing/x402/tools/wallet_tools.py rename to app/domains/billing/x402/tools/wallet_tools.py index 21d3e44..4870bea 100644 --- a/app/billing/x402/tools/wallet_tools.py +++ b/app/domains/billing/x402/tools/wallet_tools.py @@ -20,7 +20,7 @@ from datetime import datetime from fastapi import APIRouter, HTTPException -from app.billing.x402.shared import ( +from app.domains.billing.x402.shared import ( ClusterRequest, GenericRequest, WalletListRequest, diff --git a/app/facilitators/__init__.py b/app/facilitators/__init__.py index adbd531..1c391f2 100644 --- a/app/facilitators/__init__.py +++ b/app/facilitators/__init__.py @@ -1,44 +1,33 @@ +"""facilitators — DEPRECATED shim. Use app.domains.billing.facilitators. + +Phase 4.1 of AUDIT-2026-Q3.md moved app/facilitators/ to +app/domains/billing/facilitators/. This shim re-exports the public surface +and aliases submodules so legacy imports like +`from app.facilitators.base import Facilitator` keep working. + +MIGRATION: replace `from app.facilitators.base import ...` with +`from app.domains.billing.facilitators.base import ...`. """ -x402 Multi-Facilitator Registry -================================ -Pluggable facilitator modules for payment verification and settlement. -Each facilitator implements the base.Facilitator interface. +import sys as _sys +import importlib as _importlib -Architecture: - Payment Request → Smart Router → Best Facilitator → Verify/Settle - ↓ (fallback) - Next Facilitator - -Supported Facilitators: - Hosted: - - Coinbase CDP (Base, instant settlement) - - PayAI (Base, Solana, deferred) - - Cloudflare x402 (Base, Ethereum) - - AsterPay (EUR/SEPA, European) - - Primev FastRPC (Ethereum, fee-free, mev-commit) - Self-Hosted: - - x402-rs (multi-chain Rust facilitator) - Universal: - - EIP-7702 (all EVM chains, all tokens, all native coins) - OFFLINE (dead - DNS NXDOMAIN): - - BNB Pieverse (api.pieverse.xyz) - OFFLINE - - MERX x402 (api.merx.finance) - OFFLINE - - Satoshi (api.satoshi.dev) - OFFLINE -""" - -from app.facilitators.base import ( +# Re-export public surface +from app.domains.billing.facilitators import ( # noqa: F401 Facilitator, FacilitatorRegistry, SettlementResult, VerificationResult, + FacilitatorRouter, + get_facilitator_router, ) -from app.facilitators.router import FacilitatorRouter, get_facilitator_router -__all__ = [ - "Facilitator", - "FacilitatorRegistry", - "FacilitatorRouter", - "SettlementResult", - "VerificationResult", - "get_facilitator_router", -] +# Alias submodules +for _name in ( + "base", "config", "router", + "asterpay", "bitcoin_selfverify", "cloudflare_x402", "coinbase_cdp", + "eip7702", "merx_tron", "payai", "pieverse", "primev", + "satoshi", "startup", "tron_selfverify", "x402_rs", +): + _sys.modules[f"app.facilitators.{_name}"] = _importlib.import_module( + f"app.domains.billing.facilitators.{_name}" + )