syncPaymentData()
import { syncPaymentData } from '@payments/driver'Synchronizes payment provider data into your DB entitlements: Customer, Subscriptions, and Purchases. This universal function delegates to all installed payment driver implementations to keep entitlements in sync across providers.
When calling the driver version, it actually executes syncPaymentData() methods on every installed payment driver implementation. This is why we pass the user (to resolve all customer IDs across providers) instead of a provider-specific customerId.
const { customers, subscriptions, purchases } = await syncPaymentData({ user })This ensures that if a user has customers in multiple providers (e.g. Stripe for web, RevenueCat for mobile), all their entitlements stay in sync.
Implementation details
The @payments/driver entrypoint calls syncPaymentData() on all installed payment drivers:
/** --- syncCustomerData() ------------------------ */
/** -i- Populates user related Customer, Subscriptions and Purchases records */
export const syncCustomerData = createResolver(async ({ args, parseArgs, withDefaults }) => {
// Args
const { userId } = parseArgs(args)
// Retrieve all related customers for this user
const customers = await Customers.find({ userId })
// For each customer / provider, call the relevant syncPaymentData() driver method
const paymentDriversData = await Promise.all(customers.map(async (customer) => {
const paymentDriver = paymentsDrivers[customer.provider]
return paymentDriver.syncPaymentData({ customerId: customer.customerId })
}))
// Combine and return all payment provider sync results
return withDefaults({ ... })
}, syncCustomerDataBridge)Where syncCustomerData is a resolver that:
- Resolves all customer IDs for the given
useracross all payment providers - Calls each provider’s
syncPaymentData()implementation with the appropriatecustomerId - Each implementation fetches current state from its payment provider
- Each implementation upserts normalized
Customer,Subscriptions, andPurchasesrecords to your DB
Provider-specific implementations (like Stripe’s) will handle:
- Fetching provider state (customers, subscriptions, payment intents, checkouts, charges, invoices, etc.)
- Normalizing provider data to the universal
Customer,Subscriptions, andPurchasesschemas - Upserting records to your database
SyncPaymentDataInput
The underlying driver implementations will follow the driver signature SyncPaymentDataInput schema:
import { SyncPaymentDataInput } from '@payments/driver/driver.signature'export const SyncPaymentDataInput = schema('SyncPaymentDataInput', {
customerId: Customer.shape.customerId,
user: User.nullish(),
})Note: When calling through @payments/driver, you typically pass { user } to sync across all providers. When calling a provider-specific implementation directly (e.g. syncStripeData), you pass { customerId }.
SyncPaymentDataOutput
The underlying driver implementations will follow the driver signature SyncPaymentDataOutput schema:
import { SyncPaymentDataOutput } from '@payments/driver/driver.signature'export const SyncPaymentDataOutput = schema('SyncPaymentDataOutput', {
provider: PaymentProvidable.shape.provider,
user: User.nullish(),
customer: Customer.nullish(),
subscriptions: Subscription.array().nullish(),
purchases: Purchase.array().nullish(),
})When to use
Call syncPaymentData() on: webhooks (subscription changes, refunds, successful charges, invoice finalization), and after checkout/portal returns to keep entitlements consistent.
Most payment driver implementations (like Stripe) set up webhook handlers that call syncPaymentData() automatically on relevant payment events.
Custom post portal / checkout redirect URLs where syncPaymentData() is called manually are still advised to ensure immediate data consistency, regardless of webhook processing speed.
