diff --git a/data/probability_training_snapshots.jsonl b/data/probability_training_snapshots.jsonl index 84b649a6..e9c1f1d9 100644 --- a/data/probability_training_snapshots.jsonl +++ b/data/probability_training_snapshots.jsonl @@ -107,3 +107,12 @@ {"city": "test_city", "timestamp": "2026-03-04 16:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 23.0, "raw_sigma": 0.46875, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 23.0, "peak_status": "past", "prob_snapshot": [{"v": 23, "p": 0.834}, {"v": 24, "p": 0.166}], "shadow_prob_snapshot": [{"v": 23, "p": 0.834}, {"v": 24, "p": 0.166}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 23.0, "calibrated_sigma": 0.46875} {"city": "test_city", "timestamp": "2026-03-04 14:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 29.85, "raw_sigma": 1.09375, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.5, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 29.5, "peak_status": "in_window", "prob_snapshot": [{"v": 30, "p": 0.565}, {"v": 31, "p": 0.341}, {"v": 32, "p": 0.094}], "shadow_prob_snapshot": [{"v": 30, "p": 0.565}, {"v": 31, "p": 0.341}, {"v": 32, "p": 0.094}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 29.85, "calibrated_sigma": 1.09375} {"city": "test_city", "timestamp": "2026-03-04 14:30", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 29.7, "raw_sigma": 1.09375, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 28.0, "peak_status": "in_window", "prob_snapshot": [{"v": 30, "p": 0.35}, {"v": 29, "p": 0.299}, {"v": 31, "p": 0.187}, {"v": 28, "p": 0.117}], "shadow_prob_snapshot": [{"v": 30, "p": 0.35}, {"v": 29, "p": 0.299}, {"v": 31, "p": 0.187}, {"v": 28, "p": 0.117}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 29.7, "calibrated_sigma": 1.09375} +{"city": "test_city", "timestamp": "2026-03-04 10:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 29.7, "raw_sigma": 1.5625, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 26.0, "peak_status": "before", "prob_snapshot": [{"v": 30, "p": 0.254}, {"v": 29, "p": 0.234}, {"v": 31, "p": 0.185}, {"v": 28, "p": 0.146}], "shadow_prob_snapshot": [{"v": 30, "p": 0.254}, {"v": 29, "p": 0.234}, {"v": 31, "p": 0.185}, {"v": 28, "p": 0.146}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 29.7, "calibrated_sigma": 1.5625} +{"city": "test_city", "timestamp": "2026-03-04 17:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 23.0, "raw_sigma": 0.46875, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 23.0, "peak_status": "past", "prob_snapshot": [{"v": 23, "p": 0.834}, {"v": 24, "p": 0.166}], "shadow_prob_snapshot": [{"v": 23, "p": 0.834}, {"v": 24, "p": 0.166}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 23.0, "calibrated_sigma": 0.46875} +{"city": "test_city", "timestamp": "2026-03-04 14:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 33.3, "raw_sigma": 1.09375, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 33.0, "peak_status": "in_window", "prob_snapshot": [{"v": 33, "p": 0.456}, {"v": 34, "p": 0.391}, {"v": 35, "p": 0.153}], "shadow_prob_snapshot": [{"v": 33, "p": 0.456}, {"v": 34, "p": 0.391}, {"v": 35, "p": 0.153}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 33.3, "calibrated_sigma": 1.09375} +{"city": "test_city", "timestamp": "2026-03-04 17:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": null, "raw_sigma": 0.46875, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 28.0, "peak_status": "past", "prob_snapshot": [{"v": 28, "p": 1.0}], "shadow_prob_snapshot": [], "probability_engine": "legacy", "probability_mode": "legacy", "calibration_version": null, "calibration_source": null, "calibrated_mu": null, "calibrated_sigma": null} +{"city": "test_city", "timestamp": "2026-03-04 14:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 29.7, "raw_sigma": 1.09375, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 28.0, "peak_status": "in_window", "prob_snapshot": [{"v": 30, "p": 0.35}, {"v": 29, "p": 0.299}, {"v": 31, "p": 0.187}, {"v": 28, "p": 0.117}], "shadow_prob_snapshot": [{"v": 30, "p": 0.35}, {"v": 29, "p": 0.299}, {"v": 31, "p": 0.187}, {"v": 28, "p": 0.117}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 29.7, "calibrated_sigma": 1.09375} +{"city": "test_city", "timestamp": "2026-03-04 22:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": null, "raw_sigma": 0.46875, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 28.0, "peak_status": "past", "prob_snapshot": [{"v": 28, "p": 1.0}], "shadow_prob_snapshot": [], "probability_engine": "legacy", "probability_mode": "legacy", "calibration_version": null, "calibration_source": null, "calibrated_mu": null, "calibrated_sigma": null} +{"city": "test_city", "timestamp": "2026-03-04 16:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 23.0, "raw_sigma": 0.46875, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 23.0, "peak_status": "past", "prob_snapshot": [{"v": 23, "p": 0.834}, {"v": 24, "p": 0.166}], "shadow_prob_snapshot": [{"v": 23, "p": 0.834}, {"v": 24, "p": 0.166}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 23.0, "calibrated_sigma": 0.46875} +{"city": "test_city", "timestamp": "2026-03-04 14:00", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 29.85, "raw_sigma": 1.09375, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.5, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 29.5, "peak_status": "in_window", "prob_snapshot": [{"v": 30, "p": 0.565}, {"v": 31, "p": 0.341}, {"v": 32, "p": 0.094}], "shadow_prob_snapshot": [{"v": 30, "p": 0.565}, {"v": 31, "p": 0.341}, {"v": 32, "p": 0.094}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 29.85, "calibrated_sigma": 1.09375} +{"city": "test_city", "timestamp": "2026-03-04 14:30", "date": "2026-03-04", "temp_symbol": "°C", "raw_mu": 29.7, "raw_sigma": 1.09375, "deb_prediction": null, "ensemble": {"p10": 27.0, "median": 29.0, "p90": 31.0}, "multi_model": {"Open-Meteo": 30.0}, "max_so_far": 28.0, "peak_status": "in_window", "prob_snapshot": [{"v": 30, "p": 0.35}, {"v": 29, "p": 0.299}, {"v": 31, "p": 0.187}, {"v": 28, "p": 0.117}], "shadow_prob_snapshot": [{"v": 30, "p": 0.35}, {"v": 29, "p": 0.299}, {"v": 31, "p": 0.187}, {"v": 28, "p": 0.117}], "probability_engine": "legacy", "probability_mode": "emos_shadow", "calibration_version": "emos-20260320132525", "calibration_source": "artifacts\\probability_calibration\\default.json", "calibrated_mu": 29.7, "calibrated_sigma": 1.09375} diff --git a/src/payments/contract_checkout.py b/src/payments/contract_checkout.py index 884d017b..627edbc3 100644 --- a/src/payments/contract_checkout.py +++ b/src/payments/contract_checkout.py @@ -1815,6 +1815,40 @@ class PaymentContractCheckoutService: "subscription": subscription_row, } + def _attempt_confirm_repair( + self, + *, + user_id: str, + intent: PaymentIntentRecord, + tx_hash: str, + reason: str, + detail: str, + ) -> Dict[str, Any]: + self._db.append_payment_audit_event( + "payment_confirm_repair_needed", + { + "user_id": user_id, + "intent_id": intent.intent_id, + "plan_code": intent.plan_code, + "reason": str(reason or "").strip().lower(), + "detail": str(detail or "").strip(), + "tx_hash": str(tx_hash or "").strip().lower(), + }, + ) + repaired = self._ensure_confirm_side_effects(user_id, intent, tx_hash) + if repaired.get("payment") or repaired.get("subscription"): + self._db.append_payment_audit_event( + "payment_confirm_repaired", + { + "user_id": user_id, + "intent_id": intent.intent_id, + "plan_code": intent.plan_code, + "tx_hash": str(tx_hash or "").strip().lower(), + "reason": str(reason or "").strip().lower(), + }, + ) + return repaired + def _mark_intent_failed( self, *, @@ -2072,20 +2106,35 @@ class PaymentContractCheckoutService: "points_redemption": points_result, } plan = self._select_plan(intent.plan_code) - payment_row = self._insert_payment_record( - user_id=user_id, - tx_hash=tx_hash_text, - amount_units=intent.amount_units, - token_address=intent.token_address, - payload=payload, - ) - subscription_row = self._grant_subscription( - user_id=user_id, - plan_code=intent.plan_code, - duration_days=plan["duration_days"], - tx_hash=tx_hash_text, - payload=payload, - ) + payment_row = {} + subscription_row = {} + try: + payment_row = self._insert_payment_record( + user_id=user_id, + tx_hash=tx_hash_text, + amount_units=intent.amount_units, + token_address=intent.token_address, + payload=payload, + ) + subscription_row = self._grant_subscription( + user_id=user_id, + plan_code=intent.plan_code, + duration_days=plan["duration_days"], + tx_hash=tx_hash_text, + payload=payload, + ) + except PaymentCheckoutError as exc: + repaired = self._attempt_confirm_repair( + user_id=user_id, + intent=intent, + tx_hash=tx_hash_text, + reason="side_effect_failure", + detail=exc.detail, + ) + payment_row = repaired.get("payment") or payment_row + subscription_row = repaired.get("subscription") or subscription_row + if not subscription_row: + raise self._notify_telegram( user_id=user_id, plan_code=intent.plan_code, @@ -2164,5 +2213,46 @@ class PaymentContractCheckoutService: "subscription": latest_subscription, "attempts": attempts, } + + def reconcile_recent_intents(self, limit: int = 50) -> Dict[str, Any]: + self._ensure_enabled() + safe_limit = max(1, min(int(limit or 50), 200)) + rows = self._rest( + "GET", + "payment_intents", + params={ + "select": "id,user_id,status,updated_at", + "status": "in.(submitted,confirmed)", + "order": "updated_at.desc", + "limit": str(safe_limit), + }, + allowed_status=[200], + ) + if not isinstance(rows, list) or not rows: + return {"ok": True, "processed_users": 0, "repaired_users": 0} + + seen_users: set[str] = set() + repaired_users = 0 + for row in rows: + if not isinstance(row, dict): + continue + user_id = str(row.get("user_id") or "").strip() + if not user_id or user_id in seen_users: + continue + seen_users.add(user_id) + try: + result = self.reconcile_latest_intent(user_id) + if bool(result.get("ok")) and result.get("subscription"): + repaired_users += 1 + except PaymentCheckoutError: + continue + except Exception: + continue + + return { + "ok": True, + "processed_users": len(seen_users), + "repaired_users": repaired_users, + } PAYMENT_CHECKOUT = PaymentContractCheckoutService() diff --git a/tests/test_payments_runtime.py b/tests/test_payments_runtime.py index f1976713..93ce7ccc 100644 --- a/tests/test_payments_runtime.py +++ b/tests/test_payments_runtime.py @@ -1,5 +1,6 @@ from src.database.db_manager import DBManager from src.payments.contract_checkout import ( + PaymentCheckoutError, PaymentContractCheckoutService, PaymentIntentRecord, ) @@ -139,3 +140,143 @@ def test_reconcile_latest_intent_confirms_submitted_first(monkeypatch, tmp_path) assert result["ok"] is True assert result["action"] == "confirmed_submitted_intent" + + +def test_confirm_intent_tx_repairs_side_effect_failure(monkeypatch, tmp_path): + monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true") + monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co") + monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role") + monkeypatch.setenv("POLYWEATHER_PAYMENT_RPC_URL", "https://rpc-1.example") + monkeypatch.setenv( + "POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON", + '[{"code":"usdc_e","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32","is_default":true}]', + ) + monkeypatch.setenv("POLYWEATHER_DB_PATH", str(tmp_path / "payments.db")) + + service = PaymentContractCheckoutService() + submitted_intent = PaymentIntentRecord( + intent_id="intent-2", + order_id_hex="0x" + "1" * 64, + plan_code="pro_monthly", + plan_id=101, + chain_id=137, + amount_units=5000000, + amount_usdc="5", + token_address="0x2791bca1f2de4661ed88a30c99a7a9449aa84174", + token_decimals=6, + token_symbol="USDC.e", + receiver_address="0xed2f13aa5ff033c58fb436e178451cd07f693f32", + status="submitted", + payment_mode="strict", + allowed_wallet="0x1111111111111111111111111111111111111111", + expires_at="2099-01-01T00:00:00+00:00", + tx_hash="0x" + "2" * 64, + metadata={}, + ) + confirmed_intent = PaymentIntentRecord(**{**submitted_intent.__dict__, "status": "confirmed"}) + intents = [submitted_intent, confirmed_intent] + monkeypatch.setattr( + service, + "get_intent", + lambda user_id, intent_id: intents.pop(0) if intents else confirmed_intent, + ) + + class _Eth: + chain_id = 137 + block_number = 20 + + @staticmethod + def get_transaction(_tx_hash): + return { + "to": "0xed2f13aa5ff033c58fb436e178451cd07f693f32", + "from": "0x1111111111111111111111111111111111111111", + } + + class _Web3: + eth = _Eth() + + @staticmethod + def is_connected(): + return True + + monkeypatch.setattr(service, "_get_web3", lambda: _Web3()) + monkeypatch.setattr( + service, + "_wait_receipt", + lambda _tx_hash: { + "status": 1, + "to": "0xed2f13aa5ff033c58fb436e178451cd07f693f32", + "from": "0x1111111111111111111111111111111111111111", + "blockNumber": 10, + }, + ) + monkeypatch.setattr(service, "_extract_matching_event", lambda receipt, intent: {"ok": True}) + monkeypatch.setattr(service, "_consume_points_for_intent", lambda user_id, intent: {"applied": False}) + monkeypatch.setattr(service, "_select_plan", lambda plan_code: {"duration_days": 30}) + monkeypatch.setattr(service, "_insert_payment_record", lambda **kwargs: {"tx_hash": kwargs["tx_hash"]}) + monkeypatch.setattr( + service, + "_grant_subscription", + lambda **kwargs: (_ for _ in ()).throw(PaymentCheckoutError(502, "subscription insert failed")), + ) + monkeypatch.setattr( + service, + "_ensure_confirm_side_effects", + lambda user_id, local_intent, tx_hash: { + "payment": {"tx_hash": tx_hash}, + "subscription": {"plan_code": local_intent.plan_code, "status": "active"}, + }, + ) + monkeypatch.setattr(service, "_notify_telegram", lambda **kwargs: None) + + def _fake_rest(method, table, **kwargs): + if method == "PATCH" and table == "payment_intents": + return [{"id": "intent-2", "status": "confirmed"}] + if method == "POST" and table == "payment_transactions": + return [{"tx_hash": "0x" + "2" * 64, "status": "confirmed"}] + return [] + + monkeypatch.setattr(service, "_rest", _fake_rest) + + result = service.confirm_intent_tx("user-1", "intent-2") + + assert result["subscription"]["status"] == "active" + assert any( + event["event_type"] == "payment_confirm_repaired" + for event in service._db.list_payment_audit_events(limit=10) + ) + + +def test_reconcile_recent_intents_dedupes_users(monkeypatch, tmp_path): + monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true") + monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co") + monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role") + monkeypatch.setenv("POLYWEATHER_PAYMENT_RPC_URL", "https://rpc-1.example") + monkeypatch.setenv( + "POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON", + '[{"code":"usdc_e","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32","is_default":true}]', + ) + monkeypatch.setenv("POLYWEATHER_DB_PATH", str(tmp_path / "payments.db")) + + service = PaymentContractCheckoutService() + monkeypatch.setattr( + service, + "_rest", + lambda method, table, **kwargs: [ + {"id": "a", "user_id": "user-1", "status": "confirmed", "updated_at": "2026-03-22T01:00:00+00:00"}, + {"id": "b", "user_id": "user-1", "status": "submitted", "updated_at": "2026-03-22T00:59:00+00:00"}, + {"id": "c", "user_id": "user-2", "status": "submitted", "updated_at": "2026-03-22T00:58:00+00:00"}, + ], + ) + seen = [] + monkeypatch.setattr( + service, + "reconcile_latest_intent", + lambda user_id: seen.append(user_id) or {"ok": True, "subscription": {"user_id": user_id}}, + ) + + result = service.reconcile_recent_intents(limit=10) + + assert result["processed_users"] == 2 + assert result["repaired_users"] == 2 + assert seen == ["user-1", "user-2"] diff --git a/tests/test_web_observability.py b/tests/test_web_observability.py index a461b633..031ca127 100644 --- a/tests/test_web_observability.py +++ b/tests/test_web_observability.py @@ -2,6 +2,7 @@ from fastapi.testclient import TestClient from web.app import app +import web.routes as routes client = TestClient(app) @@ -45,3 +46,47 @@ def test_payment_runtime_endpoint_returns_shape(): assert 'rpc' in payload assert 'event_loop_state' in payload assert 'recent_audit_events' in payload + + +def test_auth_me_auto_reconciles_missing_subscription(monkeypatch): + monkeypatch.setattr(routes, "_assert_entitlement", lambda request: None) + + def _bind_identity(request): + request.state.auth_user_id = "user-1" + request.state.auth_email = "user@example.com" + + monkeypatch.setattr(routes, "_bind_optional_supabase_identity", _bind_identity) + monkeypatch.setattr(routes, "_resolve_auth_points", lambda request: 0) + monkeypatch.setattr(routes, "_resolve_weekly_profile", lambda request: {"weekly_points": 0, "weekly_rank": None}) + monkeypatch.setattr(routes.SUPABASE_ENTITLEMENT, "enabled", True) + + calls = {"count": 0} + + def _latest_subscription(user_id, respect_requirement=False): + calls["count"] += 1 + if calls["count"] == 1: + return None + return { + "plan_code": "pro_monthly", + "starts_at": "2026-03-22T00:00:00+00:00", + "expires_at": "2026-04-21T00:00:00+00:00", + } + + monkeypatch.setattr( + routes.SUPABASE_ENTITLEMENT, + "get_latest_active_subscription", + _latest_subscription, + ) + monkeypatch.setattr(routes.PAYMENT_CHECKOUT, "enabled", True) + monkeypatch.setattr( + routes.PAYMENT_CHECKOUT, + "reconcile_latest_intent", + lambda user_id: {"ok": True, "action": "reconciled_confirmed_intent"}, + ) + + response = client.get("/api/auth/me") + + assert response.status_code == 200 + payload = response.json() + assert payload["subscription_active"] is True + assert payload["subscription_plan_code"] == "pro_monthly" diff --git a/web/routes.py b/web/routes.py index fcd83370..5ba75108 100644 --- a/web/routes.py +++ b/web/routes.py @@ -188,6 +188,21 @@ async def auth_me(request: Request): user_id, respect_requirement=False, ) + if ( + not latest_subscription + and getattr(PAYMENT_CHECKOUT, "enabled", False) + ): + try: + PAYMENT_CHECKOUT.reconcile_latest_intent(user_id) + latest_subscription = SUPABASE_ENTITLEMENT.get_latest_active_subscription( + user_id, + respect_requirement=False, + ) + except Exception: + latest_subscription = SUPABASE_ENTITLEMENT.get_latest_active_subscription( + user_id, + respect_requirement=False, + ) subscription_active = bool(latest_subscription) if isinstance(latest_subscription, dict): subscription_plan_code = latest_subscription.get("plan_code") @@ -255,6 +270,11 @@ async def ops_memberships(request: Request, limit: int = 200): from src.database.db_manager import DBManager db = DBManager() + if getattr(PAYMENT_CHECKOUT, "enabled", False): + try: + PAYMENT_CHECKOUT.reconcile_recent_intents(limit=min(max(int(limit or 200), 20), 200)) + except Exception: + pass subscriptions = SUPABASE_ENTITLEMENT.list_active_subscriptions(limit=limit) subscription_user_ids = [str(item.get("user_id") or "") for item in subscriptions] user_map = db.get_users_by_supabase_user_ids(subscription_user_ids)