ai-lls-lib 1.1.0__py3-none-any.whl → 1.2.0__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.
@@ -0,0 +1,473 @@
1
+ """Stripe API management with metadata conventions."""
2
+
3
+ import os
4
+ from typing import List, Optional, Dict, Any
5
+ import logging
6
+
7
+ try:
8
+ import stripe
9
+ except ImportError:
10
+ stripe = None # Handle gracefully for testing
11
+
12
+ from .models import Plan
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class StripeManager:
18
+ """
19
+ Manages Stripe resources with metadata conventions.
20
+ Uses metadata to discover and filter products/prices.
21
+ """
22
+
23
+ METADATA_SCHEMA = {
24
+ "product_type": "landline_scrubber",
25
+ "environment": None, # Set at runtime
26
+ "tier": None,
27
+ "credits": None,
28
+ "active": "true"
29
+ }
30
+
31
+ def __init__(self, api_key: Optional[str] = None, environment: Optional[str] = None):
32
+ """Initialize with Stripe API key and environment."""
33
+ if not stripe:
34
+ raise ImportError("stripe package not installed. Run: pip install stripe")
35
+
36
+ self.api_key = api_key or os.environ.get("STRIPE_SECRET_KEY")
37
+ if not self.api_key:
38
+ raise ValueError("Stripe API key not provided and STRIPE_SECRET_KEY not set")
39
+
40
+ stripe.api_key = self.api_key
41
+ self.environment = environment or os.environ.get("ENVIRONMENT", "staging")
42
+
43
+ def list_plans(self) -> List[Plan]:
44
+ """
45
+ Fetch active plans from Stripe using metadata.
46
+ Returns list of Plan objects sorted by price.
47
+ """
48
+ try:
49
+ # Fetch all active prices with expanded product data
50
+ prices = stripe.Price.list(
51
+ active=True,
52
+ expand=["data.product"],
53
+ limit=100
54
+ )
55
+
56
+ plans = []
57
+ for price in prices.data:
58
+ metadata = price.metadata or {}
59
+
60
+ # Filter by our metadata conventions
61
+ if (metadata.get("product_type") == "landline_scrubber" and
62
+ metadata.get("environment") == self.environment and
63
+ metadata.get("active") == "true"):
64
+
65
+ # Convert to Plan object
66
+ plan = Plan.from_stripe_price(price, price.product)
67
+ plans.append(plan)
68
+
69
+ # Sort by price amount
70
+ plans.sort(key=lambda p: p.plan_amount)
71
+
72
+ logger.info(f"Found {len(plans)} active plans for environment {self.environment}")
73
+ return plans
74
+
75
+ except stripe.error.StripeError as e:
76
+ logger.error(f"Stripe error listing plans: {e}")
77
+ # Return mock data for development/testing
78
+ return self._get_mock_plans()
79
+
80
+ def create_setup_intent(self, user_id: str) -> Dict[str, str]:
81
+ """
82
+ Create a SetupIntent for secure payment method collection.
83
+ Frontend will confirm this with Stripe Elements.
84
+ """
85
+ try:
86
+ # Get or create customer
87
+ customer = self._get_or_create_customer(user_id)
88
+
89
+ # Create SetupIntent
90
+ setup_intent = stripe.SetupIntent.create(
91
+ customer=customer.id,
92
+ metadata={
93
+ "user_id": user_id,
94
+ "environment": self.environment
95
+ }
96
+ )
97
+
98
+ return {
99
+ "client_secret": setup_intent.client_secret,
100
+ "setup_intent_id": setup_intent.id
101
+ }
102
+
103
+ except stripe.error.StripeError as e:
104
+ logger.error(f"Stripe error creating setup intent: {e}")
105
+ raise
106
+
107
+ def attach_payment_method(self, user_id: str, payment_method_id: str, billing_details: Dict[str, Any]) -> Dict[str, Any]:
108
+ """
109
+ Attach a payment method to customer (legacy path).
110
+ Returns whether this is the first card.
111
+ """
112
+ try:
113
+ # Get or create customer
114
+ customer = self._get_or_create_customer(user_id)
115
+
116
+ # Attach payment method to customer
117
+ payment_method = stripe.PaymentMethod.attach(
118
+ payment_method_id,
119
+ customer=customer.id
120
+ )
121
+
122
+ # Update billing details if provided
123
+ if billing_details:
124
+ stripe.PaymentMethod.modify(
125
+ payment_method_id,
126
+ billing_details=billing_details
127
+ )
128
+
129
+ # Check if this is the first payment method
130
+ payment_methods = stripe.PaymentMethod.list(
131
+ customer=customer.id,
132
+ type="card"
133
+ )
134
+
135
+ first_card = len(payment_methods.data) == 1
136
+
137
+ # Set as default if first card
138
+ if first_card:
139
+ stripe.Customer.modify(
140
+ customer.id,
141
+ invoice_settings={"default_payment_method": payment_method_id}
142
+ )
143
+
144
+ return {
145
+ "payment_method_id": payment_method_id,
146
+ "first_card": first_card,
147
+ "customer_id": customer.id
148
+ }
149
+
150
+ except stripe.error.StripeError as e:
151
+ logger.error(f"Stripe error attaching payment method: {e}")
152
+ raise
153
+
154
+ def verify_payment_method(self, user_id: str, payment_method_id: str) -> Dict[str, Any]:
155
+ """
156
+ Perform $1 verification charge on new payment method.
157
+ """
158
+ try:
159
+ customer = self._get_or_create_customer(user_id)
160
+
161
+ # Create $1 verification charge
162
+ payment_intent = stripe.PaymentIntent.create(
163
+ amount=100, # $1.00 in cents
164
+ currency="usd",
165
+ customer=customer.id,
166
+ payment_method=payment_method_id,
167
+ off_session=True,
168
+ confirm=True,
169
+ description="Card verification - $1 charge",
170
+ metadata={
171
+ "user_id": user_id,
172
+ "type": "verification",
173
+ "environment": self.environment
174
+ }
175
+ )
176
+
177
+ return {
178
+ "status": payment_intent.status,
179
+ "payment_intent_id": payment_intent.id
180
+ }
181
+
182
+ except stripe.error.StripeError as e:
183
+ logger.error(f"Stripe error verifying payment method: {e}")
184
+ raise
185
+
186
+ def charge_prepaid(self, user_id: str, reference_code: str, amount: Optional[float] = None) -> Dict[str, Any]:
187
+ """
188
+ Charge saved payment method for credit purchase.
189
+ Supports both fixed-price and legacy variable-amount plans.
190
+ """
191
+ try:
192
+ customer = self._get_or_create_customer(user_id)
193
+
194
+ # Legacy variable-amount plan references
195
+ VARIABLE_AMOUNT_REFS = ["79541679412215", "79541679412216"]
196
+
197
+ if reference_code in VARIABLE_AMOUNT_REFS:
198
+ # Variable amount plan - validate amount
199
+ if not amount:
200
+ raise ValueError("Amount required for variable-amount plan")
201
+
202
+ # Validate amount against allowed values from metadata
203
+ # For now, allow common amounts
204
+ allowed_amounts = [5, 10, 20, 25, 50, 75, 100, 150, 200, 250, 500]
205
+ if amount not in allowed_amounts:
206
+ raise ValueError(f"Invalid amount ${amount}. Allowed amounts: {allowed_amounts}")
207
+
208
+ # Calculate credits based on tier
209
+ if reference_code == "79541679412215": # STANDARD
210
+ credits_per_dollar = 500
211
+ else: # POWER
212
+ credits_per_dollar = 570
213
+
214
+ credits_to_add = int(amount * credits_per_dollar)
215
+ charge_amount = int(amount * 100) # Convert to cents
216
+
217
+ else:
218
+ # Fixed price plan - look up from Stripe
219
+ prices = stripe.Price.list(active=True, limit=100)
220
+ price = None
221
+
222
+ for p in prices.data:
223
+ if p.id == reference_code or p.metadata.get("plan_reference") == reference_code:
224
+ price = p
225
+ break
226
+
227
+ if not price:
228
+ raise ValueError(f"Invalid plan reference: {reference_code}")
229
+
230
+ charge_amount = price.unit_amount
231
+ credits_to_add = int(price.metadata.get("credits", 0))
232
+
233
+ # Get default payment method
234
+ default_pm = customer.invoice_settings.get("default_payment_method")
235
+ if not default_pm:
236
+ # Try to get first payment method
237
+ payment_methods = stripe.PaymentMethod.list(
238
+ customer=customer.id,
239
+ type="card",
240
+ limit=1
241
+ )
242
+ if not payment_methods.data:
243
+ raise ValueError("No payment method on file")
244
+ default_pm = payment_methods.data[0].id
245
+
246
+ # Create payment intent
247
+ payment_intent = stripe.PaymentIntent.create(
248
+ amount=charge_amount,
249
+ currency="usd",
250
+ customer=customer.id,
251
+ payment_method=default_pm,
252
+ off_session=True,
253
+ confirm=True,
254
+ description=f"Credit purchase - {credits_to_add} credits",
255
+ metadata={
256
+ "user_id": user_id,
257
+ "credits": credits_to_add,
258
+ "reference_code": reference_code,
259
+ "environment": self.environment
260
+ }
261
+ )
262
+
263
+ return {
264
+ "id": payment_intent.id,
265
+ "status": payment_intent.status,
266
+ "credits_added": credits_to_add,
267
+ "amount_charged": charge_amount / 100 # Convert back to dollars
268
+ }
269
+
270
+ except stripe.error.StripeError as e:
271
+ logger.error(f"Stripe error processing payment: {e}")
272
+ raise
273
+
274
+ def customer_has_payment_method(self, stripe_customer_id: str) -> bool:
275
+ """
276
+ Check if customer has any saved payment methods.
277
+ """
278
+ try:
279
+ payment_methods = stripe.PaymentMethod.list(
280
+ customer=stripe_customer_id,
281
+ type="card",
282
+ limit=1
283
+ )
284
+ return len(payment_methods.data) > 0
285
+ except stripe.error.StripeError as e:
286
+ logger.error(f"Stripe error checking payment methods: {e}")
287
+ return False
288
+
289
+ def list_payment_methods(self, stripe_customer_id: str) -> Dict[str, Any]:
290
+ """
291
+ List all payment methods for a customer.
292
+ """
293
+ try:
294
+ # Get customer to find default payment method
295
+ customer = stripe.Customer.retrieve(stripe_customer_id)
296
+ default_pm_id = customer.invoice_settings.get("default_payment_method")
297
+
298
+ # List all payment methods
299
+ payment_methods = stripe.PaymentMethod.list(
300
+ customer=stripe_customer_id,
301
+ type="card"
302
+ )
303
+
304
+ items = []
305
+ for pm in payment_methods.data:
306
+ items.append({
307
+ "id": pm.id,
308
+ "brand": pm.card.brand,
309
+ "last4": pm.card.last4,
310
+ "exp_month": pm.card.exp_month,
311
+ "exp_year": pm.card.exp_year,
312
+ "is_default": pm.id == default_pm_id
313
+ })
314
+
315
+ return {
316
+ "items": items,
317
+ "default_payment_method_id": default_pm_id
318
+ }
319
+
320
+ except stripe.error.StripeError as e:
321
+ logger.error(f"Stripe error listing payment methods: {e}")
322
+ return {"items": [], "default_payment_method_id": None}
323
+
324
+ def _get_or_create_customer(self, user_id: str, email: Optional[str] = None) -> Any:
325
+ """
326
+ Get existing Stripe customer or create new one.
327
+ First checks by user_id in metadata, then by email if provided.
328
+ """
329
+ try:
330
+ # First try to find by user_id in metadata
331
+ customers = stripe.Customer.search(
332
+ query=f'metadata["user_id"]:"{user_id}"',
333
+ limit=1
334
+ )
335
+
336
+ if customers.data:
337
+ return customers.data[0]
338
+
339
+ # If email provided, try to find by email
340
+ if email:
341
+ customers = stripe.Customer.list(email=email, limit=1)
342
+ if customers.data:
343
+ # Update metadata with user_id
344
+ customer = customers.data[0]
345
+ stripe.Customer.modify(
346
+ customer.id,
347
+ metadata={"user_id": user_id}
348
+ )
349
+ return customer
350
+
351
+ # Create new customer
352
+ return stripe.Customer.create(
353
+ email=email,
354
+ metadata={
355
+ "user_id": user_id,
356
+ "environment": self.environment
357
+ }
358
+ )
359
+
360
+ except stripe.error.StripeError as e:
361
+ logger.error(f"Stripe error getting/creating customer: {e}")
362
+ raise
363
+
364
+ def create_subscription(self, user_id: str, email: str, price_id: str) -> Dict[str, Any]:
365
+ """Create a subscription for unlimited access."""
366
+ try:
367
+ # Create or retrieve customer
368
+ customers = stripe.Customer.list(email=email, limit=1)
369
+ if customers.data:
370
+ customer = customers.data[0]
371
+ else:
372
+ customer = stripe.Customer.create(
373
+ email=email,
374
+ metadata={"user_id": user_id}
375
+ )
376
+
377
+ # Create subscription
378
+ subscription = stripe.Subscription.create(
379
+ customer=customer.id,
380
+ items=[{"price": price_id}],
381
+ metadata={
382
+ "user_id": user_id,
383
+ "environment": self.environment
384
+ }
385
+ )
386
+
387
+ return {
388
+ "subscription_id": subscription.id,
389
+ "status": subscription.status,
390
+ "customer_id": customer.id
391
+ }
392
+
393
+ except stripe.error.StripeError as e:
394
+ logger.error(f"Stripe error creating subscription: {e}")
395
+ raise
396
+
397
+ def pause_subscription(self, subscription_id: str) -> Dict[str, str]:
398
+ """Pause a subscription."""
399
+ try:
400
+ subscription = stripe.Subscription.modify(
401
+ subscription_id,
402
+ pause_collection={"behavior": "mark_uncollectible"}
403
+ )
404
+ return {"message": "Subscription paused", "status": "paused"}
405
+ except stripe.error.StripeError as e:
406
+ logger.error(f"Stripe error pausing subscription: {e}")
407
+ raise
408
+
409
+ def resume_subscription(self, subscription_id: str) -> Dict[str, str]:
410
+ """Resume a paused subscription."""
411
+ try:
412
+ subscription = stripe.Subscription.modify(
413
+ subscription_id,
414
+ pause_collection="" # Remove pause
415
+ )
416
+ return {"message": "Subscription resumed", "status": "active"}
417
+ except stripe.error.StripeError as e:
418
+ logger.error(f"Stripe error resuming subscription: {e}")
419
+ raise
420
+
421
+ def cancel_subscription(self, subscription_id: str) -> Dict[str, str]:
422
+ """Cancel a subscription."""
423
+ try:
424
+ subscription = stripe.Subscription.delete(subscription_id)
425
+ return {"message": "Subscription cancelled", "status": "cancelled"}
426
+ except stripe.error.StripeError as e:
427
+ logger.error(f"Stripe error cancelling subscription: {e}")
428
+ raise
429
+
430
+ def _get_mock_plans(self) -> List[Plan]:
431
+ """Return mock plans for development/testing."""
432
+ return [
433
+ Plan(
434
+ plan_reference="79541679412215",
435
+ plan_type="prepaid",
436
+ plan_name="STANDARD",
437
+ plan_subtitle="One-time purchase",
438
+ plan_amount=10.0,
439
+ plan_credits=5000,
440
+ plan_credits_text="5,000 credits",
441
+ percent_off=""
442
+ ),
443
+ Plan(
444
+ plan_reference="79541679412216",
445
+ plan_type="prepaid",
446
+ plan_name="POWER",
447
+ plan_subtitle="Best value",
448
+ plan_amount=50.0,
449
+ plan_credits=28500,
450
+ plan_credits_text="28,500 credits",
451
+ percent_off="12.5% OFF"
452
+ ),
453
+ Plan(
454
+ plan_reference="79541679412217",
455
+ plan_type="prepaid",
456
+ plan_name="ELITE",
457
+ plan_subtitle="Maximum savings",
458
+ plan_amount=100.0,
459
+ plan_credits=66666,
460
+ plan_credits_text="66,666 credits",
461
+ percent_off="25% OFF"
462
+ ),
463
+ Plan(
464
+ plan_reference="price_unlimited",
465
+ plan_type="postpaid",
466
+ plan_name="UNLIMITED",
467
+ plan_subtitle="Monthly subscription",
468
+ plan_amount=299.0,
469
+ plan_credits=None,
470
+ plan_credits_text="Unlimited",
471
+ percent_off=""
472
+ )
473
+ ]
@@ -0,0 +1,163 @@
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 == "checkout.session.completed":
53
+ return self._handle_checkout_completed(event_data)
54
+
55
+ elif event_type == "customer.subscription.created":
56
+ return self._handle_subscription_created(event_data)
57
+
58
+ elif event_type == "customer.subscription.updated":
59
+ return self._handle_subscription_updated(event_data)
60
+
61
+ elif event_type == "customer.subscription.deleted":
62
+ return self._handle_subscription_deleted(event_data)
63
+
64
+ elif event_type == "invoice.payment_succeeded":
65
+ return self._handle_invoice_paid(event_data)
66
+
67
+ elif event_type == "invoice.payment_failed":
68
+ return self._handle_invoice_failed(event_data)
69
+
70
+ else:
71
+ logger.info(f"Unhandled event type: {event_type}")
72
+ return {"message": f"Event {event_type} received but not processed"}
73
+
74
+ def _handle_checkout_completed(self, session: Dict[str, Any]) -> Dict[str, Any]:
75
+ """Handle successful checkout session for credit purchase."""
76
+ metadata = session.get("metadata", {})
77
+ user_id = metadata.get("user_id")
78
+
79
+ if not user_id:
80
+ logger.error("No user_id in checkout session metadata")
81
+ return {"error": "Missing user_id"}
82
+
83
+ # Get line items to determine credits purchased
84
+ if session.get("mode") == "payment":
85
+ # One-time payment for credits
86
+ # In production, fetch line items from Stripe to get price metadata
87
+ # For now, extract from session metadata if available
88
+ credits = int(metadata.get("credits", 0))
89
+
90
+ if credits > 0:
91
+ new_balance = self.credit_manager.add_credits(user_id, credits)
92
+ logger.info(f"Added {credits} credits to user {user_id}, new balance: {new_balance}")
93
+ return {"credits_added": credits, "new_balance": new_balance}
94
+
95
+ return {"message": "Checkout processed"}
96
+
97
+ def _handle_subscription_created(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
98
+ """Handle new subscription creation."""
99
+ metadata = subscription.get("metadata", {})
100
+ user_id = metadata.get("user_id")
101
+ customer_id = subscription.get("customer")
102
+ subscription_id = subscription.get("id")
103
+ status = subscription.get("status")
104
+
105
+ if user_id:
106
+ self.credit_manager.set_subscription_state(
107
+ user_id=user_id,
108
+ status=status,
109
+ stripe_customer_id=customer_id,
110
+ stripe_subscription_id=subscription_id
111
+ )
112
+ logger.info(f"Created subscription {subscription_id} for user {user_id}")
113
+
114
+ return {"subscription_id": subscription_id, "status": status}
115
+
116
+ def _handle_subscription_updated(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
117
+ """Handle subscription updates (pause/resume/etc)."""
118
+ metadata = subscription.get("metadata", {})
119
+ user_id = metadata.get("user_id")
120
+ subscription_id = subscription.get("id")
121
+ status = subscription.get("status")
122
+
123
+ if user_id:
124
+ self.credit_manager.set_subscription_state(
125
+ user_id=user_id,
126
+ status=status,
127
+ stripe_subscription_id=subscription_id
128
+ )
129
+ logger.info(f"Updated subscription {subscription_id} status to {status}")
130
+
131
+ return {"subscription_id": subscription_id, "status": status}
132
+
133
+ def _handle_subscription_deleted(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
134
+ """Handle subscription cancellation."""
135
+ metadata = subscription.get("metadata", {})
136
+ user_id = metadata.get("user_id")
137
+ subscription_id = subscription.get("id")
138
+
139
+ if user_id:
140
+ self.credit_manager.set_subscription_state(
141
+ user_id=user_id,
142
+ status="cancelled",
143
+ stripe_subscription_id=subscription_id
144
+ )
145
+ logger.info(f"Cancelled subscription {subscription_id} for user {user_id}")
146
+
147
+ return {"subscription_id": subscription_id, "status": "cancelled"}
148
+
149
+ def _handle_invoice_paid(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
150
+ """Handle successful subscription payment."""
151
+ # For monthly subscriptions, could grant monthly credit allotment here
152
+ # For now, just log the payment
153
+ customer_id = invoice.get("customer")
154
+ amount = invoice.get("amount_paid", 0) / 100.0
155
+ logger.info(f"Invoice paid: ${amount} from customer {customer_id}")
156
+ return {"amount_paid": amount}
157
+
158
+ def _handle_invoice_failed(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
159
+ """Handle failed subscription payment."""
160
+ customer_id = invoice.get("customer")
161
+ logger.warning(f"Invoice payment failed for customer {customer_id}")
162
+ # Could pause subscription or send notification here
163
+ return {"status": "payment_failed"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ai-lls-lib
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Landline Scrubber core library - phone verification and DNC checking
5
5
  Author: LandlineScrubber Team
6
6
  Requires-Python: >=3.12,<4.0
@@ -14,6 +14,7 @@ Requires-Dist: httpx (>=0.25.0,<0.26.0)
14
14
  Requires-Dist: phonenumbers (>=8.13.0,<9.0.0)
15
15
  Requires-Dist: pydantic (>=2.5.0,<3.0.0)
16
16
  Requires-Dist: rich (>=14.0,<15.0)
17
+ Requires-Dist: stripe (>=12.5.1,<13.0.0)
17
18
  Description-Content-Type: text/markdown
18
19
 
19
20
  # AI LLS Library
@@ -1,24 +1,33 @@
1
- ai_lls_lib/__init__.py,sha256=d5gZDobIQ9_C7Ed5NB4RIgtoLWk_eHko_6Rh43aPPwk,584
1
+ ai_lls_lib/__init__.py,sha256=vX28RZKgWt_4PDkQ_H69AwCCp7Jt9nHwhxt4RhKOotU,584
2
+ ai_lls_lib/auth/__init__.py,sha256=c6zomHSB6y9Seakf84ciGsD3XgWarIty9xty6P8fxVw,194
3
+ ai_lls_lib/auth/context_parser.py,sha256=8I0vGbtykNLWqm8ldedxXjE-E3nqsCy113JgeyuiJoM,2222
2
4
  ai_lls_lib/cli/__init__.py,sha256=m9qjZTW1jpENwXAUeuRrlP0b66BWRcqSO28MSjvOyCs,74
3
- ai_lls_lib/cli/__main__.py,sha256=DH9x08k6GWBSqVWspHgWTxGM7NkTBZ2OS2KrVW-XQaA,671
5
+ ai_lls_lib/cli/__main__.py,sha256=8tUdq4GJwzIiTC1pvCsTkwmq8iNujdFTbI45_wm3d7A,716
4
6
  ai_lls_lib/cli/aws_client.py,sha256=YcCWCpTNOW9JPLxSNLRy5-F5HPKguJJk7dPNrPqhJv0,3952
5
7
  ai_lls_lib/cli/commands/__init__.py,sha256=_kROrYuR_p2i110c0OvNeArfEFQbn15zR1c3pdeZOoo,28
6
8
  ai_lls_lib/cli/commands/admin.py,sha256=bNBJi2fZBP0J40JQP6HP7NYadNmI214iII1TeLhooyE,6687
7
9
  ai_lls_lib/cli/commands/cache.py,sha256=vWt0vy4L9CEgUEWUzfdehU6u43PE8vUvHx7xxg4e5tw,5680
10
+ ai_lls_lib/cli/commands/stripe.py,sha256=x5QtirbCPiBrjdMRVTR0aeQN2gy0_p6jcGzELObFfzg,12006
8
11
  ai_lls_lib/cli/commands/test_stack.py,sha256=rNq4mhRXX7Ixo67kSoEPWlxqgXCzM9e2PR96qTAG7pE,7378
9
12
  ai_lls_lib/cli/commands/verify.py,sha256=V5ucjmjCUxqN8_AeEJWWgrmYin8BNV3h4WbZ-iX3loU,4238
13
+ ai_lls_lib/cli/env_loader.py,sha256=YTCB6QDyknOuedPbQsW4bezB5SgHzJMGjhpSPArHFVc,3762
10
14
  ai_lls_lib/core/__init__.py,sha256=QUaeQHIyvknkgMxIbfXRo1a5jSQpiJkB84dId5ubuEk,36
11
15
  ai_lls_lib/core/cache.py,sha256=MubgyAF3y7BBF9am39Ni98NgikZ9UBZUG-KtbE3XWX4,3711
12
16
  ai_lls_lib/core/models.py,sha256=ABRYaeMCWahQh4WdbkCxt3AO0-EvPWZwlL-JITQSnEM,2381
13
17
  ai_lls_lib/core/processor.py,sha256=6752IPDQ-Mz5i_CU7aBM0UjvV7IjyZFl35LKVPkHMpc,9974
14
18
  ai_lls_lib/core/verifier.py,sha256=6uB_jawWoIqsNYAadTKr0lSolIWygg2gK6ykf8lrul0,2716
19
+ ai_lls_lib/payment/__init__.py,sha256=xhUWgfLnk3syXXQItswmDXdfXUyJTXTQAA0zIUuCVII,295
20
+ ai_lls_lib/payment/credit_manager.py,sha256=TDrdW7BYjhtlg8MX_EJEnMNct1SU_6Tdb_k19_e8kpo,6343
21
+ ai_lls_lib/payment/models.py,sha256=JjSmWKwpuFF85Jzmabj6y7UyolJlJlsh5CmOWRg21B8,3339
22
+ ai_lls_lib/payment/stripe_manager.py,sha256=-L329stuu-6V_QKzRdkm9qnoi74SbWzST0fuf2WAJXA,17111
23
+ ai_lls_lib/payment/webhook_processor.py,sha256=UVngeAGcou5ieRAp-49pqdWh0wsJQVwpiRoLmyl5Adc,6448
15
24
  ai_lls_lib/providers/__init__.py,sha256=AEv3ARenWDwDo5PLCoszP2fQ70RgSHkrSLSUz7xHJDk,179
16
25
  ai_lls_lib/providers/base.py,sha256=344XYOg7bxDQMWJ6Lle8U7NOHpabnCp0XYbZpeWpPAk,681
17
26
  ai_lls_lib/providers/external.py,sha256=-Hhnlm8lQWRcWr5vG0dmD3sca2rohURrx0usOR2y1MM,2623
18
27
  ai_lls_lib/providers/stub.py,sha256=847Tmw522B3HQ2j38BH1sdcZQy--RdtDcXsrIrFKNBQ,1150
19
28
  ai_lls_lib/testing/__init__.py,sha256=RUxRYBzzPCPS15Umb6bUrE6rL5BQXBQf4SJM2E3ffrg,39
20
29
  ai_lls_lib/testing/fixtures.py,sha256=_n6bbr95LnQf9Dvu1qKs2HsvHEA7AAbe59B75qxE10w,3310
21
- ai_lls_lib-1.1.0.dist-info/METADATA,sha256=x9A545GW7f-YQ-IMmkKeV3Lu1JMxDX6PB4VB4_Nw_Eo,7207
22
- ai_lls_lib-1.1.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
23
- ai_lls_lib-1.1.0.dist-info/entry_points.txt,sha256=Pi0V_HBViEKGFbNQKatl5lhhnHHBXlxaom-5gH9gXZ0,55
24
- ai_lls_lib-1.1.0.dist-info/RECORD,,
30
+ ai_lls_lib-1.2.0.dist-info/METADATA,sha256=o76nenDApQ5pITFYnlmX7IS-maEi39JgU75iUyI9udk,7248
31
+ ai_lls_lib-1.2.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
+ ai_lls_lib-1.2.0.dist-info/entry_points.txt,sha256=Pi0V_HBViEKGFbNQKatl5lhhnHHBXlxaom-5gH9gXZ0,55
33
+ ai_lls_lib-1.2.0.dist-info/RECORD,,