Skip to main content

max / makenotwork

3.8 KB · 114 lines History Blame Raw
1 //! Stripe Connect, Checkout, and webhook routes.
2 //!
3 //! See also: `/docs/guide/03-selling`
4
5 mod checkout;
6 mod connect;
7 pub(crate) mod webhook;
8 mod webhook_v2;
9
10 pub(crate) use checkout::grant_bundle_items;
11 pub(crate) use webhook::process_webhook_event;
12 pub(crate) use webhook_v2::process_v2_thin_event;
13
14 use axum::routing::get;
15
16 use crate::{
17 AppState,
18 csrf::{CsrfRouter, post_csrf, post_csrf_manual, post_csrf_skip},
19 };
20
21 /// Reason string for routes that only construct a Stripe Checkout Session URL.
22 /// State mutation happens server-side via the Stripe webhook, not in these
23 /// handlers. The `tip` route is the documented exception, it inserts a
24 /// `pending_tip` row BEFORE the Stripe call, so it uses Manual posture.
25 const STRIPE_SESSION_SKIP: &str = "Stripe Checkout Session constructor, no mutation until webhook";
26
27 /// Register Stripe Connect, Checkout, and webhook routes.
28 pub fn stripe_routes() -> CsrfRouter<AppState> {
29 CsrfRouter::new()
30 // Creator onboarding (Account Links flow)
31 .route_get("/stripe/connect", get(connect::stripe_connect_disclaimer))
32 .route(
33 "/stripe/connect/proceed",
34 post_csrf(connect::stripe_connect_proceed),
35 )
36 .route_get(
37 "/stripe/connect/return",
38 get(connect::stripe_connect_return),
39 )
40 .route_get(
41 "/stripe/connect/refresh",
42 get(connect::stripe_connect_refresh),
43 )
44 // Checkout flow (idempotency handled by global middleware in metrics.rs)
45 .route(
46 "/stripe/fan-plus",
47 post_csrf(checkout::create_fan_plus_checkout),
48 )
49 .route(
50 "/stripe/fan-plus/cancel",
51 post_csrf(checkout::cancel_fan_plus),
52 )
53 .route(
54 "/stripe/fan-plus/resume",
55 post_csrf(checkout::resume_fan_plus),
56 )
57 .route(
58 "/stripe/billing-portal",
59 post_csrf(checkout::open_billing_portal),
60 )
61 .route(
62 "/stripe/creator-tier",
63 post_csrf(checkout::create_creator_tier_checkout),
64 )
65 .route(
66 "/stripe/checkout/{item_id}",
67 post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_checkout),
68 )
69 .route(
70 "/stripe/checkout/{item_id}/cancel-pending",
71 post_csrf(checkout::cancel_pending_item_checkout),
72 )
73 .route(
74 "/stripe/checkout/project/{project_id}",
75 post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_project_checkout),
76 )
77 .route(
78 "/stripe/subscribe/{tier_id}",
79 post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_subscription_checkout),
80 )
81 .route(
82 "/stripe/checkout/tip/{recipient_id}",
83 post_csrf_manual(
84 "inserts pending_tip row before Stripe call, handler validates _csrf",
85 checkout::create_tip_checkout,
86 ),
87 )
88 .route(
89 "/stripe/checkout/cart",
90 post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_cart_checkout),
91 )
92 .route(
93 "/stripe/checkout/cart/all",
94 post_csrf_skip(STRIPE_SESSION_SKIP, checkout::create_cart_checkout_all),
95 )
96 .route_get("/stripe/success", get(checkout::checkout_success))
97 .route_get("/stripe/cancel", get(checkout::checkout_cancel))
98 // Webhooks
99 .route(
100 "/stripe/webhook",
101 post_csrf_skip(
102 "webhook: stripe signature verified in handler",
103 webhook::webhook,
104 ),
105 )
106 .route(
107 "/stripe/webhook/v2",
108 post_csrf_skip(
109 "webhook: stripe signature verified in handler",
110 webhook_v2::webhook_v2,
111 ),
112 )
113 }
114