@forklaunch/implementation-billing-stripe 1.1.30 → 1.1.32

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.
@@ -3,7 +3,7 @@ import {
3
3
  OpenTelemetryCollector
4
4
  } from '@forklaunch/core/http';
5
5
  import { AnySchemaValidator } from '@forklaunch/validator';
6
- import { EntityManager } from '@mikro-orm/core';
6
+ import { EntityManager, EntityName, InferEntity } from '@mikro-orm/core';
7
7
  import Stripe from 'stripe';
8
8
  import { BillingProviderEnum } from '../domain/enum/billingProvider.enum';
9
9
  import { CurrencyEnum } from '../domain/enum/currency.enum';
@@ -18,11 +18,30 @@ import {
18
18
  } from '../domain/types/stripe.entity.types';
19
19
  import { StripeWebhookEvent } from '../persistence/entities';
20
20
  import { StripeBillingPortalService } from './billingPortal.service';
21
+
21
22
  import { StripeCheckoutSessionService } from './checkoutSession.service';
22
23
  import { StripePaymentLinkService } from './paymentLink.service';
23
24
  import { StripePlanService } from './plan.service';
24
25
  import { StripeSubscriptionService } from './subscription.service';
25
26
 
27
+ /**
28
+ * Webhook idempotency records are looked up and written BY NAME so they
29
+ * resolve against the consuming application's discovered metadata. Passing
30
+ * this package's internal `StripeWebhookEvent` object to the EntityManager
31
+ * queries an entity the app's ORM never discovered, which mikro-orm 7.1.x
32
+ * rejects deep in EntityLoader (`meta.relations` is undefined for
33
+ * undiscovered entities). Applications must discover an entity named
34
+ * `StripeWebhookEvent` — either their own definition (the blueprint app
35
+ * ships one) or this package's, importable from
36
+ * `@forklaunch/implementation-billing-stripe/persistence`.
37
+ */
38
+ type StripeWebhookEventEntity = InferEntity<typeof StripeWebhookEvent>;
39
+ // mikro-orm 7 narrowed EntityName's type to exclude strings, but the runtime
40
+ // resolves registered entity names as it always has — and name resolution is
41
+ // exactly what we need by default (see above).
42
+ const DEFAULT_STRIPE_WEBHOOK_EVENT_ENTITY =
43
+ 'StripeWebhookEvent' as unknown as EntityName<StripeWebhookEventEntity>;
44
+
26
45
  export class StripeWebhookService<
27
46
  SchemaValidator extends AnySchemaValidator,
28
47
  StatusEnum,
@@ -38,6 +57,7 @@ export class StripeWebhookService<
38
57
  StripeSubscriptionEntities<PartyEnum> = StripeSubscriptionEntities<PartyEnum>
39
58
  > {
40
59
  protected readonly partyEnum: PartyEnum;
60
+ protected readonly webhookEventEntity: EntityName<StripeWebhookEventEntity>;
41
61
  protected readonly stripeClient: Stripe;
42
62
  protected readonly em: EntityManager;
43
63
  protected readonly schemaValidator: SchemaValidator;
@@ -91,8 +111,16 @@ export class StripeWebhookService<
91
111
  PartyEnum,
92
112
  SubscriptionEntities
93
113
  >,
94
- partyEnum: PartyEnum
114
+ partyEnum: PartyEnum,
115
+ /**
116
+ * The entity used for webhook idempotency records. Defaults to
117
+ * resolving the application's discovered entity named
118
+ * 'StripeWebhookEvent'; inject your own entity (mapper-style) to use a
119
+ * different one.
120
+ */
121
+ webhookEventEntity: EntityName<StripeWebhookEventEntity> = DEFAULT_STRIPE_WEBHOOK_EVENT_ENTITY
95
122
  ) {
123
+ this.webhookEventEntity = webhookEventEntity;
96
124
  this.partyEnum = partyEnum;
97
125
  this.stripeClient = stripeClient;
98
126
  this.em = em;
@@ -154,9 +182,12 @@ export class StripeWebhookService<
154
182
  }
155
183
 
