Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 348c81ac7f | |||
| c30e5903a4 | |||
| c526e76d3b | |||
| a02ad7b61a | |||
| 6a7b8382ea | |||
| d2ae0b4b60 | |||
| 611e43fee1 | |||
| 5c8987ef59 | |||
| bfcb325352 | |||
| 61c3b1cfe9 | |||
| 57e9cafc95 | |||
| 632d95000c | |||
| ab83b1653c | |||
| 77807d30af | |||
| 8f91a80be7 | |||
| 752cc415d1 | |||
| d7bb92c37f | |||
| 4ca793ee2c | |||
| 0a599ec149 | |||
| 4123f6d5f1 | |||
| 69f348d48b | |||
| 6dedb97719 | |||
| 3d635505bc | |||
| 8528080e85 | |||
| 708dfd06cb | |||
| 3c19f4cf73 | |||
| bb98f76179 | |||
| b5285ccb74 | |||
| ba776153a9 | |||
| 853d73855d | |||
| 3ed43c0037 | |||
| 69ae0ca891 | |||
| 876f2afe27 | |||
| d12f0375f8 | |||
| 6c2f423548 | |||
| c6fb24498a | |||
| 3e230e52e5 | |||
| f06850eb1a | |||
| 0a4d454765 | |||
| 7bc2a8e8f6 | |||
| c4c30f1b33 | |||
| ae1a87de98 | |||
| f5cbc15b43 | |||
| e148e6987d | |||
| 9554430b7e | |||
| 9685a42c7d | |||
| 9650dad951 | |||
| c3aec8947a | |||
| 441d6d7cb5 | |||
| de9bf25437 | |||
| a59fdf4baa | |||
| ae633d3f71 | |||
| 43b5286fd5 | |||
| 04641ed1a3 | |||
| f4a202e220 | |||
| 3482e7e0f5 | |||
| beee0f99a7 | |||
| 4b70e8c186 | |||
| f9a5f93070 | |||
| 8e2deab5cb | |||
| ddeca2250e | |||
| d228701828 | |||
| 41f27e83aa | |||
| 533436e2c2 | |||
| aeea037e6f | |||
| 41bad94368 | |||
| 3020b4e902 | |||
| e82425a829 | |||
| c4cdb31529 | |||
| a1c0cc090d | |||
| 7df96a2273 | |||
| d3b890b739 | |||
| 3f069eeb79 | |||
| 8676751ed6 | |||
| a4f5312069 | |||
| c76e5a19eb | |||
| 58b7b32cdd | |||
| 0fdabcce86 | |||
| 79de47de2c | |||
| 45963154d9 | |||
| aabaa2ffb0 | |||
| d5dcb08ec6 | |||
| 20a36eeb9e | |||
| 32a179c24a | |||
| 6bc026de19 | |||
| 0ae526b72e | |||
| 8221aef605 | |||
| f8487c43a0 | |||
| 36b99dd9e2 |
+6
-6
@@ -5,7 +5,7 @@ from founding_badge import { founding_badge, founding_badge_css }
|
||||
|
||||
extern fn el_html_doc(lang: String, head: String, body: String) -> String
|
||||
extern fn el_meta_charset(charset: String) -> String
|
||||
extern fn el_meta(attrs: String) -> String
|
||||
extern fn el_meta(name: String, content: String) -> String
|
||||
extern fn el_title(text: String) -> String
|
||||
extern fn el_link_stylesheet(href: String) -> String
|
||||
extern fn el_script_src(src: String, defer_load: Bool) -> String
|
||||
@@ -13,7 +13,7 @@ extern fn el_script_inline(code: String) -> String
|
||||
extern fn el_nav(attrs: String, children: String) -> String
|
||||
extern fn el_div(attrs: String, children: String) -> String
|
||||
extern fn el_a(href: String, attrs: String, children: String) -> String
|
||||
extern fn el_img(attrs: String) -> String
|
||||
extern fn el_img(src: String, alt: String, attrs: String) -> String
|
||||
extern fn el_p(attrs: String, children: String) -> String
|
||||
extern fn el_h1(attrs: String, text: String) -> String
|
||||
extern fn el_button(attrs: String, label: String) -> String
|
||||
@@ -520,7 +520,7 @@ fn account_css() -> String {
|
||||
}
|
||||
|
||||
fn account_nav() -> String {
|
||||
let logo_img: String = el_img("src=\"/assets/brand/neuron-wordmark-on-light.png\" srcset=\"/assets/brand/neuron-wordmark-on-light@2x.png 2x\" alt=\"Neuron\" height=\"28\"")
|
||||
let logo_img: String = el_img("/assets/brand/neuron-wordmark-on-light.png", "Neuron", "srcset=\"/assets/brand/neuron-wordmark-on-light@2x.png 2x\" height=\"28\"")
|
||||
el_nav(
|
||||
"id=\"nav\"",
|
||||
el_div(
|
||||
@@ -818,7 +818,7 @@ fn account_devices_card() -> String {
|
||||
el_div("class=\"device-icon\"", account_signin_svg_device()) +
|
||||
el_div(
|
||||
"",
|
||||
el_p("class=\"devices-count\"", "2 devices included with your plan") +
|
||||
el_p("class=\"devices-count\" id=\"devices-count-el\"", "") +
|
||||
el_p("class=\"devices-sub\"", "Currently: Setup at launch")
|
||||
)
|
||||
) +
|
||||
@@ -965,9 +965,9 @@ fn account_dashboard_section() -> String {
|
||||
fn account_page(supabase_url: String, supabase_anon_key: String) -> String {
|
||||
let head: String =
|
||||
el_meta_charset("UTF-8") +
|
||||
el_meta("name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"") +
|
||||
el_meta("viewport", "width=device-width, initial-scale=1.0") +
|
||||
el_title("My Account - Neuron") +
|
||||
el_meta("name=\"description\" content=\"Manage your Neuron account, view your plan, and access your founding member details.\"") +
|
||||
el_meta("description", "Manage your Neuron account, view your plan, and access your founding member details.") +
|
||||
"<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/assets/favicon-16.png\">" +
|
||||
"<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/assets/favicon-32.png\">" +
|
||||
"<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">" +
|
||||
|
||||
@@ -103,6 +103,13 @@ fn main() -> Void {
|
||||
}
|
||||
setHtml('plan-billing-note-el', billingNote);
|
||||
|
||||
var devicesEl = document.getElementById('devices-count-el');
|
||||
if (devicesEl) {
|
||||
var deviceText = '2 devices included with your plan';
|
||||
if (plan === 'free') { deviceText = '1 device included with your plan'; }
|
||||
devicesEl.textContent = deviceText;
|
||||
}
|
||||
|
||||
var meta = '';
|
||||
if (createdAt) {
|
||||
var d = new Date(createdAt);
|
||||
|
||||
+17
-14
@@ -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).
|
||||
// Verifies card is valid. No charge, no capture.
|
||||
// Note: setup_future_usage cannot be used with amount=0.
|
||||
// Free tier: SetupIntent for age verification (18+ requirement).
|
||||
// Verifies card is valid and saves it. No charge, no capture.
|
||||
// $0 PaymentIntents are rejected by Stripe; SetupIntent is the correct tool.
|
||||
if str_eq(plan, "free") {
|
||||
let free_pi_body: String = "amount=0"
|
||||
+ "¤cy=usd"
|
||||
+ "&payment_method_types[]=card"
|
||||
let si_body: String = "automatic_payment_methods[enabled]=true"
|
||||
+ "&usage=off_session"
|
||||
+ "&metadata[plan]=free"
|
||||
+ "&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 free_pi_resp: String = http_post_form_auth(
|
||||
"https://api.stripe.com/v1/payment_intents",
|
||||
free_pi_body,
|
||||
let si_body = if !str_eq(pi_cus_id, "") { si_body + "&customer=" + pi_cus_id } else { si_body }
|
||||
let si_resp: String = http_post_form_auth(
|
||||
"https://api.stripe.com/v1/setup_intents",
|
||||
si_body,
|
||||
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
|
||||
@@ -2314,7 +2317,7 @@ fn sec_headers_json() -> String {
|
||||
+ "\"X-Frame-Options\":\"SAMEORIGIN\","
|
||||
+ "\"Referrer-Policy\":\"strict-origin-when-cross-origin\","
|
||||
+ "\"Permissions-Policy\":\"geolocation=(), microphone=(), camera=()\","
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; frame-src https://challenges.cloudflare.com; connect-src 'self' https://api.stripe.com https://*.supabase.co; img-src 'self' data: https:; font-src 'self' data:\"}"
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://googleads.g.doubleclick.net https://js.stripe.com https://static.cloudflareinsights.com https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; frame-src https://challenges.cloudflare.com https://js.stripe.com; connect-src 'self' https://analytics.google.com https://api.stripe.com https://*.supabase.co https://www.google.com https://www.googletagmanager.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com\"}"
|
||||
}
|
||||
|
||||
// Headers for compiled JS assets. Explicitly sets Content-Type so the browser
|
||||
@@ -2330,7 +2333,7 @@ fn js_headers_json() -> String {
|
||||
+ "\"X-Frame-Options\":\"SAMEORIGIN\","
|
||||
+ "\"Referrer-Policy\":\"strict-origin-when-cross-origin\","
|
||||
+ "\"Permissions-Policy\":\"geolocation=(), microphone=(), camera=()\","
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; frame-src https://challenges.cloudflare.com; connect-src 'self' https://api.stripe.com https://*.supabase.co; img-src 'self' data: https:; font-src 'self' data:\"}"
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://googleads.g.doubleclick.net https://js.stripe.com https://static.cloudflareinsights.com https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; frame-src https://challenges.cloudflare.com https://js.stripe.com; connect-src 'self' https://analytics.google.com https://api.stripe.com https://*.supabase.co https://www.google.com https://www.googletagmanager.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com\"}"
|
||||
}
|
||||
|
||||
// Headers for static assets under /assets/ and /brand/.
|
||||
@@ -2346,7 +2349,7 @@ fn static_asset_headers_json() -> String {
|
||||
+ "\"X-Frame-Options\":\"SAMEORIGIN\","
|
||||
+ "\"Referrer-Policy\":\"strict-origin-when-cross-origin\","
|
||||
+ "\"Permissions-Policy\":\"geolocation=(), microphone=(), camera=()\","
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; frame-src https://challenges.cloudflare.com; connect-src 'self' https://api.stripe.com https://*.supabase.co; img-src 'self' data: https:; font-src 'self' data:\"}"
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://googleads.g.doubleclick.net https://js.stripe.com https://static.cloudflareinsights.com https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; frame-src https://challenges.cloudflare.com https://js.stripe.com; connect-src 'self' https://analytics.google.com https://api.stripe.com https://*.supabase.co https://www.google.com https://www.googletagmanager.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com\"}"
|
||||
}
|
||||
|
||||
fn handle_request(method: String, path: String, headers: Map, body: String) -> String {
|
||||
|
||||
Reference in New Issue
Block a user