feat: scaffold payit skeleton

This commit is contained in:
Ruidy 2025-09-27 11:08:08 +02:00
parent 810a49aa4e
commit 17db8bb165
No known key found for this signature in database
GPG key ID: 705C24D202990805
10 changed files with 696 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*cache

View file

@ -11,6 +11,9 @@ PayIt is a Go-based Stripe integration demo. Use this guide to deliver focused c
## Build, Test, and Development Commands ## Build, Test, and Development Commands
- Prefer the Makefile workflow for local checks; these commands set local caches so they work in sandboxed environments.
- After each code change run formatting, linting, and test suites via `make fmt lint test` to ensure consistency, coverage visibility, and to prevent cached results. If the Makefile is missing, create it before coding.
- After verifying changes, create a conventional commit (e.g., `feat: add checkout handler`) so the history captures intent and scope.
- `go run ./cmd/payit` — start the local server, loading configuration from `.env.local` when present. - `go run ./cmd/payit` — start the local server, loading configuration from `.env.local` when present.
- `go build ./...` — ensure every package compiles prior to opening a PR. - `go build ./...` — ensure every package compiles prior to opening a PR.
- `go test ./...` — execute the full unit suite; add `-run TestStripe` to focus on Stripe-specific tests during iteration. - `go test ./...` — execute the full unit suite; add `-run TestStripe` to focus on Stripe-specific tests during iteration.

27
Makefile Normal file
View file

@ -0,0 +1,27 @@
SHELL := /bin/bash
GOFILES := $(shell find . -path './.modcache' -prune -o -path './.cache' -prune -o -name '*.go' -print)
GOCACHE ?= $(CURDIR)/.cache
GOMODCACHE ?= $(CURDIR)/.modcache
GOFLAGS ?= -count=1
.PHONY: fmt lint test clean tidy
fmt:
@echo "Formatting Go files"
@gofmt -w $(GOFILES)
lint:
@echo "Running linters"
@GOCACHE=$(GOCACHE) GOMODCACHE=$(GOMODCACHE) go vet ./...
@GOCACHE=$(GOCACHE) GOMODCACHE=$(GOMODCACHE) golangci-lint run --timeout=5m
test:
@echo "Running tests with coverage"
@GOCACHE=$(GOCACHE) GOMODCACHE=$(GOMODCACHE) go test $(GOFLAGS) -cover ./...
clean:
@rm -rf $(GOCACHE) $(GOMODCACHE)
tidy:
@echo "Tidying go.mod"
@GOCACHE=$(GOCACHE) GOMODCACHE=$(GOMODCACHE) go mod tidy

55
cmd/payit/main.go Normal file
View file

@ -0,0 +1,55 @@
package main
import (
"context"
"log"
"net/http"
"os/signal"
"syscall"
"time"
"github.com/rjNemo/payit/config"
"github.com/rjNemo/payit/internal/web"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatalf("failed to load configuration: %v", err)
}
handler := web.NewServer(cfg)
srv := &http.Server{
Addr: ":8080",
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Printf("Starting PayIt server on %s", srv.Addr)
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
errCh := make(chan error, 1)
go func() {
errCh <- srv.ListenAndServe()
}()
select {
case <-ctx.Done():
log.Println("Shutting down server...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Fatalf("server shutdown failed: %v", err)
}
log.Println("Server stopped cleanly")
case err := <-errCh:
if err != nil && err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}
}

149
config/config.go Normal file
View file

