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
@@ -4,7 +4,7 @@
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
|
||||
use crate::handlers::{analytics, assets, auth, cards, health, profile, share, wallet};
|
||||
use crate::handlers::{analytics, assets, auth, cards, health, profile, share, wallet, webhooks};
|
||||
use crate::middleware::cors;
|
||||
use crate::state::AppState;
|
||||
|
||||
@@ -41,6 +41,7 @@ pub fn build_router(state: AppState) -> Router {
|
||||
.route("/cards/:id/share-links", get(share::list_shares))
|
||||
.route("/s/:token", get(share::resolve_share))
|
||||
.route("/profile/:handle/contact", post(profile::submit_contact))
|
||||
.route("/webhooks/revenuecat", post(webhooks::revenuecat))
|
||||
.route("/analytics/event", post(analytics::ingest_event))
|
||||
.route("/assets/upload", post(assets::presign_upload))
|
||||
.route("/assets/*key", axum::routing::delete(assets::delete_asset));
|
||||
|
||||
Reference in New Issue
Block a user