Files
aza/AzA march 2026 - Kopie (15)/wc_routes.py
2026-04-19 20:41:37 +02:00

169 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wc_routes.py WooCommerce → Hetzner License Bridge
#
# Provides POST /wc/provision for WordPress to call after a successful
# WooCommerce subscription purchase. Creates/updates a license entry and
# sends the license key email to the customer.
#
# Mounted in backend_main.py with prefix="/wc".
# Protected by a shared secret (WC_PROVISION_SECRET env var).
from __future__ import annotations
import os
import time
import sqlite3
from typing import Any, Dict, Optional
from fastapi import APIRouter, HTTPException, Header
from pydantic import BaseModel, EmailStr
router = APIRouter()
DB_PATH: Optional[str] = None
def _get_db_path() -> str:
global DB_PATH
if DB_PATH:
return DB_PATH
try:
from stripe_routes import DB_PATH as _sr_db
DB_PATH = _sr_db
except ImportError:
DB_PATH = os.path.join(os.path.dirname(__file__), "data", "stripe_webhook.sqlite")
return DB_PATH
def _require_wc_secret(x_wc_secret: Optional[str] = Header(default=None, alias="X-WC-Secret")) -> str:
expected = os.environ.get("WC_PROVISION_SECRET", "").strip()
if not expected:
raise HTTPException(status_code=500, detail="WC_PROVISION_SECRET not configured on server")
if not x_wc_secret or x_wc_secret.strip() != expected:
raise HTTPException(status_code=401, detail="Unauthorized invalid X-WC-Secret")
return x_wc_secret
class ProvisionRequest(BaseModel):
customer_email: str
wc_order_id: int
wc_subscription_id: int
lookup_key: str = "aza_basic_monthly"
allowed_users: int = 1
devices_per_user: int = 2
@router.post("/provision")
def wc_provision(
payload: ProvisionRequest,
x_wc_secret: Optional[str] = Header(default=None, alias="X-WC-Secret"),
) -> Dict[str, Any]:
"""Called by WordPress after a successful WooCommerce subscription purchase.
Idempotent: if a license for this wc_subscription_id already exists,
returns the existing key without creating a duplicate.
"""
_require_wc_secret(x_wc_secret)
from stripe_routes import (
_ensure_storage,
_generate_license_key,
send_license_email,
_log_event,
)
_ensure_storage()
db = _get_db_path()
sub_id = f"wc_sub_{payload.wc_subscription_id}"
customer_id = f"wc_order_{payload.wc_order_id}"
email = payload.customer_email.strip().lower()
if not email:
raise HTTPException(status_code=400, detail="customer_email is required")
now = int(time.time())
with sqlite3.connect(db) as con:
row = con.execute(
"SELECT license_key, status FROM licenses WHERE subscription_id = ?",
(sub_id,),
).fetchone()
if row and row[0]:
existing_key = row[0]
existing_status = row[1] or ""
_log_event("wc_provision_idempotent", {
"wc_subscription_id": payload.wc_subscription_id,
"wc_order_id": payload.wc_order_id,
"email": email,
"license_key": existing_key,
})
return {
"status": "already_provisioned",
"license_key": existing_key,
"license_status": existing_status,
"customer_email": email,
}
new_key = _generate_license_key()
con.execute(
"""
INSERT INTO licenses(
subscription_id, customer_id, status, lookup_key,
allowed_users, devices_per_user, customer_email, client_reference_id,
current_period_end, updated_at, license_key
)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(subscription_id) DO UPDATE SET
customer_id=excluded.customer_id,
status=excluded.status,
lookup_key=COALESCE(NULLIF(excluded.lookup_key, ''), lookup_key),
allowed_users=COALESCE(excluded.allowed_users, allowed_users),
devices_per_user=COALESCE(excluded.devices_per_user, devices_per_user),
customer_email=COALESCE(excluded.customer_email, customer_email),
current_period_end=COALESCE(excluded.current_period_end, current_period_end),
updated_at=excluded.updated_at,
license_key=COALESCE(license_key, excluded.license_key)
""",
(
sub_id,
customer_id,
"active",
payload.lookup_key,
payload.allowed_users,
payload.devices_per_user,
email,
f"wc_order_{payload.wc_order_id}",
None,
now,
new_key,
),
)
con.commit()
_log_event("wc_provision_created", {
"wc_subscription_id": payload.wc_subscription_id,
"wc_order_id": payload.wc_order_id,
"email": email,
"license_key": new_key,
"lookup_key": payload.lookup_key,
})
mail_sent = False
try:
mail_sent = send_license_email(email, new_key)
except Exception as exc:
_log_event("wc_provision_mail_failed", {
"email": email,
"error": str(exc),
})
return {
"status": "provisioned",
"license_key": new_key,
"license_status": "active",
"customer_email": email,
"mail_sent": mail_sent,
}