156
184
  if (
157
- await this.em.findOne(StripeWebhookEvent, {
158
- idempotencyKey: event.request?.idempotency_key
159
- })
185
+ await this.em.findOne<StripeWebhookEventEntity>(
186
+ this.webhookEventEntity,
187
+ {
188
+ idempotencyKey: event.request?.idempotency_key
189
+ }
190
+ )
160
191
  ) {
161
192
  this.openTelemetryCollector.info(
162
193
  'Webhook event already processed',
@@ -472,12 +503,15 @@ export class StripeWebhookService<
472
503
  break;
473
504
  }
474
505
 
475
- await this.em.insert(StripeWebhookEvent, {
506
+ // em.create (not native em.insert): the app's entity generates its id and
507
+ // timestamps via onCreate hooks, which native inserts bypass — a native
508
+ // insert fails NOT NULL on id for sqlBaseProperties-style entities.
509
+ this.em.create<StripeWebhookEventEntity>(this.webhookEventEntity, {
476
510
  stripeId: event.id,
477
511
  idempotencyKey: event.request?.idempotency_key,
478
512
  eventType: event.type,
479
513
  eventData: event.data
480
- });
514
+ } as never);
481
515
  await this.em.flush();
482
516
  }
483
517
  }
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // persistence/entities/index.ts
21
+ var entities_exports = {};
22
+ __export(entities_exports, {
23
+ BillingPortal: () => BillingPortal,
24
+ CheckoutSession: () => CheckoutSession,
25
+ PaymentLink: () => PaymentLink,
26
+ Plan: () => Plan,
27
+ StripeWebhookEvent: () => StripeWebhookEvent,
28
+ Subscription: () => Subscription
29
+ });
30
+ module.exports = __toCommonJS(entities_exports);
31
+ var import_persistence = require("@forklaunch/core/persistence");
32
+ var BillingPortal = (0, import_persistence.defineComplianceEntity)({
33
+ name: "BillingPortal",
34
+ properties: {
35
+ id: import_persistence.fp.string().primary().compliance("none"),
36
+ customerId: import_persistence.fp.string().compliance("none"),
37
+ providerFields: import_persistence.fp.json().compliance("pci")
38
+ }
39
+ });
40
+ var CheckoutSession = (0, import_persistence.defineComplianceEntity)({
41
+ name: "CheckoutSession",
42
+ properties: {
43
+ id: import_persistence.fp.string().primary().compliance("none"),
44
+ customerId: import_persistence.fp.string().compliance("none"),
45
+ paymentMethods: import_persistence.fp.enum().array().compliance("none"),
46
+ currency: import_persistence.fp.enum().compliance("none"),
47
+ status: import_persistence.fp.enum().compliance("none"),
48
+ providerFields: import_persistence.fp.json().compliance("pci")
49
+ }
50
+ });
51
+ var PaymentLink = (0, import_persistence.defineComplianceEntity)({
52
+ name: "PaymentLink",
53
+ properties: {
54
+ id: import_persistence.fp.string().primary().compliance("none"),
55
+ amount: import_persistence.fp.double().compliance("none"),
56
+ paymentMethods: import_persistence.fp.enum().array().compliance("none"),
57
+ currency: import_persistence.fp.enum().compliance("none"),
58
+ status: import_persistence.fp.enum().compliance("none"),
59
+ providerFields: import_persistence.fp.json().compliance("pci")
60
+ }
61
+ });
62
+ var Plan = (0, import_persistence.defineComplianceEntity)({
63
+ name: "Plan",
64
+ properties: {
65
+ id: import_persistence.fp.string().primary().compliance("none"),
66
+ name: import_persistence.fp.string().compliance("none"),
67
+ price: import_persistence.fp.double().compliance("none"),
68
+ externalId: import_persistence.fp.string().compliance("none"),
69
+ cadence: import_persistence.fp.enum().compliance("none"),
70
+ currency: import_persistence.fp.enum().compliance("none"),
71
+ billingProvider: import_persistence.fp.enum().nullable().compliance("none"),
72
+ providerFields: import_persistence.fp.json().compliance("pci")
73
+ }
74
+ });
75
+ var Subscription = (0, import_persistence.defineComplianceEntity)({
76
+ name: "Subscription",
77
+ properties: {
78
+ id: import_persistence.fp.string().primary().compliance("none"),
79
+ partyId: import_persistence.fp.string().compliance("none"),
80
+ externalId: import_persistence.fp.string().compliance("none"),
81
+ partyType: import_persistence.fp.enum().compliance("none"),
82
+ billingProvider: import_persistence.fp.enum().nullable().compliance("none"),
83
+ active: import_persistence.fp.boolean().compliance("none"),
84
+ providerFields: import_persistence.fp.json().compliance("pci")
85
+ }
86
+ });
87
+ var StripeWebhookEvent = (0, import_persistence.defineComplianceEntity)({
88
+ name: "StripeWebhookEvent",
89
+ properties: {
90
+ id: import_persistence.fp.uuid().primary().compliance("none"),
91
+ createdAt: import_persistence.fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).compliance("none"),
92
+ updatedAt: import_persistence.fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).onUpdate(() => /* @__PURE__ */ new Date()).compliance("none"),
93
+ stripeId: import_persistence.fp.string().compliance("none"),
94
+ idempotencyKey: import_persistence.fp.string().nullable().compliance("none"),
95
+ eventType: import_persistence.fp.string().compliance("none"),
96
+ eventData: import_persistence.fp.json().compliance("pci")
97
+ }
98
+ });
99
+ // Annotate the CommonJS export names for ESM import in node:
100
+ 0 && (module.exports = {
101
+ BillingPortal,
102
+ CheckoutSession,
103
+ PaymentLink,
104
+ Plan,
105
+ StripeWebhookEvent,
106
+ Subscription
107
+ });
@@ -0,0 +1,77 @@
1
+ // persistence/entities/index.ts
2
+ import { defineComplianceEntity, fp } from "@forklaunch/core/persistence";
3
+ var BillingPortal = defineComplianceEntity({
4
+ name: "BillingPortal",
5
+ properties: {
6
+ id: fp.string().primary().compliance("none"),
7
+ customerId: fp.string().compliance("none"),
8
+ providerFields: fp.json().compliance("pci")
9
+ }
10
+ });
11
+ var CheckoutSession = defineComplianceEntity({
12
+ name: "CheckoutSession",
13
+ properties: {
14
+ id: fp.string().primary().compliance("none"),
15
+ customerId: fp.string().compliance("none"),
16
+ paymentMethods: fp.enum().array().compliance("none"),
17
+ currency: fp.enum().compliance("none"),
18
+ status: fp.enum().compliance("none"),
19
+ providerFields: fp.json().compliance("pci")
20
+ }
21
+ });
22
+ var PaymentLink = defineComplianceEntity({
23
+ name: "PaymentLink",
24
+ properties: {
25
+ id: fp.string().primary().compliance("none"),
26
+ amount: fp.double().compliance("none"),
27
+ paymentMethods: fp.enum().array().compliance("none"),
28
+ currency: fp.enum().compliance("none"),
29
+ status: fp.enum().compliance("none"),
30
+ providerFields: fp.json().compliance("pci")
31
+ }
32
+ });
33
+ var Plan = defineComplianceEntity({
34
+ name: "Plan",
35
+ properties: {
36
+ id: fp.string().primary().compliance("none"),
37
+ name: fp.string().compliance("none"),
38
+ price: fp.double().compliance("none"),
39
+ externalId: fp.string().compliance("none"),
40
+ cadence: fp.enum().compliance("none"),
41
+ currency: fp.enum().compliance("none"),
42
+ billingProvider: fp.enum().nullable().compliance("none"),
43
+ providerFields: fp.json().compliance("pci")
44
+ }
45
+ });
46
+ var Subscription = defineComplianceEntity({
47
+ name: "Subscription",
48
+ properties: {
49
+ id: fp.string().primary().compliance("none"),
50
+ partyId: fp.string().compliance("none"),
51
+ externalId: fp.string().compliance("none"),
52
+ partyType: fp.enum().compliance("none"),
53
+ billingProvider: fp.enum().nullable().compliance("none"),
54
+ active: fp.boolean().compliance("none"),
55
+ providerFields: fp.json().compliance("pci")
56
+ }
57
+ });
58
+ var StripeWebhookEvent = defineComplianceEntity({
59
+ name: "StripeWebhookEvent",
60
+ properties: {
61
+ id: fp.uuid().primary().compliance("none"),
62
+ createdAt: fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).compliance("none"),
63
+ updatedAt: fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).onUpdate(() => /* @__PURE__ */ new Date()).compliance("none"),
64
+ stripeId: fp.string().compliance("none"),
65
+ idempotencyKey: fp.string().nullable().compliance("none"),
66
+ eventType: fp.string().compliance("none"),
67
+ eventData: fp.json().compliance("pci")
68
+ }
69
+ });
70
+ export {
71
+ BillingPortal,
72
+ CheckoutSession,
73
+ PaymentLink,
74
+ Plan,
75
+ StripeWebhookEvent,
76
+ Subscription
77
+ };
@@ -626,79 +626,11 @@ var BillingProviderEnum = {
626
626
  STRIPE: "stripe"
627
627
  };
