ai-lls-lib 1.4.0rc2__py3-none-any.whl → 1.4.0rc4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ai_lls_lib/__init__.py +1 -1
- ai_lls_lib/auth/__init__.py +4 -4
- ai_lls_lib/auth/context_parser.py +68 -68
- ai_lls_lib/cli/__init__.py +3 -3
- ai_lls_lib/cli/__main__.py +30 -30
- ai_lls_lib/cli/aws_client.py +115 -115
- ai_lls_lib/cli/commands/__init__.py +3 -3
- ai_lls_lib/cli/commands/admin.py +174 -174
- ai_lls_lib/cli/commands/cache.py +142 -142
- ai_lls_lib/cli/commands/stripe.py +377 -377
- ai_lls_lib/cli/commands/test_stack.py +216 -216
- ai_lls_lib/cli/commands/verify.py +111 -111
- ai_lls_lib/cli/env_loader.py +122 -122
- ai_lls_lib/core/__init__.py +3 -3
- ai_lls_lib/core/cache.py +106 -106
- ai_lls_lib/core/models.py +77 -77
- ai_lls_lib/core/processor.py +295 -295
- ai_lls_lib/core/verifier.py +84 -84
- ai_lls_lib/payment/__init__.py +13 -13
- ai_lls_lib/payment/credit_manager.py +186 -193
- ai_lls_lib/payment/models.py +102 -102
- ai_lls_lib/payment/stripe_manager.py +487 -487
- ai_lls_lib/payment/webhook_processor.py +215 -215
- ai_lls_lib/providers/__init__.py +7 -7
- ai_lls_lib/providers/base.py +28 -28
- ai_lls_lib/providers/external.py +87 -87
- ai_lls_lib/providers/stub.py +48 -48
- ai_lls_lib/testing/__init__.py +3 -3
- ai_lls_lib/testing/fixtures.py +104 -104
- {ai_lls_lib-1.4.0rc2.dist-info → ai_lls_lib-1.4.0rc4.dist-info}/METADATA +1 -1
- ai_lls_lib-1.4.0rc4.dist-info/RECORD +33 -0
- ai_lls_lib-1.4.0rc2.dist-info/RECORD +0 -33
- {ai_lls_lib-1.4.0rc2.dist-info → ai_lls_lib-1.4.0rc4.dist-info}/WHEEL +0 -0
- {ai_lls_lib-1.4.0rc2.dist-info → ai_lls_lib-1.4.0rc4.dist-info}/entry_points.txt +0 -0
@@ -1,215 +1,215 @@
|
|
1
|
-
"""Stripe webhook event processing."""
|
2
|
-
|
3
|
-
import json
|
4
|
-
import logging
|
5
|
-
from typing import Dict, Any
|
6
|
-
|
7
|
-
try:
|
8
|
-
import stripe
|
9
|
-
except ImportError:
|
10
|
-
stripe = None
|
11
|
-
|
12
|
-
from .credit_manager import CreditManager
|
13
|
-
|
14
|
-
logger = logging.getLogger(__name__)
|
15
|
-
|
16
|
-
|
17
|
-
class WebhookProcessor:
|
18
|
-
"""Process Stripe webhook events."""
|
19
|
-
|
20
|
-
def __init__(self, webhook_secret: str, credit_manager: CreditManager):
|
21
|
-
"""Initialize with webhook secret and credit manager."""
|
22
|
-
self.webhook_secret = webhook_secret
|
23
|
-
self.credit_manager = credit_manager
|
24
|
-
|
25
|
-
def verify_and_parse(self, payload: str, signature: str) -> Dict[str, Any]:
|
26
|
-
"""Verify webhook signature and parse event."""
|
27
|
-
if not stripe:
|
28
|
-
raise ImportError("stripe package not installed")
|
29
|
-
|
30
|
-
try:
|
31
|
-
event = stripe.Webhook.construct_event(
|
32
|
-
payload, signature, self.webhook_secret
|
33
|
-
)
|
34
|
-
return event
|
35
|
-
except ValueError as e:
|
36
|
-
logger.error(f"Invalid webhook payload: {e}")
|
37
|
-
raise
|
38
|
-
except stripe.error.SignatureVerificationError as e:
|
39
|
-
logger.error(f"Invalid webhook signature: {e}")
|
40
|
-
raise
|
41
|
-
|
42
|
-
def process_event(self, event: Dict[str, Any]) -> Dict[str, Any]:
|
43
|
-
"""
|
44
|
-
Process a verified webhook event.
|
45
|
-
Returns response data.
|
46
|
-
"""
|
47
|
-
event_type = event.get("type")
|
48
|
-
event_data = event.get("data", {}).get("object", {})
|
49
|
-
|
50
|
-
logger.info(f"Processing webhook event: {event_type}")
|
51
|
-
|
52
|
-
if event_type == "payment_intent.succeeded":
|
53
|
-
return self._handle_payment_intent_succeeded(event_data)
|
54
|
-
|
55
|
-
elif event_type == "checkout.session.completed":
|
56
|
-
return self._handle_checkout_completed(event_data)
|
57
|
-
|
58
|
-
elif event_type == "customer.subscription.created":
|
59
|
-
return self._handle_subscription_created(event_data)
|
60
|
-
|
61
|
-
elif event_type == "customer.subscription.updated":
|
62
|
-
return self._handle_subscription_updated(event_data)
|
63
|
-
|
64
|
-
elif event_type == "customer.subscription.deleted":
|
65
|
-
return self._handle_subscription_deleted(event_data)
|
66
|
-
|
67
|
-
elif event_type == "invoice.payment_succeeded":
|
68
|
-
return self._handle_invoice_paid(event_data)
|
69
|
-
|
70
|
-
elif event_type == "invoice.payment_failed":
|
71
|
-
return self._handle_invoice_failed(event_data)
|
72
|
-
|
73
|
-
elif event_type == "charge.dispute.created":
|
74
|
-
return self._handle_dispute_created(event_data)
|
75
|
-
|
76
|
-
else:
|
77
|
-
logger.info(f"Unhandled event type: {event_type}")
|
78
|
-
return {"message": f"Event {event_type} received but not processed"}
|
79
|
-
|
80
|
-
def _handle_checkout_completed(self, session: Dict[str, Any]) -> Dict[str, Any]:
|
81
|
-
"""Handle successful checkout session for credit purchase."""
|
82
|
-
metadata = session.get("metadata", {})
|
83
|
-
user_id = metadata.get("user_id")
|
84
|
-
|
85
|
-
if not user_id:
|
86
|
-
logger.error("No user_id in checkout session metadata")
|
87
|
-
return {"error": "Missing user_id"}
|
88
|
-
|
89
|
-
# Get line items to determine credits purchased
|
90
|
-
if session.get("mode") == "payment":
|
91
|
-
# One-time payment for credits
|
92
|
-
# In production, fetch line items from Stripe to get price metadata
|
93
|
-
# For now, extract from session metadata if available
|
94
|
-
credits = int(metadata.get("credits", 0))
|
95
|
-
|
96
|
-
if credits > 0:
|
97
|
-
new_balance = self.credit_manager.add_credits(user_id, credits)
|
98
|
-
logger.info(f"Added {credits} credits to user {user_id}, new balance: {new_balance}")
|
99
|
-
return {"credits_added": credits, "new_balance": new_balance}
|
100
|
-
|
101
|
-
return {"message": "Checkout processed"}
|
102
|
-
|
103
|
-
def _handle_subscription_created(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
104
|
-
"""Handle new subscription creation."""
|
105
|
-
metadata = subscription.get("metadata", {})
|
106
|
-
user_id = metadata.get("user_id")
|
107
|
-
customer_id = subscription.get("customer")
|
108
|
-
subscription_id = subscription.get("id")
|
109
|
-
status = subscription.get("status")
|
110
|
-
|
111
|
-
if user_id:
|
112
|
-
self.credit_manager.set_subscription_state(
|
113
|
-
user_id=user_id,
|
114
|
-
status=status,
|
115
|
-
stripe_customer_id=customer_id,
|
116
|
-
stripe_subscription_id=subscription_id
|
117
|
-
)
|
118
|
-
logger.info(f"Created subscription {subscription_id} for user {user_id}")
|
119
|
-
|
120
|
-
return {"subscription_id": subscription_id, "status": status}
|
121
|
-
|
122
|
-
def _handle_subscription_updated(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
123
|
-
"""Handle subscription updates (pause/resume/etc)."""
|
124
|
-
metadata = subscription.get("metadata", {})
|
125
|
-
user_id = metadata.get("user_id")
|
126
|
-
subscription_id = subscription.get("id")
|
127
|
-
status = subscription.get("status")
|
128
|
-
|
129
|
-
if user_id:
|
130
|
-
self.credit_manager.set_subscription_state(
|
131
|
-
user_id=user_id,
|
132
|
-
status=status,
|
133
|
-
stripe_subscription_id=subscription_id
|
134
|
-
)
|
135
|
-
logger.info(f"Updated subscription {subscription_id} status to {status}")
|
136
|
-
|
137
|
-
return {"subscription_id": subscription_id, "status": status}
|
138
|
-
|
139
|
-
def _handle_subscription_deleted(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
140
|
-
"""Handle subscription cancellation."""
|
141
|
-
metadata = subscription.get("metadata", {})
|
142
|
-
user_id = metadata.get("user_id")
|
143
|
-
subscription_id = subscription.get("id")
|
144
|
-
|
145
|
-
if user_id:
|
146
|
-
self.credit_manager.set_subscription_state(
|
147
|
-
user_id=user_id,
|
148
|
-
status="cancelled",
|
149
|
-
stripe_subscription_id=subscription_id
|
150
|
-
)
|
151
|
-
logger.info(f"Cancelled subscription {subscription_id} for user {user_id}")
|
152
|
-
|
153
|
-
return {"subscription_id": subscription_id, "status": "cancelled"}
|
154
|
-
|
155
|
-
def _handle_invoice_paid(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
|
156
|
-
"""Handle successful subscription payment."""
|
157
|
-
# For monthly subscriptions, could grant monthly credit allotment here
|
158
|
-
# For now, just log the payment
|
159
|
-
customer_id = invoice.get("customer")
|
160
|
-
amount = invoice.get("amount_paid", 0) / 100.0
|
161
|
-
logger.info(f"Invoice paid: ${amount} from customer {customer_id}")
|
162
|
-
return {"amount_paid": amount}
|
163
|
-
|
164
|
-
def _handle_invoice_failed(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
|
165
|
-
"""Handle failed subscription payment."""
|
166
|
-
customer_id = invoice.get("customer")
|
167
|
-
logger.warning(f"Invoice payment failed for customer {customer_id}")
|
168
|
-
# Could pause subscription or send notification here
|
169
|
-
return {"status": "payment_failed"}
|
170
|
-
|
171
|
-
def _handle_payment_intent_succeeded(self, payment_intent: Dict[str, Any]) -> Dict[str, Any]:
|
172
|
-
"""Handle successful payment intent (credit purchase)."""
|
173
|
-
metadata = payment_intent.get("metadata", {})
|
174
|
-
user_id = metadata.get("user_id")
|
175
|
-
|
176
|
-
if not user_id:
|
177
|
-
logger.error("No user_id in payment_intent metadata")
|
178
|
-
return {"error": "Missing user_id"}
|
179
|
-
|
180
|
-
# Check if this is a verification charge ($1)
|
181
|
-
if metadata.get("type") == "verification":
|
182
|
-
# This was the $1 verification, credits already added in payment_setup handler
|
183
|
-
logger.info(f"Verification charge completed for user {user_id}")
|
184
|
-
return {"type": "verification", "status": "completed"}
|
185
|
-
|
186
|
-
# Get credits from metadata (set during payment creation)
|
187
|
-
credits = int(metadata.get("credits", 0))
|
188
|
-
|
189
|
-
if credits > 0:
|
190
|
-
new_balance = self.credit_manager.add_credits(user_id, credits)
|
191
|
-
logger.info(f"Added {credits} credits to user {user_id}, new balance: {new_balance}")
|
192
|
-
return {"credits_added": credits, "new_balance": new_balance}
|
193
|
-
|
194
|
-
return {"message": "Payment processed"}
|
195
|
-
|
196
|
-
def _handle_dispute_created(self, dispute: Dict[str, Any]) -> Dict[str, Any]:
|
197
|
-
"""Handle charge dispute (mark account as disputed)."""
|
198
|
-
# Get the charge and its metadata
|
199
|
-
charge_id = dispute.get("charge")
|
200
|
-
|
201
|
-
if not charge_id:
|
202
|
-
logger.error("No charge_id in dispute")
|
203
|
-
return {"error": "Missing charge_id"}
|
204
|
-
|
205
|
-
# In production, would fetch the charge from Stripe to get metadata
|
206
|
-
# For now, log the dispute for manual handling
|
207
|
-
amount = dispute.get("amount", 0) / 100.0
|
208
|
-
reason = dispute.get("reason", "unknown")
|
209
|
-
|
210
|
-
logger.warning(f"Dispute created for charge {charge_id}: ${amount}, reason: {reason}")
|
211
|
-
|
212
|
-
# TODO: Mark user account as disputed in CreditsTable
|
213
|
-
# This would prevent new purchases until resolved
|
214
|
-
|
215
|
-
return {"dispute_id": dispute.get("id"), "status": "created", "amount": amount}
|
1
|
+
"""Stripe webhook event processing."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
from typing import Dict, Any
|
6
|
+
|
7
|
+
try:
|
8
|
+
import stripe
|
9
|
+
except ImportError:
|
10
|
+
stripe = None
|
11
|
+
|
12
|
+
from .credit_manager import CreditManager
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class WebhookProcessor:
|
18
|
+
"""Process Stripe webhook events."""
|
19
|
+
|
20
|
+
def __init__(self, webhook_secret: str, credit_manager: CreditManager):
|
21
|
+
"""Initialize with webhook secret and credit manager."""
|
22
|
+
self.webhook_secret = webhook_secret
|
23
|
+
self.credit_manager = credit_manager
|
24
|
+
|
25
|
+
def verify_and_parse(self, payload: str, signature: str) -> Dict[str, Any]:
|
26
|
+
"""Verify webhook signature and parse event."""
|
27
|
+
if not stripe:
|
28
|
+
raise ImportError("stripe package not installed")
|
29
|
+
|
30
|
+
try:
|
31
|
+
event = stripe.Webhook.construct_event(
|
32
|
+
payload, signature, self.webhook_secret
|
33
|
+
)
|
34
|
+
return event
|
35
|
+
except ValueError as e:
|
36
|
+
logger.error(f"Invalid webhook payload: {e}")
|
37
|
+
raise
|
38
|
+
except stripe.error.SignatureVerificationError as e:
|
39
|
+
logger.error(f"Invalid webhook signature: {e}")
|
40
|
+
raise
|
41
|
+
|
42
|
+
def process_event(self, event: Dict[str, Any]) -> Dict[str, Any]:
|
43
|
+
"""
|
44
|
+
Process a verified webhook event.
|
45
|
+
Returns response data.
|
46
|
+
"""
|
47
|
+
event_type = event.get("type")
|
48
|
+
event_data = event.get("data", {}).get("object", {})
|
49
|
+
|
50
|
+
logger.info(f"Processing webhook event: {event_type}")
|
51
|
+
|
52
|
+
if event_type == "payment_intent.succeeded":
|
53
|
+
return self._handle_payment_intent_succeeded(event_data)
|
54
|
+
|
55
|
+
elif event_type == "checkout.session.completed":
|
56
|
+
return self._handle_checkout_completed(event_data)
|
57
|
+
|
58
|
+
elif event_type == "customer.subscription.created":
|
59
|
+
return self._handle_subscription_created(event_data)
|
60
|
+
|
61
|
+
elif event_type == "customer.subscription.updated":
|
62
|
+
return self._handle_subscription_updated(event_data)
|
63
|
+
|
64
|
+
elif event_type == "customer.subscription.deleted":
|
65
|
+
return self._handle_subscription_deleted(event_data)
|
66
|
+
|
67
|
+
elif event_type == "invoice.payment_succeeded":
|
68
|
+
return self._handle_invoice_paid(event_data)
|
69
|
+
|
70
|
+
elif event_type == "invoice.payment_failed":
|
71
|
+
return self._handle_invoice_failed(event_data)
|
72
|
+
|
73
|
+
elif event_type == "charge.dispute.created":
|
74
|
+
return self._handle_dispute_created(event_data)
|
75
|
+
|
76
|
+
else:
|
77
|
+
logger.info(f"Unhandled event type: {event_type}")
|
78
|
+
return {"message": f"Event {event_type} received but not processed"}
|
79
|
+
|
80
|
+
def _handle_checkout_completed(self, session: Dict[str, Any]) -> Dict[str, Any]:
|
81
|
+
"""Handle successful checkout session for credit purchase."""
|
82
|
+
metadata = session.get("metadata", {})
|
83
|
+
user_id = metadata.get("user_id")
|
84
|
+
|
85
|
+
if not user_id:
|
86
|
+
logger.error("No user_id in checkout session metadata")
|
87
|
+
return {"error": "Missing user_id"}
|
88
|
+
|
89
|
+
# Get line items to determine credits purchased
|
90
|
+
if session.get("mode") == "payment":
|
91
|
+
# One-time payment for credits
|
92
|
+
# In production, fetch line items from Stripe to get price metadata
|
93
|
+
# For now, extract from session metadata if available
|
94
|
+
credits = int(metadata.get("credits", 0))
|
95
|
+
|
96
|
+
if credits > 0:
|
97
|
+
new_balance = self.credit_manager.add_credits(user_id, credits)
|
98
|
+
logger.info(f"Added {credits} credits to user {user_id}, new balance: {new_balance}")
|
99
|
+
return {"credits_added": credits, "new_balance": new_balance}
|
100
|
+
|
101
|
+
return {"message": "Checkout processed"}
|
102
|
+
|
103
|
+
def _handle_subscription_created(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
104
|
+
"""Handle new subscription creation."""
|
105
|
+
metadata = subscription.get("metadata", {})
|
106
|
+
user_id = metadata.get("user_id")
|
107
|
+
customer_id = subscription.get("customer")
|
108
|
+
subscription_id = subscription.get("id")
|
109
|
+
status = subscription.get("status")
|
110
|
+
|
111
|
+
if user_id:
|
112
|
+
self.credit_manager.set_subscription_state(
|
113
|
+
user_id=user_id,
|
114
|
+
status=status,
|
115
|
+
stripe_customer_id=customer_id,
|
116
|
+
stripe_subscription_id=subscription_id
|
117
|
+
)
|
118
|
+
logger.info(f"Created subscription {subscription_id} for user {user_id}")
|
119
|
+
|
120
|
+
return {"subscription_id": subscription_id, "status": status}
|
121
|
+
|
122
|
+
def _handle_subscription_updated(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
123
|
+
"""Handle subscription updates (pause/resume/etc)."""
|
124
|
+
metadata = subscription.get("metadata", {})
|
125
|
+
user_id = metadata.get("user_id")
|
126
|
+
subscription_id = subscription.get("id")
|
127
|
+
status = subscription.get("status")
|
128
|
+
|
129
|
+
if user_id:
|
130
|
+
self.credit_manager.set_subscription_state(
|
131
|
+
user_id=user_id,
|
132
|
+
status=status,
|
133
|
+
stripe_subscription_id=subscription_id
|
134
|
+
)
|
135
|
+
logger.info(f"Updated subscription {subscription_id} status to {status}")
|
136
|
+
|
137
|
+
return {"subscription_id": subscription_id, "status": status}
|
138
|
+
|
139
|
+
def _handle_subscription_deleted(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
140
|
+
"""Handle subscription cancellation."""
|
141
|
+
metadata = subscription.get("metadata", {})
|
142
|
+
user_id = metadata.get("user_id")
|
143
|
+
subscription_id = subscription.get("id")
|
144
|
+
|
145
|
+
if user_id:
|
146
|
+
self.credit_manager.set_subscription_state(
|
147
|
+
user_id=user_id,
|
148
|
+
status="cancelled",
|
149
|
+
stripe_subscription_id=subscription_id
|
150
|
+
)
|
151
|
+
logger.info(f"Cancelled subscription {subscription_id} for user {user_id}")
|
152
|
+
|
153
|
+
return {"subscription_id": subscription_id, "status": "cancelled"}
|
154
|
+
|
155
|
+
def _handle_invoice_paid(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
|
156
|
+
"""Handle successful subscription payment."""
|
157
|
+
# For monthly subscriptions, could grant monthly credit allotment here
|
158
|
+
# For now, just log the payment
|
159
|
+
customer_id = invoice.get("customer")
|
160
|
+
amount = invoice.get("amount_paid", 0) / 100.0
|
161
|
+
logger.info(f"Invoice paid: ${amount} from customer {customer_id}")
|
162
|
+
return {"amount_paid": amount}
|
163
|
+
|
164
|
+
def _handle_invoice_failed(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
|
165
|
+
"""Handle failed subscription payment."""
|
166
|
+
customer_id = invoice.get("customer")
|
167
|
+
logger.warning(f"Invoice payment failed for customer {customer_id}")
|
168
|
+
# Could pause subscription or send notification here
|
169
|
+
return {"status": "payment_failed"}
|
170
|
+
|
171
|
+
def _handle_payment_intent_succeeded(self, payment_intent: Dict[str, Any]) -> Dict[str, Any]:
|
172
|
+
"""Handle successful payment intent (credit purchase)."""
|
173
|
+
metadata = payment_intent.get("metadata", {})
|
174
|
+
user_id = metadata.get("user_id")
|
175
|
+
|
176
|
+
if not user_id:
|
177
|
+
logger.error("No user_id in payment_intent metadata")
|
178
|
+
return {"error": "Missing user_id"}
|
179
|
+
|
180
|
+
# Check if this is a verification charge ($1)
|
181
|
+
if metadata.get("type") == "verification":
|
182
|
+
# This was the $1 verification, credits already added in payment_setup handler
|
183
|
+
logger.info(f"Verification charge completed for user {user_id}")
|
184
|
+
return {"type": "verification", "status": "completed"}
|
185
|
+
|
186
|
+
# Get credits from metadata (set during payment creation)
|
187
|
+
credits = int(metadata.get("credits", 0))
|
188
|
+
|
189
|
+
if credits > 0:
|
190
|
+
new_balance = self.credit_manager.add_credits(user_id, credits)
|
191
|
+
logger.info(f"Added {credits} credits to user {user_id}, new balance: {new_balance}")
|
192
|
+
return {"credits_added": credits, "new_balance": new_balance}
|
193
|
+
|
194
|
+
return {"message": "Payment processed"}
|
195
|
+
|
196
|
+
def _handle_dispute_created(self, dispute: Dict[str, Any]) -> Dict[str, Any]:
|
197
|
+
"""Handle charge dispute (mark account as disputed)."""
|
198
|
+
# Get the charge and its metadata
|
199
|
+
charge_id = dispute.get("charge")
|
200
|
+
|
201
|
+
if not charge_id:
|
202
|
+
logger.error("No charge_id in dispute")
|
203
|
+
return {"error": "Missing charge_id"}
|
204
|
+
|
205
|
+
# In production, would fetch the charge from Stripe to get metadata
|
206
|
+
# For now, log the dispute for manual handling
|
207
|
+
amount = dispute.get("amount", 0) / 100.0
|
208
|
+
reason = dispute.get("reason", "unknown")
|
209
|
+
|
210
|
+
logger.warning(f"Dispute created for charge {charge_id}: ${amount}, reason: {reason}")
|
211
|
+
|
212
|
+
# TODO: Mark user account as disputed in CreditsTable
|
213
|
+
# This would prevent new purchases until resolved
|
214
|
+
|
215
|
+
return {"dispute_id": dispute.get("id"), "status": "created", "amount": amount}
|
ai_lls_lib/providers/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
"""
|
2
|
-
Verification providers for phone number checking
|
3
|
-
"""
|
4
|
-
from .base import VerificationProvider
|
5
|
-
from .stub import StubProvider
|
6
|
-
|
7
|
-
__all__ = ["VerificationProvider", "StubProvider"]
|
1
|
+
"""
|
2
|
+
Verification providers for phone number checking
|
3
|
+
"""
|
4
|
+
from .base import VerificationProvider
|
5
|
+
from .stub import StubProvider
|
6
|
+
|
7
|
+
__all__ = ["VerificationProvider", "StubProvider"]
|
ai_lls_lib/providers/base.py
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
"""
|
2
|
-
Base protocol for verification providers
|
3
|
-
"""
|
4
|
-
from typing import Protocol, Tuple
|
5
|
-
from ..core.models import LineType
|
6
|
-
|
7
|
-
|
8
|
-
class VerificationProvider(Protocol):
|
9
|
-
"""
|
10
|
-
Protocol for phone verification providers.
|
11
|
-
All providers must implement this interface.
|
12
|
-
"""
|
13
|
-
|
14
|
-
def verify_phone(self, phone: str) -> Tuple[LineType, bool]:
|
15
|
-
"""
|
16
|
-
Verify a phone number's line type and DNC status.
|
17
|
-
|
18
|
-
Args:
|
19
|
-
phone: E.164 formatted phone number
|
20
|
-
|
21
|
-
Returns:
|
22
|
-
Tuple of (line_type, is_on_dnc_list)
|
23
|
-
|
24
|
-
Raises:
|
25
|
-
ValueError: If phone format is invalid
|
26
|
-
Exception: For provider-specific errors
|
27
|
-
"""
|
28
|
-
...
|
1
|
+
"""
|
2
|
+
Base protocol for verification providers
|
3
|
+
"""
|
4
|
+
from typing import Protocol, Tuple
|
5
|
+
from ..core.models import LineType
|
6
|
+
|
7
|
+
|
8
|
+
class VerificationProvider(Protocol):
|
9
|
+
"""
|
10
|
+
Protocol for phone verification providers.
|
11
|
+
All providers must implement this interface.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def verify_phone(self, phone: str) -> Tuple[LineType, bool]:
|
15
|
+
"""
|
16
|
+
Verify a phone number's line type and DNC status.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
phone: E.164 formatted phone number
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
Tuple of (line_type, is_on_dnc_list)
|
23
|
+
|
24
|
+
Raises:
|
25
|
+
ValueError: If phone format is invalid
|
26
|
+
Exception: For provider-specific errors
|
27
|
+
"""
|
28
|
+
...
|