---
title: Stripe payments
description: Let your users accept payments — Stripe Connect, fully wired.
category: payments
order: 1
agent: "Connect a Stripe account to a project via 'coduck stripe connect' — OAuth flow. Your app receives payments to that account directly. CoDuck's CODUCK_STRIPE_KEY env var is auto-injected. Status/transactions/revenue via 'coduck stripe payments'."
---

# Stripe payments

CoDuck integrates with [Stripe Connect](https://stripe.com/connect) so your apps can accept payments. Money goes directly to your Stripe account — CoDuck never touches it. **Available on Pro and Studio plans.** This is BYOS ("Bring Your Own Stripe"): sign in once, your app gets credentials, Stripe handles the rest. CoDuck takes 0% of your revenue.

## Connect your Stripe account

1. Open your project at `https://app.coduck.ai/project/<projectId>`.
2. Switch to the **Cloud** panel and select the **Payments** tab.
3. Click **Connect Stripe**. A Stripe OAuth window opens.
4. Sign in to Stripe (or create an account) and approve the connection.
5. CoDuck stores the connection. On the next deploy, the keys are injected into your container.

## What gets injected

When a Stripe account is connected, the next deploy injects these env vars into your container:

| Variable | Description |
|---|---|
| `STRIPE_SECRET_KEY` | Restricted API key for the connected account (auto-rotated on refresh) |
| `STRIPE_PUBLISHABLE_KEY` | Public key for the connected account |
| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Same publishable key, prefixed for client-side access in Next.js |

These are reserved — you can't override them in the **Env** sub-tab.

## Charging in your code

Two patterns work — pick whichever you prefer.

### CoDuck SDK (recommended)

The SDK reads `STRIPE_SECRET_KEY` from the environment automatically:

```ts
import { stripe } from "@coduckai/sdk/payments";

const session = await stripe.checkout.sessions.create({
  line_items: [{ price: "price_xxx", quantity: 1 }],
  mode: "payment",
  success_url: "https://example.com/success",
  cancel_url: "https://example.com/cancel",
});
```

### Raw `stripe` npm package

Works the same way — instantiate with `process.env.STRIPE_SECRET_KEY`. Useful if you want full TypeScript types for every Stripe API surface.

```ts
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

const session = await stripe.checkout.sessions.create({
  line_items: [{ price: "price_xxx", quantity: 1 }],
  mode: "payment",
  success_url: "https://example.com/success",
  cancel_url: "https://example.com/cancel",
});
```

## Webhooks to your app

Stripe webhooks (`charge.succeeded`, `customer.subscription.updated`, etc.) go directly to your app via the webhook endpoint you configure in your Stripe dashboard.

1. In the Stripe dashboard, add an endpoint pointing at your deployed app — e.g. `https://<project>.coduck.app/api/stripe/webhook`.
2. Copy the signing secret Stripe gives you.
3. In CoDuck, set it under **Cloud panel → Hosting → Env** as `STRIPE_WEBHOOK_SECRET`. (This one is user-set, not auto-injected.)

A minimal Next.js Route Handler that verifies the signature with the raw body and the `stripe-signature` header:

```ts
// app/api/stripe/webhook/route.ts
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: Request) {
  const sig = req.headers.get("stripe-signature")!;
  const body = await req.text(); // raw body required for verification

  const event = stripe.webhooks.constructEvent(
    body,
    sig,
    process.env.STRIPE_WEBHOOK_SECRET!,
  );

  switch (event.type) {
    case "charge.succeeded":
      // ...
      break;
  }

  return new Response("ok");
}
```

Note: CoDuck handles connection-level events (e.g. account deauthorized) on its own platform webhook — you don't have to set those up.

## Analytics

View stats, transactions, a revenue chart, and connection health under **Cloud panel → Payments**.

## Status

The connection has three states, shown in the **Payments** tab:

- `connected` — working normally.
- `needs_reconnect` — Stripe returned 401 on a recent call. Re-run the OAuth flow to refresh the token.
- `revoked` — you or Stripe deauthorized the connection. Reconnect to restore.

## Disconnect

In **Cloud panel → Payments**, click **Disconnect Stripe**. This revokes CoDuck's OAuth access and removes the injected env vars on the next deploy. Your Stripe account itself is untouched.

## Notes

> [!NOTE]
> CoDuck takes 0% of your revenue. The Stripe connection is included in your Pro or Studio plan.

- One Stripe account per CoDuck project. Different projects can connect to different accounts.
- Refunds, subscriptions, and disputes are handled through the standard Stripe API using the injected key. CoDuck doesn't proxy them.

## Common failures

- **"Connect Stripe" returns `upgrade_required`** — you're on the Free plan. Upgrade to Pro or Studio first, then retry the OAuth flow.
- **"No Stripe account connected"** when calling the SDK at runtime — you connected but haven't redeployed yet, so the env vars aren't in the container. Redeploy to pick them up.
- **Status flipped to `needs_reconnect`** — Stripe returned 401 on a recent call. Re-run the OAuth flow from the Payments tab.

## For developers

A CLI is also available for connecting Stripe, checking status, and viewing payments from the terminal — see [CLI commands](/docs/cli/commands).

## Next

- [Deploying a project](/docs/projects/deploy) — when injected env vars take effect
- [Database overview](/docs/database/overview) — the other half of a typical paid app