628
628
 
629
- // persistence/entities/index.ts
630
- var import_persistence = require("@forklaunch/core/persistence");
631
- var BillingPortal = (0, import_persistence.defineComplianceEntity)({
632
- name: "BillingPortal",
633
- properties: {
634
- id: import_persistence.fp.string().primary().compliance("none"),
635
- customerId: import_persistence.fp.string().compliance("none"),
636
- providerFields: import_persistence.fp.json().compliance("pci")
637
- }
638
- });
639
- var CheckoutSession = (0, import_persistence.defineComplianceEntity)({
640
- name: "CheckoutSession",
641
- properties: {
642
- id: import_persistence.fp.string().primary().compliance("none"),
643
- customerId: import_persistence.fp.string().compliance("none"),
644
- paymentMethods: import_persistence.fp.enum().array().compliance("none"),
645
- currency: import_persistence.fp.enum().compliance("none"),
646
- status: import_persistence.fp.enum().compliance("none"),
647
- providerFields: import_persistence.fp.json().compliance("pci")
648
- }
649
- });
650
- var PaymentLink = (0, import_persistence.defineComplianceEntity)({
651
- name: "PaymentLink",
652
- properties: {
653
- id: import_persistence.fp.string().primary().compliance("none"),
654
- amount: import_persistence.fp.double().compliance("none"),
655
- paymentMethods: import_persistence.fp.enum().array().compliance("none"),
656
- currency: import_persistence.fp.enum().compliance("none"),
657
- status: import_persistence.fp.enum().compliance("none"),
658
- providerFields: import_persistence.fp.json().compliance("pci")
659
- }
660
- });
661
- var Plan = (0, import_persistence.defineComplianceEntity)({
662
- name: "Plan",
663
- properties: {
664
- id: import_persistence.fp.string().primary().compliance("none"),
665
- name: import_persistence.fp.string().compliance("none"),
666
- price: import_persistence.fp.double().compliance("none"),
667
- externalId: import_persistence.fp.string().compliance("none"),
668
- cadence: import_persistence.fp.enum().compliance("none"),
669
- currency: import_persistence.fp.enum().compliance("none"),
670
- billingProvider: import_persistence.fp.enum().nullable().compliance("none"),
671
- providerFields: import_persistence.fp.json().compliance("pci")
672
- }
673
- });
674
- var Subscription = (0, import_persistence.defineComplianceEntity)({
675
- name: "Subscription",
676
- properties: {
677
- id: import_persistence.fp.string().primary().compliance("none"),
678
- partyId: import_persistence.fp.string().compliance("none"),
679
- externalId: import_persistence.fp.string().compliance("none"),
680
- partyType: import_persistence.fp.enum().compliance("none"),
681
- billingProvider: import_persistence.fp.enum().nullable().compliance("none"),
682
- active: import_persistence.fp.boolean().compliance("none"),
683
- providerFields: import_persistence.fp.json().compliance("pci")
684
- }
685
- });
686
- var StripeWebhookEvent = (0, import_persistence.defineComplianceEntity)({
687
- name: "StripeWebhookEvent",
688
- properties: {
689
- id: import_persistence.fp.uuid().primary().compliance("none"),
690
- createdAt: import_persistence.fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).compliance("none"),
691
- updatedAt: import_persistence.fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).onUpdate(() => /* @__PURE__ */ new Date()).compliance("none"),
692
- stripeId: import_persistence.fp.string().compliance("none"),
693
- idempotencyKey: import_persistence.fp.string().nullable().compliance("none"),
694
- eventType: import_persistence.fp.string().compliance("none"),
695
- eventData: import_persistence.fp.json().compliance("pci")
696
- }
697
- });
698
-
699
629
  // services/webhook.service.ts