@ -0,0 +1,149 @@
package config
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
// ProductConfig holds metadata for the single demo product.
type ProductConfig struct {
Name string
Description string
PriceCents int64
Currency string
SuccessURL string
CancelURL string
}
// Config aggregates all runtime configuration required by the server.
type Config struct {
StripeSecretKey string
StripePublishableKey string
Product ProductConfig
}
// Load reads configuration from environment variables, optionally sourcing
// a .env.local file when present.
func Load() (Config, error) {
_ = loadDotEnv()
priceRaw := strings.TrimSpace(os.Getenv("PAYIT_PRODUCT_PRICE_CENTS"))
cfg := Config{
StripeSecretKey: os.Getenv("PAYIT_STRIPE_SECRET_KEY"),
StripePublishableKey: os.Getenv("PAYIT_STRIPE_PUBLISHABLE_KEY"),
Product: ProductConfig{
Name: os.Getenv("PAYIT_PRODUCT_NAME"),
Description: os.Getenv("PAYIT_PRODUCT_DESCRIPTION"),
Currency: os.Getenv("PAYIT_PRODUCT_CURRENCY"),
SuccessURL: os.Getenv("PAYIT_PRODUCT_SUCCESS_URL"),
CancelURL: os.Getenv("PAYIT_PRODUCT_CANCEL_URL"),
},
}
if missing := validate(cfg, priceRaw); len(missing) > 0 {
return Config{}, fmt.Errorf("missing required environment variables: %s", strings.Join(missing, ", "))
}
price, err := parsePrice(priceRaw)
if err != nil {
return Config{}, err
}
cfg.Product.PriceCents = price
return cfg, nil
}
func loadDotEnv() error {
filename := ".env.local"
relPaths := []string{
filename,
filepath.Join("..", filename),
}
for _, path := range relPaths {
if err := applyEnvFile(path); err != nil {
return err
}
}
return nil
}
func applyEnvFile(path string) (retErr error) {
file, err := os.Open(path)
if err != nil {
return nil
}
defer func() {
if cerr := file.Close(); retErr == nil && cerr != nil {
retErr = cerr
}
}()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
_ = os.Setenv(key, value)
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
func parsePrice(value string) (int64, error) {
price, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return 0, fmt.Errorf("PAYIT_PRODUCT_PRICE_CENTS must be a positive integer: %w", err)
}
if price <= 0 {
return 0, fmt.Errorf("PAYIT_PRODUCT_PRICE_CENTS must be a positive integer")
}
return price, nil
}
func validate(cfg Config, priceRaw string) []string {
missing := make([]string, 0)
if cfg.StripeSecretKey == "" {
missing = append(missing, "PAYIT_STRIPE_SECRET_KEY")
}
if cfg.StripePublishableKey == "" {
missing = append(missing, "PAYIT_STRIPE_PUBLISHABLE_KEY")
}
if cfg.Product.Name == "" {
missing = append(missing, "PAYIT_PRODUCT_NAME")
}
if cfg.Product.Description == "" {
missing = append(missing, "PAYIT_PRODUCT_DESCRIPTION")
}
if priceRaw == "" {
missing = append(missing, "PAYIT_PRODUCT_PRICE_CENTS")
}
if cfg.Product.Currency == "" {
missing = append(missing, "PAYIT_PRODUCT_CURRENCY")
}
if cfg.Product.SuccessURL == "" {
missing = append(missing, "PAYIT_PRODUCT_SUCCESS_URL")
}
if cfg.Product.CancelURL == "" {
missing = append(missing, "PAYIT_PRODUCT_CANCEL_URL")
}
return missing
}

77
config/config_test.go Normal file
View file

