# Paddle Payment Setup — Paid AI Birthday Card V2

The application is already wired for this production flow:

**Checkout -> verified `transaction.completed` webhook -> card credit -> AI generation -> credit consumed only on success.**

## 1. Start in Paddle Sandbox

1. Open Paddle Sandbox.
2. Create a product such as **AI Birthday Card**.
3. Add a **one-time price** (for example USD 1.99).
4. Copy the price ID (`pri_...`).
5. Go to **Developer tools -> Authentication -> Client-side tokens** and create a sandbox token (`test_...`).
6. Go to **Developer tools -> Notifications** and create a webhook destination:
   - URL: `https://YOUR-DOMAIN.example/api/webhooks/paddle`
   - Subscribe at minimum to: `transaction.completed`
7. Copy that notification destination's webhook secret.

## 2. Configure `.env`

```env
REQUIRE_PAYMENT=1
PADDLE_ENV=sandbox
PADDLE_CLIENT_TOKEN=test_xxxxxxxxx
PADDLE_PRICE_ID=pri_xxxxxxxxx
PADDLE_WEBHOOK_SECRET=pdl_ntfset_xxxxxxxxx
CARD_PRICE_LABEL=$1.99
CREDITS_PER_PURCHASE=1
```

Also set your server-side `OPENAI_API_KEY`.

## 3. Test Sandbox Checkout

Open the app. The **Buy 1 AI Card — $1.99** button should become active.

Use Paddle's sandbox checkout test details. After checkout:

1. Paddle emits checkout completion in the browser.
2. The browser waits for server confirmation.
3. Paddle sends `transaction.completed` to `/api/webhooks/paddle`.
4. The server validates `Paddle-Signature` before doing anything.
5. The server checks that the transaction contains the configured `PADDLE_PRICE_ID` and this project's `custom_data`.
6. The session receives the configured number of credits.
7. **Generate AI Background** unlocks.

## 4. Go Live

When the product/domain is approved in Paddle:

```env
PADDLE_ENV=live
PADDLE_CLIENT_TOKEN=live_xxxxxxxxx
PADDLE_PRICE_ID=pri_LIVE_PRICE_ID
PADDLE_WEBHOOK_SECRET=pdl_ntfset_LIVE_SECRET
```

Use the live notification destination URL and live price. Restart the Node application after changing environment variables.

## Security behavior already built in

- OpenAI secret key never reaches browser JavaScript.
- Paddle webhook secret never reaches browser JavaScript.
- Paddle client-side token and price ID are the only payment values exposed to the frontend.
- Payment is **not** trusted from the browser's checkout event.
- AI credit is provisioned only after a valid signed `transaction.completed` webhook.
- Event IDs and transaction IDs are stored to prevent duplicate credits on webhook retries.
- A credit is reserved before image generation; if generation fails, the credit is restored.
- User session uses an HttpOnly, SameSite=Lax cookie.
- Generated images remain subject to the daily API cost guard.

## Local persistent files

`storage/payments.json` stores anonymous browser-session credit balances and processed Paddle event/transaction IDs. Back this file up with the application data. It contains no card details.

For larger traffic or multiple Node workers, replace this JSON store with SQLite/PostgreSQL/Redis before scaling horizontally.
