update
This commit is contained in:
168
AzA march 2026 - Kopie (18)/wc_routes.py
Normal file
168
AzA march 2026 - Kopie (18)/wc_routes.py
Normal file
@@ -0,0 +1,168 @@
|
||||
# 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,
|
||||
}
|
||||
Reference in New Issue
Block a user