~/VibeHandbook
PDF Gratis

Bab 17 · 01

Studi Kasus 1: Halaman Landing Multibahasa + Pembayaran

Idenya

Seorang teman menjual panduan PDF seharga $29 untuk ekspatriat yang pindah ke Korea. Dia ingin satu halaman yang memuat cepat di seluruh dunia, berbicara dalam bahasa Inggris, Korea, dan Mandarin, serta menerima pembayaran kartu tanpa dia perlu menyentuh server sendiri.

Spesifikasinya

Kami menjaga spesifikasi tetap satu paragraf sebelum menulis kode apa pun:

Build a single-page marketing site for a $29 digital product.
Three languages (en/ko/zh), auto-detected from the browser but
switchable with a toggle. One "Buy" button that opens Stripe
Checkout and redirects to a thank-you page on success. No backend
server I have to maintain. Must load fast from anywhere.

Perhatikan apa yang tidak disebutkan spesifikasi ini: tanpa , tanpa design system, tanpa analitik, tanpa penangkapan email. Setiap fitur itu sebenarnya masuk akal untuk ditambahkan, dan masing-masing akan memperlambat peluncuran pertama. Disiplin spesifikasi satu paragraf sebagian besar adalah disiplin untuk sengaja meninggalkan hal-hal di luar cakupan.

Stack-nya

"Tanpa yang saya rawat sendiri" ditambah "cepat di seluruh dunia" langsung mengarah ke hosting statis pada CDN (Content Delivery Network — salinan berkas-berkasmu yang disimpan di pusat data di seluruh dunia sehingga setiap pengunjung memuat dari yang terdekat) dengan satu fungsi kecil untuk satu-satunya hal yang benar-benar memerlukan kunci rahasia: membuat sesi Stripe. Kami memilih situs statis di Cloudflare Pages dengan satu Pages Function. Seluruh arsitekturnya bisa dijelaskan dalam satu kalimat — halaman statis yang memanggil satu fungsi yang berbicara dengan Stripe — dan kesederhanaan itulah yang membuatnya mudah didiagnosis (debug) belakangan.

Seluruh sistem hanya terdiri dari tiga kotak, dan hanya kotak tengah yang pernah menyentuh kunci rahasia:

 ┌─────────────┐  POST /api/checkout  ┌──────────────┐  secret key  ┌────────┐
 │ STATIC PAGE │─────────────────────▶│ PAGES        │─────────────▶│ STRIPE │
 │ (HTML/JS)   │◀─────────────────────┤ FUNCTION     │◀─────────────┤        │
 │ en/ko/zh    │   checkout URL        │ (the only    │  session URL └────────┘
 └─────────────┘                       │  secret-side)│
       served from CDN edge            └──────────────┘

Prompt-prompt kuncinya

Kami mulai dengan cakupan luas dan membiarkan AI mengusulkan strukturnya:

Scaffold a static landing page (plain HTML/CSS/JS, no framework)
with a language toggle for en/ko/zh. Store the copy in a single
JS object keyed by language. Detect the default from
navigator.language. Keep it to one index.html plus one main.js.

Lalu jalur pembayaran, tempat kunci rahasia itu berada:

Add a Cloudflare Pages Function at /api/checkout that creates a
Stripe Checkout session for a single $29 product and returns the
redirect URL. Read STRIPE_SECRET_KEY from the environment, never
hardcode it. The Buy button should POST to this function and then
window.location to the returned URL.

Frasa "never hardcode it" (jangan pernah tulis langsung di kode) itu layak dipertahankan. Kalau dibiarkan dengan pengaturan bawaannya, AI akan dengan senang hati menaruh kunci contoh langsung di kode agar contohnya "berhasil", dan sebuah kunci contoh punya cara untuk berubah menjadi kunci asli yang ikut terkirim ke produksi. Mengucapkannya sekali, di dalam , lebih murah daripada menangkapnya saat review.

Hambatannya

Uji coba langsung yang pertama gagal: tombol Buy tidak melakukan apa-apa, dan konsol peramban menunjukkan error CORS (Cross-Origin Resource Sharing — aturan peramban tentang situs mana yang boleh memanggil situs mana). Kami tidak menebak-nebak. Kami menempelkan error persisnya ke AI:

Clicking Buy logs: "Access to fetch at '/api/checkout' blocked
by CORS policy." The function and page are on the same domain.
Here's the function code: [pasted]. What's actually wrong?

AI langsung menemukannya: fungsi tersebut mengembalikan Stripe sebagai (JavaScript Object Notation — format teks sederhana dan mudah dibaca yang dipakai program untuk bertukar data terstruktur), tapi permintaan fetch dilakukan sebelum preflight OPTIONS milik fungsi itu ditangani, karena kami tidak sengaja men-deploy halaman statis dan fungsinya ke dua proyek Pages yang berbeda. Perbaikan sebenarnya ada pada topologi deployment, bukan kode. Kami memindahkan fungsi itu ke dalam direktori /functions milik proyek yang sama, dan errornya hilang.

Pelajaran ini layak direnungkan: gejalanya ada di kode (error CORS dari sebuah fetch), tapi penyebabnya ada di infrastruktur (dua proyek, bukan satu). Kalau kami membiarkan AI "memperbaiki error CORS" tanpa menempelkan konteks setup-nya, ia akan menambahkan header Access-Control-Allow-Origin — perbaikan yang bisa dikompilasi, menutupi gejalanya, tapi membiarkan bug sebenarnya tetap hidup. Tempelkan pesan error yang persis dan konteks di sekitarnya, dan jangan menerima penjelasan pertama yang masuk akal kalau itu tidak cocok dengan setup-mu.

Peluncurannya

Kami menambahkan kunci Stripe yang asli sebagai variabel lingkungan () terenkripsi di dashboard Pages (tidak pernah di dalam repositori kode), mengarahkan ( Name System, buku alamat internet yang memetakan sebuah nama ke sebuah server) domainnya ke Pages, dan melakukan satu kali pembelian uji nyata seharga $29 dengan kartu, lalu mengembalikan dananya (refund). Kami juga sengaja mengklik jalur kegagalan — sebuah kartu uji yang ditolak — untuk memastikan dia tidak tertagih dan melihat pesan yang masuk akal alih-alih layar kosong. Tayang dalam satu sore. Dia mendapat penjualan pertamanya malam itu juga.

Mau versi offline?

Unduh seluruh buku sebagai PDF atau EPUB — gratis.