//! Stripe Connect, Checkout, and webhook routes. //! //! See also: `/docs/guide/03-selling` mod checkout; mod connect; mod webhook; mod webhook_v2; pub(crate) use checkout::grant_bundle_items; pub(crate) use webhook::process_webhook_event; use axum::routing::get; use crate::{ csrf::{post_csrf, post_csrf_manual, post_csrf_skip, CsrfRouter}, AppState, }; /// Reason string for routes that only construct a Stripe Checkout Session URL. /// State mutation happens server-side via the Stripe webhook, not in these /// handlers. The `tip` route is the documented exception — it inserts a /// `pending_tip` row BEFORE the Stripe call, so it uses Manual posture. const STRIPE_SESSION_SKIP: &str = "Stripe Checkout Session constructor — no mutation until webhook"; /// Register Stripe Connect, Checkout, and webhook routes. pub fn stripe_routes() -> CsrfRouter { CsrfRouter::new() // Creator onboarding (Account Links flow) .route_get("/stripe/connect", get(connect::stripe_connect_disclaimer)) .route("/stripe/connect/proceed", post_csrf(connect::stripe_connect_proceed)) .route_get("/stripe/connect/return", get(connect::stripe_connect_return)) .route_get("/stripe/connect/refresh", get(connect::stripe_connect_refresh)) // Checkout flow (idempotency handled by global middleware in metrics.rs) .route("/stripe/fan-plus", post_csrf(checkout::create_fan_plus_checkout)) .route("/stripe/fan-plus/cancel", post_csrf(checkout::cancel_fan_plus)) .route("/stripe/fan-plus/resume", post_csrf(checkout::resume_fan_plus)) .route("/stripe/billing-portal", post_csrf(checkout::open_billing_portal)) .route("/stripe/creator-tier", post_csrf(checkout::create_creator_tier_checkout)) .route("/stripe/checkout/{item_id}", post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_checkout)) .route("/stripe/checkout/{item_id}/cancel-pending", post_csrf(checkout::cancel_pending_item_checkout)) .route("/stripe/checkout/project/{project_id}", post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_project_checkout)) .route("/stripe/subscribe/{tier_id}", post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_subscription_checkout)) .route("/stripe/checkout/tip/{recipient_id}", post_csrf_manual( "inserts pending_tip row before Stripe call — handler validates _csrf", checkout::create_tip_checkout, )) .route("/stripe/checkout/cart", post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_cart_checkout)) .route("/stripe/checkout/cart/all", post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_cart_checkout_all)) .route_get("/stripe/success", get(checkout::checkout_success)) .route_get("/stripe/cancel", get(checkout::checkout_cancel)) // Webhooks .route("/stripe/webhook", post_csrf_skip("webhook: stripe signature verified in handler", webhook::webhook)) .route("/stripe/webhook/v2", post_csrf_skip("webhook: stripe signature verified in handler", webhook_v2::webhook_v2)) }