Skip to content

Commit 77af126

Browse files
authored
fix: release (#148)
1 parent 368d462 commit 77af126

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+674
-163
lines changed

.github/workflows/release.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: release
1+
name: Release
22

33
on:
44
#push:
@@ -86,6 +86,14 @@ jobs:
8686
runs-on: ubuntu-24.04
8787

8888
steps:
89+
- uses: actions/checkout@v4
90+
with:
91+
sparse-checkout: |
92+
packages/sync-engine
93+
94+
- name: Install pnpm
95+
uses: pnpm/action-setup@v4
96+
8997
- name: Set new version in sync-engine
9098
run: |
9199
cd packages/sync-engine
@@ -94,6 +102,9 @@ jobs:
94102
- name: Publish sync-engine to npm
95103
run: |
96104
cd packages/sync-engine
105+
pnpm install --frozen-lockfile
106+
pnpm run build
107+
pnpm pack
97108
npm publish --access public
98109
env:
99110
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ node_modules/
44
.env
55
.env.*
66
!.env.sample
7+
.idea
8+
*.tgz

README.md

Lines changed: 25 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,46 @@
1-
# Stripe Sync Engine
1+
# Stripe Sync Engine Monorepo
22

3-
Continuously synchronizes a Stripe account to a Postgres database.
3+
![GitHub License](https://img.shields.io/github/license/supabase/stripe-sync-engine)
4+
![NPM Version](https://img.shields.io/npm/v/%40supabase%2Fstripe-sync-engine)
5+
![Docker Image Version](https://img.shields.io/docker/v/supabase/stripe-sync-engine?label=Docker)
6+
7+
This monorepo contains two packages for synchronizing your Stripe account with a Postgres database:
8+
9+
- [`@supabase/stripe-sync-engine`](./packages/sync-engine/README.md): A TypeScript library for syncing Stripe data to Postgres, designed for integration into your own Node.js backend or serverless environment.
10+
- [`stripe-sync-fastify`](./packages/fastify-app/README.md): A Fastify-based server and Docker image that exposes a `/webhooks` endpoint for Stripe, providing a ready-to-run service for real-time Stripe-to-Postgres sync.
411

512
![Sync Stripe with Postgres](./docs/stripe-sync-engine.jpg)
613

14+
---
15+
716
## Motivation
817

918
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.
1019

11-
This server synchronizes your Stripe account to a Postgres database. It can be a new database, or an existing Postgres database.
20+
This project synchronizes your Stripe account to a Postgres database. It can be a new database, or an existing Postgres database.
21+
22+
---
1223

1324
## How it works
1425

1526
![How it works](./docs/sync-engine-how.png)
1627

1728
- Creates a new schema `stripe` in a Postgres database, with tables & columns matching Stripe.
18-
- Exposes a `/webhooks` endpoint that listens to any Stripe webhooks.
29+
- Exposes a `/webhooks` endpoint that listens to any Stripe webhooks (via the Fastify app).
1930
- Inserts/updates/deletes changes into the tables whenever there is a change to Stripe.
2031

21-
**Not implemented**
32+
---
33+
34+
## Packages
35+
36+
- [Library: @supabase/stripe-sync-engine](./packages/sync-engine/README.md)
37+
- [Docker/Server: supabase/stripe-sync-engine](./packages/fastify-app/README.md)
2238

23-
- This will not do an initial load of existing Stripe data. You should use CSV loads for this. We might implement this in the future.
24-
- We are progressively working through webhooks.
39+
Each package has its own README with installation, configuration, and usage instructions.
40+
41+
---
2542

26-
## Webhook Progress
43+
## Webhook Support
2744

2845
- [ ] `balance.available`
2946
- [x] `charge.captured` 🟢
@@ -120,89 +137,3 @@ This server synchronizes your Stripe account to a Postgres database. It can be a
120137
- [x] `subscription_schedule.expiring` 🟢
121138
- [x] `subscription_schedule.released` 🟢
122139
- [x] `subscription_schedule.updated` 🟢
123-
124-
## Usage
125-
126-
- Update your Stripe account with all valid webhooks and get the webhook secret
127-
- `mv .env.sample .env` and then rename all the variables
128-
- Make sure the database URL has search_path `stripe`. eg: `DATABASE_URL=postgres://postgres:postgres@hostname:5432/postgres?sslmode=disable&search_path=stripe`
129-
- Deploy the [docker image](https://hub.docker.com/r/supabase/stripe-sync-engine) to your favourite hosting service and expose port `8080`
130-
- eg: `docker run -e PORT=8080 --env-file .env supabase/stripe-sync-engine`
131-
- This will automatically run any migrations on your database
132-
- Point your Stripe webooks to your deployed app.
133-
134-
## Backfill from Stripe
135-
136-
```
137-
POST /sync
138-
body: {
139-
"object": "product",
140-
"created": {
141-
"gte": 1643872333
142-
}
143-
}
144-
```
145-
146-
- `object` **all** | **charge** | **customer** | **dispute** | **invoice** | **payment_method** | **payment_intent** | **plan** | **price** | **product** | **setup_intent** | **subscription**
147-
- `created` is Stripe.RangeQueryParam. It supports **gt**, **gte**, **lt**, **lte**
148-
149-
#### Alternative routes to sync `daily/weekly/monthly` data
150-
151-
```
152-
POST /sync/daily
153-
154-
---
155-
156-
POST /sync/daily
157-
body: {
158-
"object": "product"
159-
}
160-
```
161-
162-
### Syncing single entity
163-
164-
To backfill/update a single entity, you can use
165-
166-
```
167-
POST /sync/single/cus_12345
168-
```
169-
170-
The entity type is recognized automatically, based on the prefix.
171-
172-
## Future ideas
173-
174-
- Expose an "initialize" endpoint that will fetch data from Stripe and do an initial load (or perhaps `POST` a CSV to an endpoint).
175-
176-
## Development
177-
178-
**Set up**
179-
180-
- Create a Postgres database on [supabase.com](https://supabase.com) (or another Postgres provider)
181-
- Update Stripe with all valid webhooks and get the webhook secret
182-
- `mv .env.sample .env` and then rename all the variables
183-
184-
**Develop**
185-
186-
- `pnpm dev` to start the local server
187-
- `pnpm t` to run tests
188-
189-
**Building Docker**
190-
191-
```bash
192-
docker build -t stripe-sync-engine .
193-
docker run -p 8080:8080 stripe-sync-engine
194-
```
195-
196-
**Release**
197-
198-
Handled by GitHub actions whenever their is a commit to the `main` branch with `fix` or `feat` in the description.
199-
200-
## License
201-
202-
Apache 2.0
203-
204-
## Sponsors
205-
206-
Supabase is building the features of Firebase using enterprise-grade, open source products. We support existing communities wherever possible, and if the products don’t exist we build them and open source them ourselves.
207-
208-
[![New Sponsor](https://user-images.githubusercontent.com/10214025/90518111-e74bbb00-e198-11ea-8f88-c9e3c1aa4b5b.png)](https://github.com/sponsors/supabase)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"pino": "^9.7.0",
2525
"prettier": "^3.5.3",
2626
"rimraf": "^6.0.1",
27+
"tsup": "^8.5.0",
2728
"typescript": "^5.8.3",
2829
"vite": "^6.3.5"
2930
},

packages/fastify-app/README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Stripe Sync - Fastify App
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.

packages/fastify-app/src/app.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fastify, { FastifyInstance, FastifyServerOptions } from 'fastify'
22
import autoload from '@fastify/autoload'
33
import fastifySwagger from '@fastify/swagger'
44
import fastifySwaggerUi from '@fastify/swagger-ui'
5-
import path from 'node:path'
5+
import { join } from 'node:path'
66
import { getConfig } from './utils/config'
77
import { StripeSync } from '@supabase/stripe-sync-engine'
88
import { errorSchema } from './error'
@@ -55,9 +55,7 @@ export async function createServer(opts: buildOpts = {}): Promise<FastifyInstanc
5555
}
5656
done(null, newBody)
5757
} catch (error) {
58-
// @ts-expect-error
5958
error.statusCode = 400
60-
// @ts-expect-error
6159
done(error, undefined)
6260
}
6361
})
@@ -71,7 +69,7 @@ export async function createServer(opts: buildOpts = {}): Promise<FastifyInstanc
7169
* Expose all routes in './routes'
7270
*/
7371
await app.register(autoload, {
74-
dir: path.join(__dirname, 'routes'),
72+
dir: join(__dirname, 'routes'),
7573
})
7674

7775
await app.ready()

packages/fastify-app/src/utils/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import dotenv from 'dotenv'
1+
import { config } from 'dotenv'
22

33
function getConfigFromEnv(key: string, defaultValue?: string): string {
44
const value = process.env[key]
@@ -47,7 +47,7 @@ export type StripeSyncServerConfig = {
4747
}
4848

4949
export function getConfig(): StripeSyncServerConfig {
50-
dotenv.config()
50+
config()
5151

5252
return {
5353
databaseUrl: getConfigFromEnv('DATABASE_URL'),

packages/fastify-app/tsconfig.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
{
2-
"extends": "../../tsconfig.base.json",
32
"compilerOptions": {
43
"rootDir": ".",
54
"module": "commonjs",
65
"outDir": "dist",
76
"declaration": true,
87
"declarationMap": true,
9-
"composite": true,
108
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
11-
"resolveJsonModule": true
9+
"resolveJsonModule": true,
10+
"skipLibCheck": true
1211
},
1312
"include": ["src"],
14-
"exclude": ["src/test", "node_modules", "dist"],
15-
"references": [{ "path": "../sync-engine" }]
13+
"exclude": ["src/test", "node_modules", "dist"]
1614
}

0 commit comments

Comments
 (0)