checkout: drop auth wall so payment form mounts on page load
The auth-first flow blocked Stripe Elements from initialising for any visitor without an existing Supabase session. Users hit the checkout page, saw "Sign in to continue", and could not get to a card field at all. Restored the inline-JS path (HEAD before extraction broke it), flipped payment-section visible by default, kept the sign-in panel behind an explicit "Already have an account? Sign in" link. Build pipeline: added supabase_get stub injection and -lssl/-lcrypto linker flags (web_stubs.c uses EVP for the AES-256-GCM transport). Without those the Docker build aborts at link time.
This commit is contained in:
+10
-2
@@ -25,6 +25,14 @@ WORKDIR /build
|
||||
COPY runtime/el_runtime.c runtime/el_runtime.h ./
|
||||
|
||||
# ── Build neuron-web ──────────────────────────────────────────────────────────
|
||||
#
|
||||
# Inline-JS extraction (scripts/extract-js.py) is expected to run BEFORE the
|
||||
# wrapper concatenates src/*.el into dist/main-combined.el. That side of the
|
||||
# pipeline lives in build-local.sh (gated by EXTRACT_JS=1) and the outer
|
||||
# orchestrator. By the time we reach this Dockerfile, main-combined.el
|
||||
# already references /assets/js/<hash>.js and the corresponding asset files
|
||||
# have been emitted under src/assets/js/. The COPY of src/assets at the
|
||||
# runtime stage below is what ships those files into the container.
|
||||
COPY dist/web_stubs.c ./
|
||||
COPY dist/bootstrap.py ./
|
||||
COPY dist/main-combined.el ./
|
||||
@@ -36,7 +44,7 @@ RUN python3 bootstrap.py main-combined.el > main.c && \
|
||||
cc -O2 -rdynamic \
|
||||
-o neuron-web \
|
||||
main.c web_stubs.c el_runtime.c \
|
||||
-lcurl -lpthread -ldl -lm
|
||||
-lcurl -lpthread -ldl -lm -lssl -lcrypto
|
||||
|
||||
# ── Build soul-demo ───────────────────────────────────────────────────────────
|
||||
COPY dist/soul-demo.c ./
|
||||
@@ -45,7 +53,7 @@ COPY dist/vessel_stubs.c ./
|
||||
RUN cc -O2 -rdynamic \
|
||||
-o soul-demo \
|
||||
soul-demo.c vessel_stubs.c el_runtime.c \
|
||||
-lcurl -lpthread -ldl -lm
|
||||
-lcurl -lpthread -ldl -lm -lssl -lcrypto
|
||||
|
||||
# ── Stage 2: runtime image ────────────────────────────────────────────────────
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
+19
-3
@@ -6,8 +6,12 @@
|
||||
# and the soul dies with it. No separate process manager. No port conflicts.
|
||||
#
|
||||
# Usage:
|
||||
# ./build-local.sh — build only
|
||||
# ./build-local.sh --run — build and start both servers
|
||||
# ./build-local.sh — build only (fast, leaves inline JS as-is)
|
||||
# ./build-local.sh --run — build and start both servers
|
||||
# EXTRACT_JS=1 ./build-local.sh — also extract inline <script> blocks
|
||||
# into hashed, minified, obfuscated
|
||||
# /assets/js/<hash>.js files. Production
|
||||
# deploys should always extract.
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
@@ -22,6 +26,18 @@ COMPONENTS=(nav hero pillars how_it_works inference efficiency comparison
|
||||
footer styles about founding_badge terms enterprise_terms checkout safety
|
||||
gallery account)
|
||||
|
||||
# Optional inline-JS extraction. Off by default for fast dev iteration; the
|
||||
# script is idempotent so flipping the flag on a prior tree just reuses
|
||||
# previously-extracted assets and emits a complete manifest.
|
||||
if [[ "${EXTRACT_JS:-0}" == "1" ]]; then
|
||||
echo "==> Extracting inline JS → src/assets/js/"
|
||||
if [ ! -x "node_modules/.bin/terser" ] || [ ! -x "node_modules/.bin/javascript-obfuscator" ]; then
|
||||
echo " installing terser + javascript-obfuscator (no-save)..."
|
||||
npm install --no-save --silent terser javascript-obfuscator
|
||||
fi
|
||||
python3 scripts/extract-js.py
|
||||
fi
|
||||
|
||||
echo "==> Combining El sources"
|
||||
{
|
||||
for f in "${COMPONENTS[@]}"; do
|
||||
@@ -35,7 +51,7 @@ echo "==> Bootstrap El → C"
|
||||
python3 "${BOOTSTRAP}" dist/main-combined.el > dist/main.c
|
||||
|
||||
echo "==> Injecting stubs"
|
||||
sed -i '' 's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);|' dist/main.c
|
||||
sed -i '' 's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);\nel_val_t supabase_get(el_val_t project_url, el_val_t service_key, el_val_t table_and_query);|' dist/main.c
|
||||
|
||||
echo "==> Compiling neuron-web"
|
||||
cc -O2 \
|
||||
|
||||
+24
-13
@@ -97,10 +97,10 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
<p class=\"label\" style=\"margin-bottom: 1.5rem; color: var(--navy);\">Create your account.</p>
|
||||
<p class=\"checkout-auth-hint\" style=\"margin-bottom: 2rem;\">Create an account to reserve your free tier spot. You'll receive your download link and setup instructions at launch.</p>
|
||||
" } else { "
|
||||
<!-- Step 1: Social sign-in -->
|
||||
<div id=\"auth-section\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">Sign in to continue</p>
|
||||
<p class=\"checkout-auth-hint\">Create your account or sign in. Your purchase will be linked to this account.</p>
|
||||
<!-- Optional sign-in (collapsed by default - users can purchase without an account) -->
|
||||
<div id=\"auth-section\" style=\"display:none;\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">Sign in (optional)</p>
|
||||
<p class=\"checkout-auth-hint\">Sign in to link this purchase to an existing account. Or skip and create one later - we'll match it to your email.</p>
|
||||
|
||||
<div class=\"checkout-social-btns\">
|
||||
<button type=\"button\" class=\"checkout-social-btn\" id=\"btn-google\" onclick=\"signInWith('google')\">
|
||||
@@ -136,9 +136,10 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Payment form (revealed after auth) -->
|
||||
<div id=\"payment-section\" style=\"display:none;\">
|
||||
<!-- Payment form (visible immediately - no auth wall) -->
|
||||
<div id=\"payment-section\">
|
||||
<div id=\"auth-badge\" style=\"display:none; margin-bottom: 1.5rem;\"></div>
|
||||
<p class=\"checkout-auth-hint\" id=\"signin-prompt\" style=\"margin-bottom:1.25rem;font-size:.8125rem\">Already have an account? <a href=\"#\" onclick=\"document.getElementById('auth-section').style.display='';this.parentNode.style.display='none';return false;\" style=\"color:var(--navy);text-decoration:underline\">Sign in</a> to link your purchase.</p>
|
||||
|
||||
<!-- Founding Member attestation (only shown for founding plan) -->
|
||||
" + (if is_founding { "
|
||||
@@ -512,10 +513,13 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
}
|
||||
|
||||
function revealPaymentForm(user) {
|
||||
document.getElementById('auth-section').style.display = 'none';
|
||||
document.getElementById('payment-section').style.display = '';
|
||||
// Hide optional auth section if it was shown
|
||||
var auth = document.getElementById('auth-section');
|
||||
if (auth) auth.style.display = 'none';
|
||||
var payment = document.getElementById('payment-section');
|
||||
if (payment) payment.style.display = '';
|
||||
|
||||
// Show auth badge if we have a user
|
||||
// Show auth badge if we have a user, hide the inline 'sign in' prompt
|
||||
if (user) {
|
||||
var badge = document.getElementById('auth-badge');
|
||||
var name = user.user_metadata && user.user_metadata.full_name
|
||||
@@ -526,13 +530,20 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
+ 'Signed in as <strong>' + name + '</strong>'
|
||||
+ '</div>';
|
||||
badge.style.display = '';
|
||||
var prompt = document.getElementById('signin-prompt');
|
||||
if (prompt) prompt.style.display = 'none';
|
||||
}
|
||||
|
||||
// Pre-fill email only (not name — let user enter their own)
|
||||
// Pre-fill email only (not name - let user enter their own)
|
||||
if (user && user.email) {
|
||||
var emailEl = document.getElementById('buyer-email');
|
||||
if (emailEl) { emailEl.value = user.email; }
|
||||
}
|
||||
|
||||
// Initialize Stripe Elements with this user's email (or empty if guest)
|
||||
var userEmail = user ? (user.email || '') : '';
|
||||
var userName = user ? ((user.user_metadata && user.user_metadata.full_name) || '') : '';
|
||||
if (typeof initStripe === 'function') initStripe(userEmail, userName);
|
||||
}
|
||||
|
||||
// Check if already signed in on load
|
||||
@@ -573,7 +584,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
showAuthMessage(result.error.message || 'Sign-in failed. Please try again.', true);
|
||||
btns.forEach(function(b) { b.disabled = false; });
|
||||
}
|
||||
// On success, browser redirects to OAuth provider — no further action needed here.
|
||||
// On success, browser redirects to OAuth provider - no further action needed here.
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -780,7 +791,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save founding member attestation before charging — independent audit record
|
||||
// Save founding member attestation before charging - independent audit record
|
||||
if (attestCb) {
|
||||
try {
|
||||
await fetch('/api/attest', {
|
||||
@@ -796,7 +807,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
})
|
||||
});
|
||||
} catch(e) {
|
||||
// Non-blocking — attestation log failure does not stop payment
|
||||
// Non-blocking - attestation log failure does not stop payment
|
||||
console.warn('attestation log failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user