@soulbatical/tetra-core 0.1.8 → 0.1.10

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.
Files changed (41) hide show
  1. package/dist/core/adminDb.d.ts +12 -3
  2. package/dist/core/adminDb.d.ts.map +1 -1
  3. package/dist/core/adminDb.js +12 -3
  4. package/dist/core/adminDb.js.map +1 -1
  5. package/dist/index.d.ts +5 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/shared/affiliate/AffiliateAttributionService.d.ts +47 -0
  10. package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +1 -0
  11. package/dist/shared/affiliate/AffiliateAttributionService.js +308 -0
  12. package/dist/shared/affiliate/AffiliateAttributionService.js.map +1 -0
  13. package/dist/shared/affiliate/AffiliateClickService.d.ts +35 -0
  14. package/dist/shared/affiliate/AffiliateClickService.d.ts.map +1 -0
  15. package/dist/shared/affiliate/AffiliateClickService.js +87 -0
  16. package/dist/shared/affiliate/AffiliateClickService.js.map +1 -0
  17. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +11 -0
  18. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +1 -0
  19. package/dist/shared/affiliate/affiliateFeatureConfig.js +242 -0
  20. package/dist/shared/affiliate/affiliateFeatureConfig.js.map +1 -0
  21. package/dist/shared/affiliate/index.d.ts +11 -0
  22. package/dist/shared/affiliate/index.d.ts.map +1 -0
  23. package/dist/shared/affiliate/index.js +13 -0
  24. package/dist/shared/affiliate/index.js.map +1 -0
  25. package/dist/shared/affiliate/routes.d.ts +87 -0
  26. package/dist/shared/affiliate/routes.d.ts.map +1 -0
  27. package/dist/shared/affiliate/routes.js +404 -0
  28. package/dist/shared/affiliate/routes.js.map +1 -0
  29. package/dist/shared/affiliate/types.d.ts +170 -0
  30. package/dist/shared/affiliate/types.d.ts.map +1 -0
  31. package/dist/shared/affiliate/types.js +11 -0
  32. package/dist/shared/affiliate/types.js.map +1 -0
  33. package/package.json +5 -3
  34. package/src/shared/affiliate/migrations/001_create_affiliates.sql +49 -0
  35. package/src/shared/affiliate/migrations/002_create_affiliate_commissions.sql +31 -0
  36. package/src/shared/affiliate/migrations/003_create_affiliate_clicks.sql +26 -0
  37. package/src/shared/affiliate/migrations/004_create_affiliate_payments.sql +34 -0
  38. package/src/shared/affiliate/migrations/005_create_affiliate_tier_history.sql +19 -0
  39. package/src/shared/affiliate/migrations/006_create_affiliate_rpc_functions.sql +209 -0
  40. package/src/shared/affiliate/migrations/007_create_affiliate_rls_policies.sql +123 -0
  41. package/src/shared/auth/migrations/000_auth_org_helpers.sql +71 -0
@@ -1,8 +1,17 @@
1
1
  import { AuthenticatedRequest } from '../middleware/authMiddleware.js';
