"""Stripe billing: signature verification, automatic onboarding (tenant + token + credentials email), idempotency, and subscription lifecycle.""" from __future__ import annotations import hashlib import hmac import time from sqlalchemy import select from app import billing from app.models import Tenant def _sign(payload: bytes, secret: str, ts: int | None = None) -> str: t = ts if ts is not None else int(time.time()) sig = hmac.new(secret.encode(), f"{t}.".encode() + payload, hashlib.sha256).hexdigest() return f"t={t},v1={sig}" def test_signature_valid_invalid_and_stale(): body = b'{"x":1}' good = _sign(body, "whsec_abc") assert billing.verify_stripe_signature(body, good, "whsec_abc") is True # wrong secret assert billing.verify_stripe_signature(body, good, "whsec_OTHER") is False # tampered body assert billing.verify_stripe_signature(b'{"x":2}', good, "whsec_abc") is False # stale timestamp (replay) old = _sign(body, "whsec_abc", ts=int(time.time()) - 4000) assert billing.verify_stripe_signature(body, old, "whsec_abc") is False # garbage header assert billing.verify_stripe_signature(body, "nonsense", "whsec_abc") is False def _checkout_event(email="dueno@nuevocliente.com", business="Paraguas Luna", customer="cus_123", sub="sub_456"): return { "type": "checkout.session.completed", "data": {"object": { "customer_details": {"email": email}, "customer": customer, "subscription": sub, "custom_fields": [ {"key": "negocio", "type": "text", "text": {"value": business}} ], "mode": "subscription", "payment_status": "paid", }}, } async def test_checkout_completed_provisions_tenant_and_emails(db_session, monkeypatch): sent = [] async def fake_send(settings, to, subject, body, **kw): sent.append({"to": to, "subject": subject, "body": body, "html": kw.get("html")}) return True import app.mailer monkeypatch.setattr(app.mailer, "send_email", fake_send) out = await billing.handle_checkout_completed( db_session, _checkout_event(), origin="https://bot.example" ) assert out["status"] == "provisioned" and out["email_sent"] is True t = ( await db_session.execute(select(Tenant).where(Tenant.slug == out["slug"])) ).scalar_one() # account fully usable: branding, notify email, portal token, stripe link assert t.brand_name == "Paraguas Luna" assert t.support_email == "dueno@nuevocliente.com" assert t.dashboard_token.startswith("cli_") assert t.stripe_customer_id == "cus_123" and t.stripe_subscription_id == "sub_456" by_to = {m["to"]: m for m in sent} # the CUSTOMER email contains everything they need to self-configure cust = by_to["dueno@nuevocliente.com"] assert t.dashboard_token in cust["body"] assert "/portal" in cust["body"] and "widget.js?t=" + t.slug in cust["body"] html = cust.get("html") or "" assert "<script" in html and t.dashboard_token in html # the FOUNDER got a 'new paying customer' alert assert "comercial@flexigobe.com" in by_to assert "