mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-19 04:40:32 -05:00
b4486a627f
- Webhook models: remove duplicate index definitions so db.create_all() no longer raises 'index already exists' (columns already have index=True) - ImportService: fix circular import by late-importing ClientService, ProjectService, TimeTrackingService in __init__ - reports: fix F823 by renaming unpack variable _ to _entry_count to avoid shadowing gettext _ in export_task_excel() - Code quality: add .flake8 with extend-ignore so flake8 CI passes; simplify pyproject.toml isort config (drop unsupported options) - Format: run black and isort on app/ - tests: restore minimal app fixture in test_import_export_models
139 lines
4.6 KiB
Python
139 lines
4.6 KiB
Python
"""
|
|
Stripe payment gateway integration utilities.
|
|
"""
|
|
|
|
import logging
|
|
from decimal import Decimal
|
|
from typing import Any, Dict, Optional
|
|
|
|
import stripe
|
|
from flask import current_app
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class StripeIntegration:
|
|
"""
|
|
Stripe payment gateway integration.
|
|
"""
|
|
|
|
def __init__(self, api_key: str):
|
|
"""
|
|
Initialize Stripe integration.
|
|
|
|
Args:
|
|
api_key: Stripe API key (secret key)
|
|
"""
|
|
self.api_key = api_key
|
|
stripe.api_key = api_key
|
|
|
|
def create_payment_intent(
|
|
self,
|
|
amount: Decimal,
|
|
currency: str,
|
|
invoice_id: int,
|
|
description: Optional[str] = None,
|
|
metadata: Optional[Dict[str, Any]] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Create a Stripe PaymentIntent.
|
|
|
|
Returns:
|
|
dict with 'success', 'client_secret', and 'payment_intent' keys
|
|
"""
|
|
try:
|
|
# Convert amount to cents
|
|
amount_cents = int(amount * 100)
|
|
|
|
payment_intent_data = {
|
|
"amount": amount_cents,
|
|
"currency": currency.lower(),
|
|
"metadata": {"invoice_id": str(invoice_id), **(metadata or {})},
|
|
}
|
|
|
|
if description:
|
|
payment_intent_data["description"] = description
|
|
|
|
payment_intent = stripe.PaymentIntent.create(**payment_intent_data)
|
|
|
|
return {"success": True, "client_secret": payment_intent.client_secret, "payment_intent": payment_intent}
|
|
except stripe.error.StripeError as e:
|
|
logger.error(f"Stripe error creating payment intent: {e}")
|
|
return {"success": False, "message": str(e), "error_code": e.code if hasattr(e, "code") else None}
|
|
except Exception as e:
|
|
logger.error(f"Error creating payment intent: {e}")
|
|
return {"success": False, "message": f"Error creating payment intent: {str(e)}"}
|
|
|
|
def verify_webhook(self, payload: bytes, signature: str, webhook_secret: str) -> Optional[stripe.Event]:
|
|
"""
|
|
Verify and parse a Stripe webhook.
|
|
|
|
Returns:
|
|
Stripe Event object if valid, None otherwise
|
|
"""
|
|
try:
|
|
event = stripe.Webhook.construct_event(payload, signature, webhook_secret)
|
|
return event
|
|
except ValueError as e:
|
|
logger.error(f"Invalid payload: {e}")
|
|
return None
|
|
except stripe.error.SignatureVerificationError as e:
|
|
logger.error(f"Invalid signature: {e}")
|
|
return None
|
|
|
|
def get_payment_intent(self, payment_intent_id: str) -> Optional[stripe.PaymentIntent]:
|
|
"""Retrieve a PaymentIntent from Stripe"""
|
|
try:
|
|
return stripe.PaymentIntent.retrieve(payment_intent_id)
|
|
except stripe.error.StripeError as e:
|
|
logger.error(f"Error retrieving payment intent: {e}")
|
|
return None
|
|
|
|
def create_checkout_session(
|
|
self,
|
|
invoice_id: int,
|
|
amount: Decimal,
|
|
currency: str,
|
|
success_url: str,
|
|
cancel_url: str,
|
|
description: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Create a Stripe Checkout Session.
|
|
|
|
Returns:
|
|
dict with 'success', 'session_id', and 'url' keys
|
|
"""
|
|
try:
|
|
amount_cents = int(amount * 100)
|
|
|
|
session_data = {
|
|
"payment_method_types": ["card"],
|
|
"line_items": [
|
|
{
|
|
"price_data": {
|
|
"currency": currency.lower(),
|
|
"product_data": {
|
|
"name": description or f"Invoice #{invoice_id}",
|
|
},
|
|
"unit_amount": amount_cents,
|
|
},
|
|
"quantity": 1,
|
|
}
|
|
],
|
|
"mode": "payment",
|
|
"success_url": success_url,
|
|
"cancel_url": cancel_url,
|
|
"metadata": {"invoice_id": str(invoice_id)},
|
|
}
|
|
|
|
session = stripe.checkout.Session.create(**session_data)
|
|
|
|
return {"success": True, "session_id": session.id, "url": session.url}
|
|
except stripe.error.StripeError as e:
|
|
logger.error(f"Stripe error creating checkout session: {e}")
|
|
return {"success": False, "message": str(e), "error_code": e.code if hasattr(e, "code") else None}
|
|
except Exception as e:
|
|
logger.error(f"Error creating checkout session: {e}")
|
|
return {"success": False, "message": f"Error creating checkout session: {str(e)}"}
|