@ -0,0 +1,77 @@
package config
import (
"strings"
"testing"
)
func TestLoadSuccess(t *testing.T) {
t.Setenv("PAYIT_STRIPE_SECRET_KEY", "sk_test")
t.Setenv("PAYIT_STRIPE_PUBLISHABLE_KEY", "pk_test")
t.Setenv("PAYIT_PRODUCT_NAME", "Demo product")
t.Setenv("PAYIT_PRODUCT_DESCRIPTION", "Great product")
t.Setenv("PAYIT_PRODUCT_PRICE_CENTS", "2500")
t.Setenv("PAYIT_PRODUCT_CURRENCY", "usd")
t.Setenv("PAYIT_PRODUCT_SUCCESS_URL", "https://example.com/success")
t.Setenv("PAYIT_PRODUCT_CANCEL_URL", "https://example.com/cancel")
cfg, err := Load()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.Product.PriceCents != 2500 {
t.Fatalf("expected price 2500, got %d", cfg.Product.PriceCents)
}
if cfg.Product.Name != "Demo product" {
t.Fatalf("unexpected product name: %s", cfg.Product.Name)
}
}
func TestLoadMissingMandatoryVariables(t *testing.T) {
clearAllEnv(t)
_, err := Load()
if err == nil {
t.Fatal("expected error when required variables are missing")
}
if !strings.Contains(err.Error(), "missing required environment variables") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestLoadInvalidPrice(t *testing.T) {
t.Setenv("PAYIT_STRIPE_SECRET_KEY", "sk_test")
t.Setenv("PAYIT_STRIPE_PUBLISHABLE_KEY", "pk_test")
t.Setenv("PAYIT_PRODUCT_NAME", "Demo product")
t.Setenv("PAYIT_PRODUCT_DESCRIPTION", "Great product")
t.Setenv("PAYIT_PRODUCT_PRICE_CENTS", "-1")
t.Setenv("PAYIT_PRODUCT_CURRENCY", "usd")
t.Setenv("PAYIT_PRODUCT_SUCCESS_URL", "https://example.com/success")
t.Setenv("PAYIT_PRODUCT_CANCEL_URL", "https://example.com/cancel")
_, err := Load()
if err == nil {
t.Fatal("expected error for invalid price")
}
if !strings.Contains(err.Error(), "PAYIT_PRODUCT_PRICE_CENTS") {
t.Fatalf("unexpected error: %v", err)
}
}
func clearAllEnv(t *testing.T) {
t.Helper()
envs := []string{
"PAYIT_STRIPE_SECRET_KEY",
"PAYIT_STRIPE_PUBLISHABLE_KEY",
"PAYIT_PRODUCT_NAME",
"PAYIT_PRODUCT_DESCRIPTION",
"PAYIT_PRODUCT_PRICE_CENTS",
"PAYIT_PRODUCT_CURRENCY",
"PAYIT_PRODUCT_SUCCESS_URL",
"PAYIT_PRODUCT_CANCEL_URL",
}
for _, env := range envs {
t.Setenv(env, "")
}
}

28
internal/web/server.go Normal file
View file

@ -0,0 +1,28 @@
package web
import (
"net/http"
"github.com/rjNemo/payit/config"
)
// Handler aggregates dependencies required by HTTP handlers.
type Handler struct {
cfg config.Config
}
// NewServer constructs the root HTTP handler. The initial implementation only
// exposes placeholder routes; later phases will wire Stripe-backed handlers and
// templates.
func NewServer(cfg config.Config) http.Handler {
h := &Handler{cfg: cfg}
mux := http.NewServeMux()
mux.HandleFunc("/", h.notImplemented)
return mux
}
func (h *Handler) notImplemented(w http.ResponseWriter, r *http.Request) {
http.Error(w, "PayIt demo coming soon", http.StatusNotImplemented)
}

View file

@ -0,0 +1,5 @@
# Implementation TODOs - Stripe Checkout Demo
- [x] Phase 1: Project skeleton & configuration
- [ ] Phase 2: Stripe checkout backend
- [ ] Phase 3: Static frontend & routing
- [ ] Phase 4: Testing & developer experience

View file

@ -0,0 +1,230 @@
# Stripe Checkout Demo Implementation Plan
## Overview
Implement a Go-based Stripe Checkout demo that serves a static landing page for a single hardcoded product and uses Stripe-hosted checkout to process payments, aligning with the previously captured research.
## Current State Analysis
- Repository currently contains only `go.mod`, `README.md`, and `AGENTS.md`; there is no executable application code or directory structure yet (`README.md:1-8`, `AGENTS.md:5-27`).
- No configuration handling, HTTP server, or Stripe integration exists; we must introduce all components from scratch.
- Research document `thoughts/shared/research/2025-09-27-stripe-integration-demo.md` outlines recommended architecture, folder layout, and open questions.
## Desired End State
Deliver a runnable Go service (`cmd/payit/main.go`) that serves a static product page, exposes a `/api/checkout` endpoint to create a Stripe Checkout Session for a single hardcoded product, and documents setup/testing steps. The codebase should follow the structure in `AGENTS.md`, with automated tests covering the Stripe service wrapper and HTTP handler behavior.
### Key Discoveries
- `thoughts/shared/research/2025-09-27-stripe-integration-demo.md:21-55` Defines target architecture for server, Stripe layer, assets, and testing.
- `AGENTS.md:5-18` Specifies preferred directory layout (`cmd/`, `internal/`, `web/`, `config/`) and formatting expectations.
- `AGENTS.md:26-27` Emphasizes proper handling of Stripe secrets via environment variables.
## Out of Scope
- Supporting multiple or dynamic products; only a single hardcoded demo product is required.
- Implementing Stripe webhooks or fulfillment workflows; success relies on Stripe redirect pages.
- Adding auxiliary tooling such as `hack/spec_metadata.sh` or deployment scripts.
## Implementation Approach
Build the application in four incremental phases: establish project skeleton and configuration, implement Stripe Checkout backend logic, wire static frontend assets with routing, and finish with testing plus documentation updates. Each phase will introduce isolated packages, enabling focused testing and straightforward iteration.
## Phase 1: Project Skeleton & Configuration
### Overview
Create the application structure, entrypoint, and configuration loader for environment variables.
### Changes Required
**File**: `go.mod`
**Changes**: Add required module dependencies (`github.com/stripe/stripe-go/v78`, optional `github.com/joho/godotenv` if used) and tidy module.
**File**: `cmd/payit/main.go` (new)
**Changes**: Bootstrap configuration, initialize logger, construct HTTP server (delegated to `internal/web`).
**File**: `config/config.go` (new)
**Changes**: Define `Config` struct holding Stripe secret key, publishable key, and product metadata (name, price, currency, success/cancel URLs). Load values from environment (with optional `.env.local` support) and validate presence.
**File**: `config/config_test.go` (new)
**Changes**: Unit tests ensuring configuration loading validates required fields and default behaviors.
**File**: Directory scaffolding (`internal/stripe/`, `internal/web/`, `web/templates/`, `web/static/`, `testdata/`)
**Changes**: Create empty placeholder files or README stubs as needed so later phases can populate them.
```go
// config/config.go
package config
type ProductConfig struct {
Name string
Description string
PriceCents int64
Currency string
SuccessURL string
CancelURL string
}
type Config struct {
StripeSecretKey string
StripePublishableKey string
Product ProductConfig
}
```
### Success Criteria
#### Automated Verification
- [x] `go fmt ./...`
- [x] `go build ./...`
#### Manual Verification
- [x] Application starts with placeholder server: `go run ./cmd/payit` *(bind restricted in sandbox; confirmed startup log before failure)*
- [x] Missing environment variables cause a clear startup error
## Phase 2: Stripe Checkout Backend
### Overview
Implement the Stripe client wrapper, checkout session creator, and API handler.
### Changes Required
**File**: `internal/stripe/client.go` (new)
**Changes**: Wrap Stripe SDK, expose interface for session creation, configure Stripe API key.
**File**: `internal/stripe/types.go` (new)
**Changes**: Define request/response structs (e.g., `CheckoutSessionRequest`, `CheckoutSessionResult`).
**File**: `internal/stripe/client_test.go` (new)
**Changes**: Table-driven tests using a fake Stripe API client to validate payload construction.
**File**: `internal/web/handlers.go` (new)
**Changes**: Implement `/api/checkout` HTTP handler accepting POST, calling Stripe service, returning JSON (session ID / URL) with proper error handling.
**File**: `internal/web/server.go` (new)
**Changes**: Build `http.Handler` wiring API routes and injecting Stripe service.
**File**: `internal/web/handlers_test.go` (new)
**Changes**: Use `httptest` with a mocked Stripe service to assert status codes, response payloads, and error cases.
```go
// internal/web/handlers.go
func (h *Handler) CreateCheckoutSession(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, err := h.CheckoutService.CreateSession(ctx)
if err != nil {
http.Error(w, "checkout session failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(session)
}
```
### Success Criteria
#### Automated Verification
- [ ] `go test ./internal/stripe`
- [ ] `go test ./internal/web`
#### Manual Verification
- [ ] `curl -X POST http://localhost:8080/api/checkout` returns session payload with mock keys
- [ ] Error cases logged and surfaced with 500 response when Stripe call fails
## Phase 3: Static Frontend & Routing
### Overview
Implement landing page, static assets, and route wiring for root and assets.
### Changes Required
**File**: `web/templates/index.html` (new)
**Changes**: HTML page describing the product with “Buy Now” button invoking JavaScript to POST to `/api/checkout` and redirect to returned `url`.
**File**: `web/static/styles.css` (new)
**Changes**: Minimal styling for the product page.
**File**: `web/static/app.js` (new)
**Changes**: JS `fetch` call to `/api/checkout`, handles response, redirects or displays error.
**File**: `internal/web/server.go`
**Changes**: Serve template on `/`, static assets via `http.FileServer`, inject publishable key into template data if needed.
**File**: `internal/web/templates.go` (new)
**Changes**: Helper to parse templates and render with provided data (publishable key, product info).
**File**: `internal/web/server_test.go` (new)
**Changes**: Verify root route renders successfully and static assets are served.
### Success Criteria
#### Automated Verification
- [ ] `go test ./internal/web`
#### Manual Verification
- [ ] Visit `http://localhost:8080/` and confirm product page renders with publishable key available to JS
- [ ] Clicking “Buy Now” redirects to Stripe Checkout when using valid keys
## Phase 4: Testing & Developer Experience
### Overview
Finalize tests, documentation, and developer onboarding.
### Changes Required
**File**: `README.md`
**Changes**: Add setup instructions (env vars, running server, creating test mode keys), testing commands, and manual verification steps.
**File**: `.env.example` (new)
**Changes**: Document expected environment variables (`PAYIT_STRIPE_SECRET_KEY`, `PAYIT_STRIPE_PUBLISHABLE_KEY`, `PAYIT_PRODUCT_NAME`, etc.).
**File**: `Makefile` (new, optional)
**Changes**: Provide convenience targets (`make run`, `make test`) if deemed helpful for onboarding.
**File**: `internal/stripe/mock.go` (new)
**Changes**: Provide simple mock implementation used in tests to avoid Stripe network calls.
**File**: `thoughts/shared/plans/2025-09-27-stripe-checkout-demo.md`
**Changes**: Update checkboxes for completed phases as work progresses.
### Success Criteria
#### Automated Verification
- [x] `go fmt ./...`
- [ ] `go test ./...`
- [x] `go build ./...`
#### Manual Verification
- [ ] README instructions reproduce successful checkout flow in Stripe test mode
- [ ] `.env.example` allows quick setup with test keys
## Testing Strategy
- Unit tests for configuration loader, Stripe client wrapper, and HTTP handlers (`config`, `internal/stripe`, `internal/web`).
- Integration-style handler tests using mocks to simulate Stripe responses.
- Manual validation of end-to-end flow with Stripe test keys in browser.
## Performance Considerations
- Single product flow with minimal traffic; standard `net/http` defaults suffice. Ensure Stripe client is reused to avoid unnecessary overhead.
## Migration Notes
- No existing users; deployment is greenfield. Ensure new directories and files are committed together.
## References
- Research: `thoughts/shared/research/2025-09-27-stripe-integration-demo.md`
- Guidelines: `AGENTS.md`
- Ticket (README context): `README.md`

View file

@ -0,0 +1,121 @@
---
date: 2025-09-27T01:47:51+02:00
researcher: Codex
git_commit: 810a49aa4ef6e6b21cb88a6da0e8693e22cf5ae0
branch: main
repository: payit
topic: "Stripe integration demo architecture"
tags: [research, codebase, stripe, go]
status: complete
last_updated: 2025-09-27
last_updated_by: Codex
last_updated_note: Merged initial planning notes into research doc
---
# Research: Stripe integration demo architecture
## Research Question
How should we implement a Go-first web demo that lets users purchase a fake product
via Stripe in one click without adopting a frontend framework?
## Summary
A minimalist Stripe Checkout Session flow fits the project goals: serve a static
landing page from Go, expose a `/api/checkout` endpoint that creates sessions with the Stripe Go SDK, and redirect users to Stripe-hosted checkout for payment. Organize code following the guidelines in `AGENTS.md`, isolating Stripe logic under `internal/stripe`, HTTP handlers under `internal/web`, and using `cmd/payit/main.go` for bootstrapping. Local configuration should rely on environment variables (e.g., `.env.local`) to avoid embedding secrets. Automated tests can cover session creation and configuration handling with mocked Stripe clients.
## Detailed Findings
### Project Framing
- `README.md:1-8` states the app is a Stripe integration demo featuring one-time payments, so focusing on Checkout Sessions aligns with stated goals.
- `AGENTS.md:5-9` prescribes directory boundaries (`cmd/`, `internal/payments`, `internal/stripe`, `internal/web`, `web/templates`, `web/static`), providing a scaffold for new components.
### Backend HTTP Server
- Proposed entrypoint: `cmd/payit/main.go` spinning up `net/http` with routes `/` (serve landing page) and `/api/checkout` (POST to create session). Use `http.FileServer` for static assets and a custom handler for the API.
- `internal/web/server.go` (new) can construct the router, inject Stripe services, and encapsulate middleware (logging, recovery).
### Stripe Integration Layer
- `internal/stripe/client.go` (new) should wrap the official Stripe Go SDK, exposing `CreateCheckoutSession(ctx, params)` to keep handlers lightweight.
- Use environment variable `PAYIT_STRIPE_SECRET_KEY` for authentication; load via `os.LookupEnv` and fail fast if missing.
### Static Frontend Assets
- Store landing page at `web/templates/index.html` with a simple product card and a “Buy Now” button wired to POST JSON to `/api/checkout` via `fetch`.
- Serve minimal styling and JS from `web/static/` to keep the Go server responsible for asset delivery without frameworks.
### Configuration & Secrets
- Follow `AGENTS.md:17-18,26-27` guidance: run `go fmt`/`goimports`, centralize config under `config/config.go`, and never commit Stripe secrets. Support `.env.local` loading via a small helper (e.g., `github.com/joho/godotenv`) if allowed.
### Testing Strategy
- Create table-driven tests under `internal/stripe/client_test.go` to validate request payloads, stubbing Stripe client calls.
- Integration-style tests in `internal/web/server_test.go` can hit the `/api/checkout` handler using `httptest` with a fake Stripe service implementation.
## Code References
- `README.md:1-8` Declares project purpose as a Stripe integration demo.
- `AGENTS.md:5-27` Defines intended project layout, coding style, and security practices.
## Architecture Insights
- Adopt layered structure: handler → service (`internal/stripe`) → external Stripe API, decoupled through interfaces for testing.
- Prefer Stripe Checkout Sessions over custom payment intents to minimize client-side complexity while delivering real Stripe UX.
- Expose configuration via constructor parameters so services are testable without relying on global state.
## Planning Notes (2025-02-14)
### Objectives
- Understand user goal: Go-powered web page offering single-click Stripe checkout for a demo product without a frontend framework.
- Identify necessary backend components (HTTP server, Stripe client, config management).
- Determine minimal frontend assets required (static HTML/CSS/JS) and how to integrate Stripe Checkout or Payment Links.
- Outline testing, environment, and deployment considerations specific to this repository.
### Key Questions
1. What project structure best supports a Go-centric implementation while keeping room for future growth?
2. Which Stripe integration approach (Checkout Sessions vs. Payment Elements) balances simplicity with demo fidelity?
3. How should environment configuration and secret management be handled for local development?
4. What testing strategy ensures confidence without overcomplicating the demo?
5. Are there existing docs or notes in `./thoughts/` that can inform architectural decisions?
### Planned Research Tasks
- **Repo Survey**: Confirm current files and identify gaps for server, handlers, static assets.
- **Stripe Flow Analysis**: Compare Checkout Session vs. Payment Intent flows for a quick demo.
- **Go HTTP Server Design**: Sketch routing, handler responsibilities, and integration points.
- **Static Asset Strategy**: Determine minimal HTML/CSS/JS structure without frameworks.
- **Configuration & Secrets**: Document use of `.env` or config package for API keys.
- **Testing Considerations**: Identify unit/integration tests and Stripe mocking options.
- **Prior Research Review**: Scan `./thoughts/` for relevant history (none yet, but confirm).
### Parallel Task Outline
- Task A: Analyze current repo layout and any existing guidelines (AGENTS.md).
- Task B: Research Stripe official guidance for Go integration (if needed from prior knowledge; no external fetch unless requested).
- Task C: Design server architecture and endpoints handling Checkout session creation.
- Task D: Plan static frontend assets and their interaction with the Go backend.
- Task E: Define testing and configuration practices referencing Go ecosystem tools.
### Deliverables
- Comprehensive research document stored under `thoughts/shared/research/` following required template.
- High-level summary for user with key file/path references and recommended next steps.
## Historical Context (from ./thoughts/)
- `thoughts/2025-02-14-stripe-demo-plan.md` Original research plan detailing objectives, tasks, and integration questions.
## Related Research
- None yet; this is the first entry in `thoughts/shared/research/`.
## Open Questions
- `hack/spec_metadata.sh` is missing, so metadata gathering requires manual commands; consider adding the script for future research compliance.
- Decide whether to embed a mock pricing catalog or keep a single hard-coded product configuration for the demo.
- Evaluate if webhooks are necessary for the demo or if redirect-based success pages suffice.