Compare commits

...

1 Commits

Author SHA1 Message Date
will.anderson 69f348d48b Fix free plan checkout: use SetupIntent instead of $0 PaymentIntent
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m40s
Stripe rejects amount=0 PaymentIntents. Free plan age verification should
use a SetupIntent (no charge, saves payment method). The JS already handles
setup_mode:true by calling stripe.confirmSetup instead of confirmPayment.
Mirrors the existing professional-later SetupIntent path.
2026-05-13 12:12:10 -05:00
+14 -11
View File
@@ -699,21 +699,24 @@ fn handle_request_inner(method: String, path: String, headers: Map, body: String
} }
} }
// Free tier: $0 PaymentIntent for age verification (18+ requirement). // Free tier: SetupIntent for age verification (18+ requirement).
// Verifies card is valid. No charge, no capture. // Verifies card is valid and saves it. No charge, no capture.
// Note: setup_future_usage cannot be used with amount=0. // $0 PaymentIntents are rejected by Stripe; SetupIntent is the correct tool.
if str_eq(plan, "free") { if str_eq(plan, "free") {
let free_pi_body: String = "amount=0" let si_body: String = "automatic_payment_methods[enabled]=true"
+ "&currency=usd" + "&usage=off_session"
+ "&payment_method_types[]=card"
+ "&metadata[plan]=free" + "&metadata[plan]=free"
+ "&metadata[purpose]=age_verification" + "&metadata[purpose]=age_verification"
let free_pi_body = if !str_eq(pi_cus_id, "") { free_pi_body + "&customer=" + pi_cus_id } else { free_pi_body } let si_body = if !str_eq(pi_cus_id, "") { si_body + "&customer=" + pi_cus_id } else { si_body }
let free_pi_resp: String = http_post_form_auth( let si_resp: String = http_post_form_auth(
"https://api.stripe.com/v1/payment_intents", "https://api.stripe.com/v1/setup_intents",
free_pi_body, si_body,
auth_header) auth_header)
return free_pi_resp if str_starts_with(si_resp, "{") {
let inner: String = str_slice(si_resp, 1, str_len(si_resp))
return "{\"setup_mode\":true,\"plan\":\"free\"," + inner
}
return si_resp
} }
// Setup-mode path: save payment method, do not charge. Only valid // Setup-mode path: save payment method, do not charge. Only valid