630
+ var DEFAULT_STRIPE_WEBHOOK_EVENT_ENTITY = "StripeWebhookEvent";
700
631
  var StripeWebhookService = class {
701
632
  partyEnum;
633
+ webhookEventEntity;
702
634
  stripeClient;
703
635
  em;
704
636
  schemaValidator;
@@ -708,7 +640,8 @@ var StripeWebhookService = class {
708
640
  paymentLinkService;
709
641
  planService;
710
642
  subscriptionService;
711
- constructor(stripeClient, em, schemaValidator, openTelemetryCollector, billingPortalService, checkoutSessionService, paymentLinkService, planService, subscriptionService, partyEnum) {
643
+ constructor(stripeClient, em, schemaValidator, openTelemetryCollector, billingPortalService, checkoutSessionService, paymentLinkService, planService, subscriptionService, partyEnum, webhookEventEntity = DEFAULT_STRIPE_WEBHOOK_EVENT_ENTITY) {
644
+ this.webhookEventEntity = webhookEventEntity;
712
645
  this.partyEnum = partyEnum;
713
646
  this.stripeClient = stripeClient;
714
647
  this.em = em;
@@ -754,9 +687,12 @@ var StripeWebhookService = class {
754
687
  if (this.openTelemetryCollector) {
755
688
  this.openTelemetryCollector.info("Handling webhook event", event);
756
689
  }
757
- if (await this.em.findOne(StripeWebhookEvent, {
758
- idempotencyKey: event.request?.idempotency_key
759
- })) {
690
+ if (await this.em.findOne(
691
+ this.webhookEventEntity,
692
+ {
693
+ idempotencyKey: event.request?.idempotency_key
694
+ }
695
+ )) {
760
696
  this.openTelemetryCollector.info(
761
697
  "Webhook event already processed",
762
698
  event
@@ -999,7 +935,7 @@ var StripeWebhookService = class {
999
935
  );
1000
936
  break;
1001
937
  }
1002
- await this.em.insert(StripeWebhookEvent, {
938
+ this.em.create(this.webhookEventEntity, {
1003
939
  stripeId: event.id,
1004
940
  idempotencyKey: event.request?.idempotency_key,
1005
941
  eventType: event.type,
@@ -594,79 +594,11 @@ var BillingProviderEnum = {
594
594
  STRIPE: "stripe"
595
595
  };
596
596
 
597
- // persistence/entities/index.ts
598
- import { defineComplianceEntity, fp } from "@forklaunch/core/persistence";
599
- var BillingPortal = defineComplianceEntity({
600
- name: "BillingPortal",
601
- properties: {
602
- id: fp.string().primary().compliance("none"),
603
- customerId: fp.string().compliance("none"),
604
- providerFields: fp.json().compliance("pci")
605
- }
606
- });
607
- var CheckoutSession = defineComplianceEntity({
608
- name: "CheckoutSession",
609
- properties: {
610
- id: fp.string().primary().compliance("none"),
611
- customerId: fp.string().compliance("none"),
612
- paymentMethods: fp.enum().array().compliance("none"),
613
- currency: fp.enum().compliance("none"),
614
- status: fp.enum().compliance("none"),
615
- providerFields: fp.json().compliance("pci")
616
- }
617
- });
618
- var PaymentLink = defineComplianceEntity({
619
- name: "PaymentLink",
620
- properties: {
621
- id: fp.string().primary().compliance("none"),
622
- amount: fp.double().compliance("none"),
623
- paymentMethods: fp.enum().array().compliance("none"),
624
- currency: fp.enum().compliance("none"),
625
- status: fp.enum().compliance("none"),
626
- providerFields: fp.json().compliance("pci")
627
- }
628
- });
629
- var Plan = defineComplianceEntity({
630
- name: "Plan",
631
- properties: {
632
- id: fp.string().primary().compliance("none"),
633
- name: fp.string().compliance("none"),
634
- price: fp.double().compliance("none"),
635
- externalId: fp.string().compliance("none"),
636
- cadence: fp.enum().compliance("none"),
637
- currency: fp.enum().compliance("none"),
638
- billingProvider: fp.enum().nullable().compliance("none"),
639
- providerFields: fp.json().compliance("pci")
640
- }
641
- });
642
- var Subscription = defineComplianceEntity({
643
- name: "Subscription",
644
- properties: {
645
- id: fp.string().primary().compliance("none"),
646
- partyId: fp.string().compliance("none"),
647
- externalId: fp.string().compliance("none"),
648
- partyType: fp.enum().compliance("none"),
649
- billingProvider: fp.enum().nullable().compliance("none"),
650
- active: fp.boolean().compliance("none"),
651
- providerFields: fp.json().compliance("pci")
652
- }
653
- });
654
- var StripeWebhookEvent = defineComplianceEntity({
655
- name: "StripeWebhookEvent",
656
- properties: {
657
- id: fp.uuid().primary().compliance("none"),
658
- createdAt: fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).compliance("none"),
659
- updatedAt: fp.datetime().onCreate(() => /* @__PURE__ */ new Date()).onUpdate(() => /* @__PURE__ */ new Date()).compliance("none"),
660
- stripeId: fp.string().compliance("none"),
661
- idempotencyKey: fp.string().nullable().compliance("none"),
662
- eventType: fp.string().compliance("none"),
663
- eventData: fp.json().compliance("pci")
664
- }
665
- });
666
-
667
597
  // services/webhook.service.ts
598
+ var DEFAULT_STRIPE_WEBHOOK_EVENT_ENTITY = "StripeWebhookEvent";
668
599
  var StripeWebhookService = class {
669
600
  partyEnum;
601
+ webhookEventEntity;
670
602
  stripeClient;
671
603
  em;
672
604
  schemaValidator;
@@ -676,7 +608,8 @@ var StripeWebhookService = class {
676
608
  paymentLinkService;
677
609
  planService;
678
610
  subscriptionService;
679
- constructor(stripeClient, em, schemaValidator, openTelemetryCollector, billingPortalService, checkoutSessionService, paymentLinkService, planService, subscriptionService, partyEnum) {
611
+ constructor(stripeClient, em, schemaValidator, openTelemetryCollector, billingPortalService, checkoutSessionService, paymentLinkService, planService, subscriptionService, partyEnum, webhookEventEntity = DEFAULT_STRIPE_WEBHOOK_EVENT_ENTITY) {
612
+ this.webhookEventEntity = webhookEventEntity;
680
613
  this.partyEnum = partyEnum;
681
614
  this.stripeClient = stripeClient;
682
615
  this.em = em;
@@ -722,9 +655,12 @@ var StripeWebhookService = class {
722
655
  if (this.openTelemetryCollector) {
723
656
  this.openTelemetryCollector.info("Handling webhook event", event);
724
657
  }
725
- if (await this.em.findOne(StripeWebhookEvent, {
726
- idempotencyKey: event.request?.idempotency_key
727
- })) {
658
+ if (await this.em.findOne(
659
+ this.webhookEventEntity,
660
+ {
661
+ idempotencyKey: event.request?.idempotency_key
662
+ }
663
+ )) {
728
664
  this.openTelemetryCollector.info(
729
665
  "Webhook event already processed",
730
666
  event
@@ -967,7 +903,7 @@ var StripeWebhookService = class {
967
903
  );
968
904
  break;
969
905
  }
970
- await this.em.insert(StripeWebhookEvent, {
906
+ this.em.create(this.webhookEventEntity, {
971
907
  stripeId: event.id,
972
908
  idempotencyKey: event.request?.idempotency_key,
973
909
  eventType: event.type,
@@ -1,15 +1,29 @@
1
1
  import { MetricsDefinition, OpenTelemetryCollector } from '@forklaunch/core/http';
2
2
  import { AnySchemaValidator } from '@forklaunch/validator';
3
- import { EntityManager } from '@mikro-orm/core';
3
+ import { EntityManager, EntityName, InferEntity } from '@mikro-orm/core';
4
4
  import Stripe from 'stripe';
5
5
  import { StripeBillingPortalEntities, StripeCheckoutSessionEntities, StripePaymentLinkEntities, StripePlanEntities, StripeSubscriptionEntities } from '../domain/types/stripe.entity.types';
6
+ import { StripeWebhookEvent } from '../persistence/entities';
6
7
  import { StripeBillingPortalService } from './billingPortal.service';
7
8
  import { StripeCheckoutSessionService } from './checkoutSession.service';
8
9
  import { StripePaymentLinkService } from './paymentLink.service';
9
10
  import { StripePlanService } from './plan.service';
10
11
  import { StripeSubscriptionService } from './subscription.service';
12
+ /**
13
+ * Webhook idempotency records are looked up and written BY NAME so they
14
+ * resolve against the consuming application's discovered metadata. Passing
15
+ * this package's internal `StripeWebhookEvent` object to the EntityManager
16
+ * queries an entity the app's ORM never discovered, which mikro-orm 7.1.x
17
+ * rejects deep in EntityLoader (`meta.relations` is undefined for
18
+ * undiscovered entities). Applications must discover an entity named
19
+ * `StripeWebhookEvent` — either their own definition (the blueprint app
20
+ * ships one) or this package's, importable from
21
+ * `@forklaunch/implementation-billing-stripe/persistence`.
22
+ */
23
+ type StripeWebhookEventEntity = InferEntity<typeof StripeWebhookEvent>;
11
24
  export declare class StripeWebhookService<SchemaValidator extends AnySchemaValidator, StatusEnum, PartyEnum, BillingPortalEntities extends StripeBillingPortalEntities = StripeBillingPortalEntities, CheckoutSessionEntities extends StripeCheckoutSessionEntities<StatusEnum> = StripeCheckoutSessionEntities<StatusEnum>, PaymentLinkEntities extends StripePaymentLinkEntities<StatusEnum> = StripePaymentLinkEntities<StatusEnum>, PlanEntities extends StripePlanEntities = StripePlanEntities, SubscriptionEntities extends StripeSubscriptionEntities<PartyEnum> = StripeSubscriptionEntities<PartyEnum>> {
12
25
  protected readonly partyEnum: PartyEnum;
26
+ protected readonly webhookEventEntity: EntityName<StripeWebhookEventEntity>;
13
27
  protected readonly stripeClient: Stripe;
14
28
  protected readonly em: EntityManager;
15
29
  protected readonly schemaValidator: SchemaValidator;
@@ -19,7 +33,14 @@ export declare class StripeWebhookService<SchemaValidator extends AnySchemaValid
19
33
  protected readonly paymentLinkService: StripePaymentLinkService<SchemaValidator, StatusEnum, PaymentLinkEntities>;
20
34
  protected readonly planService: StripePlanService<SchemaValidator, PlanEntities>;
21
35
  protected readonly subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>;
22
- constructor(stripeClient: Stripe, em: EntityManager, schemaValidator: SchemaValidator, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, billingPortalService: StripeBillingPortalService<SchemaValidator, BillingPortalEntities>, checkoutSessionService: StripeCheckoutSessionService<SchemaValidator, StatusEnum, CheckoutSessionEntities>, paymentLinkService: StripePaymentLinkService<SchemaValidator, StatusEnum, PaymentLinkEntities>, planService: StripePlanService<SchemaValidator, PlanEntities>, subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>, partyEnum: PartyEnum);
36
+ constructor(stripeClient: Stripe, em: EntityManager, schemaValidator: SchemaValidator, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, billingPortalService: StripeBillingPortalService<SchemaValidator, BillingPortalEntities>, checkoutSessionService: StripeCheckoutSessionService<SchemaValidator, StatusEnum, CheckoutSessionEntities>, paymentLinkService: StripePaymentLinkService<SchemaValidator, StatusEnum, PaymentLinkEntities>, planService: StripePlanService<SchemaValidator, PlanEntities>, subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>, partyEnum: PartyEnum,
37
+ /**
38
+ * The entity used for webhook idempotency records. Defaults to
39
+ * resolving the application's discovered entity named
40
+ * 'StripeWebhookEvent'; inject your own entity (mapper-style) to use a
41
+ * different one.
42
+ */
43
+ webhookEventEntity?: EntityName<StripeWebhookEventEntity>);
23
44
  /**
24
45
  * Resolve the party type for a subscription event.
25
46
  * Stripe subscriptions are customer-scoped — customers map to users
@@ -36,4 +57,5 @@ export declare class StripeWebhookService<SchemaValidator extends AnySchemaValid
36
57
  private extractFeaturesFromProduct;
37
58
  handleWebhookEvent(event: Stripe.Event): Promise<void>;
38
59
  }
60
+ export {};
39
61
  //# sourceMappingURL=webhook.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.service.d.ts","sourceRoot":"","sources":["../../services/webhook.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook.service.d.ts","sourceRoot":"","sources":["../../services/webhook.service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/implementation-billing-stripe",
3
- "version": "1.1.30",
3
+ "version": "1.1.32",
4
4
  "description": "Stripe implementation for forklaunch billing",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -48,6 +48,15 @@
48
48
  },
49
49
  "./types/*": {
50
50
  "types": "./lib/domain/types/*.d.ts"
51
+ },
52
+ "./persistence": {
53
+ "types": "./lib/persistence/entities/index.d.ts",
54
+ "import": "./lib/persistence/entities/index.mjs",
55
+ "require": "./lib/persistence/entities/index.js",
56
+ "default": "./lib/persistence/entities/index.js"
57
+ },
58
+ "./persistence/*": {
59
+ "types": "./lib/persistence/entities/*.d.ts"
51
60
  }
52
61
  },
53
62
  "files": [
@@ -70,13 +79,15 @@
70
79
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
71
80
  "depcheck": "^1.4.7",
72
81
  "prettier": "^3.9.6",
73
- "typedoc": "^0.28.20"
82
+ "typedoc": "^0.28.20",
83
+ "@mikro-orm/sqlite": "7.1.11",
84
+ "uuid": "^14.0.1"
74
85
  },
75
86
  "publishConfig": {
76
87
  "access": "public"
77
88
  },
78
89
  "scripts": {
79
- "build": "tsgo --noEmit && tsup domain/schemas/index.ts services/index.ts domain/enum/index.ts domain/types/index.ts --format cjs,esm --no-splitting --tsconfig tsconfig.json --out-dir lib --clean && tsgo -p tsconfig.build.json --emitDeclarationOnly && if [ -f eject-package.bash ]; then pnpm package:eject; fi",
90
+ "build": "tsgo --noEmit && tsup persistence/entities/index.ts domain/schemas/index.ts services/index.ts domain/enum/index.ts domain/types/index.ts --format cjs,esm --no-splitting --tsconfig tsconfig.json --out-dir lib --clean && tsgo -p tsconfig.build.json --emitDeclarationOnly && if [ -f eject-package.bash ]; then pnpm package:eject; fi",
80
91
  "clean": "rm -rf lib pnpm.lock.yaml node_modules",
81
92
  "docs": "typedoc --out docs *",
82
93
  "format": "prettier --ignore-path=.prettierignore --config .prettierrc '**/*.{ts,tsx,json}' --write",