Live: https://letterstudio.net
We write it. You send it. Custom letter drafts from the details you type. Not legal advice.
Products: gym cancellation, diplomat rewrite, eulogy, toast, apology, resignation, neighbor note, company complaint.
Guides: https://letterstudio.net/guides
cp .env.example .env
# put your Stripe test keys in .env, then:
set -a && . ./.env && set +a
npm install
npm startApp listens on http://localhost:3000 (PORT overrides).
In another terminal, forward webhooks to the local app:
stripe listen --forward-to localhost:3000/webhookCopy the whsec_... it prints into STRIPE_WEBHOOK_SECRET, restart the server, then open /, fill a form, and pay with test card 4242 4242 4242 4242.
After a successful payment the webhook drafts the letter and appends it here (mock mailer, no Resend key):
cat data/mail-log.jsonlIf RESEND_API_KEY is set, the same payload is POSTed to Resend instead.
stripe trigger checkout.session.completed builds a fixture with no metadata.orderId. Lettermill will return 400 and will not send mail. Use a real Checkout session (or a signed fixture that includes metadata.orderId for an existing pending order).
npm test
# or
./scripts/verify.shNo live network. Checkout tests inject a fake Stripe client. Webhook tests sign payloads with stripe.webhooks.generateTestHeaderString.