"""Stripe invoice + Checkout pay-link helpers. Requires FEATURE_EMAIL_SMS to send.""" from __future__ import annotations import logging from datetime import date from django.conf import settings from django.urls import reverse from django.utils import timezone from payments.models import Invoice logger = logging.getLogger(__name__) class PaymentsError(RuntimeError): pass def _stripe(): secret = (settings.STRIPE_SECRET_KEY or "").strip() if not secret: raise PaymentsError("STRIPE_SECRET_KEY is not configured") try: import stripe except ImportError as exc: raise PaymentsError("stripe package is not installed") from exc stripe.api_key = secret return stripe def next_invoice_number() -> str: today = date.today().strftime("%Y%m%d") prefix = f"INV-{today}-" existing = Invoice.objects.filter(number__startswith=prefix).count() return f"{prefix}{existing + 1:03d}" def create_checkout_session(invoice: Invoice, *, success_url: str, cancel_url: str) -> str: stripe = _stripe() session = stripe.checkout.Session.create( mode="payment", customer_email=invoice.contact.email or None, line_items=[ { "quantity": 1, "price_data": { "currency": (invoice.currency or "usd").lower(), "unit_amount": invoice.amount_cents, "product_data": { "name": invoice.description or f"Invoice {invoice.number}", }, }, } ], metadata={"invoice_id": str(invoice.pk), "invoice_number": invoice.number}, success_url=success_url, cancel_url=cancel_url, ) invoice.stripe_checkout_session_id = session.id invoice.hosted_invoice_url = session.url or "" invoice.status = Invoice.Status.OPEN invoice.save( update_fields=[ "stripe_checkout_session_id", "hosted_invoice_url", "status", "updated_at", ] ) return session.url or "" def mark_paid(invoice: Invoice, *, stripe_id: str = "") -> None: invoice.status = Invoice.Status.PAID invoice.paid_at = timezone.now() if stripe_id and not invoice.stripe_invoice_id: invoice.stripe_invoice_id = stripe_id invoice.save( update_fields=["status", "paid_at", "stripe_invoice_id", "updated_at"] ) def send_invoice_email(invoice: Invoice, pay_url: str) -> bool: """Send pay-link email through Django mail (SMTP2GO).""" to_email = (invoice.contact.email or "").strip() if not to_email: raise PaymentsError("Contact has no email address") from django.core.mail import EmailMultiAlternatives name = invoice.contact.full_name or "there" amount = f"{invoice.amount} {invoice.currency.upper()}" subject = f"Invoice {invoice.number} from {settings.SITE_NAME}" text = ( f"Hi {name},\n\n" f"Invoice {invoice.number} for {amount} is ready.\n" f"{invoice.description}\n\n" f"Pay securely: {pay_url}\n" ) html = ( f"

Hi {name},

" f"

Invoice {invoice.number} for " f"{amount} is ready.

" f"

{invoice.description}

" f'

Pay this invoice

' ) mail = EmailMultiAlternatives( subject=subject, body=text, from_email=settings.DEFAULT_FROM_EMAIL, to=[to_email], ) mail.attach_alternative(html, "text/html") mail.send(fail_silently=False) return True