safely run database <--> stripe operations.
npm install -g TheProfs/bp-syncThis is a script runner that runs mapping files, which contain some custom logic to sync data between your database and Stripe.
It abstracts away authentication, pagination, error-handling, and logging so you don't have to worry about that.
The first time you run it, you'll be prompted for:
- Stripe API Key
- Database URL
These are stored in your macOS keychain.
Then, to perform a mapping:
- Run
bp-sync init, which createsmapping.js - Replace the loop in
mapping.jswith your own custom logic. - or use an LLM to generate it, see LLM Prompt.
- Run it using
bp-sync exec mapping.js
bp-sync initwhich creates:
// mapping.js// this is a sample, edit me!forawait(constuserofusers()){awaituser.set({stripe_subscription_id: user.subscription?.id||null}).save()}// mapping.jsforawait(constuserofusers()){awaituser.set({stripe_subsription_status: user.subscription?.status||null}).save()}bp-sync exec mapping.jsthe end.
Usage:
bp-sync Show this help
bp-sync -h, --help Show this help
bp-sync init Create mapping.js
bp-sync exec<file> Execute script
Examples:
bp-sync init
bp-sync exec mapping.jsBulk update:
Activate all inactive users and update timestamps
forawait(constuserofusers('active = false')){awaituser.set({active: true,updated_at: newDate()}).save()}Backfill from Stripe:
Copy customer email from Stripe to NULL database fields
forawait(constuserofusers('field1 IS NULL',[],{fetchCustomer: true})){if(!user.customer)continueawaituser.set({field1: user.customer.email}).save()}Process subscriptions:
Set DB plan_id from all Stripe active subscriptions
forawait(constsubofsubscriptions({status: 'active'})){awaitquery('UPDATE users SET plan_id = $1 WHERE stripe_id = $2',[sub.items.data[0].price.id,sub.customer])}Iterate customers:
Find and log customers by email
forawait(constcustomerofcustomers({email: 'test@example.com'})){log.info('Customer: $1',[customer.id])}Cancel subscriptions:
Cancel all active subscriptions for specific plan
forawait(constsubofsubscriptions({status: 'active',price: 'price_premium_monthly'})){awaitstripe.subscriptions.cancel(sub.id)log.success('Cancelled subscription $1',[sub.id])}Sync subscription status:
Update database with current Stripe subscription status
forawait(constuserofusers('stripe_id IS NOT NULL')){constsubs=awaitstripe.subscriptions.list({customer: user.stripe_id})constactive=subs.data.find(s=>s.status==='active')awaituser.set({subscription_status: active?.status||'none',plan_id: active?.items.data[0]?.price.id||null}).save()}Instead of writing mapping.js scripts manually, you can use an LLM to generate them from natural language.
Copy the following prompt and run it:
Code Generator Prompt
# Generate bp-sync mapping.js
You are a code generator for bp-sync mapping scripts.
Generate valid JavaScript code for `mapping.js` based on the
user's query.
## User Query
The user wants to: **[YOUR QUERY HERE]**## API Reference
<API>
**Database:**-`users(where, params, options)` - Generator yielding User instances
-`where`: SQL WHERE clause (without WHERE keyword)
-`params`: Array of parameterized values for $1, $2, etc.
-`options.fetchCustomer`: Boolean, auto-fetches Stripe customer if true
- Returns User with `.set(data)` and `.save()` methods
-`query(sql, params)` - Execute raw SQL (INSERT/UPDATE/DELETE)
- Returns query result directly
**Stripe:**-`customers(filters)` - Generator for Stripe customers
-`subscriptions(filters)` - Generator for Stripe subscriptions
-`invoices(filters)` - Generator for Stripe invoices
-`charges(filters)` - Generator for Stripe charges
- All resources auto-discovered, accept Stripe API filter objects
**Logging:**-`log.info(msg, args)` - General messages
-`log.warning(msg, args)` - Non-fatal issues
-`log.error(msg, args)` - Errors
-`log.success(msg, args)` - Success messages
- Placeholders: $1, $2, $3 replaced with args array values
</API>
## Code Patterns
<PATTERNS>
**User updates with Stripe data:**```jsforawait (constuserofusers('field IS NULL', [], { fetchCustomer:true })) {
if (!user.customer) continueawaituser.set({ field:user.customer.email }).save()
}Bulk user updates:
forawait(constuserofusers('active = false')){awaituser.set({active: true,updated_at: newDate()}).save()}Stripe to DB sync:
forawait(constsubofsubscriptions({status: 'active'})){awaitquery('UPDATE users SET plan_id = $1 WHERE stripe_id = $2',[sub.items.data[0].price.id,sub.customer])}Iterate and log:
forawait(constcustomerofcustomers({email: 'test@example.com'})){log.info('Customer: $1',[customer.id])}Generate the mapping.js code now:
**Usage with codex:**
```bash
cat prompt.md | codex exec
Stripe:
customers(filters),subscriptions(filters),invoices(filters), etc.stripe- raw client
Database:
users(where, params, options)- generator with.set()and.save()query(sql, params)- direct queriesdb- raw pg client
Logging:
log.error(),log.success(),log.warning(),log.info()
All output to stderr. Placeholders: $1, $2.
npm ci
npm testauthor: TheProfs