convex-affiliates 1.1.3 → 2.0.0

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
@@ -255,14 +255,22 @@ If you're not using `@convex-dev/stripe`, use the standalone handler with built-
255
255
  // convex/http.ts
256
256
  import { httpRouter } from "convex/server";
257
257
  import { components } from "./_generated/api";
258
- import { createStripeWebhookHandler } from "convex-affiliates";
258
+ import { createAffiliateApi } from "convex-affiliates";
259
259
 
260
260
  const http = httpRouter();
261
261
 
262
+ const affiliates = createAffiliateApi(components.affiliates, {
263
+ auth: async (ctx) => {
264
+ const identity = await ctx.auth.getUserIdentity();
265
+ if (!identity) throw new Error("Not authenticated");
266
+ return identity.subject;
267
+ },
268
+ });
269
+
262
270
  http.route({
263
271
  path: "/webhooks/stripe",
264
272
  method: "POST",
265
- handler: createStripeWebhookHandler(components.affiliates, {
273
+ handler: affiliates.createStripeWebhookHandler({
266
274
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
267
275
  }),
268
276
  });
@@ -168,19 +168,14 @@ export interface CreateAffiliateApiConfig extends AffiliateConfig {
168
168
  * import { createAffiliateApi } from "convex-affiliates";
169
169
  * import { components } from "./_generated/api";
170
170
  *
171
- * export const {
172
- * trackClick,
173
- * register,
174
- * getPortalData,
175
- * adminDashboard,
176
- * } = createAffiliateApi(components.affiliates, {
177
- * defaultCommissionValue: 25,
178
- * auth: async (ctx) => {
179
- * const identity = await ctx.auth.getUserIdentity();
180
- * if (!identity) throw new Error("Not authenticated");
181
- * return identity.subject;
182
- * },
183
- * });
171
+ * export const { trackClick, register, getPortalData } =
172
+ * createAffiliateApi(components.affiliates, {
173
+ * auth: async (ctx) => {
174
+ * const identity = await ctx.auth.getUserIdentity();
175
+ * if (!identity) throw new Error("Not authenticated");
176
+ * return identity.subject;
177
+ * },
178
+ * });
184
179
  * ```
185
180
  */
186
181
  export declare function createAffiliateApi(component: ComponentApi, config: CreateAffiliateApiConfig): {
@@ -950,14 +945,44 @@ export declare function createAffiliateApi(component: ComponentApi, config: Crea
950
945
  slug: string;
951
946
  commissionValue: number;
952
947
  }, Promise<import("../component/_generated/dataModel.js").Id<"campaigns">>>;
948
+ /**
949
+ * Register HTTP routes for affiliate functionality.
950
+ * The component reference is already available from the closure.
951
+ *
952
+ * @param http - The Convex HTTP router
953
+ * @param options - Optional configuration (e.g., pathPrefix)
954
+ *
955
+ * @example
956
+ * ```typescript
957
+ * const affiliates = createAffiliateApi(components.affiliates, { ... });
958
+ * affiliates.registerRoutes(http, { pathPrefix: "/affiliates" });
959
+ * ```
960
+ */
961
+ registerRoutes(http: HttpRouter, options?: {
962
+ pathPrefix?: string;
963
+ }): void;
964
+ /**
965
+ * Create a Stripe webhook handler that processes affiliate-related events.
966
+ * The component reference is already available from the closure.
967
+ *
968
+ * @param webhookConfig - Configuration with webhookSecret
969
+ * @returns An HTTP action handler for Stripe webhooks
970
+ *
971
+ * @example
972
+ * ```typescript
973
+ * const affiliates = createAffiliateApi(components.affiliates, { ... });
974
+ *
975
+ * http.route({
976
+ * path: "/webhooks/stripe",
977
+ * method: "POST",
978
+ * handler: affiliates.createStripeWebhookHandler({
979
+ * webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
980
+ * }),
981
+ * });
982
+ * ```
983
+ */
984
+ createStripeWebhookHandler(webhookConfig: StripeWebhookConfig): import("convex/server").PublicHttpAction;
953
985
  };
954
- /**
955
- * Register HTTP routes for affiliate functionality.
956
- * Useful for public API endpoints and webhook handling.
957
- */
958
- export declare function registerRoutes(http: HttpRouter, component: ComponentApi, options?: {
959
- pathPrefix?: string;
960
- }): void;
961
986
  export interface StripeWebhookConfig {
962
987
  /**
963
988
  * Stripe webhook signing secret (whsec_...).
@@ -966,30 +991,6 @@ export interface StripeWebhookConfig {
966
991
  */
967
992
  webhookSecret: string;
968
993
  }
969
- /**
970
- * Create a Stripe webhook handler that processes affiliate-related events.
971
- * Handles invoice.paid, charge.refunded, and checkout.session.completed events.
972
- *
973
- * @example
974
- * ```typescript
975
- * import { httpRouter } from "convex/server";
976
- * import { createStripeWebhookHandler } from "convex-affiliates";
977
- * import { components } from "./_generated/api";
978
- *
979
- * const http = httpRouter();
980
- *
981
- * http.route({
982
- * path: "/webhooks/stripe",
983
- * method: "POST",
984
- * handler: createStripeWebhookHandler(components.affiliates, {
985
- * webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
986
- * }),
987
- * });
988
- *
989
- * export default http;
990
- * ```
991
- */
992
- export declare function createStripeWebhookHandler(component: ComponentApi, config: StripeWebhookConfig): import("convex/server").PublicHttpAction;
993
994
  /**
994
995
  * Options for Stripe event handlers.
995
996
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -14,22 +14,20 @@ import { affiliateStatusValidator, payoutTermValidator, commissionTypeValidator,
14
14
  * import { createAffiliateApi } from "convex-affiliates";
15
15
  * import { components } from "./_generated/api";
16
16
  *
17
- * export const {
18
- * trackClick,
19
- * register,
20
- * getPortalData,
21
- * adminDashboard,
22
- * } = createAffiliateApi(components.affiliates, {
23
- * defaultCommissionValue: 25,
24
- * auth: async (ctx) => {
25
- * const identity = await ctx.auth.getUserIdentity();
26
- * if (!identity) throw new Error("Not authenticated");
27
- * return identity.subject;
28
- * },
29
- * });
17
+ * export const { trackClick, register, getPortalData } =
18
+ * createAffiliateApi(components.affiliates, {
19
+ * auth: async (ctx) => {
20
+ * const identity = await ctx.auth.getUserIdentity();
21
+ * if (!identity) throw new Error("Not authenticated");
22
+ * return identity.subject;
23
+ * },
24
+ * });
30
25
  * ```
31
26
  */
32
27
  export function createAffiliateApi(component, config) {
28
+ const auth = config.auth;
29
+ const isAdmin = config.isAdmin;
30
+ const hooks = config.hooks;
33
31
  const defaults = {
34
32
  defaultCommissionType: config.defaultCommissionType ?? "percentage",
35
33
  defaultCommissionValue: config.defaultCommissionValue ?? 20,
@@ -57,14 +55,14 @@ export function createAffiliateApi(component, config) {
57
55
  };
58
56
  // Helper to check admin access
59
57
  const requireAdmin = async (ctx) => {
60
- if (config.isAdmin) {
61
- const isAdmin = await config.isAdmin(ctx);
62
- if (!isAdmin)
58
+ if (isAdmin) {
59
+ const isAdminResult = await isAdmin(ctx);
60
+ if (!isAdminResult)
63
61
  throw new Error("Not authorized - admin access required");
64
62
  }
65
63
  else {
66
64
  // Fall back to just requiring auth
67
- await config.auth(ctx);
65
+ await auth(ctx);
68
66
  }
69
67
  };
70
68
  // Helper to get affiliate by userId
@@ -73,7 +71,7 @@ export function createAffiliateApi(component, config) {
73
71
  };
74
72
  // Helper to call lifecycle hooks safely (errors are logged, not thrown)
75
73
  async function callHook(hookName, data) {
76
- const hook = config.hooks?.[hookName];
74
+ const hook = hooks?.[hookName];
77
75
  if (hook) {
78
76
  try {
79
77
  await hook(data);
@@ -201,7 +199,7 @@ export function createAffiliateApi(component, config) {
201
199
  customCode: v.optional(v.string()),
202
200
  },
203
201
  handler: async (ctx, args) => {
204
- const userId = await config.auth(ctx);
202
+ const userId = await auth(ctx);
205
203
  const campaign = await ensureDefaultCampaign(ctx);
206
204
  const result = await ctx.runMutation(component.affiliates.register, {
207
205
  userId,
@@ -243,7 +241,7 @@ export function createAffiliateApi(component, config) {
243
241
  getAffiliate: queryGeneric({
244
242
  args: {},
245
243
  handler: async (ctx) => {
246
- const userId = await config.auth(ctx);
244
+ const userId = await auth(ctx);
247
245
  return ctx.runQuery(component.affiliates.getByUserId, { userId });
248
246
  },
249
247
  }),
@@ -280,7 +278,7 @@ export function createAffiliateApi(component, config) {
280
278
  payoutEmail: v.optional(v.string()),
281
279
  },
282
280
  handler: async (ctx, args) => {
283
- const userId = await config.auth(ctx);
281
+ const userId = await auth(ctx);
284
282
  const affiliate = await ctx.runQuery(component.affiliates.getByUserId, {
285
283
  userId,
286
284
  });
@@ -313,7 +311,7 @@ export function createAffiliateApi(component, config) {
313
311
  getPortalData: queryGeneric({
314
312
  args: {},
315
313
  handler: async (ctx) => {
316
- const userId = await config.auth(ctx);
314
+ const userId = await auth(ctx);
317
315
  return ctx.runQuery(component.analytics.getPortalData, { userId });
318
316
  },
319
317
  }),
@@ -345,7 +343,7 @@ export function createAffiliateApi(component, config) {
345
343
  paginationOpts: paginationOptsValidator,
346
344
  },
347
345
  handler: async (ctx, args) => {
348
- const userId = await config.auth(ctx);
346
+ const userId = await auth(ctx);
349
347
  const affiliate = await getAffiliateByUserId(ctx, userId);
350
348
  if (!affiliate) {
351
349
  return { page: [], isDone: true, continueCursor: "" };
@@ -384,7 +382,7 @@ export function createAffiliateApi(component, config) {
384
382
  paginationOpts: paginationOptsValidator,
385
383
  },
386
384
  handler: async (ctx, args) => {
387
- const userId = await config.auth(ctx);
385
+ const userId = await auth(ctx);
388
386
  const affiliate = await getAffiliateByUserId(ctx, userId);
389
387
  if (!affiliate) {
390
388
  return { page: [], isDone: true, continueCursor: "" };
@@ -421,7 +419,7 @@ export function createAffiliateApi(component, config) {
421
419
  limit: v.optional(v.number()),
422
420
  },
423
421
  handler: async (ctx, args) => {
424
- const userId = await config.auth(ctx);
422
+ const userId = await auth(ctx);
425
423
  const affiliate = await getAffiliateByUserId(ctx, userId);
426
424
  if (!affiliate) {
427
425
  return [];
@@ -465,7 +463,7 @@ export function createAffiliateApi(component, config) {
465
463
  subId: v.optional(v.string()),
466
464
  },
467
465
  handler: async (ctx, args) => {
468
- const userId = await config.auth(ctx);
466
+ const userId = await auth(ctx);
469
467
  const affiliate = await ctx.runQuery(component.affiliates.getByUserId, {
470
468
  userId,
471
469
  });
@@ -857,52 +855,149 @@ export function createAffiliateApi(component, config) {
857
855
  });
858
856
  },
859
857
  }),
860
- };
861
- }
862
- // =============================================================================
863
- // HTTP Routes Helper
864
- // =============================================================================
865
- /**
866
- * Register HTTP routes for affiliate functionality.
867
- * Useful for public API endpoints and webhook handling.
868
- */
869
- export function registerRoutes(http, component, options = {}) {
870
- const prefix = options.pathPrefix ?? "/affiliates";
871
- // Get affiliate by code (public endpoint for link validation)
872
- http.route({
873
- path: `${prefix}/affiliate/:code`,
874
- method: "GET",
875
- handler: httpActionGeneric(async (ctx, request) => {
876
- const url = new URL(request.url);
877
- const code = url.pathname.split("/").pop();
878
- if (!code) {
879
- return new Response(JSON.stringify({ error: "Code required" }), {
880
- status: 400,
881
- headers: { "Content-Type": "application/json" },
882
- });
883
- }
884
- const affiliate = await ctx.runQuery(component.affiliates.getByCode, {
885
- code: code.toUpperCase(),
858
+ // =========================================================================
859
+ // HTTP Routes
860
+ // =========================================================================
861
+ /**
862
+ * Register HTTP routes for affiliate functionality.
863
+ * The component reference is already available from the closure.
864
+ *
865
+ * @param http - The Convex HTTP router
866
+ * @param options - Optional configuration (e.g., pathPrefix)
867
+ *
868
+ * @example
869
+ * ```typescript
870
+ * const affiliates = createAffiliateApi(components.affiliates, { ... });
871
+ * affiliates.registerRoutes(http, { pathPrefix: "/affiliates" });
872
+ * ```
873
+ */
874
+ registerRoutes(http, options = {}) {
875
+ const prefix = options.pathPrefix ?? "/affiliates";
876
+ http.route({
877
+ path: `${prefix}/affiliate/:code`,
878
+ method: "GET",
879
+ handler: httpActionGeneric(async (ctx, request) => {
880
+ const url = new URL(request.url);
881
+ const code = url.pathname.split("/").pop();
882
+ if (!code) {
883
+ return new Response(JSON.stringify({ error: "Code required" }), {
884
+ status: 400,
885
+ headers: { "Content-Type": "application/json" },
886
+ });
887
+ }
888
+ const affiliate = await ctx.runQuery(component.affiliates.getByCode, {
889
+ code: code.toUpperCase(),
890
+ });
891
+ if (!affiliate) {
892
+ return new Response(JSON.stringify({ error: "Affiliate not found" }), {
893
+ status: 404,
894
+ headers: { "Content-Type": "application/json" },
895
+ });
896
+ }
897
+ return new Response(JSON.stringify({
898
+ code: affiliate.code,
899
+ displayName: affiliate.displayName,
900
+ valid: affiliate.status === "approved",
901
+ }), {
902
+ status: 200,
903
+ headers: { "Content-Type": "application/json" },
904
+ });
905
+ }),
886
906
  });
887
- if (!affiliate) {
888
- return new Response(JSON.stringify({ error: "Affiliate not found" }), {
889
- status: 404,
890
- headers: { "Content-Type": "application/json" },
891
- });
907
+ },
908
+ // =========================================================================
909
+ // Stripe Webhook Handler
910
+ // =========================================================================
911
+ /**
912
+ * Create a Stripe webhook handler that processes affiliate-related events.
913
+ * The component reference is already available from the closure.
914
+ *
915
+ * @param webhookConfig - Configuration with webhookSecret
916
+ * @returns An HTTP action handler for Stripe webhooks
917
+ *
918
+ * @example
919
+ * ```typescript
920
+ * const affiliates = createAffiliateApi(components.affiliates, { ... });
921
+ *
922
+ * http.route({
923
+ * path: "/webhooks/stripe",
924
+ * method: "POST",
925
+ * handler: affiliates.createStripeWebhookHandler({
926
+ * webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
927
+ * }),
928
+ * });
929
+ * ```
930
+ */
931
+ createStripeWebhookHandler(webhookConfig) {
932
+ if (!webhookConfig.webhookSecret) {
933
+ throw new Error("webhookSecret is required for Stripe webhook handler. " +
934
+ "Set STRIPE_WEBHOOK_SECRET in your Convex environment variables.");
892
935
  }
893
- return new Response(JSON.stringify({
894
- code: affiliate.code,
895
- displayName: affiliate.displayName,
896
- valid: affiliate.status === "approved",
897
- }), {
898
- status: 200,
899
- headers: { "Content-Type": "application/json" },
936
+ return httpActionGeneric(async (ctx, request) => {
937
+ const rawBody = await request.text();
938
+ const signature = request.headers.get("stripe-signature");
939
+ if (!signature) {
940
+ return new Response(JSON.stringify({ error: "Missing stripe-signature header" }), { status: 400, headers: { "Content-Type": "application/json" } });
941
+ }
942
+ const isValid = await verifyStripeSignature(rawBody, signature, webhookConfig.webhookSecret);
943
+ if (!isValid) {
944
+ return new Response(JSON.stringify({ error: "Invalid signature" }), {
945
+ status: 401,
946
+ headers: { "Content-Type": "application/json" },
947
+ });
948
+ }
949
+ const event = JSON.parse(rawBody);
950
+ try {
951
+ switch (event.type) {
952
+ case "invoice.paid": {
953
+ const invoice = event.data.object;
954
+ await ctx.runMutation(component.commissions.createFromInvoice, {
955
+ stripeInvoiceId: invoice.id,
956
+ stripeCustomerId: invoice.customer,
957
+ stripeChargeId: invoice.charge,
958
+ stripeSubscriptionId: invoice.subscription,
959
+ stripeProductId: invoice.lines?.data?.[0]?.price?.product,
960
+ amountPaidCents: invoice.amount_paid,
961
+ currency: invoice.currency,
962
+ affiliateCode: invoice.metadata?.affiliate_code,
963
+ });
964
+ break;
965
+ }
966
+ case "charge.refunded": {
967
+ const charge = event.data.object;
968
+ await ctx.runMutation(component.commissions.reverseByCharge, {
969
+ stripeChargeId: charge.id,
970
+ reason: charge.refunds?.data?.[0]?.reason ?? "Charge refunded",
971
+ });
972
+ break;
973
+ }
974
+ case "checkout.session.completed": {
975
+ const session = event.data.object;
976
+ await ctx.runMutation(component.referrals.linkStripeCustomer, {
977
+ stripeCustomerId: session.customer,
978
+ userId: session.client_reference_id,
979
+ affiliateCode: session.metadata?.affiliate_code,
980
+ });
981
+ break;
982
+ }
983
+ }
984
+ return new Response(JSON.stringify({ received: true }), {
985
+ status: 200,
986
+ headers: { "Content-Type": "application/json" },
987
+ });
988
+ }
989
+ catch (error) {
990
+ console.error("Stripe webhook error:", error);
991
+ return new Response(JSON.stringify({
992
+ error: error instanceof Error ? error.message : "Unknown error",
993
+ }), { status: 500, headers: { "Content-Type": "application/json" } });
994
+ }
900
995
  });
901
- }),
902
- });
996
+ },
997
+ };
903
998
  }
904
999
  // =============================================================================
905
- // Stripe Webhook Handler
1000
+ // Stripe Webhook Signature Verification
906
1001
  // =============================================================================
907
1002
  /**
908
1003
  * Verify Stripe webhook signature without the Stripe SDK.
@@ -930,98 +1025,6 @@ async function verifyStripeSignature(payload, signature, secret) {
930
1025
  // Constant-time comparison
931
1026
  return sig === expectedSig;
932
1027
  }
933
- /**
934
- * Create a Stripe webhook handler that processes affiliate-related events.
935
- * Handles invoice.paid, charge.refunded, and checkout.session.completed events.
936
- *
937
- * @example
938
- * ```typescript
939
- * import { httpRouter } from "convex/server";
940
- * import { createStripeWebhookHandler } from "convex-affiliates";
941
- * import { components } from "./_generated/api";
942
- *
943
- * const http = httpRouter();
944
- *
945
- * http.route({
946
- * path: "/webhooks/stripe",
947
- * method: "POST",
948
- * handler: createStripeWebhookHandler(components.affiliates, {
949
- * webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
950
- * }),
951
- * });
952
- *
953
- * export default http;
954
- * ```
955
- */
956
- export function createStripeWebhookHandler(component, config) {
957
- // Validate webhook secret at creation time for helpful error messages
958
- if (!config.webhookSecret) {
959
- throw new Error("webhookSecret is required for Stripe webhook handler. " +
960
- "Set STRIPE_WEBHOOK_SECRET in your Convex environment variables.");
961
- }
962
- return httpActionGeneric(async (ctx, request) => {
963
- // Get raw body for signature verification
964
- const rawBody = await request.text();
965
- // Verify signature (required)
966
- const signature = request.headers.get("stripe-signature");
967
- if (!signature) {
968
- return new Response(JSON.stringify({ error: "Missing stripe-signature header" }), { status: 400, headers: { "Content-Type": "application/json" } });
969
- }
970
- const isValid = await verifyStripeSignature(rawBody, signature, config.webhookSecret);
971
- if (!isValid) {
972
- return new Response(JSON.stringify({ error: "Invalid signature" }), {
973
- status: 401,
974
- headers: { "Content-Type": "application/json" },
975
- });
976
- }
977
- const event = JSON.parse(rawBody);
978
- try {
979
- switch (event.type) {
980
- case "invoice.paid": {
981
- const invoice = event.data.object;
982
- await ctx.runMutation(component.commissions.createFromInvoice, {
983
- stripeInvoiceId: invoice.id,
984
- stripeCustomerId: invoice.customer,
985
- stripeChargeId: invoice.charge,
986
- stripeSubscriptionId: invoice.subscription,
987
- stripeProductId: invoice.lines?.data?.[0]?.price?.product,
988
- amountPaidCents: invoice.amount_paid,
989
- currency: invoice.currency,
990
- affiliateCode: invoice.metadata?.affiliate_code,
991
- });
992
- break;
993
- }
994
- case "charge.refunded": {
995
- const charge = event.data.object;
996
- await ctx.runMutation(component.commissions.reverseByCharge, {
997
- stripeChargeId: charge.id,
998
- reason: charge.refunds?.data?.[0]?.reason ?? "Charge refunded",
999
- });
1000
- break;
1001
- }
1002
- case "checkout.session.completed": {
1003
- const session = event.data.object;
1004
- await ctx.runMutation(component.referrals.linkStripeCustomer, {
1005
- stripeCustomerId: session.customer,
1006
- userId: session.client_reference_id,
1007
- affiliateCode: session.metadata?.affiliate_code,
1008
- });
1009
- break;
1010
- }
1011
- }
1012
- return new Response(JSON.stringify({ received: true }), {
1013
- status: 200,
1014
- headers: { "Content-Type": "application/json" },
1015
- });
1016
- }
1017
- catch (error) {
1018
- console.error("Stripe webhook error:", error);
1019
- return new Response(JSON.stringify({
1020
- error: error instanceof Error ? error.message : "Unknown error",
1021
- }), { status: 500, headers: { "Content-Type": "application/json" } });
1022
- }
1023
- });
1024
- }
1025
1028
  /**
1026
1029
  * Get Stripe event handlers for affiliate tracking.
1027
1030
  * Optionally merge with your own handlers (affiliate runs first).