Skip to content

Commit 9c835f0

Browse files
authored
Merge pull request #162 from supabase/docs/init
init docs
2 parents 839bf42 + 1545237 commit 9c835f0

File tree

9 files changed

+494
-1
lines changed

9 files changed

+494
-1
lines changed

.github/workflows/docs.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Publish docs
2+
on:
3+
push:
4+
branches:
5+
- main
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: actions/setup-python@v5
13+
with:
14+
python-version: 3.x
15+
- run: pip install mkdocs
16+
- run: pip install mkdocs-material
17+
- run: mkdocs gh-deploy --force --clean --verbose

docs/contributing.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Contributing
2+
3+
Contributions are welcome.
4+
5+
## Contributing to the Docs
6+
7+
Building documentation requires Python 3.8+ and uv.
8+
9+
### Install Dependencies
10+
11+
Create a virtual environment and install mkdocs, themes, and extensions using uv.
12+
13+
```shell
14+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
15+
uv pip install -r docs/requirements_docs.txt
16+
```
17+
18+
### Serving
19+
20+
To serve the documentation locally, make sure your virtual environment is activated and run:
21+
22+
```shell
23+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
24+
mkdocs serve
25+
```
26+
27+
and visit the docs at [http://127.0.0.1:8000/](http://127.0.0.1:8000/)
28+
29+
### Deploying
30+
31+
If you have write access to the repo, docs can be updated using
32+
33+
```
34+
mkdocs gh-deploy
35+
```

docs/docker.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# With Docker
2+
3+
![GitHub License](https://img.shields.io/github/license/supabase/stripe-sync-engine)
4+
![Docker Image Version](https://img.shields.io/docker/v/supabase/stripe-sync-engine?label=Docker)
5+
6+
A Fastify-based server for syncing your Stripe account to a Postgres database in real time. Built on top of the Stripe Sync Engine.
7+
8+
## Features
9+
10+
- Exposes a `/webhooks` endpoint to receive Stripe webhooks and sync data to Postgres
11+
- Supports syncing customers, invoices, products, subscriptions, and more
12+
- Runs as a lightweight Docker container
13+
- Designed for easy deployment to any cloud or self-hosted environment
14+
15+
## Quick Start
16+
17+
### 1. Pull the image
18+
19+
```sh
20+
docker pull supabase/stripe-sync-engine:latest
21+
```
22+
23+
### 2. Run the container
24+
25+
```sh
26+
docker run -d \
27+
-e DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres \
28+
-e STRIPE_SECRET_KEY=sk_test_... \
29+
-e STRIPE_WEBHOOK_SECRET=... \
30+
-e API_KEY="my-secret" \
31+
-p 8080:8080 \
32+
supabase/stripe-sync-engine:latest
33+
```
34+
35+
### 3. Configuration
36+
37+
Set your webhook endpoint in the Stripe dashboard to point to your server’s `/webhooks` route (e.g., `https://yourdomain.com/webhooks`).
38+
39+
## Environment Variables
40+
41+
| Variable | Description | Required |
42+
| ---------------------------------- | ------------------------------------------------------------------- | -------- |
43+
| `DATABASE_URL` | Postgres connection string (with `search_path=stripe`) | Yes |
44+
| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | Yes |
45+
| `API_KEY` | API key for admin endpoints (backfilling, etc.) | Yes |
46+
| `SCHEMA` | Database schema name (default: `stripe`) | No |
47+
| `STRIPE_SECRET_KEY` | Stripe secret key (needed for active sync/backfill) | No |
48+
| `PORT` | Port to run the server on (default: 8080) | No |
49+
| `STRIPE_API_VERSION` | Stripe API version (default: `2020-08-27`) | No |
50+
| `AUTO_EXPAND_LISTS` | Fetch all list items from Stripe (default: false) | No |
51+
| `BACKFILL_RELATED_ENTITIES` | Backfill related entities for foreign key integrity (default: true) | No |
52+
| `MAX_POSTGRES_CONNECTIONS` | Max Postgres connection pool size (default: 10) | No |
53+
| `REVALIDATE_ENTITY_VIA_STRIPE_API` | Always fetch latest entity from Stripe (default: false) | No |
54+
55+
## Endpoints
56+
57+
- `POST /webhooks` — Receives Stripe webhook events and syncs data to Postgres
58+
- `GET /health` — Health check endpoint
59+
- `POST /sync` — Backfill Stripe data to Postgres (API key required)
60+
- `POST /sync/single/:stripeId` — Backfill or update a single Stripe entity by ID (API key required)
61+
- `POST /daily` — Backfill data from the last 24 hours (API key required)
62+
- `POST /weekly` — Backfill data from the last 7 days (API key required)
63+
- `POST /monthly` — Backfill data from the last 30 days (API key required)
64+
65+
## Example Docker Compose
66+
67+
```yaml
68+
version: '3'
69+
services:
70+
postgres:
71+
image: postgres:17
72+
restart: always
73+
environment:
74+
POSTGRES_USER: postgres
75+
POSTGRES_PASSWORD: postgres
76+
POSTGRES_DB: postgres
77+
ports:
78+
- 5432:5432
79+
volumes:
80+
- pgdata:/var/lib/postgresql/data
81+
82+
stripe-sync:
83+
image: supabase/stripe-sync-fastify:latest
84+
depends_on:
85+
- postgres
86+
ports:
87+
- 8080:8080
88+
environment:
89+
DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable&search_path=stripe
90+
STRIPE_SECRET_KEY: sk_test_...
91+
STRIPE_WEBHOOK_SECRET: whsec_...
92+
API_KEY: my-secret
93+
94+
volumes:
95+
pgdata:
96+
```
97+
98+
## Backfill from Stripe
99+
100+
> **Note:**
101+
> The `/sync` endpoints are **NOT** recommended for use if you have more than 10,000 objects in Stripe. For large backfills, it is best to write a script that loops through each day and sets the `created` date filters to the start and end of day.
102+
103+
```
104+
POST /sync
105+
body: {
106+
"object": "product",
107+
"created": {
108+
"gte": 1643872333
109+
}
110+
}
111+
```
112+
113+
- `object` **all** | **charge** | **customer** | **dispute** | **invoice** | **payment_method** | **payment_intent** | **plan** | **price** | **product** | **setup_intent** | **subscription** | **early_fraud_warning** | **refund** | **credit_note** | **tax_id** | **subscription_schedules**
114+
- `created` is Stripe.RangeQueryParam. It supports **gt**, **gte**, **lt**, **lte**
115+
116+
#### Alternative routes to sync `daily/weekly/monthly` data
117+
118+
```
119+
POST /sync/daily
120+
121+
---
122+
123+
POST /sync/daily
124+
body: {
125+
"object": "product"
126+
}
127+
```
128+
129+
### Syncing single entity
130+
131+
To backfill/update a single entity, you can use
132+
133+
```
134+
POST /sync/single/cus_12345
135+
```
136+
137+
The entity type is recognized automatically, based on the prefix.

edge-function.md renamed to docs/edge-function.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
# Stripe-Sync inside Supabase Edge Function
1+
# With Supabase Edge Functions
22

33
Create a new [Supabase](https://supabase.com) and create a new [Edge Function](https://supabase.com/docs/guides/functions/quickstart).
44

5+
## Prepare your database
6+
57
Make sure to run the [migrations](./packages/sync-engine/src/database/migrations/), either by executing them manually, adding them into your CI or running this locally once:
68

79
```ts
@@ -15,6 +17,8 @@ import { runMigrations } from '@supabase/stripe-sync-engine'
1517
})()
1618
```
1719

20+
## Usage
21+
1822
Sample code:
1923

2024
```ts

docs/index.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
![Sync Stripe with Postgres](./stripe-sync-engine.jpg)
2+
3+
# Stripe Sync Engine
4+
5+
Sometimes you want to analyze your billing data using SQL. Even more importantly, you want to join your billing data to your product/business data.
6+
7+
This project synchronizes your Stripe account to a Postgres database. It can be a new database, or an existing Postgres database.
8+
9+
---
10+
11+
## How it works
12+
13+
![How it works](./sync-engine-how.png)
14+
15+
- Creates a new schema `stripe` in a Postgres database, with tables & columns matching Stripe.
16+
- Exposes a `/webhooks` endpoint that listens to any Stripe webhooks (via the Fastify app).
17+
- Inserts/updates/deletes changes into the tables whenever there is a change to Stripe.
18+
19+
## Webhook Support
20+
21+
- [ ] `balance.available`
22+
- [x] `charge.captured` 🟢
23+
- [x] `charge.expired` 🟢
24+
- [x] `charge.failed` 🟢
25+
- [x] `charge.pending` 🟢
26+
- [x] `charge.refunded` 🟢
27+
- [x] `charge.refund.updated` 🟡 - For updates on all refunds, listen to `refund.updated` instead
28+
- [x] `charge.succeeded` 🟢
29+
- [x] `charge.updated` 🟢
30+
- [x] `charge.dispute.closed` 🟢
31+
- [x] `charge.dispute.created` 🟢
32+
- [x] `charge.dispute.funds_reinstated` 🟢
33+
- [x] `charge.dispute.funds_withdrawn` 🟢
34+
- [x] `charge.dispute.updated` 🟢
35+
- [ ] `checkout.session.async_payment_failed`
36+
- [ ] `checkout.session.async_payment_succeeded`
37+
- [ ] `checkout.session.completed`
38+
- [x] `credit_note.created` 🟢
39+
- [x] `credit_note.updated` 🟢
40+
- [x] `credit_note.voided` 🟢
41+
- [x] `customer.created` 🟢
42+
- [x] `customer.deleted` 🟢
43+
- [ ] `customer.source.created`
44+
- [ ] `customer.source.updated`
45+
- [x] `customer.subscription.created` 🟢
46+
- [x] `customer.subscription.deleted` 🟢
47+
- [x] `customer.subscription.paused` 🟢
48+
- [x] `customer.subscription.pending_update_applied` 🟢
49+
- [x] `customer.subscription.pending_update_expired` 🟢
50+
- [x] `customer.subscription.resumed` 🟢
51+
- [x] `customer.subscription.trial_will_end` 🟢
52+
- [x] `customer.subscription.updated` 🟢
53+
- [x] `customer.tax_id.created` 🟢
54+
- [x] `customer.tax_id.deleted` 🟢
55+
- [x] `customer.tax_id.updated` 🟢
56+
- [x] `customer.updated` 🟢
57+
- [x] `invoice.created` 🟢
58+
- [x] `invoice.deleted` 🟢
59+
- [x] `invoice.finalized` 🟢
60+
- [x] `invoice.finalization_failed` 🟢
61+
- [x] `invoice.marked_uncollectible` 🟢
62+
- [x] `invoice.paid` 🟢
63+
- [x] `invoice.payment_action_required` 🟢
64+
- [x] `invoice.payment_failed` 🟢
65+
- [x] `invoice.payment_succeeded` 🟢
66+
- [x] `invoice.sent` 🟢
67+
- [ ] `invoice.upcoming` 🔴 - Event has no id and cannot be processed
68+
- [x] `invoice.updated` 🟢
69+
- [x] `invoice.overdue` 🟢
70+
- [x] `invoice.overpaid` 🟢
71+
- [x] `invoice.will_be_due` 🟢
72+
- [x] `invoice.voided` 🟢
73+
- [ ] `issuing_authorization.request`
74+
- [ ] `issuing_card.created`
75+
- [ ] `issuing_cardholder.created`
76+
- [x] `payment_intent.amount_capturable_updated` 🟢
77+
- [x] `payment_intent.canceled` 🟢
78+
- [x] `payment_intent.created` 🟢
79+
- [x] `payment_intent.partially_refunded` 🟢
80+
- [x] `payment_intent.payment_failed` 🟢
81+
- [x] `payment_intent.processing` 🟢
82+
- [x] `payment_intent.requires_action` 🟢
83+
- [x] `payment_intent.succeeded` 🟢
84+
- [x] `payment_method.attached` 🟢
85+
- [x] `payment_method.automatically_updated` 🟢
86+
- [x] `payment_method.detached` 🟢
87+
- [x] `payment_method.updated` 🟢
88+
- [x] `plan.created` 🟢
89+
- [x] `plan.deleted` 🟢
90+
- [x] `plan.updated` 🟢
91+
- [x] `price.created` 🟢
92+
- [x] `price.deleted` 🟢
93+
- [x] `price.updated` 🟢
94+
- [x] `product.created` 🟢
95+
- [x] `product.deleted` 🟢
96+
- [x] `product.updated` 🟢
97+
- [x] `radar.early_fraud_warning.created` 🟢
98+
- [x] `radar.early_fraud_warning.updated` 🟢
99+
- [x] `refund.created` 🟢
100+
- [x] `refund.failed` 🟢
101+
- [x] `refund.updated` 🟢
102+
- [x] `review.opened` 🟢
103+
- [x] `review.closed` 🟢
104+
- [x] `setup_intent.canceled` 🟢
105+
- [x] `setup_intent.created` 🟢
106+
- [x] `setup_intent.requires_action` 🟢
107+
- [x] `setup_intent.setup_failed` 🟢
108+
- [x] `setup_intent.succeeded` 🟢
109+
- [x] `subscription_schedule.aborted` 🟢
110+
- [x] `subscription_schedule.canceled` 🟢
111+
- [x] `subscription_schedule.completed` 🟢
112+
- [x] `subscription_schedule.created` 🟢
113+
- [x] `subscription_schedule.expiring` 🟢
114+
- [x] `subscription_schedule.released` 🟢
115+
- [x] `subscription_schedule.updated` 🟢

docs/requirements_docs.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mkdocs
2+
mkdocs-material

docs/stylesheets/extra.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
2+
3+
:root {
4+
--md-text-font: 'IBM Plex Sans', 'Roboto', sans-serif;
5+
/* color: red; */
6+
}
7+
8+
[data-md-color-scheme='slate'] {
9+
--md-default-bg-color: #121212;
10+
--md-default-fg-color--light: white;
11+
--md-code-bg-color: #2a2929;
12+
--md-code-hl-keyword-color: #569cd6;
13+
}
14+
15+
.md-header,
16+
.md-tabs {
17+
background-color: var(--md-default-bg-color);
18+
color: var(--md-default-fg-color--light);
19+
font-family: var(--md-text-font);
20+
}

0 commit comments

Comments
 (0)