Phase 3 backend: tier-gate enforcement + billing webhook
- Tier capability model (pro-layers, geo-analytics, custom-domain, retention) - Pro-only layer types (video/particle/animatedGradient) rejected on card create/replace/patch for Free; geo analytics gated to Pro+ (402 tier_limit) - Gates read the authoritative DB tier, so upgrades apply without re-login - POST /v1/webhooks/revenuecat: shared-secret auth (constant-time), maps RevenueCat events to users.tier (purchase→pro/team/enterprise, cancel→free), unknown user = 2xx no-op; users::update_tier query 93 backend tests; fmt + clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9b7c845319
commit
4ad3220917
@@ -52,6 +52,8 @@ pub struct Config {
|
||||
pub profile_base_url: String,
|
||||
/// Per-day rotating salt seed used for hashing visitor IPs (PRD §18.3).
|
||||
pub ip_hash_secret: String,
|
||||
/// Shared secret RevenueCat sends in the webhook `Authorization` header.
|
||||
pub billing_webhook_secret: String,
|
||||
pub bind_addr: String,
|
||||
pub r2: R2Config,
|
||||
pub wallet: WalletConfig,
|
||||
@@ -99,6 +101,7 @@ impl Config {
|
||||
jwt_secret: require(src, "JWT_SECRET").await?,
|
||||
profile_base_url: optional(src, "PROFILE_BASE_URL", "https://cardclaws.com").await,
|
||||
ip_hash_secret: require(src, "IP_HASH_SECRET").await?,
|
||||
billing_webhook_secret: optional(src, "BILLING_WEBHOOK_SECRET", "").await,
|
||||
bind_addr: optional(src, "BIND_ADDR", "0.0.0.0:8080").await,
|
||||
r2: R2Config {
|
||||
endpoint: optional(
|
||||
|
||||
Reference in New Issue
Block a user