@raideno/convex-stripe 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,9 +17,9 @@ Stripe [syncing](./documentation/references/tables.md), subscriptions, [checkout
17
17
  - [7. Run the `sync` Action](#7-run-the-sync-action)
18
18
  - [8. Start Building](#8-start-building)
19
19
  - [Configuration](#configuration)
20
+ - [Schema Configuration](#schema-configuration)
20
21
  - [Stripe Configuration](#stripe-configuration)
21
22
  - [Sync Configuration](#sync-configuration)
22
- - [Table Selection](#table-selection)
23
23
  - [Catalog (Unstable)](#catalog-unstable)
24
24
  - [Webhook Configuration](#webhook-configuration)
25
25
  - [Portal Configuration](#portal-configuration)
@@ -46,6 +46,11 @@ Stripe [syncing](./documentation/references/tables.md), subscriptions, [checkout
46
46
  - [`stripe.client`](#stripeclient)
47
47
  - [`sync` Action](#sync-action)
48
48
  - [`store` Mutation](#store-mutation)
49
+ - [`stripe.helpers`](#stripehelpers)
50
+ - [`helpers.createCustomer`](#helperscreatecustomer)
51
+ - [`helpers.products`](#helpersproducts)
52
+ - [`helpers.subscription`](#helperssubscription)
53
+ - [`helpers.customer`](#helperscustomer)
49
54
  - [Synced Tables](#synced-tables)
50
55
  - [Synced Events](#synced-events)
51
56
  - [Best Practices](#best-practices)
@@ -113,8 +118,7 @@ export default defineSchema({
113
118
  });
114
119
  ```
115
120
 
116
- > [!WARNING]
117
- > Ensure that your table configuration in `convex/schema.ts` matches your `sync.tables` configuration in `convex/stripe.ts` exactly! If your `sync.tables` tries to sync a table that is excluded from your schema, Convex Stripe will throw a runtime error when processing webhooks.
121
+ The package will only sync the tables you defined.
118
122
 
119
123
  See [Tables Reference](./documentation/references/tables.md) for the full list of tables and their schemas.
120
124
 
@@ -125,16 +129,16 @@ Call `internalConvexStripe` with your Stripe credentials and sync configuration.
125
129
  ```ts
126
130
  // convex/stripe.ts
127
131
 
128
- import { internalConvexStripe, syncAllTables } from "@raideno/convex-stripe/server";
132
+ import { internalConvexStripe } from "@raideno/convex-stripe/server";
133
+
134
+ import schema from "./schema";
129
135
 
130
136
  export const { stripe, store, sync } = internalConvexStripe({
137
+ schema: schema,
131
138
  stripe: {
132
139
  secret_key: process.env.STRIPE_SECRET_KEY!,
133
140
  account_webhook_secret: process.env.STRIPE_ACCOUNT_WEBHOOK_SECRET!,
134
141
  },
135
- sync: {
136
- tables: syncAllTables(),
137
- },
138
142
  });
139
143
  ```
140
144
 
@@ -260,6 +264,9 @@ The `internalConvexStripe` function accepts a configuration object and an option
260
264
  ```ts
261
265
  const { stripe, store, sync } = internalConvexStripe(configuration, options);
262
266
  ```
267
+ ### Schema Configuration
268
+
269
+ The `schema` key holds your convex app schema, from it the package will infer which stripe tables should be synced or not depending on what stripeTables where defined.
263
270
 
264
271
  ### Stripe Configuration
265
272
 
@@ -276,29 +283,6 @@ The `stripe` key in the configuration object holds your Stripe credentials and A
276
283
 
277
284
  The `sync` key controls which tables are synced and allows you to define catalog items, webhook endpoints, and billing portal configuration.
278
285
 
279
- #### Table Selection
280
-
281
- Three helper functions are provided to control which Stripe tables are synced into Convex:
282
-
283
- ```ts
284
- import {
285
- syncAllTables,
286
- syncAllTablesExcept,
287
- syncOnlyTables,
288
- } from "@raideno/convex-stripe/server";
289
-
290
- // Sync all 24 tables (default)
291
- { tables: syncAllTables() }
292
-
293
- // Sync all tables except specific ones
294
- { tables: syncAllTablesExcept(["stripeReviews", "stripePlans"]) }
295
-
296
- // Sync only specific tables
297
- { tables: syncOnlyTables(["stripeCustomers", "stripeSubscriptions", "stripeProducts", "stripePrices"]) }
298
- ```
299
-
300
- All 24 available tables are listed in the [Synced Tables](#synced-tables) section.
301
-
302
286
  #### Catalog (Unstable)
303
287
 
304
288
  The `sync.catalog` key lets you pre-define products and prices that should exist in Stripe. When the `sync` action is called with `{ catalog: true }`, the library ensures these objects exist in your Stripe account.
@@ -307,7 +291,6 @@ The `sync.catalog` key lets you pre-define products and prices that should exist
307
291
  internalConvexStripe({
308
292
  // ...
309
293
  sync: {
310
- tables: syncAllTables(),
311
294
  catalog: {
312
295
  products: [
313
296
  { name: "Pro Plan", metadata: { convex_stripe_key: "pro" } },
@@ -502,15 +485,17 @@ Then update your configuration to include it:
502
485
  ```ts
503
486
  // convex/stripe.ts
504
487
 
488
+ import { internalConvexStripe } from "@raideno/convex-stripe/server";
489
+
490
+ import schema from "./schema";
491
+
505
492
  export const { stripe, store, sync } = internalConvexStripe({
493
+ schema: schema,
506
494
  stripe: {
507
495
  secret_key: process.env.STRIPE_SECRET_KEY!,
508
496
  account_webhook_secret: process.env.STRIPE_ACCOUNT_WEBHOOK_SECRET!,
509
497
  connect_webhook_secret: process.env.STRIPE_CONNECT_WEBHOOK_SECRET!,
510
- },
511
- sync: {
512
- tables: syncAllTables(),
513
- },
498
+ }
514
499
  });
515
500
  ```
516
501
 
@@ -817,6 +802,151 @@ This action is typically called manually from the Convex dashboard, or set up to
817
802
 
818
803
  An internal mutation that persists Stripe objects into your Convex database. This is called automatically from within the webhook handler and is not meant for direct use. It must be exported from the same file as your `internalConvexStripe` call.
819
804
 
805
+ ### `stripe.helpers`
806
+
807
+ Returns a set of pre-built, authorization-aware Convex functions that cover the most common Stripe operations. These are ready to export and call from your frontend directly — no boilerplate required.
808
+
809
+ Each returned function invokes your `authenticateAndAuthorize` callback to resolve the caller's identity before delegating to the underlying Stripe implementation. When a caller omits `entityId`, it means they want to act on themselves — your callback is responsible for deriving their identity from the Convex `context`.
810
+
811
+ stripe.helpers({
812
+ authenticateAndAuthorize: async ({ context, operation, entityId }) => {
813
+ // Return [isAuthorized, resolvedEntityId | null]
814
+ }
815
+ })
816
+ ```
817
+
818
+ **`authenticateAndAuthorize` parameters (passed as a single object):**
819
+
820
+ | Parameter | Type | Description |
821
+ | :---------- | :--------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
822
+ | `context` | `GenericActionCtx<any>` or `GenericQueryCtx<any>` | The Convex context, **narrowed by `operation`**: action ops (`createCustomer`, `subscribe`, `pay`, `portal`) receive `GenericActionCtx`; query ops (`products`, `subscription`, `customer`) receive `GenericQueryCtx`. |
823
+ | `operation` | `"createCustomer" \| "subscribe" \| "pay" \| "products" \| "subscription" \| "customer" \| "portal"` | The operation being performed. |
824
+ | `entityId` | `string \| undefined` | The entity ID passed by the caller, or `undefined` if acting on themselves. |
825
+
826
+ **Returns:** `Promise<[boolean, string | null]>` — `[isAuthorized, entityId]`.
827
+
828
+ **Returned functions:**
829
+
830
+ | Name | Kind | Description |
831
+ | :--------------- | :--------------- | :------------------------------------------- |
832
+ | `createCustomer` | `internalAction` | Create a Stripe customer for a given entity. |
833
+ | `products` | `query` | List all synced products with their prices. |
834
+ | `subscription` | `query` | Get the entity's active subscription. |
835
+ | `customer` | `query` | Get the entity's Stripe customer record. |
836
+
837
+ ## Pre-built Helper Functions
838
+
839
+ `stripe.helpers()` is the fastest way to add Stripe to your app. Instead of hand-writing each Convex function, call `stripe.helpers()` once and export the returned functions:
840
+
841
+ ```ts
842
+ // convex/stripe.ts
843
+ import { getAuthUserId } from "@convex-dev/auth/server";
844
+ import { internalConvexStripe } from "@raideno/convex-stripe/server";
845
+ import type { HelperAuthCallback } from "@raideno/convex-stripe/server";
846
+ import schema from "./schema";
847
+
848
+ export const { stripe, store, sync } = internalConvexStripe({
849
+ schema,
850
+ stripe: {
851
+ secret_key: process.env.STRIPE_SECRET_KEY!,
852
+ account_webhook_secret: process.env.STRIPE_ACCOUNT_WEBHOOK_SECRET!,
853
+ },
854
+ });
855
+
856
+ // A single callback that authenticates every helper function.
857
+ // `context` is automatically typed as GenericActionCtx or GenericQueryCtx
858
+ // depending on which `operation` is being executed.
859
+ const authenticateAndAuthorize: HelperAuthCallback = async ({
860
+ context, // GenericActionCtx<any> or GenericQueryCtx<any>
861
+ operation,
862
+ entityId,
863
+ }) => {
864
+ // For product listings there is no specific entity — allow everyone.
865
+ if (operation === "products") return [true, null];
866
+
867
+ const userId = await getAuthUserId(context);
868
+ if (!userId) return [false, null];
869
+
870
+ // If the caller passed an explicit entityId, use it; otherwise they act on themselves.
871
+ return [true, entityId ?? userId];
872
+ };
873
+
874
+ export const {
875
+ createCustomer,
876
+ products,
877
+ subscription,
878
+ customer,
879
+ } = stripe.helpers({
880
+ authenticateAndAuthorize
881
+ });
882
+ ```
883
+
884
+ Then register `createCustomer` with your auth callbacks (the same way as the manual approach), and call the rest from your frontend:
885
+
886
+ ```ts
887
+ // frontend
888
+ const items = await convex.query(api.stripe.products, {});
889
+ const sub = await convex.query(api.stripe.subscription, {});
890
+ const me = await convex.query(api.stripe.customer, {});
891
+
892
+ // Return URLs are handled by helper config, so frontend calls stay clean.
893
+ const { url } = await convex.action(api.stripe.subscribe, {
894
+ priceId: "price_xxx",
895
+ });
896
+
897
+ const { url } = await convex.action(api.stripe.pay, {
898
+ referenceId: "order_123",
899
+ line_items: [{ price: "price_yyy", quantity: 1 }],
900
+ });
901
+
902
+ const { url } = await convex.action(api.stripe.portal, {});
903
+ ```
904
+
905
+ ### `helpers.createCustomer`
906
+
907
+ An **internal** Convex action that creates a Stripe customer for the given `entityId`. Designed to be called from auth lifecycle callbacks (e.g. `afterUserCreatedOrUpdated`), not from the frontend directly.
908
+
909
+ **Arguments:**
910
+
911
+ | Parameter | Type | Required | Description |
912
+ | :--------- | :------- | :------- | :-------------------------------------------------- |
913
+ | `entityId` | `string` | Yes | Your app's internal identifier for the entity. |
914
+ | `email` | `string` | No | Email address to set on the Stripe customer record. |
915
+
916
+ **Returns:** The Stripe customer document from your Convex database.
917
+
918
+ ### `helpers.products`
919
+
920
+ A **public** Convex query that returns all synced Stripe products with their associated prices nested inside.
921
+
922
+ **Arguments:** none
923
+
924
+ **Returns:** An array of product documents from `stripeProducts`, each with a `prices` field containing all related entries from `stripePrices`.
925
+
926
+ ### `helpers.subscription`
927
+
928
+ A **public** Convex query that returns the active Stripe subscription for the authenticated entity, or `null` if they have no subscription.
929
+
930
+ **Arguments:**
931
+
932
+ | Parameter | Type | Required | Description |
933
+ | :--------- | :------- | :------- | :-------------------------------------------- |
934
+ | `entityId` | `string` | No | Override the entity (defaults to the caller). |
935
+
936
+ **Returns:** The subscription document from `stripeSubscriptions`, or `null`.
937
+
938
+ ### `helpers.customer`
939
+
940
+ A **public** Convex query that returns the Stripe customer record for the authenticated entity, or `null` if they have no customer yet.
941
+
942
+ **Arguments:**
943
+
944
+ | Parameter | Type | Required | Description |
945
+ | :--------- | :------- | :------- | :-------------------------------------------- |
946
+ | `entityId` | `string` | No | Override the entity (defaults to the caller). |
947
+
948
+ **Returns:** The customer document from `stripeCustomers`, or `null`.
949
+
820
950
  ## Synced Tables
821
951
 
822
952
  The library automatically syncs the following 24 Stripe resource types into your Convex database:
package/dist/index.d.ts CHANGED
@@ -4,10 +4,13 @@ import { GenericActionCtx } from 'convex/server';
4
4
  import { GenericDataModel } from 'convex/server';
5
5
  import { GenericId } from 'convex/values';
6
6
  import { GenericMutationCtx } from 'convex/server';
7
+ import { GenericQueryCtx } from 'convex/server';
8
+ import { GenericSchema } from 'convex/server';
7
9
  import { HttpRouter } from 'convex/server';
8
10
  import { Infer } from 'convex/values';
9
11
  import { RegisteredAction } from 'convex/server';
10
12
  import { RegisteredMutation } from 'convex/server';
13
+ import { RegisteredQuery } from 'convex/server';
11
14
  import { RoutableMethod } from 'convex/server';
12
15
  import { SchemaDefinition } from 'convex/server';
13
16
  import { TableDefinition } from 'convex/server';
@@ -23,12 +26,34 @@ import { VRecord } from 'convex/values';
23
26
  import { VString } from 'convex/values';
24
27
  import { VUnion } from 'convex/values';
25
28
 
29
+ /**
30
+ * The operations handled by action-type helpers (run in an action context).
31
+ */
32
+ declare type ActionOperation = "createCustomer";
33
+
26
34
  declare type AllRedirectHandlers = (typeof REDIRECT_HANDLERS)[number];
27
35
 
28
36
  export declare const allStripeTablesExcept: (tables: Array<keyof typeof stripeTables>) => typeof stripeTables;
29
37
 
30
38
  declare type ArgSchema = Record<string, Validator<any, "optional" | "required", any>>;
31
39
 
40
+ /**
41
+ * Arguments passed to the `authenticateAndAuthorize` callback.
42
+ *
43
+ * This is a discriminated union: when `operation` is an action-type operation,
44
+ * `context` is narrowed to `GenericActionCtx<any>`. For query-type operations,
45
+ * it is narrowed to `GenericQueryCtx<any>`.
46
+ */
47
+ declare type AuthArgs = {
48
+ context: GenericActionCtx<any>;
49
+ operation: ActionOperation;
50
+ entityId?: string;
51
+ } | {
52
+ context: GenericQueryCtx<any>;
53
+ operation: QueryOperation;
54
+ entityId?: string;
55
+ };
56
+
32
57
  export declare function buildSignedReturnUrl<O extends ReturnOrigin>({ configuration, origin, failureUrl, targetUrl, data, }: {
33
58
  configuration: InternalConfiguration;
34
59
  origin: O;
@@ -169,6 +194,32 @@ export declare function defineRedirectHandler<const T extends readonly string[],
169
194
 
170
195
  export declare function defineWebhookHandler<const T extends default_2.Event.Type>(handler: WebhookHandler<T>): WebhookHandler<T>;
171
196
 
197
+ /**
198
+ * The callback used by `stripe.helpers()` to authenticate and authorize
199
+ * the caller for a given operation.
200
+ *
201
+ * **Contract:**
202
+ * - Return `[true, entityId]` when the caller is authenticated and the
203
+ * resolved `entityId` should be used for the Stripe operation.
204
+ * - Return `[false, null]` (or throw) to reject the request.
205
+ * - When no `entityId` is passed to the helper function, it means the
206
+ * caller wants to act on themselves — the callback is responsible for
207
+ * deriving their identity from `context` (e.g. via `getAuthUserId`).
208
+ *
209
+ * @example
210
+ * const authenticateAndAuthorize: HelperAuthCallback = async ({
211
+ * context,
212
+ * operation,
213
+ * entityId,
214
+ * }) => {
215
+ * if (operation === "products") return [true, null];
216
+ * const userId = await getAuthUserId(context);
217
+ * if (!userId) return [false, null];
218
+ * return [true, entityId ?? userId];
219
+ * };
220
+ */
221
+ export declare type HelperAuthCallback = (args: AuthArgs) => Promise<[boolean, string | null]>;
222
+
172
223
  declare type InferArgs<S extends ArgSchema> = {
173
224
  [K in keyof S as S[K] extends Validator<any, "required", any> ? K : never]: Infer<S[K]>;
174
225
  } & {
@@ -176,6 +227,7 @@ declare type InferArgs<S extends ArgSchema> = {
176
227
  };
177
228
 
178
229
  export declare interface InputConfiguration {
230
+ schema: SchemaDefinition<GenericSchema, true>;
179
231
  stripe: {
180
232
  /** Stripe API version to pin against (recommended for stability). */
181
233
  version?: default_2.StripeConfig["apiVersion"];
@@ -189,7 +241,7 @@ export declare interface InputConfiguration {
189
241
  */
190
242
  connect_webhook_secret?: string;
191
243
  };
192
- sync: {
244
+ sync?: {
193
245
  catalog?: {
194
246
  /** Products to ensure exist in Stripe (optional bootstrap). */
195
247
  products?: default_2.ProductCreateParams[];
@@ -235,7 +287,7 @@ export declare interface InputConfiguration {
235
287
  /** Optional Billing Portal configuration to create/sync. */
236
288
  portal?: default_2.BillingPortal.ConfigurationCreateParams;
237
289
  /** Which Stripe tables you want to sync into Convex. */
238
- tables: Record<keyof typeof stripeTables, boolean>;
290
+ tables?: Record<keyof typeof stripeTables, boolean>;
239
291
  };
240
292
  callbacks?: {
241
293
  /** Called after a row is inserted/upserted/deleted in your Stripe tables. */
@@ -282,7 +334,11 @@ export declare type InternalConfiguration = RecursiveDeepRequired<InputConfigura
282
334
  * },
283
335
  * });
284
336
  */
285
- export declare const internalConvexStripe: (configuration_: InputConfiguration, options_?: InputOptions) => {
337
+ export declare const internalConvexStripe: (configuration_: InputConfiguration & {
338
+ sync?: {
339
+ tables?: never;
340
+ };
341
+ }, options_?: InputOptions) => {
286
342
  stripe: {
287
343
  /** Raw HTTP handler descriptors. Prefer `addHttpRoutes` unless you need manual control. */
288
344
  http: {
@@ -318,7 +374,7 @@ export declare const internalConvexStripe: (configuration_: InputConfiguration,
318
374
  * stripe.addHttpRoutes(http);
319
375
  * export default http;
320
376
  */
321
- addHttpRoutes: (http: HttpRouter, config?: InputConfiguration) => void;
377
+ addHttpRoutes: (http: HttpRouter) => void;
322
378
  /**
323
379
  * Opens a Stripe Billing Portal session for an existing customer.
324
380
  * Use this to let users manage their subscription, invoices, and payment methods.
@@ -471,6 +527,283 @@ export declare const internalConvexStripe: (configuration_: InputConfiguration,
471
527
  */
472
528
  link: (context: GenericActionCtx<any>, args: Parameters<(typeof CreateAccountLinkImplementation)["handler"]>[1], options?: Parameters<(typeof CreateAccountLinkImplementation)["handler"]>[2]) => Promise<default_2.Response<default_2.AccountLink>>;
473
529
  };
530
+ /**
531
+ * Returns a set of pre-built, authorization-aware Convex functions
532
+ * (actions and queries) that cover the most common Stripe operations.
533
+ *
534
+ * Each returned function calls your `authenticateAndAuthorize` callback
535
+ * to resolve the caller's identity and enforce access control before
536
+ * delegating to the underlying Stripe implementation.
537
+ *
538
+ * When a caller omits `entityId`, it signals that they want to act on
539
+ * themselves — your callback is responsible for deriving their identity
540
+ * from `context` (e.g. via `getAuthUserId`).
541
+ *
542
+ * **Returned functions:**
543
+ * - `internalCreateCustomer` — internal action: create a Stripe customer for a given entity.
544
+ * - `subscribe` — public action: create a subscription checkout session.
545
+ * - `pay` — public action: create a one-time payment checkout session.
546
+ * - `products` — public query: list all products with their prices.
547
+ * - `subscription` — public query: get the entity's active subscription.
548
+ * - `customer` — public query: get the entity's Stripe customer record.
549
+ * - `portal` — public action: open a Billing Portal session.
550
+ *
551
+ * @param config - Configuration for the helpers.
552
+ * @param config.authenticateAndAuthorize - Callback that authenticates the
553
+ * caller and returns `[isAuthorized, entityId | null]`.
554
+ * @param config.urls - Centralized return URL configuration (required). URLs are grouped
555
+ * by operation: `subscribe`, `pay`, and `portal`.
556
+ *
557
+ * @example
558
+ * // convex/stripe.ts
559
+ * import { getAuthUserId } from "@convex-dev/auth/server";
560
+ *
561
+ * export const { stripe, store, sync } = internalConvexStripe({ ... });
562
+ *
563
+ * export const { internalCreateCustomer, subscribe, pay, products, subscription, customer, portal } =
564
+ * stripe.helpers({
565
+ * authenticateAndAuthorize: async ({ context, operation, entityId }) => {
566
+ * const userId = await getAuthUserId(context);
567
+ * if (!userId) return [false, null];
568
+ * // If caller passed an explicit entityId, use it; otherwise act on themselves.
569
+ * return [true, entityId ?? userId];
570
+ * },
571
+ * urls: {
572
+ * subscribe: {
573
+ * success: "https://example.com/success",
574
+ * cancel: "https://example.com/cancel",
575
+ * failure: "https://example.com/error",
576
+ * },
577
+ * pay: {
578
+ * success: "https://example.com/pay-success",
579
+ * cancel: "https://example.com/pay-cancel",
580
+ * failure: "https://example.com/pay-error",
581
+ * },
582
+ * portal: {
583
+ * return: "https://example.com/account",
584
+ * failure: "https://example.com/portal-error",
585
+ * },
586
+ * },
587
+ * });
588
+ */
589
+ helpers: (config: StripeHelpersConfig) => {
590
+ createCustomer: RegisteredAction<"internal", {
591
+ email?: string | undefined;
592
+ entityId: string;
593
+ }, Promise<{
594
+ _id: GenericId<"stripeCustomers">;
595
+ _creationTime: number;
596
+ accountId?: string | undefined;
597
+ entityId?: string | undefined;
598
+ stripe: {
599
+ email?: string | null | undefined;
600
+ metadata?: Record<string, string | number | null> | null | undefined;
601
+ address?: {
602
+ country?: string | null | undefined;
603
+ city?: string | null | undefined;
604
+ line1?: string | null | undefined;
605
+ line2?: string | null | undefined;
606
+ postal_code?: string | null | undefined;
607
+ state?: string | null | undefined;
608
+ } | null | undefined;
609
+ name?: string | null | undefined;
610
+ phone?: string | null | undefined;
611
+ shipping?: {
612
+ address?: {
613
+ country?: string | null | undefined;
614
+ city?: string | null | undefined;
615
+ line1?: string | null | undefined;
616
+ line2?: string | null | undefined;
617
+ postal_code?: string | null | undefined;
618
+ state?: string | null | undefined;
619
+ } | undefined;
620
+ name?: string | undefined;
621
+ phone?: string | null | undefined;
622
+ carrier?: string | null | undefined;
623
+ tracking_number?: string | null | undefined;
624
+ } | null | undefined;
625
+ currency?: string | null | undefined;
626
+ description?: string | null | undefined;
627
+ discount?: any;
628
+ tax?: {
629
+ ip_address?: string | null | undefined;
630
+ automatic_tax: "failed" | "not_collecting" | "supported" | "unrecognized_location";
631
+ location: {
632
+ state?: string | null | undefined;
633
+ country: string;
634
+ source: string;
635
+ } | null;
636
+ } | undefined;
637
+ cash_balance?: any;
638
+ default_source?: string | null | undefined;
639
+ delinquent?: boolean | null | undefined;
640
+ invoice_credit_balance?: any;
641
+ invoice_prefix?: string | null | undefined;
642
+ invoice_settings?: any;
643
+ next_invoice_sequence?: number | null | undefined;
644
+ preferred_locales?: string[] | null | undefined;
645
+ sources?: any;
646
+ subscriptions?: any;
647
+ tax_exempt?: "none" | "reverse" | "exempt" | null | undefined;
648
+ tax_ids?: any;
649
+ test_clock?: string | null | undefined;
650
+ object: string;
651
+ id: string;
652
+ created: number;
653
+ livemode: boolean;
654
+ balance: number;
655
+ };
656
+ lastSyncedAt: number;
657
+ customerId: string;
658
+ }>>;
659
+ products: RegisteredQuery<"public", {}, Promise<{
660
+ prices: {
661
+ _id: GenericId<"stripePrices">;
662
+ _creationTime: number;
663
+ accountId?: string | undefined;
664
+ stripe: {
665
+ metadata?: Record<string, string | number | null> | null | undefined;
666
+ object: string;
667
+ type: "one_time" | "recurring";
668
+ id: string;
669
+ created: number;
670
+ active: boolean;
671
+ livemode: boolean;
672
+ currency: string;
673
+ nickname: string | null;
674
+ billing_scheme: "per_unit" | "tiered";
675
+ tiers_mode: "graduated" | "volume" | null;
676
+ recurring: {
677
+ interval: "day" | "week" | "month" | "year";
678
+ interval_count: number;
679
+ meter: string | null;
680
+ trial_period_days: number | null;
681
+ usage_type: "licensed" | "metered";
682
+ } | null;
683
+ productId: string;
684
+ unit_amount: number | null;
685
+ lookup_key: string | null;
686
+ transform_quantity: {
687
+ divide_by: number;
688
+ round: "up" | "down";
689
+ } | null;
690
+ unit_amount_decimal: string | null;
691
+ };
692
+ lastSyncedAt: number;
693
+ priceId: string;
694
+ }[];
695
+ _id: GenericId<"stripeProducts">;
696
+ _creationTime: number;
697
+ accountId?: string | undefined;
698
+ stripe: {
699
+ metadata?: Record<string, string | number | null> | null | undefined;
700
+ statement_descriptor?: string | null | undefined;
701
+ unit_label?: string | null | undefined;
702
+ object: string;
703
+ id: string;
704
+ created: number;
705
+ active: boolean;
706
+ name: string;
707
+ url: string | null;
708
+ livemode: boolean;
709
+ updated: number;
710
+ description: string | null;
711
+ images: string[];
712
+ package_dimensions: {
713
+ length: number;
714
+ height: number;
715
+ weight: number;
716
+ width: number;
717
+ } | null;
718
+ shippable: boolean | null;
719
+ marketing_features: {
720
+ name?: string | null | undefined;
721
+ }[];
722
+ default_price: string | null;
723
+ };
724
+ productId: string;
725
+ lastSyncedAt: number;
726
+ }[]>>;
727
+ subscription: RegisteredQuery<"public", {
728
+ entityId?: string | undefined;
729
+ }, Promise<{
730
+ _id: GenericId<"stripeSubscriptions">;
731
+ _creationTime: number;
732
+ accountId?: string | undefined;
733
+ stripe: any;
734
+ lastSyncedAt: number;
735
+ customerId: string;
736
+ subscriptionId: string | null;
737
+ } | null>>;
738
+ customer: RegisteredQuery<"public", {
739
+ entityId?: string | undefined;
740
+ }, Promise<{
741
+ _id: GenericId<"stripeCustomers">;
742
+ _creationTime: number;
743
+ accountId?: string | undefined;
744
+ entityId?: string | undefined;
745
+ stripe: {
746
+ email?: string | null | undefined;
747
+ metadata?: Record<string, string | number | null> | null | undefined;
748
+ address?: {
749
+ country?: string | null | undefined;
750
+ city?: string | null | undefined;
751
+ line1?: string | null | undefined;
752
+ line2?: string | null | undefined;
753
+ postal_code?: string | null | undefined;
754
+ state?: string | null | undefined;
755
+ } | null | undefined;
756
+ name?: string | null | undefined;
757
+ phone?: string | null | undefined;
758
+ shipping?: {
759
+ address?: {
760
+ country?: string | null | undefined;
761
+ city?: string | null | undefined;
762
+ line1?: string | null | undefined;
763
+ line2?: string | null | undefined;
764
+ postal_code?: string | null | undefined;
765
+ state?: string | null | undefined;
766
+ } | undefined;
767
+ name?: string | undefined;
768
+ phone?: string | null | undefined;
769
+ carrier?: string | null | undefined;
770
+ tracking_number?: string | null | undefined;
771
+ } | null | undefined;
772
+ currency?: string | null | undefined;
773
+ description?: string | null | undefined;
774
+ discount?: any;
775
+ tax?: {
776
+ ip_address?: string | null | undefined;
777
+ automatic_tax: "failed" | "not_collecting" | "supported" | "unrecognized_location";
778
+ location: {
779
+ state?: string | null | undefined;
780
+ country: string;
781
+ source: string;
782
+ } | null;
783
+ } | undefined;
784
+ cash_balance?: any;
785
+ default_source?: string | null | undefined;
786
+ delinquent?: boolean | null | undefined;
787
+ invoice_credit_balance?: any;
788
+ invoice_prefix?: string | null | undefined;
789
+ invoice_settings?: any;
790
+ next_invoice_sequence?: number | null | undefined;
791
+ preferred_locales?: string[] | null | undefined;
792
+ sources?: any;
793
+ subscriptions?: any;
794
+ tax_exempt?: "none" | "reverse" | "exempt" | null | undefined;
795
+ tax_ids?: any;
796
+ test_clock?: string | null | undefined;
797
+ object: string;
798
+ id: string;
799
+ created: number;
800
+ livemode: boolean;
801
+ balance: number;
802
+ };
803
+ lastSyncedAt: number;
804
+ customerId: string;
805
+ } | null>>;
806
+ };
474
807
  };
475
808
  /**
476
809
  * Internal Convex mutation that persists a Stripe object into your database.
@@ -1656,6 +1989,11 @@ declare const PortalImplementation: {
1656
1989
  } & Omit<default_2.BillingPortal.SessionCreateParams, "customer" | "return_url">, stripeOptions: default_2.RequestOptions, configuration: InternalConfiguration, options: InternalOptions) => Promise<default_2.Response<default_2.BillingPortal.Session>>;
1657
1990
  };
1658
1991
 
1992
+ /**
1993
+ * The operations handled by query-type helpers (run in a query context).
1994
+ */
1995
+ declare type QueryOperation = "products" | "subscription" | "customer";
1996
+
1659
1997
  declare type RecursiveDeepRequired<T> = T extends (...args: any[]) => any ? T : T extends object ? {
1660
1998
  [K in keyof T]-?: RecursiveDeepRequired<T[K]>;
1661
1999
  } : T;
@@ -1691,6 +2029,16 @@ declare type ReturnOrigin = (typeof RETURN_ORIGINS)[number];
1691
2029
 
1692
2030
  declare type StripeDataModel = DataModelFromSchemaDefinition<typeof stripeSchema>;
1693
2031
 
2032
+ /**
2033
+ * Configuration for the pre-built Stripe helpers.
2034
+ */
2035
+ export declare interface StripeHelpersConfig {
2036
+ /**
2037
+ * Callback that authenticates the caller and returns [isAuthorized, resolvedEntityId].
2038
+ */
2039
+ authenticateAndAuthorize: HelperAuthCallback;
2040
+ }
2041
+
1694
2042
  declare const stripeSchema: SchemaDefinition< {
1695
2043
  stripeAccounts: TableDefinition<VObject< {
1696
2044
  entityId?: string | undefined;
@@ -9312,12 +9660,6 @@ export declare const SYNC_HANDLERS: readonly [{
9312
9660
  }, configuration: InternalConfiguration, options: InternalOptions) => Promise<void>;
9313
9661
  }];
9314
9662
 
9315
- export declare const syncAllTables: () => Record<keyof typeof stripeTables, boolean>;
9316
-
9317
- export declare const syncAllTablesExcept: (tables: Array<keyof typeof stripeTables>) => Record<keyof typeof stripeTables, boolean>;
9318
-
9319
- export declare const syncOnlyTables: (tables: Array<keyof typeof stripeTables>) => Record<keyof typeof stripeTables, boolean>;
9320
-
9321
9663
  export declare const WEBHOOK_HANDLERS: readonly [WebhookHandler<"account.updated">, WebhookHandler<"billing_portal.configuration.created" | "billing_portal.configuration.updated">, WebhookHandler<"capability.updated">, WebhookHandler<"charge.captured" | "charge.expired" | "charge.failed" | "charge.pending" | "charge.refunded" | "charge.succeeded" | "charge.updated">, WebhookHandler<"checkout.session.async_payment_failed" | "checkout.session.async_payment_succeeded" | "checkout.session.completed" | "checkout.session.expired">, WebhookHandler<"coupon.created" | "coupon.deleted" | "coupon.updated">, WebhookHandler<"credit_note.created" | "credit_note.updated" | "credit_note.voided">, WebhookHandler<"customer.created" | "customer.deleted" | "customer.updated">, WebhookHandler<"charge.dispute.closed" | "charge.dispute.created" | "charge.dispute.funds_reinstated" | "charge.dispute.funds_withdrawn" | "charge.dispute.updated">, WebhookHandler<"radar.early_fraud_warning.created" | "radar.early_fraud_warning.updated">, WebhookHandler<"invoice.created" | "invoice.deleted" | "invoice.finalization_failed" | "invoice.finalized" | "invoice.marked_uncollectible" | "invoice.overdue" | "invoice.overpaid" | "invoice.paid" | "invoice.payment_action_required" | "invoice.payment_failed" | "invoice.payment_succeeded" | "invoice.sent" | "invoice.upcoming" | "invoice.updated" | "invoice.voided" | "invoice.will_be_due">, WebhookHandler<"mandate.updated">, WebhookHandler<"payment_intent.amount_capturable_updated" | "payment_intent.canceled" | "payment_intent.created" | "payment_intent.partially_funded" | "payment_intent.payment_failed" | "payment_intent.processing" | "payment_intent.requires_action" | "payment_intent.succeeded">, WebhookHandler<"payment_method.attached" | "payment_method.automatically_updated" | "payment_method.detached" | "payment_method.updated">, WebhookHandler<"payout.canceled" | "payout.created" | "payout.failed" | "payout.paid" | "payout.reconciliation_completed" | "payout.updated">, WebhookHandler<"plan.created" | "plan.deleted" | "plan.updated">, WebhookHandler<"price.created" | "price.deleted" | "price.updated">, WebhookHandler<"product.created" | "product.deleted" | "product.updated">, WebhookHandler<"promotion_code.created" | "promotion_code.updated">, WebhookHandler<"charge.refund.updated" | "refund.created" | "refund.failed" | "refund.updated">, WebhookHandler<"review.closed" | "review.opened">, WebhookHandler<"setup_intent.canceled" | "setup_intent.created" | "setup_intent.requires_action" | "setup_intent.setup_failed" | "setup_intent.succeeded">, WebhookHandler<"subscription_schedule.aborted" | "subscription_schedule.canceled" | "subscription_schedule.completed" | "subscription_schedule.created" | "subscription_schedule.expiring" | "subscription_schedule.released" | "subscription_schedule.updated">, WebhookHandler<"customer.subscription.created" | "customer.subscription.deleted" | "customer.subscription.paused" | "customer.subscription.pending_update_applied" | "customer.subscription.pending_update_expired" | "customer.subscription.resumed" | "customer.subscription.trial_will_end" | "customer.subscription.updated">, WebhookHandler<"customer.tax_id.created" | "customer.tax_id.deleted" | "customer.tax_id.updated">, WebhookHandler<"transfer.created" | "transfer.reversed" | "transfer.updated">];
9322
9664
 
9323
9665
  declare type WebhookHandler<TEvents extends default_2.Event.Type> = {
package/dist/server.js CHANGED
@@ -2182,6 +2182,31 @@ const internalMutationGeneric = ((functionDefinition) => {
2182
2182
  func._handler = handler;
2183
2183
  return func;
2184
2184
  });
2185
+ async function invokeQuery(func, argsStr) {
2186
+ const requestId = "";
2187
+ const args = jsonToConvex(JSON.parse(argsStr));
2188
+ const queryCtx = {
2189
+ db: setupReader(),
2190
+ auth: setupAuth(requestId),
2191
+ storage: setupStorageReader(requestId),
2192
+ runQuery: (reference, args2) => runUdf("query", reference, args2)
2193
+ };
2194
+ const result = await invokeFunction(func, queryCtx, args);
2195
+ validateReturnValue(result);
2196
+ return JSON.stringify(convexToJson(result === void 0 ? null : result));
2197
+ }
2198
+ const queryGeneric = ((functionDefinition) => {
2199
+ const handler = typeof functionDefinition === "function" ? functionDefinition : functionDefinition.handler;
2200
+ const func = dontCallDirectly("query", handler);
2201
+ assertNotBrowser();
2202
+ func.isQuery = true;
2203
+ func.isPublic = true;
2204
+ func.invokeQuery = (argsStr) => invokeQuery(handler, argsStr);
2205
+ func.exportArgs = exportArgs(functionDefinition);
2206
+ func.exportReturns = exportReturns(functionDefinition);
2207
+ func._handler = handler;
2208
+ return func;
2209
+ });
2185
2210
  async function invokeAction(func, requestId, argsStr) {
2186
2211
  const args = jsonToConvex(JSON.parse(argsStr));
2187
2212
  const calls = setupActionCalls(requestId);
@@ -5612,21 +5637,6 @@ const stripeTables = {
5612
5637
  }).index("byAccountId", ["accountId"]).index(BY_STRIPE_ID_INDEX_NAME, ["capabilityId"])
5613
5638
  };
5614
5639
  defineSchema(stripeTables);
5615
- const syncAllTables = () => Object.fromEntries(
5616
- Object.keys(stripeTables).map((table) => [table, true])
5617
- );
5618
- const syncAllTablesExcept = (tables) => Object.fromEntries(
5619
- Object.keys(stripeTables).map((table) => [
5620
- table,
5621
- !tables.includes(table)
5622
- ])
5623
- );
5624
- const syncOnlyTables = (tables) => Object.fromEntries(
5625
- Object.keys(stripeTables).map((table) => [
5626
- table,
5627
- tables.includes(table)
5628
- ])
5629
- );
5630
5640
  const allStripeTablesExcept = (tables) => Object.fromEntries(
5631
5641
  Object.entries(stripeTables).map(([table, definition]) => [
5632
5642
  table,
@@ -5640,6 +5650,7 @@ const onlyStripeTables = (tables) => Object.fromEntries(
5640
5650
  ])
5641
5651
  );
5642
5652
  const DEFAULT_CONFIGURATION = {
5653
+ schema: void 0,
5643
5654
  stripe: {
5644
5655
  version: "2025-08-27.basil",
5645
5656
  secret_key: "",
@@ -5659,42 +5670,42 @@ const DEFAULT_CONFIGURATION = {
5659
5670
  }
5660
5671
  },
5661
5672
  sync: {
5662
- catalog: {
5663
- products: [],
5664
- prices: [],
5665
- metadataKey: "convex_stripe_key",
5666
- behavior: {
5667
- onExisting: "update",
5668
- onMissingKey: "create"
5669
- }
5670
- },
5671
5673
  tables: {
5672
5674
  stripeAccounts: true,
5675
+ stripeBillingPortalConfigurations: true,
5673
5676
  stripeCapabilities: true,
5674
- stripeTransfers: true,
5677
+ stripeCharges: true,
5678
+ stripeCheckoutSessions: true,
5675
5679
  stripeCoupons: true,
5680
+ stripeCreditNotes: true,
5676
5681
  stripeCustomers: true,
5682
+ stripeDisputes: true,
5683
+ stripeEarlyFraudWarnings: true,
5684
+ stripeInvoices: true,
5685
+ stripeMandates: true,
5686
+ stripePaymentIntents: true,
5687
+ stripePaymentMethods: true,
5688
+ stripePayouts: true,
5689
+ stripePlans: true,
5677
5690
  stripePrices: true,
5678
5691
  stripeProducts: true,
5679
5692
  stripePromotionCodes: true,
5680
- stripeSubscriptions: true,
5681
- stripePayouts: true,
5682
- stripeCheckoutSessions: true,
5683
- stripePaymentIntents: true,
5684
5693
  stripeRefunds: true,
5685
- stripeInvoices: true,
5686
5694
  stripeReviews: true,
5687
- stripeCharges: true,
5688
- stripeCreditNotes: true,
5689
- stripeDisputes: true,
5690
- stripeEarlyFraudWarnings: true,
5691
- stripePaymentMethods: true,
5692
- stripePlans: true,
5693
5695
  stripeSetupIntents: true,
5696
+ stripeSubscriptions: true,
5694
5697
  stripeSubscriptionSchedules: true,
5695
5698
  stripeTaxIds: true,
5696
- stripeMandates: true,
5697
- stripeBillingPortalConfigurations: true
5699
+ stripeTransfers: true
5700
+ },
5701
+ catalog: {
5702
+ products: [],
5703
+ prices: [],
5704
+ metadataKey: "convex_stripe_key",
5705
+ behavior: {
5706
+ onExisting: "update",
5707
+ onMissingKey: "create"
5708
+ }
5698
5709
  },
5699
5710
  webhooks: {
5700
5711
  account: {
@@ -5763,8 +5774,13 @@ const DEFAULT_CONFIGURATION = {
5763
5774
  }
5764
5775
  }
5765
5776
  };
5766
- const normalizeConfiguration = (config) => {
5767
- return deepmerge(DEFAULT_CONFIGURATION, config);
5777
+ const normalizeConfiguration = (configuration) => {
5778
+ const output = deepmerge(DEFAULT_CONFIGURATION, configuration);
5779
+ const stripeTableNames = Object.keys(stripeTables);
5780
+ output.sync.tables = Object.fromEntries(
5781
+ Object.keys(configuration.schema.tables).filter((table) => stripeTableNames.includes(table)).map((table) => [table, true])
5782
+ );
5783
+ return output;
5768
5784
  };
5769
5785
  const DEFAULT_OPTIONS = {
5770
5786
  store: "store",
@@ -6938,6 +6954,110 @@ const SubscribeImplementation = defineActionCallableFunction({
6938
6954
  return checkout;
6939
6955
  }
6940
6956
  });
6957
+ const buildCreateCustomer = (configuration, options, authenticateAndAuthorize) => internalActionGeneric({
6958
+ args: {
6959
+ entityId: v.string(),
6960
+ email: v.optional(v.string())
6961
+ },
6962
+ handler: async (context_, args) => {
6963
+ const context = context_;
6964
+ const [authorized, resolvedEntityId] = await authenticateAndAuthorize({
6965
+ context,
6966
+ operation: "createCustomer",
6967
+ entityId: args.entityId
6968
+ });
6969
+ if (!authorized || !resolvedEntityId) {
6970
+ throw new Error("Unauthorized");
6971
+ }
6972
+ return CreateCustomerImplementation.handler(
6973
+ context,
6974
+ { entityId: resolvedEntityId, email: args.email },
6975
+ {},
6976
+ configuration,
6977
+ options
6978
+ );
6979
+ }
6980
+ });
6981
+ const buildCustomer = (_configuration, _options, authenticateAndAuthorize) => queryGeneric({
6982
+ args: {
6983
+ entityId: v.optional(v.string())
6984
+ },
6985
+ handler: async (context_, args) => {
6986
+ const context = context_;
6987
+ const [authorized, resolvedEntityId] = await authenticateAndAuthorize({
6988
+ context,
6989
+ operation: "customer",
6990
+ entityId: args.entityId
6991
+ });
6992
+ if (!authorized || !resolvedEntityId) {
6993
+ throw new Error("Unauthorized");
6994
+ }
6995
+ const customer = await context.db.query("stripeCustomers").withIndex("byEntityId", (q) => q.eq("entityId", resolvedEntityId)).unique();
6996
+ return customer ?? null;
6997
+ }
6998
+ });
6999
+ const buildProducts = (_configuration, _options, authenticateAndAuthorize) => queryGeneric({
7000
+ args: {},
7001
+ handler: async (context_) => {
7002
+ const context = context_;
7003
+ const [authorized] = await authenticateAndAuthorize({
7004
+ context,
7005
+ operation: "products",
7006
+ entityId: void 0
7007
+ });
7008
+ if (!authorized) {
7009
+ throw new Error("Unauthorized");
7010
+ }
7011
+ const prices = await context.db.query("stripePrices").collect();
7012
+ const products = await context.db.query("stripeProducts").collect();
7013
+ return products.map(
7014
+ (product) => ({
7015
+ ...product,
7016
+ prices: prices.filter((price) => price.stripe.productId === product.productId)
7017
+ })
7018
+ );
7019
+ }
7020
+ });
7021
+ const buildSubscription = (_configuration, _options, authenticateAndAuthorize) => queryGeneric({
7022
+ args: {
7023
+ entityId: v.optional(v.string())
7024
+ },
7025
+ handler: async (context_, args) => {
7026
+ const context = context_;
7027
+ const [authorized, resolvedEntityId] = await authenticateAndAuthorize({
7028
+ context,
7029
+ operation: "subscription",
7030
+ entityId: args.entityId
7031
+ });
7032
+ if (!authorized || !resolvedEntityId) {
7033
+ throw new Error("Unauthorized");
7034
+ }
7035
+ const customer = await context.db.query("stripeCustomers").withIndex("byEntityId", (q) => q.eq("entityId", resolvedEntityId)).unique();
7036
+ if (!customer) return null;
7037
+ const subscription = await context.db.query("stripeSubscriptions").withIndex(
7038
+ "byCustomerId",
7039
+ (q) => q.eq("customerId", customer.customerId)
7040
+ ).unique();
7041
+ return subscription ?? null;
7042
+ }
7043
+ });
7044
+ const buildHelpers = (configuration, options, config) => {
7045
+ const { authenticateAndAuthorize } = config;
7046
+ return {
7047
+ createCustomer: buildCreateCustomer(
7048
+ configuration,
7049
+ options,
7050
+ authenticateAndAuthorize
7051
+ ),
7052
+ products: buildProducts(configuration, options, authenticateAndAuthorize),
7053
+ subscription: buildSubscription(
7054
+ configuration,
7055
+ options,
7056
+ authenticateAndAuthorize
7057
+ ),
7058
+ customer: buildCustomer(configuration, options, authenticateAndAuthorize)
7059
+ };
7060
+ };
6941
7061
  const pickProductUpdateParams = (product) => {
6942
7062
  const {
6943
7063
  active,
@@ -9527,8 +9647,7 @@ const internalConvexStripe = (configuration_, options_) => {
9527
9647
  * stripe.addHttpRoutes(http);
9528
9648
  * export default http;
9529
9649
  */
9530
- addHttpRoutes: (http, config) => {
9531
- config = normalizeConfiguration(config || configuration_);
9650
+ addHttpRoutes: (http) => {
9532
9651
  http.route({
9533
9652
  path: http_.webhook.path,
9534
9653
  method: http_.webhook.method,
@@ -9637,7 +9756,71 @@ const internalConvexStripe = (configuration_, options_) => {
9637
9756
  ConvexStripeInternalOptions
9638
9757
  );
9639
9758
  }
9640
- }
9759
+ },
9760
+ /**
9761
+ * Returns a set of pre-built, authorization-aware Convex functions
9762
+ * (actions and queries) that cover the most common Stripe operations.
9763
+ *
9764
+ * Each returned function calls your `authenticateAndAuthorize` callback
9765
+ * to resolve the caller's identity and enforce access control before
9766
+ * delegating to the underlying Stripe implementation.
9767
+ *
9768
+ * When a caller omits `entityId`, it signals that they want to act on
9769
+ * themselves — your callback is responsible for deriving their identity
9770
+ * from `context` (e.g. via `getAuthUserId`).
9771
+ *
9772
+ * **Returned functions:**
9773
+ * - `internalCreateCustomer` — internal action: create a Stripe customer for a given entity.
9774
+ * - `subscribe` — public action: create a subscription checkout session.
9775
+ * - `pay` — public action: create a one-time payment checkout session.
9776
+ * - `products` — public query: list all products with their prices.
9777
+ * - `subscription` — public query: get the entity's active subscription.
9778
+ * - `customer` — public query: get the entity's Stripe customer record.
9779
+ * - `portal` — public action: open a Billing Portal session.
9780
+ *
9781
+ * @param config - Configuration for the helpers.
9782
+ * @param config.authenticateAndAuthorize - Callback that authenticates the
9783
+ * caller and returns `[isAuthorized, entityId | null]`.
9784
+ * @param config.urls - Centralized return URL configuration (required). URLs are grouped
9785
+ * by operation: `subscribe`, `pay`, and `portal`.
9786
+ *
9787
+ * @example
9788
+ * // convex/stripe.ts
9789
+ * import { getAuthUserId } from "@convex-dev/auth/server";
9790
+ *
9791
+ * export const { stripe, store, sync } = internalConvexStripe({ ... });
9792
+ *
9793
+ * export const { internalCreateCustomer, subscribe, pay, products, subscription, customer, portal } =
9794
+ * stripe.helpers({
9795
+ * authenticateAndAuthorize: async ({ context, operation, entityId }) => {
9796
+ * const userId = await getAuthUserId(context);
9797
+ * if (!userId) return [false, null];
9798
+ * // If caller passed an explicit entityId, use it; otherwise act on themselves.
9799
+ * return [true, entityId ?? userId];
9800
+ * },
9801
+ * urls: {
9802
+ * subscribe: {
9803
+ * success: "https://example.com/success",
9804
+ * cancel: "https://example.com/cancel",
9805
+ * failure: "https://example.com/error",
9806
+ * },
9807
+ * pay: {
9808
+ * success: "https://example.com/pay-success",
9809
+ * cancel: "https://example.com/pay-cancel",
9810
+ * failure: "https://example.com/pay-error",
9811
+ * },
9812
+ * portal: {
9813
+ * return: "https://example.com/account",
9814
+ * failure: "https://example.com/portal-error",
9815
+ * },
9816
+ * },
9817
+ * });
9818
+ */
9819
+ helpers: (config) => buildHelpers(
9820
+ ConvexStripeInternalConfiguration,
9821
+ ConvexStripeInternalOptions,
9822
+ config
9823
+ )
9641
9824
  },
9642
9825
  /**
9643
9826
  * Internal Convex mutation that persists a Stripe object into your database.
@@ -9679,8 +9862,5 @@ export {
9679
9862
  defineWebhookHandler,
9680
9863
  internalConvexStripe,
9681
9864
  onlyStripeTables,
9682
- stripeTables,
9683
- syncAllTables,
9684
- syncAllTablesExcept,
9685
- syncOnlyTables
9865
+ stripeTables
9686
9866
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raideno/convex-stripe",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Easy stripe billing for convex apps.",
5
5
  "keywords": [
6
6
  "billing",