2
2
  /**
3
- * Admin database helper - for organization admin operations
4
- * Enforces admin role and logs access for audit
5
- * RLS policies will allow access to all organization data
3
+ * Admin database helper - for organization admin operations.
4
+ * Creates a Supabase client authenticated with the user's JWT token,
5
+ * so RLS policies are enforced using the user's identity.
6
+ *
7
+ * IMPORTANT — RLS Organization Filtering:
8
+ * This client passes the user's JWT to Supabase. RLS policies that use
9
+ * auth_org_id() will filter data by organization. The auth_org_id()
10
+ * function MUST read from users_public.active_organization_id (database),
11
+ * NOT from JWT app_metadata claims. JWT claims are set at login time and
12
+ * become stale when users switch organizations.
13
+ *
14
+ * See: packages/core/src/shared/auth/migrations/000_auth_org_helpers.sql
6
15
  *
7
16
  * @example
8
17
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"adminDb.d.ts","sourceRoot":"","sources":["../../src/core/adminDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAMvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,oBAAoB,8FA0BtD"}
1
+ {"version":3,"file":"adminDb.d.ts","sourceRoot":"","sources":["../../src/core/adminDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAMvE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,oBAAoB,8FA0BtD"}
@@ -2,9 +2,18 @@ import { SupabaseUserClient } from './SupabaseUserClient.js';
2
2
  import { createLogger } from '../utils/logger.js';
3
3
  const logger = createLogger('security:db:admin');
4
4
  /**
5
- * Admin database helper - for organization admin operations
6
- * Enforces admin role and logs access for audit
7
- * RLS policies will allow access to all organization data
5
+ * Admin database helper - for organization admin operations.
6
+ * Creates a Supabase client authenticated with the user's JWT token,
7
+ * so RLS policies are enforced using the user's identity.
8
+ *
9
+ * IMPORTANT — RLS Organization Filtering:
10
+ * This client passes the user's JWT to Supabase. RLS policies that use
11
+ * auth_org_id() will filter data by organization. The auth_org_id()
12
+ * function MUST read from users_public.active_organization_id (database),
13
+ * NOT from JWT app_metadata claims. JWT claims are set at login time and
14
+ * become stale when users switch organizations.
15
+ *
16
+ * See: packages/core/src/shared/auth/migrations/000_auth_org_helpers.sql
8
17
  *
9
18
  * @example
10
19
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"adminDb.js","sourceRoot":"","sources":["../../src/core/adminDb.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAyB;IACrD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACnB,0EAA0E;QAC1E,MAAM,IAAI,KAAK,CACb,0CAA0C;YAC1C,wEAAwE;YACxE,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IAED,sEAAsE;IACtE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,kDAAkD,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/E,4EAA4E;QAC5E,MAAM,IAAI,KAAK,CACb,qCAAqC;YACrC,sFAAsF;YACtF,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,gEAAgE;IAChE,MAAM,CAAC,KAAK,CAAC,+BAA+B,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,YAAY,EAAE,CAAC,CAAC;IAE1G,8DAA8D;IAC9D,OAAO,kBAAkB,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"adminDb.js","sourceRoot":"","sources":["../../src/core/adminDb.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAyB;IACrD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACnB,0EAA0E;QAC1E,MAAM,IAAI,KAAK,CACb,0CAA0C;YAC1C,wEAAwE;YACxE,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IAED,sEAAsE;IACtE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,kDAAkD,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/E,4EAA4E;QAC5E,MAAM,IAAI,KAAK,CACb,qCAAqC;YACrC,sFAAsF;YACtF,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,gEAAgE;IAChE,MAAM,CAAC,KAAK,CAAC,+BAA+B,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,YAAY,EAAE,CAAC,CAAC;IAE1G,8DAA8D;IAC9D,OAAO,kBAAkB,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACzD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -78,6 +78,11 @@ export { userDB } from './core/userDb.js';
78
78
  export { superadminDB } from './core/superadminDb.js';
79
79
  export { webhookDB } from './core/webhookDb.js';
80
80
  export { SupabaseUserClient } from './core/SupabaseUserClient.js';
81
+ export { AffiliateAttributionService } from './shared/affiliate/AffiliateAttributionService.js';
82
+ export { AffiliateClickService } from './shared/affiliate/AffiliateClickService.js';
83
+ export { defaultAffiliateFeatureConfig } from './shared/affiliate/affiliateFeatureConfig.js';
84
+ export { addAffiliateAdminRoutes, addAffiliateUserRoutes, addAffiliatePublicRoutes } from './shared/affiliate/routes.js';
85
+ export type { AffiliateConfig, AffiliateTierConfig, AffiliateAttribution, AffiliateOrder, Affiliate, AffiliateCommission, AffiliateClick, AffiliatePayment, AffiliateTierHistory, AffiliateFilters, AffiliateDashboard, AffiliateDashboardStats, AffiliateDashboardCommission, AffiliateTierProgress } from './shared/affiliate/types.js';
81
86
  export { createApp } from './core/createApp.js';
82
87
  export type { CreateAppConfig } from './core/createApp.js';
83
88
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAE,uBAAuB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAClY,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACjF,YAAY,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACjG,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC/J,YAAY,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAG9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AACxF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gDAAgD,CAAC;AAG9F,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAC3G,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,YAAY,EAAE,iBAAiB,IAAI,UAAU,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAC3G,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAGjE,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,wBAAwB,EAAE,WAAW,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC9N,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvH,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAChJ,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AAC5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAGzE,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC9K,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAGjH,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACtP,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAGjI,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACpO,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACtJ,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AACpG,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACpH,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAGzF,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC1H,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAGpI,YAAY,EAAE,oBAAoB,EAAE,QAAQ,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAOjL,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGlE,OAAO,EAAE,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AAChG,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACzH,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,cAAc,EAAE,SAAS,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAG1U,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -74,6 +74,11 @@ export { userDB } from './core/userDb.js';
74
74
  export { superadminDB } from './core/superadminDb.js';
75
75
  export { webhookDB } from './core/webhookDb.js';
76
76
  export { SupabaseUserClient } from './core/SupabaseUserClient.js';
77
+ // ─── Affiliate Module ──────────────────────────────────────
78
+ export { AffiliateAttributionService } from './shared/affiliate/AffiliateAttributionService.js';
79
+ export { AffiliateClickService } from './shared/affiliate/AffiliateClickService.js';
80
+ export { defaultAffiliateFeatureConfig } from './shared/affiliate/affiliateFeatureConfig.js';
81
+ export { addAffiliateAdminRoutes, addAffiliateUserRoutes, addAffiliatePublicRoutes } from './shared/affiliate/routes.js';
77
82
  // ─── App Bootstrap ──────────────────────────────────────────
78
83
  export { createApp } from './core/createApp.js';
79
84
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * AffiliateAttributionService
3
+ *
4
+ * Generic affiliate attribution and commission engine.
5
+ * Internal referral only (voucher > URL > cookie).
6
+ * Config-driven tier upgrades.
7
+ *
8
+ * @module @soulbatical/tetra-core/affiliate
9
+ */
10
+ import type { SupabaseClient } from '@supabase/supabase-js';
11
+ import type { Request } from 'express';
12
+ import type { AffiliateAttribution, AffiliateConfig, AffiliateOrder } from './types.js';
13
+ export declare class AffiliateAttributionService {
14
+ private supabase;
15
+ private organizationId?;
16
+ private config;
17
+ constructor(supabase: SupabaseClient, config?: Partial<AffiliateConfig>, organizationId?: string);
18
+ /**
19
+ * Determine affiliate attribution for an order.
20
+ * Priority: Voucher > URL (?ref=) > Cookie
21
+ */
22
+ determineAttribution(req: Request, voucherCode?: string): Promise<AffiliateAttribution | null>;
23
+ /**
24
+ * Create a commission record for an affiliate sale.
25
+ * Updates affiliate stats and checks for tier upgrade.
26
+ */
27
+ createCommission(order: AffiliateOrder, attribution: AffiliateAttribution): Promise<void>;
28
+ /**
29
+ * Generate a referral code for a new affiliate.
30
+ * Uses custom generator from config or default format.
31
+ */
32
+ generateReferralCode(contactName: string): string;
33
+ private checkVoucherAttribution;
34
+ private checkUrlParameter;
35
+ private checkCookie;
36
+ /**
37
+ * Resolve an affiliate from referral code or ID.
38
+ */
39
+ resolveAffiliate(reference: string, affiliateId: string | null): Promise<any>;
40
+ private updateAffiliateStats;
41
+ /**
42
+ * Check if affiliate qualifies for tier upgrade (config-driven).
43
+ */
44
+ checkTierUpgrade(affiliateId: string, totalSales: number): Promise<void>;
45
+ private upgradeAffiliateTier;
46
+ }
47
+ //# sourceMappingURL=AffiliateAttributionService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AffiliateAttributionService.d.ts","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateAttributionService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA0BxF,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,MAAM,CAAkB;gBAG9B,QAAQ,EAAE,cAAc,EACxB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,cAAc,CAAC,EAAE,MAAM;IASzB;;;OAGG;IACG,oBAAoB,CACxB,GAAG,EAAE,OAAO,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAgCvC;;;OAGG;IACG,gBAAgB,CACpB,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,oBAAoB,GAChC,OAAO,CAAC,IAAI,CAAC;IA6DhB;;;OAGG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;YAOnC,uBAAuB;IAwBrC,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAiCnB;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,GAAG,CAAC;YAkCD,oBAAoB;IAgBlC;;OAEG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YA8ChE,oBAAoB;CAkDnC"}
@@ -0,0 +1,308 @@
1
+ import crypto from 'crypto';
2
+ import { createLogger } from '../../utils/logger.js';
3
+ const logger = createLogger('service:affiliate:attribution');
4
+ const DEFAULT_CONFIG = {
5
+ defaultCommissionPercentage: 30,
6
+ cookieDurationDays: 90,
7
+ tiers: [
8
+ { name: 'starter', minSales: 0, commissionPercentage: 30 },
9
+ { name: 'active', minSales: 10, commissionPercentage: 35 },
10
+ ],
11
+ };
12
+ /**
13
+ * Generate a referral code from a contact name.
14
+ * Format: firstname-XXXX (e.g., 'monique-1234')
15
+ */
16
+ function defaultReferralCodeGenerator(contactName) {
17
+ const normalized = contactName
18
+ .toLowerCase()
19
+ .replace(/[^a-z0-9]/g, '')
20
+ .substring(0, 15);
21
+ const randomDigits = crypto.randomInt(1000, 10000);
22
+ return `${normalized}-${randomDigits}`;
23
+ }
24
+ export class AffiliateAttributionService {
25
+ supabase;
26
+ organizationId;
27
+ config;
28
+ constructor(supabase, config, organizationId) {
29
+ this.supabase = supabase;
30
+ this.organizationId = organizationId;
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+ // ─── Attribution ────────────────────────────────────────────
34
+ /**
35
+ * Determine affiliate attribution for an order.
36
+ * Priority: Voucher > URL (?ref=) > Cookie
37
+ */
38
+ async determineAttribution(req, voucherCode) {
39
+ logger.info('Determining affiliate attribution', { voucherCode });
40
+ // 1. Voucher with affiliate
41
+ if (voucherCode) {
42
+ const attribution = await this.checkVoucherAttribution(voucherCode);
43
+ if (attribution) {
44
+ logger.info('Attribution via voucher', attribution);
45
+ return attribution;
46
+ }
47
+ }
48
+ // 2. URL parameter (?ref=)
49
+ const urlAttribution = this.checkUrlParameter(req);
50
+ if (urlAttribution) {
51
+ logger.info('Attribution via URL', urlAttribution);
52
+ return urlAttribution;
53
+ }
54
+ // 3. Cookie fallback
55
+ const cookieAttribution = this.checkCookie(req);
56
+ if (cookieAttribution) {
57
+ logger.info('Attribution via cookie', cookieAttribution);
58
+ return cookieAttribution;
59
+ }
60
+ logger.info('No affiliate attribution found');
61
+ return null;
62
+ }
63
+ // ─── Commission ─────────────────────────────────────────────
64
+ /**
65
+ * Create a commission record for an affiliate sale.
66
+ * Updates affiliate stats and checks for tier upgrade.
67
+ */
68
+ async createCommission(order, attribution) {
69
+ logger.info('Creating commission', { orderId: order.id, attribution });
70
+ const affiliate = await this.resolveAffiliate(attribution.reference, attribution.affiliate_id);
71
+ if (!affiliate) {
72
+ logger.warn('Affiliate not found', { attribution });
73
+ return;
74
+ }
75
+ const orderAmount = order.total_excl_vat;
76
+ const commissionAmount = orderAmount * (affiliate.commission_percentage / 100);
77
+ logger.info('Commission calculated', {
78
+ affiliateId: affiliate.id,
79
+ orderAmount,
80
+ commissionPercentage: affiliate.commission_percentage,
81
+ commissionAmount,
82
+ });
83
+ // Create commission record
84
+ const { data: commission, error } = await this.supabase
85
+ .from('affiliate_commissions')
86
+ .insert({
87
+ organization_id: order.organization_id,
88
+ affiliate_id: affiliate.id,
89
+ order_id: order.id,
90
+ affiliate_source: 'internal',
91
+ product_name: 'Order',
92
+ order_amount_excl_vat: orderAmount,
93
+ commission_percentage: affiliate.commission_percentage,
94
+ commission_amount: commissionAmount,
95
+ status: 'pending',
96
+ })
97
+ .select()
98
+ .single();
99
+ if (error) {
100
+ logger.error('Failed to create commission', { error, orderId: order.id });
101
+ throw error;
102
+ }
103
+ logger.info('Commission created', { commissionId: commission.id });
104
+ // Update affiliate stats
105
+ await this.updateAffiliateStats(affiliate.id, {
106
+ total_sales: affiliate.total_sales + 1,
107
+ total_revenue: affiliate.total_revenue + orderAmount,
108
+ total_commission_earned: affiliate.total_commission_earned + commissionAmount,
109
+ last_sale_at: new Date().toISOString(),
110
+ });
111
+ // Check for tier upgrade
112
+ await this.checkTierUpgrade(affiliate.id, affiliate.total_sales + 1);
113
+ }
114
+ // ─── Referral Code ──────────────────────────────────────────
115
+ /**
116
+ * Generate a referral code for a new affiliate.
117
+ * Uses custom generator from config or default format.
118
+ */
119
+ generateReferralCode(contactName) {
120
+ const generator = this.config.referralCodeGenerator || defaultReferralCodeGenerator;
121
+ return generator(contactName);
122
+ }
123
+ // ─── Private: Attribution Checks ────────────────────────────
124
+ async checkVoucherAttribution(voucherCode) {
125
+ try {
126
+ const { data: voucher, error } = await this.supabase
127
+ .from('vouchers')
128
+ .select('id, affiliate_id')
129
+ .eq('code', voucherCode)
130
+ .single();
131
+ if (error || !voucher?.affiliate_id) {
132
+ return null;
133
+ }
134
+ return {
135
+ affiliate_id: voucher.affiliate_id,
136
+ source: 'internal',
137
+ attribution_type: 'voucher',
138
+ reference: voucherCode,
139
+ };
140
+ }
141
+ catch (error) {
142
+ logger.error('Error checking voucher attribution', error);
143
+ return null;
144
+ }
145
+ }
146
+ checkUrlParameter(req) {
147
+ const ref = req.query.ref;
148
+ if (!ref)
149
+ return null;
150
+ return {
151
+ affiliate_id: null,
152
+ source: 'internal',
153
+ attribution_type: 'url',
154
+ reference: ref,
155
+ };
156
+ }
157
+ checkCookie(req) {
158
+ const cookie = req.cookies?.affiliate_ref;
159
+ if (!cookie)
160
+ return null;
161
+ try {
162
+ const data = JSON.parse(cookie);
163
+ const age = Date.now() - data.timestamp;
164
+ const maxAge = this.config.cookieDurationDays * 24 * 60 * 60 * 1000;
165
+ if (age > maxAge) {
166
+ logger.info('Affiliate cookie expired', { ageDays: Math.floor(age / (24 * 60 * 60 * 1000)) });
167
+ return null;
168
+ }
169
+ // Only accept internal source in tetra-core
170
+ if (data.source && data.source !== 'internal') {
171
+ return null;
172
+ }
173
+ return {
174
+ affiliate_id: null,
175
+ source: 'internal',
176
+ attribution_type: 'cookie',
177
+ reference: data.ref,
178
+ };
179
+ }
180
+ catch (error) {
181
+ logger.error('Error parsing affiliate cookie', error);
182
+ return null;
183
+ }
184
+ }
185
+ // ─── Private: Affiliate Resolution ──────────────────────────
186
+ /**
187
+ * Resolve an affiliate from referral code or ID.
188
+ */
189
+ async resolveAffiliate(reference, affiliateId) {
190
+ // If affiliate ID already known (voucher attribution)
191
+ if (affiliateId) {
192
+ const { data, error } = await this.supabase
193
+ .from('affiliates')
194
+ .select('*')
195
+ .eq('id', affiliateId)
196
+ .eq('status', 'active')
197
+ .single();
198
+ if (error) {
199
+ logger.error('Failed to fetch affiliate by ID', { error, affiliateId });
200
+ return null;
201
+ }
202
+ return data;
203
+ }
204
+ // Look up by referral code
205
+ const { data, error } = await this.supabase
206
+ .from('affiliates')
207
+ .select('*')
208
+ .eq('referral_code', reference)
209
+ .eq('status', 'active')
210
+ .single();
211
+ if (error) {
212
+ logger.error('Failed to fetch affiliate by referral code', { error, reference });
213
+ return null;
214
+ }
215
+ return data;
216
+ }
217
+ // ─── Private: Stats & Tiers ─────────────────────────────────
218
+ async updateAffiliateStats(affiliateId, updates) {
219
+ const { error } = await this.supabase
220
+ .from('affiliates')
221
+ .update(updates)
222
+ .eq('id', affiliateId);
223
+ if (error) {
224
+ logger.error('Failed to update affiliate stats', { error, affiliateId });
225
+ throw error;
226
+ }
227
+ logger.info('Affiliate stats updated', { affiliateId, updates });
228
+ }
229
+ /**
230
+ * Check if affiliate qualifies for tier upgrade (config-driven).
231
+ */
232
+ async checkTierUpgrade(affiliateId, totalSales) {
233
+ const { data: affiliate, error } = await this.supabase
234
+ .from('affiliates')
235
+ .select('tier, commission_percentage')
236
+ .eq('id', affiliateId)
237
+ .single();
238
+ if (error || !affiliate) {
239
+ logger.error('Failed to fetch affiliate for tier check', { error, affiliateId });
240
+ return;
241
+ }
242
+ // Find the highest qualifying tier
243
+ const sortedTiers = [...this.config.tiers].sort((a, b) => b.minSales - a.minSales);
244
+ const qualifyingTier = sortedTiers.find(t => totalSales >= t.minSales);
245
+ if (!qualifyingTier || qualifyingTier.name === affiliate.tier) {
246
+ return; // No upgrade needed
247
+ }
248
+ // Check if this is actually an upgrade (not a downgrade)
249
+ const currentTierIndex = this.config.tiers.findIndex(t => t.name === affiliate.tier);
250
+ const newTierIndex = this.config.tiers.findIndex(t => t.name === qualifyingTier.name);
251
+ if (newTierIndex <= currentTierIndex) {
252
+ return;
253
+ }
254
+ logger.info('Tier upgrade triggered', {
255
+ affiliateId,
256
+ oldTier: affiliate.tier,
257
+ newTier: qualifyingTier.name,
258
+ oldCommission: affiliate.commission_percentage,
259
+ newCommission: qualifyingTier.commissionPercentage,
260
+ totalSales,
261
+ });
262
+ await this.upgradeAffiliateTier(affiliateId, {
263
+ old_tier: affiliate.tier,
264
+ new_tier: qualifyingTier.name,
265
+ old_commission: affiliate.commission_percentage,
266
+ new_commission: qualifyingTier.commissionPercentage,
267
+ reason: 'milestone_reached',
268
+ triggered_by_sale_count: totalSales,
269
+ });
270
+ }
271
+ async upgradeAffiliateTier(affiliateId, upgrade) {
272
+ // Update affiliate tier and commission
273
+ const { error: updateError } = await this.supabase
274
+ .from('affiliates')
275
+ .update({
276
+ tier: upgrade.new_tier,
277
+ commission_percentage: upgrade.new_commission,
278
+ updated_at: new Date().toISOString(),
279
+ })
280
+ .eq('id', affiliateId);
281
+ if (updateError) {
282
+ logger.error('Failed to update affiliate tier', { error: updateError, affiliateId });
283
+ throw updateError;
284
+ }
285
+ // Record in tier history
286
+ const { error: historyError } = await this.supabase
287
+ .from('affiliate_tier_history')
288
+ .insert({
289
+ affiliate_id: affiliateId,
290
+ old_tier: upgrade.old_tier,
291
+ new_tier: upgrade.new_tier,
292
+ old_commission_percentage: upgrade.old_commission,
293
+ new_commission_percentage: upgrade.new_commission,
294
+ reason: upgrade.reason,
295
+ triggered_by_sale_count: upgrade.triggered_by_sale_count,
296
+ });
297
+ if (historyError) {
298
+ logger.error('Failed to record tier history', { error: historyError, affiliateId });
299
+ // Don't throw — tier upgrade succeeded even if history failed
300
+ }
301
+ logger.info('Tier upgraded successfully', {
302
+ affiliateId,
303
+ oldTier: upgrade.old_tier,
304
+ newTier: upgrade.new_tier,
305
+ });
306
+ }
307
+ }
308
+ //# sourceMappingURL=AffiliateAttributionService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AffiliateAttributionService.js","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateAttributionService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * AffiliateClickService
3
+ *
4
+ * Handles click tracking and conversion marking for the affiliate module.
5
+ *
6
+ * @module @soulbatical/tetra-core/affiliate
7
+ */
8
+ import type { SupabaseClient } from '@supabase/supabase-js';
9
+ export declare class AffiliateClickService {
10
+ private supabase;
11
+ constructor(supabase: SupabaseClient);
12
+ /**
13
+ * Register an affiliate click event.
14
+ * Returns the click record ID.
15
+ */
16
+ registerClick(data: {
17
+ affiliate_id?: string | null;
18
+ organization_id?: string | null;
19
+ affiliate_source: string;
20
+ visitor_ip?: string | null;
21
+ user_agent?: string | null;
22
+ referrer_url?: string | null;
23
+ landing_page?: string | null;
24
+ external_click_ref?: string | null;
25
+ }): Promise<string | null>;
26
+ /**
27
+ * Increment affiliate total_clicks counter.
28
+ */
29
+ incrementClickCount(affiliateId: string): Promise<void>;
30
+ /**
31
+ * Mark a click as converted after a successful order.
32
+ */
33
+ markConverted(clickId: string, orderId: string): Promise<void>;
34
+ }
35
+ //# sourceMappingURL=AffiliateClickService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AffiliateClickService.d.ts","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateClickService.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAK5D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAIpC;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE;QACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0B1B;;OAEG;IACG,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB7D;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4BrE"}
@@ -0,0 +1,87 @@
1
+ import { createLogger } from '../../utils/logger.js';
2
+ const logger = createLogger('service:affiliate:clicks');
3
+ export class AffiliateClickService {
4
+ supabase;
5
+ constructor(supabase) {
6
+ this.supabase = supabase;
7
+ }
8
+ /**
9
+ * Register an affiliate click event.
10
+ * Returns the click record ID.
11
+ */
12
+ async registerClick(data) {
13
+ const { data: click, error } = await this.supabase
14
+ .from('affiliate_clicks')
15
+ .insert({
16
+ organization_id: data.organization_id || null,
17
+ affiliate_id: data.affiliate_id || null,
18
+ visitor_ip: data.visitor_ip || null,
19
+ user_agent: data.user_agent || null,
20
+ referrer_url: data.referrer_url || null,
21
+ landing_page: data.landing_page || null,
22
+ affiliate_source: data.affiliate_source,
23
+ external_click_ref: data.external_click_ref || null,
24
+ converted: false,
25
+ })
26
+ .select('id')
27
+ .single();
28
+ if (error) {
29
+ logger.error('Failed to record click', { error });
30
+ return null;
31
+ }
32
+ logger.info('Click registered', { clickId: click.id, affiliateId: data.affiliate_id });
33
+ return click.id;
34
+ }
35
+ /**
36
+ * Increment affiliate total_clicks counter.
37
+ */
38
+ async incrementClickCount(affiliateId) {
39
+ // Fetch current count, then increment (safe for low-concurrency affiliate use case)
40
+ const { data: affiliate, error: fetchError } = await this.supabase
41
+ .from('affiliates')
42
+ .select('total_clicks')
43
+ .eq('id', affiliateId)
44
+ .single();
45
+ if (fetchError || !affiliate) {
46
+ logger.error('Failed to fetch affiliate for click count update', { error: fetchError, affiliateId });
47
+ return;
48
+ }
49
+ const { error: updateError } = await this.supabase
50
+ .from('affiliates')
51
+ .update({ total_clicks: (affiliate.total_clicks || 0) + 1 })
52
+ .eq('id', affiliateId);
53
+ if (updateError) {
54
+ logger.error('Failed to update click count', { error: updateError, affiliateId });
55
+ }
56
+ }
57
+ /**
58
+ * Mark a click as converted after a successful order.
59
+ */
60
+ async markConverted(clickId, orderId) {
61
+ const { data: click, error: fetchError } = await this.supabase
62
+ .from('affiliate_clicks')
63
+ .select('clicked_at')
64
+ .eq('id', clickId)
65
+ .single();
66
+ let conversionTimeHours = null;
67
+ if (!fetchError && click?.clicked_at) {
68
+ const clickedAt = new Date(click.clicked_at).getTime();
69
+ conversionTimeHours = Math.round((Date.now() - clickedAt) / (1000 * 60 * 60));
70
+ }
71
+ const { error } = await this.supabase
72
+ .from('affiliate_clicks')
73
+ .update({
74
+ converted: true,
75
+ order_id: orderId,
76
+ conversion_time_hours: conversionTimeHours,
77
+ })
78
+ .eq('id', clickId);
79
+ if (error) {
80
+ logger.error('Failed to mark click as converted', { error, clickId, orderId });
81
+ }
82
+ else {
83
+ logger.info('Click marked as converted', { clickId, orderId, conversionTimeHours });
84
+ }
85
+ }
86
+ }
87
+ //# sourceMappingURL=AffiliateClickService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AffiliateClickService.js","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateClickService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Default Affiliate FeatureConfig
3
+ *
4
+ * Projects can use this directly or extend with project-specific fields.
5
+ * Config-driven: auto-generates admin pages, filters, sorting, and pagination.
6
+ *
7
+ * @module @soulbatical/tetra-core/affiliate
8
+ */
9
+ import type { FeatureConfig } from '../types/feature-config.js';
10
+ export declare const defaultAffiliateFeatureConfig: FeatureConfig;
11
+ //# sourceMappingURL=affiliateFeatureConfig.d.ts.map