update
This commit is contained in:
@@ -120,6 +120,12 @@ def _ensure_storage() -> None:
|
||||
con.execute("ALTER TABLE licenses ADD COLUMN practice_id TEXT")
|
||||
if "current_period_start" not in cols:
|
||||
con.execute("ALTER TABLE licenses ADD COLUMN current_period_start INTEGER")
|
||||
try:
|
||||
from aza_ai_credit import ensure_ai_credit_schema
|
||||
|
||||
ensure_ai_credit_schema(con)
|
||||
except Exception:
|
||||
pass
|
||||
con.commit()
|
||||
|
||||
|
||||
@@ -898,113 +904,167 @@ async def stripe_webhook(
|
||||
|
||||
try:
|
||||
if etype == "checkout.session.completed":
|
||||
subscription_id = obj.get("subscription")
|
||||
customer_id = obj.get("customer")
|
||||
client_reference_id = obj.get("client_reference_id")
|
||||
checkout_md = obj.get("metadata") or {}
|
||||
checkout_purpose = (checkout_md.get("aza_purpose") or "").strip()
|
||||
if checkout_purpose in ("ai_topup", "ai_topup_setup"):
|
||||
from aza_ai_credit import (
|
||||
ensure_ai_credit_schema,
|
||||
process_setup_checkout_completed,
|
||||
process_topup_checkout_completed,
|
||||
)
|
||||
|
||||
_log_event("checkout_entered", {
|
||||
"event_id": event_id,
|
||||
"subscription_id": subscription_id,
|
||||
"customer_id": customer_id,
|
||||
})
|
||||
|
||||
if not subscription_id or not customer_id:
|
||||
_log_event("license_skip", {
|
||||
with sqlite3.connect(DB_PATH) as con:
|
||||
ensure_ai_credit_schema(con)
|
||||
if checkout_purpose == "ai_topup":
|
||||
ai_result = process_topup_checkout_completed(con, session=obj)
|
||||
else:
|
||||
ai_result = process_setup_checkout_completed(con, session=obj)
|
||||
_log_event("ai_credit_checkout", {
|
||||
"event_id": event_id,
|
||||
"purpose": checkout_purpose,
|
||||
"result": ai_result,
|
||||
})
|
||||
else:
|
||||
subscription_id = obj.get("subscription")
|
||||
customer_id = obj.get("customer")
|
||||
client_reference_id = obj.get("client_reference_id")
|
||||
|
||||
_log_event("checkout_entered", {
|
||||
"event_id": event_id,
|
||||
"reason": "missing_subscription_or_customer",
|
||||
"subscription_id": subscription_id,
|
||||
"customer_id": customer_id,
|
||||
})
|
||||
else:
|
||||
# customer_email: cascade through all known locations.
|
||||
customer_email = (
|
||||
obj.get("customer_email")
|
||||
or (obj.get("customer_details") or {}).get("email")
|
||||
)
|
||||
if not customer_email and customer_id:
|
||||
try:
|
||||
cust = _stripe_to_dict(stripe.Customer.retrieve(customer_id))
|
||||
customer_email = cust.get("email")
|
||||
except Exception:
|
||||
pass
|
||||
customer_email = (customer_email or "").strip() or None
|
||||
|
||||
sub = _stripe_to_dict(stripe.Subscription.retrieve(subscription_id, expand=["items.data.price"]))
|
||||
status = sub.get("status", "") or ""
|
||||
current_period_end = sub.get("current_period_end")
|
||||
if not current_period_end:
|
||||
try:
|
||||
current_period_end = sub["items"]["data"][0]["current_period_end"]
|
||||
except Exception:
|
||||
current_period_end = None
|
||||
current_period_start = sub.get("current_period_start")
|
||||
if not current_period_start:
|
||||
try:
|
||||
current_period_start = sub["items"]["data"][0]["current_period_start"]
|
||||
except Exception:
|
||||
current_period_start = None
|
||||
md = sub.get("metadata") or {}
|
||||
lookup_key = (md.get("lookup_key") or "").strip()
|
||||
allowed_users = md.get("allowed_users")
|
||||
devices_per_user = md.get("devices_per_user")
|
||||
|
||||
if not lookup_key:
|
||||
try:
|
||||
price = sub["items"]["data"][0]["price"]
|
||||
lookup_key = _lookup_key_from_price(price)
|
||||
except Exception:
|
||||
lookup_key = ""
|
||||
|
||||
def _to_int(x: Any) -> Optional[int]:
|
||||
try:
|
||||
return int(x)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if lookup_key and not allowed_users:
|
||||
policy = _policy_for_lookup_key(lookup_key)
|
||||
allowed_users = allowed_users or str(policy.allowed_users)
|
||||
devices_per_user = devices_per_user or str(policy.devices_per_user)
|
||||
|
||||
if not lookup_key:
|
||||
if not subscription_id or not customer_id:
|
||||
_log_event("license_skip", {
|
||||
"event_id": event_id,
|
||||
"reason": "missing_lookup_key",
|
||||
"reason": "missing_subscription_or_customer",
|
||||
"subscription_id": subscription_id,
|
||||
"customer_id": customer_id,
|
||||
})
|
||||
else:
|
||||
# customer_email: cascade through all known locations.
|
||||
customer_email = (
|
||||
obj.get("customer_email")
|
||||
or (obj.get("customer_details") or {}).get("email")
|
||||
)
|
||||
if not customer_email and customer_id:
|
||||
try:
|
||||
cust = _stripe_to_dict(stripe.Customer.retrieve(customer_id))
|
||||
customer_email = cust.get("email")
|
||||
except Exception:
|
||||
pass
|
||||
customer_email = (customer_email or "").strip() or None
|
||||
|
||||
sub = _stripe_to_dict(stripe.Subscription.retrieve(subscription_id, expand=["items.data.price"]))
|
||||
status = sub.get("status", "") or ""
|
||||
current_period_end = sub.get("current_period_end")
|
||||
if not current_period_end:
|
||||
try:
|
||||
current_period_end = sub["items"]["data"][0]["current_period_end"]
|
||||
except Exception:
|
||||
current_period_end = None
|
||||
current_period_start = sub.get("current_period_start")
|
||||
if not current_period_start:
|
||||
try:
|
||||
current_period_start = sub["items"]["data"][0]["current_period_start"]
|
||||
except Exception:
|
||||
current_period_start = None
|
||||
md = sub.get("metadata") or {}
|
||||
lookup_key = (md.get("lookup_key") or "").strip()
|
||||
allowed_users = md.get("allowed_users")
|
||||
devices_per_user = md.get("devices_per_user")
|
||||
|
||||
if not lookup_key:
|
||||
try:
|
||||
price = sub["items"]["data"][0]["price"]
|
||||
lookup_key = _lookup_key_from_price(price)
|
||||
except Exception:
|
||||
lookup_key = ""
|
||||
|
||||
def _to_int(x: Any) -> Optional[int]:
|
||||
try:
|
||||
return int(x)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if lookup_key and not allowed_users:
|
||||
policy = _policy_for_lookup_key(lookup_key)
|
||||
allowed_users = allowed_users or str(policy.allowed_users)
|
||||
devices_per_user = devices_per_user or str(policy.devices_per_user)
|
||||
|
||||
if not lookup_key:
|
||||
_log_event("license_skip", {
|
||||
"event_id": event_id,
|
||||
"reason": "missing_lookup_key",
|
||||
"subscription_id": subscription_id,
|
||||
})
|
||||
|
||||
generated_key = _upsert_license(
|
||||
subscription_id=subscription_id,
|
||||
customer_id=customer_id,
|
||||
status=status,
|
||||
lookup_key=lookup_key,
|
||||
allowed_users=_to_int(allowed_users),
|
||||
devices_per_user=_to_int(devices_per_user),
|
||||
customer_email=customer_email,
|
||||
client_reference_id=(client_reference_id or "").strip() or None,
|
||||
current_period_end=_to_int(current_period_end),
|
||||
current_period_start=_to_int(current_period_start),
|
||||
)
|
||||
_log_event("license_upsert", {
|
||||
"event_id": event_id,
|
||||
"etype": etype,
|
||||
"subscription_id": subscription_id,
|
||||
"status": status,
|
||||
"lookup_key": lookup_key,
|
||||
"email": customer_email,
|
||||
"client_reference_id": client_reference_id,
|
||||
"license_key": generated_key,
|
||||
})
|
||||
|
||||
generated_key = _upsert_license(
|
||||
subscription_id=subscription_id,
|
||||
customer_id=customer_id,
|
||||
status=status,
|
||||
lookup_key=lookup_key,
|
||||
allowed_users=_to_int(allowed_users),
|
||||
devices_per_user=_to_int(devices_per_user),
|
||||
customer_email=customer_email,
|
||||
client_reference_id=(client_reference_id or "").strip() or None,
|
||||
current_period_end=_to_int(current_period_end),
|
||||
current_period_start=_to_int(current_period_start),
|
||||
)
|
||||
_log_event("license_upsert", {
|
||||
"event_id": event_id,
|
||||
"etype": etype,
|
||||
"subscription_id": subscription_id,
|
||||
"status": status,
|
||||
"lookup_key": lookup_key,
|
||||
"email": customer_email,
|
||||
"client_reference_id": client_reference_id,
|
||||
"license_key": generated_key,
|
||||
})
|
||||
if customer_email and generated_key:
|
||||
try:
|
||||
send_license_email(customer_email, generated_key)
|
||||
except Exception as mail_exc:
|
||||
_log_event("license_email_failed", {
|
||||
"event_id": event_id,
|
||||
"email": customer_email,
|
||||
"error": str(mail_exc),
|
||||
})
|
||||
|
||||
if customer_email and generated_key:
|
||||
try:
|
||||
send_license_email(customer_email, generated_key)
|
||||
except Exception as mail_exc:
|
||||
_log_event("license_email_failed", {
|
||||
"event_id": event_id,
|
||||
"email": customer_email,
|
||||
"error": str(mail_exc),
|
||||
})
|
||||
elif etype == "setup_intent.succeeded":
|
||||
from aza_ai_credit import ensure_ai_credit_schema, process_setup_intent_succeeded
|
||||
|
||||
with sqlite3.connect(DB_PATH) as con:
|
||||
ensure_ai_credit_schema(con)
|
||||
ai_result = process_setup_intent_succeeded(con, setup_intent=obj)
|
||||
_log_event("ai_credit_setup_intent", {
|
||||
"event_id": event_id,
|
||||
"result": ai_result,
|
||||
})
|
||||
|
||||
elif etype == "payment_intent.succeeded":
|
||||
pi_md = obj.get("metadata") or {}
|
||||
pi_purpose = (pi_md.get("aza_purpose") or "").strip()
|
||||
if pi_purpose in ("ai_topup", "ai_auto_topup"):
|
||||
from aza_ai_credit import (
|
||||
ensure_ai_credit_schema,
|
||||
process_auto_topup_payment_intent_succeeded,
|
||||
process_topup_payment_intent_succeeded,
|
||||
)
|
||||
|
||||
with sqlite3.connect(DB_PATH) as con:
|
||||
ensure_ai_credit_schema(con)
|
||||
if pi_purpose == "ai_topup":
|
||||
ai_result = process_topup_payment_intent_succeeded(con, payment_intent=obj)
|
||||
else:
|
||||
ai_result = process_auto_topup_payment_intent_succeeded(con, payment_intent=obj)
|
||||
_log_event("ai_credit_payment_intent", {
|
||||
"event_id": event_id,
|
||||
"purpose": pi_purpose,
|
||||
"result": ai_result,
|
||||
})
|
||||
|
||||
elif etype in ("customer.subscription.updated", "customer.subscription.deleted"):
|
||||
subscription_id = obj.get("id")
|
||||
|
||||
Reference in New Issue
Block a user