@forklaunch/implementation-billing-stripe 0.4.4 → 0.5.1
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/lib/eject/services/billingPortal.service.ts +2 -2
- package/lib/eject/services/checkoutSession.service.ts +1 -0
- package/lib/eject/services/paymentLink.service.ts +7 -7
- package/lib/eject/services/plan.service.ts +70 -55
- package/lib/eject/services/subscription.service.ts +96 -64
- package/lib/services/index.d.mts +4 -8
- package/lib/services/index.d.ts +4 -8
- package/lib/services/index.js +150 -109
- package/lib/services/index.mjs +150 -109
- package/package.json +11 -11
|
@@ -120,13 +120,13 @@ export class StripeBillingPortalService<
|
|
|
120
120
|
});
|
|
121
121
|
const session = await this.stripeClient.billingPortal.sessions.create({
|
|
122
122
|
...billingPortalDto.stripeFields,
|
|
123
|
-
customer: existingSession.customerId
|
|
123
|
+
customer: billingPortalDto.customerId || existingSession.customerId
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
return await this.baseBillingPortalService.updateBillingPortalSession(
|
|
127
127
|
{
|
|
128
128
|
...billingPortalDto,
|
|
129
|
-
id:
|
|
129
|
+
id: existingSession.id, // Use the original database ID, not the new Stripe session ID
|
|
130
130
|
uri: session.url,
|
|
131
131
|
expiresAt: new Date(
|
|
132
132
|
Date.now() + this.billingPortalSessionExpiryDurationMs
|
|
@@ -97,6 +97,7 @@ export class StripeCheckoutSessionService<
|
|
|
97
97
|
): Promise<Dto['CheckoutSessionMapper']> {
|
|
98
98
|
const session = await this.stripeClient.checkout.sessions.create({
|
|
99
99
|
...checkoutSessionDto.stripeFields,
|
|
100
|
+
mode: 'subscription',
|
|
100
101
|
payment_method_types: checkoutSessionDto.paymentMethods,
|
|
101
102
|
currency: checkoutSessionDto.currency as string,
|
|
102
103
|
success_url: checkoutSessionDto.successRedirectUri,
|
|
@@ -109,10 +109,11 @@ export class StripePaymentLinkService<
|
|
|
109
109
|
...paymentLinkDto,
|
|
110
110
|
id: session.id,
|
|
111
111
|
amount:
|
|
112
|
+
paymentLinkDto.amount ??
|
|
112
113
|
session.line_items?.data.reduce<number>(
|
|
113
114
|
(total, item) => total + item.amount_total,
|
|
114
115
|
0
|
|
115
|
-
)
|
|
116
|
+
)
|
|
116
117
|
},
|
|
117
118
|
this.em,
|
|
118
119
|
session,
|
|
@@ -197,6 +198,7 @@ export class StripePaymentLinkService<
|
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
async listPaymentLinks(idsDto?: IdsDto): Promise<Dto['PaymentLinkMapper'][]> {
|
|
201
|
+
this.openTelemetryCollector.log('info', idsDto ?? 'idsDto is undefined');
|
|
200
202
|
const stripePaymentLinks = await this.stripeClient.paymentLinks.list({
|
|
201
203
|
active: true
|
|
202
204
|
});
|
|
@@ -204,19 +206,17 @@ export class StripePaymentLinkService<
|
|
|
204
206
|
const databasePaymentLinks =
|
|
205
207
|
await this.basePaymentLinkService.listPaymentLinks(idsDto);
|
|
206
208
|
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
+
return databasePaymentLinks
|
|
210
|
+
.map((paymentLink) => {
|
|
209
211
|
const stripePaymentLink = stripePaymentLinks.data.find(
|
|
210
212
|
(sp) => sp.id === paymentLink.id
|
|
211
213
|
);
|
|
212
214
|
if (!stripePaymentLink) {
|
|
213
|
-
|
|
214
|
-
`Stripe payment link not found for id: ${paymentLink.id}`
|
|
215
|
-
);
|
|
215
|
+
return null;
|
|
216
216
|
}
|
|
217
217
|
paymentLink.stripeFields = stripePaymentLink;
|
|
218
218
|
return paymentLink;
|
|
219
219
|
})
|
|
220
|
-
|
|
220
|
+
.filter((paymentLink) => paymentLink !== null);
|
|
221
221
|
}
|
|
222
222
|
}
|
|
@@ -91,6 +91,7 @@ export class StripePlanService<
|
|
|
91
91
|
): Promise<Dto['PlanMapper']> {
|
|
92
92
|
const stripePlan = await this.stripeClient.plans.create({
|
|
93
93
|
...planDto.stripeFields,
|
|
94
|
+
amount: planDto.price,
|
|
94
95
|
interval: planDto.cadence,
|
|
95
96
|
product: planDto.name,
|
|
96
97
|
currency: planDto.currency as string
|
|
@@ -110,17 +111,11 @@ export class StripePlanService<
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
async getPlan(idDto: IdDto, em?: EntityManager): Promise<Dto['PlanMapper']> {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
await em?.findOne<{ id: string; externalId: string }>(
|
|
116
|
-
this.options?.databaseTableName ?? 'plan',
|
|
117
|
-
{ externalId: idDto.id }
|
|
118
|
-
)
|
|
119
|
-
)?.id;
|
|
120
|
-
if (!id) {
|
|
114
|
+
const planEntity = await this.basePlanService.getPlan(idDto, em);
|
|
115
|
+
if (!planEntity.externalId) {
|
|
121
116
|
throw new Error('Plan not found');
|
|
122
117
|
}
|
|
123
|
-
const
|
|
118
|
+
const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
|
|
124
119
|
planEntity.stripeFields = plan;
|
|
125
120
|
return planEntity;
|
|
126
121
|
}
|
|
@@ -129,35 +124,58 @@ export class StripePlanService<
|
|
|
129
124
|
planDto: StripeUpdatePlanDto,
|
|
130
125
|
em?: EntityManager
|
|
131
126
|
): Promise<Dto['PlanMapper']> {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
product: planDto.name,
|
|
138
|
-
currency: planDto.currency ?? existingPlan.currency
|
|
139
|
-
})
|
|
127
|
+
const planEntity = await this.basePlanService.getPlan(
|
|
128
|
+
{
|
|
129
|
+
id: planDto.id
|
|
130
|
+
},
|
|
131
|
+
em
|
|
140
132
|
);
|
|
141
133
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
...planDto,
|
|
146
|
-
externalId: plan.id,
|
|
147
|
-
billingProvider: 'stripe'
|
|
148
|
-
},
|
|
149
|
-
em ?? this.em,
|
|
150
|
-
plan
|
|
151
|
-
),
|
|
152
|
-
em
|
|
134
|
+
const existingPlan = await this.stripeClient.plans.retrieve(
|
|
135
|
+
planEntity.externalId
|
|
153
136
|
);
|
|
154
|
-
planEntity.stripeFields = plan;
|
|
155
137
|
|
|
156
|
-
|
|
138
|
+
const existingProduct = existingPlan.product;
|
|
139
|
+
if (!existingProduct) {
|
|
140
|
+
throw new Error('Plan product not found');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const productId =
|
|
144
|
+
typeof existingProduct === 'string'
|
|
145
|
+
? existingProduct
|
|
146
|
+
: existingProduct.id;
|
|
147
|
+
|
|
148
|
+
await this.stripeClient.plans.del(planEntity.externalId);
|
|
149
|
+
const updatedPlan = await this.stripeClient.plans.create({
|
|
150
|
+
...planDto.stripeFields,
|
|
151
|
+
interval: planDto.cadence ?? existingPlan.interval,
|
|
152
|
+
currency: planDto.currency ?? existingPlan.currency,
|
|
153
|
+
amount: planDto.price ?? existingPlan.amount ?? undefined,
|
|
154
|
+
product: productId
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const updatedPlanEntity = await this.basePlanService.updatePlan(
|
|
158
|
+
{
|
|
159
|
+
...planDto,
|
|
160
|
+
externalId: updatedPlan.id,
|
|
161
|
+
name: planDto.name,
|
|
162
|
+
billingProvider: 'stripe'
|
|
163
|
+
},
|
|
164
|
+
em,
|
|
165
|
+
updatedPlan
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
updatedPlanEntity.stripeFields = updatedPlan;
|
|
169
|
+
|
|
170
|
+
return updatedPlanEntity;
|
|
157
171
|
}
|
|
158
172
|
|
|
159
|
-
async deletePlan(idDto:
|
|
160
|
-
await this.
|
|
173
|
+
async deletePlan(idDto: IdDto, em?: EntityManager): Promise<void> {
|
|
174
|
+
const plan = await this.basePlanService.getPlan(idDto, em);
|
|
175
|
+
if (!plan.externalId) {
|
|
176
|
+
throw new Error('Plan not found');
|
|
177
|
+
}
|
|
178
|
+
await this.stripeClient.plans.del(plan.externalId);
|
|
161
179
|
await this.basePlanService.deletePlan(idDto, em);
|
|
162
180
|
}
|
|
163
181
|
|
|
@@ -165,30 +183,27 @@ export class StripePlanService<
|
|
|
165
183
|
idsDto?: IdsDto,
|
|
166
184
|
em?: EntityManager
|
|
167
185
|
): Promise<Dto['PlanMapper'][]> {
|
|
168
|
-
const plans = await this.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
await em?.findAll<{ id: string; externalId: string }>(
|
|
173
|
-
this.options?.databaseTableName ?? 'plan',
|
|
174
|
-
{ where: { externalId: { $in: plans.data.map((plan) => plan.id) } } }
|
|
175
|
-
)
|
|
176
|
-
)
|
|
177
|
-
?.filter((s) => idsDto?.ids?.includes(s.id))
|
|
178
|
-
?.map((s) => s.id);
|
|
179
|
-
|
|
180
|
-
if (!planIds) {
|
|
181
|
-
throw new Error('Plans not found');
|
|
186
|
+
const plans = await this.basePlanService.listPlans(idsDto, em);
|
|
187
|
+
|
|
188
|
+
if (!plans || plans.length === 0) {
|
|
189
|
+
return [];
|
|
182
190
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
)
|
|
191
|
+
|
|
192
|
+
const stripePlans = await Promise.all(
|
|
193
|
+
plans.map(async (plan) => {
|
|
194
|
+
try {
|
|
195
|
+
return await this.stripeClient.plans.retrieve(plan.externalId);
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
})
|
|
192
200
|
);
|
|
201
|
+
|
|
202
|
+
return plans
|
|
203
|
+
.map((plan, index) => ({
|
|
204
|
+
...plan,
|
|
205
|
+
stripeFields: stripePlans[index]
|
|
206
|
+
}))
|
|
207
|
+
.filter((plan) => plan.stripeFields !== null);
|
|
193
208
|
}
|
|
194
209
|
}
|
|
@@ -117,8 +117,11 @@ export class StripeSubscriptionService<
|
|
|
117
117
|
): Promise<Dto['SubscriptionMapper']> {
|
|
118
118
|
const subscriptionEntity =
|
|
119
119
|
await this.baseSubscriptionService.getSubscription(idDto, em);
|
|
120
|
+
if (!subscriptionEntity.externalId) {
|
|
121
|
+
throw new Error('Subscription not found');
|
|
122
|
+
}
|
|
120
123
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
121
|
-
|
|
124
|
+
subscriptionEntity.externalId
|
|
122
125
|
);
|
|
123
126
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
124
127
|
return subscriptionEntity;
|
|
@@ -130,8 +133,11 @@ export class StripeSubscriptionService<
|
|
|
130
133
|
): Promise<Dto['SubscriptionMapper']> {
|
|
131
134
|
const subscriptionEntity =
|
|
132
135
|
await this.baseSubscriptionService.getUserSubscription(idDto, em);
|
|
136
|
+
if (!subscriptionEntity.externalId) {
|
|
137
|
+
throw new Error('Subscription not found');
|
|
138
|
+
}
|
|
133
139
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
134
|
-
|
|
140
|
+
subscriptionEntity.externalId
|
|
135
141
|
);
|
|
136
142
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
137
143
|
return subscriptionEntity;
|
|
@@ -141,104 +147,130 @@ export class StripeSubscriptionService<
|
|
|
141
147
|
idDto: IdDto,
|
|
142
148
|
em?: EntityManager
|
|
143
149
|
): Promise<Dto['SubscriptionMapper']> {
|
|
144
|
-
const
|
|
145
|
-
await em
|
|
146
|
-
|
|
147
|
-
{ externalId: idDto.id }
|
|
148
|
-
)
|
|
149
|
-
)?.id;
|
|
150
|
-
if (!id) {
|
|
150
|
+
const subscriptionEntity =
|
|
151
|
+
await this.baseSubscriptionService.getOrganizationSubscription(idDto, em);
|
|
152
|
+
if (!subscriptionEntity.externalId) {
|
|
151
153
|
throw new Error('Subscription not found');
|
|
152
154
|
}
|
|
153
|
-
const subscriptionEntity =
|
|
154
|
-
await this.baseSubscriptionService.getOrganizationSubscription(
|
|
155
|
-
{ id },
|
|
156
|
-
em
|
|
157
|
-
);
|
|
158
155
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
159
|
-
|
|
156
|
+
subscriptionEntity.externalId
|
|
160
157
|
);
|
|
161
158
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
162
159
|
return subscriptionEntity;
|
|
163
160
|
}
|
|
164
161
|
|
|
165
162
|
async updateSubscription(
|
|
166
|
-
subscriptionDto: StripeUpdateSubscriptionDto<PartyType
|
|
163
|
+
subscriptionDto: StripeUpdateSubscriptionDto<PartyType> & IdDto,
|
|
167
164
|
em?: EntityManager
|
|
168
165
|
): Promise<Dto['SubscriptionMapper']> {
|
|
169
|
-
const
|
|
170
|
-
|
|
166
|
+
const subscriptionEntity =
|
|
167
|
+
await this.baseSubscriptionService.getSubscription(
|
|
168
|
+
{
|
|
169
|
+
id: subscriptionDto.id
|
|
170
|
+
},
|
|
171
|
+
em
|
|
172
|
+
);
|
|
173
|
+
if (!subscriptionEntity.externalId) {
|
|
174
|
+
throw new Error('Subscription not found');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const existingStripeSubscription =
|
|
178
|
+
await this.stripeClient.subscriptions.retrieve(
|
|
179
|
+
subscriptionEntity.externalId
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const updatedSubscription = await this.stripeClient.subscriptions.update(
|
|
183
|
+
subscriptionEntity.externalId,
|
|
171
184
|
{
|
|
172
185
|
...subscriptionDto.stripeFields,
|
|
173
|
-
items:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
]
|
|
186
|
+
items: existingStripeSubscription.items.data.map((item) => ({
|
|
187
|
+
id: item.id,
|
|
188
|
+
plan: subscriptionDto.productId || item.plan?.id
|
|
189
|
+
}))
|
|
178
190
|
}
|
|
179
191
|
);
|
|
180
192
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
193
|
+
const updatedSubscriptionEntity =
|
|
194
|
+
await this.baseSubscriptionService.updateSubscription(
|
|
195
|
+
{
|
|
196
|
+
...subscriptionDto,
|
|
197
|
+
externalId: updatedSubscription.id,
|
|
198
|
+
billingProvider: 'stripe'
|
|
199
|
+
},
|
|
200
|
+
em ?? this.em,
|
|
201
|
+
updatedSubscription
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
updatedSubscriptionEntity.stripeFields = updatedSubscription;
|
|
205
|
+
return updatedSubscriptionEntity;
|
|
191
206
|
}
|
|
192
207
|
|
|
193
|
-
async deleteSubscription(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
async deleteSubscription(idDto: IdDto, em?: EntityManager): Promise<void> {
|
|
209
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
210
|
+
idDto,
|
|
211
|
+
em
|
|
212
|
+
);
|
|
213
|
+
if (!subscription.externalId) {
|
|
214
|
+
throw new Error('Subscription not found');
|
|
215
|
+
}
|
|
216
|
+
await this.stripeClient.subscriptions.cancel(subscription.externalId);
|
|
198
217
|
await this.baseSubscriptionService.deleteSubscription(idDto, em);
|
|
199
218
|
}
|
|
200
219
|
|
|
201
220
|
async listSubscriptions(
|
|
202
|
-
idsDto
|
|
221
|
+
idsDto?: IdsDto,
|
|
203
222
|
em?: EntityManager
|
|
204
223
|
): Promise<Dto['SubscriptionMapper'][]> {
|
|
205
|
-
const subscriptions = (
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
).data.filter((s) => idsDto.ids?.includes(s.id));
|
|
210
|
-
|
|
211
|
-
const ids = (
|
|
212
|
-
await em?.findAll<{ id: string; externalId: string }>(
|
|
213
|
-
this.options?.databaseTableName ?? 'subscription',
|
|
214
|
-
{ where: { externalId: { $in: subscriptions.map((s) => s.id) } } }
|
|
215
|
-
)
|
|
216
|
-
)?.map((s) => s.id);
|
|
224
|
+
const subscriptions = await this.baseSubscriptionService.listSubscriptions(
|
|
225
|
+
idsDto,
|
|
226
|
+
em
|
|
227
|
+
);
|
|
217
228
|
|
|
218
|
-
if (!
|
|
219
|
-
|
|
229
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
230
|
+
return [];
|
|
220
231
|
}
|
|
221
232
|
|
|
222
|
-
|
|
223
|
-
(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
return
|
|
233
|
+
const stripeSubscriptions = await Promise.all(
|
|
234
|
+
subscriptions.map(async (subscription) => {
|
|
235
|
+
try {
|
|
236
|
+
return await this.stripeClient.subscriptions.retrieve(
|
|
237
|
+
subscription.externalId
|
|
238
|
+
);
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
230
241
|
}
|
|
231
|
-
)
|
|
242
|
+
})
|
|
232
243
|
);
|
|
244
|
+
|
|
245
|
+
return subscriptions
|
|
246
|
+
.map((subscription, index) => ({
|
|
247
|
+
...subscription,
|
|
248
|
+
stripeFields: stripeSubscriptions[index]
|
|
249
|
+
}))
|
|
250
|
+
.filter((subscription) => subscription.stripeFields !== null);
|
|
233
251
|
}
|
|
234
252
|
|
|
235
253
|
async cancelSubscription(idDto: IdDto, em?: EntityManager): Promise<void> {
|
|
236
|
-
await this.
|
|
254
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
255
|
+
idDto,
|
|
256
|
+
em
|
|
257
|
+
);
|
|
258
|
+
if (!subscription.externalId) {
|
|
259
|
+
throw new Error('Subscription not found');
|
|
260
|
+
}
|
|
261
|
+
await this.stripeClient.subscriptions.cancel(subscription.externalId);
|
|
237
262
|
await this.baseSubscriptionService.cancelSubscription(idDto, em);
|
|
238
263
|
}
|
|
239
264
|
|
|
240
265
|
async resumeSubscription(idDto: IdDto, em?: EntityManager): Promise<void> {
|
|
241
|
-
await this.
|
|
266
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
267
|
+
idDto,
|
|
268
|
+
em
|
|
269
|
+
);
|
|
270
|
+
if (!subscription.externalId) {
|
|
271
|
+
throw new Error('Subscription not found');
|
|
272
|
+
}
|
|
273
|
+
await this.stripeClient.subscriptions.resume(subscription.externalId);
|
|
242
274
|
await this.baseSubscriptionService.resumeSubscription(idDto, em);
|
|
243
275
|
}
|
|
244
276
|
}
|
package/lib/services/index.d.mts
CHANGED
|
@@ -121,9 +121,7 @@ declare class StripePlanService<SchemaValidator extends AnySchemaValidator, Enti
|
|
|
121
121
|
createPlan(planDto: StripeCreatePlanDto, em?: EntityManager): Promise<Dto['PlanMapper']>;
|
|
122
122
|
getPlan(idDto: IdDto, em?: EntityManager): Promise<Dto['PlanMapper']>;
|
|
123
123
|
updatePlan(planDto: StripeUpdatePlanDto, em?: EntityManager): Promise<Dto['PlanMapper']>;
|
|
124
|
-
deletePlan(idDto:
|
|
125
|
-
id: string;
|
|
126
|
-
}, em?: EntityManager): Promise<void>;
|
|
124
|
+
deletePlan(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
127
125
|
listPlans(idsDto?: IdsDto, em?: EntityManager): Promise<Dto['PlanMapper'][]>;
|
|
128
126
|
}
|
|
129
127
|
|
|
@@ -152,11 +150,9 @@ declare class StripeSubscriptionService<SchemaValidator extends AnySchemaValidat
|
|
|
152
150
|
getSubscription(idDto: IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
153
151
|
getUserSubscription(idDto: IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
154
152
|
getOrganizationSubscription(idDto: IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
155
|
-
updateSubscription(subscriptionDto: StripeUpdateSubscriptionDto<PartyType
|
|
156
|
-
deleteSubscription(idDto:
|
|
157
|
-
|
|
158
|
-
}, em?: EntityManager): Promise<void>;
|
|
159
|
-
listSubscriptions(idsDto: IdsDto, em?: EntityManager): Promise<Dto['SubscriptionMapper'][]>;
|
|
153
|
+
updateSubscription(subscriptionDto: StripeUpdateSubscriptionDto<PartyType> & IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
154
|
+
deleteSubscription(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
155
|
+
listSubscriptions(idsDto?: IdsDto, em?: EntityManager): Promise<Dto['SubscriptionMapper'][]>;
|
|
160
156
|
cancelSubscription(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
161
157
|
resumeSubscription(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
162
158
|
}
|
package/lib/services/index.d.ts
CHANGED
|
@@ -121,9 +121,7 @@ declare class StripePlanService<SchemaValidator extends AnySchemaValidator, Enti
|
|
|
121
121
|
createPlan(planDto: StripeCreatePlanDto, em?: EntityManager): Promise<Dto['PlanMapper']>;
|
|
122
122
|
getPlan(idDto: IdDto, em?: EntityManager): Promise<Dto['PlanMapper']>;
|
|
123
123
|
updatePlan(planDto: StripeUpdatePlanDto, em?: EntityManager): Promise<Dto['PlanMapper']>;
|
|
124
|
-
deletePlan(idDto:
|
|
125
|
-
id: string;
|
|
126
|
-
}, em?: EntityManager): Promise<void>;
|
|
124
|
+
deletePlan(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
127
125
|
listPlans(idsDto?: IdsDto, em?: EntityManager): Promise<Dto['PlanMapper'][]>;
|
|
128
126
|
}
|
|
129
127
|
|
|
@@ -152,11 +150,9 @@ declare class StripeSubscriptionService<SchemaValidator extends AnySchemaValidat
|
|
|
152
150
|
getSubscription(idDto: IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
153
151
|
getUserSubscription(idDto: IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
154
152
|
getOrganizationSubscription(idDto: IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
155
|
-
updateSubscription(subscriptionDto: StripeUpdateSubscriptionDto<PartyType
|
|
156
|
-
deleteSubscription(idDto:
|
|
157
|
-
|
|
158
|
-
}, em?: EntityManager): Promise<void>;
|
|
159
|
-
listSubscriptions(idsDto: IdsDto, em?: EntityManager): Promise<Dto['SubscriptionMapper'][]>;
|
|
153
|
+
updateSubscription(subscriptionDto: StripeUpdateSubscriptionDto<PartyType> & IdDto, em?: EntityManager): Promise<Dto['SubscriptionMapper']>;
|
|
154
|
+
deleteSubscription(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
155
|
+
listSubscriptions(idsDto?: IdsDto, em?: EntityManager): Promise<Dto['SubscriptionMapper'][]>;
|
|
160
156
|
cancelSubscription(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
161
157
|
resumeSubscription(idDto: IdDto, em?: EntityManager): Promise<void>;
|
|
162
158
|
}
|
package/lib/services/index.js
CHANGED
|
@@ -89,12 +89,13 @@ var StripeBillingPortalService = class {
|
|
|
89
89
|
});
|
|
90
90
|
const session = await this.stripeClient.billingPortal.sessions.create({
|
|
91
91
|
...billingPortalDto.stripeFields,
|
|
92
|
-
customer: existingSession.customerId
|
|
92
|
+
customer: billingPortalDto.customerId || existingSession.customerId
|
|
93
93
|
});
|
|
94
94
|
return await this.baseBillingPortalService.updateBillingPortalSession(
|
|
95
95
|
{
|
|
96
96
|
...billingPortalDto,
|
|
97
|
-
id:
|
|
97
|
+
id: existingSession.id,
|
|
98
|
+
// Use the original database ID, not the new Stripe session ID
|
|
98
99
|
uri: session.url,
|
|
99
100
|
expiresAt: new Date(
|
|
100
101
|
Date.now() + this.billingPortalSessionExpiryDurationMs
|
|
@@ -137,6 +138,7 @@ var StripeCheckoutSessionService = class {
|
|
|
137
138
|
async createCheckoutSession(checkoutSessionDto, ...args) {
|
|
138
139
|
const session = await this.stripeClient.checkout.sessions.create({
|
|
139
140
|
...checkoutSessionDto.stripeFields,
|
|
141
|
+
mode: "subscription",
|
|
140
142
|
payment_method_types: checkoutSessionDto.paymentMethods,
|
|
141
143
|
currency: checkoutSessionDto.currency,
|
|
142
144
|
success_url: checkoutSessionDto.successRedirectUri,
|
|
@@ -222,10 +224,10 @@ var StripePaymentLinkService = class {
|
|
|
222
224
|
{
|
|
223
225
|
...paymentLinkDto,
|
|
224
226
|
id: session.id,
|
|
225
|
-
amount: session.line_items?.data.reduce(
|
|
227
|
+
amount: paymentLinkDto.amount ?? session.line_items?.data.reduce(
|
|
226
228
|
(total, item) => total + item.amount_total,
|
|
227
229
|
0
|
|
228
|
-
)
|
|
230
|
+
)
|
|
229
231
|
},
|
|
230
232
|
this.em,
|
|
231
233
|
session,
|
|
@@ -291,24 +293,21 @@ var StripePaymentLinkService = class {
|
|
|
291
293
|
await this.basePaymentLinkService.handlePaymentFailure({ id });
|
|
292
294
|
}
|
|
293
295
|
async listPaymentLinks(idsDto) {
|
|
296
|
+
this.openTelemetryCollector.log("info", idsDto ?? "idsDto is undefined");
|
|
294
297
|
const stripePaymentLinks = await this.stripeClient.paymentLinks.list({
|
|
295
298
|
active: true
|
|
296
299
|
});
|
|
297
300
|
const databasePaymentLinks = await this.basePaymentLinkService.listPaymentLinks(idsDto);
|
|
298
|
-
return
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
paymentLink.stripeFields = stripePaymentLink;
|
|
309
|
-
return paymentLink;
|
|
310
|
-
})
|
|
311
|
-
);
|
|
301
|
+
return databasePaymentLinks.map((paymentLink) => {
|
|
302
|
+
const stripePaymentLink = stripePaymentLinks.data.find(
|
|
303
|
+
(sp) => sp.id === paymentLink.id
|
|
304
|
+
);
|
|
305
|
+
if (!stripePaymentLink) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
paymentLink.stripeFields = stripePaymentLink;
|
|
309
|
+
return paymentLink;
|
|
310
|
+
}).filter((paymentLink) => paymentLink !== null);
|
|
312
311
|
}
|
|
313
312
|
};
|
|
314
313
|
|
|
@@ -339,6 +338,7 @@ var StripePlanService = class {
|
|
|
339
338
|
async createPlan(planDto, em) {
|
|
340
339
|
const stripePlan = await this.stripeClient.plans.create({
|
|
341
340
|
...planDto.stripeFields,
|
|
341
|
+
amount: planDto.price,
|
|
342
342
|
interval: planDto.cadence,
|
|
343
343
|
product: planDto.name,
|
|
344
344
|
currency: planDto.currency
|
|
@@ -355,68 +355,76 @@ var StripePlanService = class {
|
|
|
355
355
|
return plan;
|
|
356
356
|
}
|
|
357
357
|
async getPlan(idDto, em) {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
this.options?.databaseTableName ?? "plan",
|
|
361
|
-
{ externalId: idDto.id }
|
|
362
|
-
))?.id;
|
|
363
|
-
if (!id) {
|
|
358
|
+
const planEntity = await this.basePlanService.getPlan(idDto, em);
|
|
359
|
+
if (!planEntity.externalId) {
|
|
364
360
|
throw new Error("Plan not found");
|
|
365
361
|
}
|
|
366
|
-
const
|
|
362
|
+
const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
|
|
367
363
|
planEntity.stripeFields = plan;
|
|
368
364
|
return planEntity;
|
|
369
365
|
}
|
|
370
366
|
async updatePlan(planDto, em) {
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
interval: planDto.cadence ?? existingPlan.interval,
|
|
376
|
-
product: planDto.name,
|
|
377
|
-
currency: planDto.currency ?? existingPlan.currency
|
|
378
|
-
})
|
|
379
|
-
);
|
|
380
|
-
const planEntity = await this.basePlanService.updatePlan(
|
|
381
|
-
await this.mappers.UpdatePlanMapper.toEntity(
|
|
382
|
-
{
|
|
383
|
-
...planDto,
|
|
384
|
-
externalId: plan.id,
|
|
385
|
-
billingProvider: "stripe"
|
|
386
|
-
},
|
|
387
|
-
em ?? this.em,
|
|
388
|
-
plan
|
|
389
|
-
),
|
|
367
|
+
const planEntity = await this.basePlanService.getPlan(
|
|
368
|
+
{
|
|
369
|
+
id: planDto.id
|
|
370
|
+
},
|
|
390
371
|
em
|
|
391
372
|
);
|
|
392
|
-
|
|
393
|
-
|
|
373
|
+
const existingPlan = await this.stripeClient.plans.retrieve(
|
|
374
|
+
planEntity.externalId
|
|
375
|
+
);
|
|
376
|
+
const existingProduct = existingPlan.product;
|
|
377
|
+
if (!existingProduct) {
|
|
378
|
+
throw new Error("Plan product not found");
|
|
379
|
+
}
|
|
380
|
+
const productId = typeof existingProduct === "string" ? existingProduct : existingProduct.id;
|
|
381
|
+
await this.stripeClient.plans.del(planEntity.externalId);
|
|
382
|
+
const updatedPlan = await this.stripeClient.plans.create({
|
|
383
|
+
...planDto.stripeFields,
|
|
384
|
+
interval: planDto.cadence ?? existingPlan.interval,
|
|
385
|
+
currency: planDto.currency ?? existingPlan.currency,
|
|
386
|
+
amount: planDto.price ?? existingPlan.amount ?? void 0,
|
|
387
|
+
product: productId
|
|
388
|
+
});
|
|
389
|
+
const updatedPlanEntity = await this.basePlanService.updatePlan(
|
|
390
|
+
{
|
|
391
|
+
...planDto,
|
|
392
|
+
externalId: updatedPlan.id,
|
|
393
|
+
name: planDto.name,
|
|
394
|
+
billingProvider: "stripe"
|
|
395
|
+
},
|
|
396
|
+
em,
|
|
397
|
+
updatedPlan
|
|
398
|
+
);
|
|
399
|
+
updatedPlanEntity.stripeFields = updatedPlan;
|
|
400
|
+
return updatedPlanEntity;
|
|
394
401
|
}
|
|
395
402
|
async deletePlan(idDto, em) {
|
|
396
|
-
await this.
|
|
403
|
+
const plan = await this.basePlanService.getPlan(idDto, em);
|
|
404
|
+
if (!plan.externalId) {
|
|
405
|
+
throw new Error("Plan not found");
|
|
406
|
+
}
|
|
407
|
+
await this.stripeClient.plans.del(plan.externalId);
|
|
397
408
|
await this.basePlanService.deletePlan(idDto, em);
|
|
398
409
|
}
|
|
399
410
|
async listPlans(idsDto, em) {
|
|
400
|
-
const plans = await this.
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const planIds = (await em?.findAll(
|
|
404
|
-
this.options?.databaseTableName ?? "plan",
|
|
405
|
-
{ where: { externalId: { $in: plans.data.map((plan) => plan.id) } } }
|
|
406
|
-
))?.filter((s) => idsDto?.ids?.includes(s.id))?.map((s) => s.id);
|
|
407
|
-
if (!planIds) {
|
|
408
|
-
throw new Error("Plans not found");
|
|
411
|
+
const plans = await this.basePlanService.listPlans(idsDto, em);
|
|
412
|
+
if (!plans || plans.length === 0) {
|
|
413
|
+
return [];
|
|
409
414
|
}
|
|
410
|
-
|
|
411
|
-
(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
)
|
|
415
|
+
const stripePlans = await Promise.all(
|
|
416
|
+
plans.map(async (plan) => {
|
|
417
|
+
try {
|
|
418
|
+
return await this.stripeClient.plans.retrieve(plan.externalId);
|
|
419
|
+
} catch {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
})
|
|
419
423
|
);
|
|
424
|
+
return plans.map((plan, index) => ({
|
|
425
|
+
...plan,
|
|
426
|
+
stripeFields: stripePlans[index]
|
|
427
|
+
})).filter((plan) => plan.stripeFields !== null);
|
|
420
428
|
}
|
|
421
429
|
};
|
|
422
430
|
|
|
@@ -466,94 +474,127 @@ var StripeSubscriptionService = class {
|
|
|
466
474
|
}
|
|
467
475
|
async getSubscription(idDto, em) {
|
|
468
476
|
const subscriptionEntity = await this.baseSubscriptionService.getSubscription(idDto, em);
|
|
477
|
+
if (!subscriptionEntity.externalId) {
|
|
478
|
+
throw new Error("Subscription not found");
|
|
479
|
+
}
|
|
469
480
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
470
|
-
|
|
481
|
+
subscriptionEntity.externalId
|
|
471
482
|
);
|
|
472
483
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
473
484
|
return subscriptionEntity;
|
|
474
485
|
}
|
|
475
486
|
async getUserSubscription(idDto, em) {
|
|
476
487
|
const subscriptionEntity = await this.baseSubscriptionService.getUserSubscription(idDto, em);
|
|
488
|
+
if (!subscriptionEntity.externalId) {
|
|
489
|
+
throw new Error("Subscription not found");
|
|
490
|
+
}
|
|
477
491
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
478
|
-
|
|
492
|
+
subscriptionEntity.externalId
|
|
479
493
|
);
|
|
480
494
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
481
495
|
return subscriptionEntity;
|
|
482
496
|
}
|
|
483
497
|
async getOrganizationSubscription(idDto, em) {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
{ externalId: idDto.id }
|
|
487
|
-
))?.id;
|
|
488
|
-
if (!id) {
|
|
498
|
+
const subscriptionEntity = await this.baseSubscriptionService.getOrganizationSubscription(idDto, em);
|
|
499
|
+
if (!subscriptionEntity.externalId) {
|
|
489
500
|
throw new Error("Subscription not found");
|
|
490
501
|
}
|
|
491
|
-
const subscriptionEntity = await this.baseSubscriptionService.getOrganizationSubscription(
|
|
492
|
-
{ id },
|
|
493
|
-
em
|
|
494
|
-
);
|
|
495
502
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
496
|
-
|
|
503
|
+
subscriptionEntity.externalId
|
|
497
504
|
);
|
|
498
505
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
499
506
|
return subscriptionEntity;
|
|
500
507
|
}
|
|
501
508
|
async updateSubscription(subscriptionDto, em) {
|
|
502
|
-
const
|
|
503
|
-
|
|
509
|
+
const subscriptionEntity = await this.baseSubscriptionService.getSubscription(
|
|
510
|
+
{
|
|
511
|
+
id: subscriptionDto.id
|
|
512
|
+
},
|
|
513
|
+
em
|
|
514
|
+
);
|
|
515
|
+
if (!subscriptionEntity.externalId) {
|
|
516
|
+
throw new Error("Subscription not found");
|
|
517
|
+
}
|
|
518
|
+
const existingStripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
519
|
+
subscriptionEntity.externalId
|
|
520
|
+
);
|
|
521
|
+
const updatedSubscription = await this.stripeClient.subscriptions.update(
|
|
522
|
+
subscriptionEntity.externalId,
|
|
504
523
|
{
|
|
505
524
|
...subscriptionDto.stripeFields,
|
|
506
|
-
items:
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
]
|
|
525
|
+
items: existingStripeSubscription.items.data.map((item) => ({
|
|
526
|
+
id: item.id,
|
|
527
|
+
plan: subscriptionDto.productId || item.plan?.id
|
|
528
|
+
}))
|
|
511
529
|
}
|
|
512
530
|
);
|
|
513
|
-
|
|
531
|
+
const updatedSubscriptionEntity = await this.baseSubscriptionService.updateSubscription(
|
|
514
532
|
{
|
|
515
533
|
...subscriptionDto,
|
|
516
|
-
externalId:
|
|
517
|
-
billingProvider: "stripe"
|
|
518
|
-
providerFields: subscription
|
|
534
|
+
externalId: updatedSubscription.id,
|
|
535
|
+
billingProvider: "stripe"
|
|
519
536
|
},
|
|
520
537
|
em ?? this.em,
|
|
521
|
-
|
|
538
|
+
updatedSubscription
|
|
522
539
|
);
|
|
540
|
+
updatedSubscriptionEntity.stripeFields = updatedSubscription;
|
|
541
|
+
return updatedSubscriptionEntity;
|
|
523
542
|
}
|
|
524
543
|
async deleteSubscription(idDto, em) {
|
|
525
|
-
await this.
|
|
544
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
545
|
+
idDto,
|
|
546
|
+
em
|
|
547
|
+
);
|
|
548
|
+
if (!subscription.externalId) {
|
|
549
|
+
throw new Error("Subscription not found");
|
|
550
|
+
}
|
|
551
|
+
await this.stripeClient.subscriptions.cancel(subscription.externalId);
|
|
526
552
|
await this.baseSubscriptionService.deleteSubscription(idDto, em);
|
|
527
553
|
}
|
|
528
554
|
async listSubscriptions(idsDto, em) {
|
|
529
|
-
const subscriptions =
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
))?.map((s) => s.id);
|
|
536
|
-
if (!ids) {
|
|
537
|
-
throw new Error("Subscriptions not found");
|
|
555
|
+
const subscriptions = await this.baseSubscriptionService.listSubscriptions(
|
|
556
|
+
idsDto,
|
|
557
|
+
em
|
|
558
|
+
);
|
|
559
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
560
|
+
return [];
|
|
538
561
|
}
|
|
539
|
-
|
|
540
|
-
(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
562
|
+
const stripeSubscriptions = await Promise.all(
|
|
563
|
+
subscriptions.map(async (subscription) => {
|
|
564
|
+
try {
|
|
565
|
+
return await this.stripeClient.subscriptions.retrieve(
|
|
566
|
+
subscription.externalId
|
|
544
567
|
);
|
|
545
|
-
|
|
546
|
-
return
|
|
568
|
+
} catch {
|
|
569
|
+
return null;
|
|
547
570
|
}
|
|
548
|
-
)
|
|
571
|
+
})
|
|
549
572
|
);
|
|
573
|
+
return subscriptions.map((subscription, index) => ({
|
|
574
|
+
...subscription,
|
|
575
|
+
stripeFields: stripeSubscriptions[index]
|
|
576
|
+
})).filter((subscription) => subscription.stripeFields !== null);
|
|
550
577
|
}
|
|
551
578
|
async cancelSubscription(idDto, em) {
|
|
552
|
-
await this.
|
|
579
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
580
|
+
idDto,
|
|
581
|
+
em
|
|
582
|
+
);
|
|
583
|
+
if (!subscription.externalId) {
|
|
584
|
+
throw new Error("Subscription not found");
|
|
585
|
+
}
|
|
586
|
+
await this.stripeClient.subscriptions.cancel(subscription.externalId);
|
|
553
587
|
await this.baseSubscriptionService.cancelSubscription(idDto, em);
|
|
554
588
|
}
|
|
555
589
|
async resumeSubscription(idDto, em) {
|
|
556
|
-
await this.
|
|
590
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
591
|
+
idDto,
|
|
592
|
+
em
|
|
593
|
+
);
|
|
594
|
+
if (!subscription.externalId) {
|
|
595
|
+
throw new Error("Subscription not found");
|
|
596
|
+
}
|
|
597
|
+
await this.stripeClient.subscriptions.resume(subscription.externalId);
|
|
557
598
|
await this.baseSubscriptionService.resumeSubscription(idDto, em);
|
|
558
599
|
}
|
|
559
600
|
};
|
package/lib/services/index.mjs
CHANGED
|
@@ -57,12 +57,13 @@ var StripeBillingPortalService = class {
|
|
|
57
57
|
});
|
|
58
58
|
const session = await this.stripeClient.billingPortal.sessions.create({
|
|
59
59
|
...billingPortalDto.stripeFields,
|
|
60
|
-
customer: existingSession.customerId
|
|
60
|
+
customer: billingPortalDto.customerId || existingSession.customerId
|
|
61
61
|
});
|
|
62
62
|
return await this.baseBillingPortalService.updateBillingPortalSession(
|
|
63
63
|
{
|
|
64
64
|
...billingPortalDto,
|
|
65
|
-
id:
|
|
65
|
+
id: existingSession.id,
|
|
66
|
+
// Use the original database ID, not the new Stripe session ID
|
|
66
67
|
uri: session.url,
|
|
67
68
|
expiresAt: new Date(
|
|
68
69
|
Date.now() + this.billingPortalSessionExpiryDurationMs
|
|
@@ -105,6 +106,7 @@ var StripeCheckoutSessionService = class {
|
|
|
105
106
|
async createCheckoutSession(checkoutSessionDto, ...args) {
|
|
106
107
|
const session = await this.stripeClient.checkout.sessions.create({
|
|
107
108
|
...checkoutSessionDto.stripeFields,
|
|
109
|
+
mode: "subscription",
|
|
108
110
|
payment_method_types: checkoutSessionDto.paymentMethods,
|
|
109
111
|
currency: checkoutSessionDto.currency,
|
|
110
112
|
success_url: checkoutSessionDto.successRedirectUri,
|
|
@@ -190,10 +192,10 @@ var StripePaymentLinkService = class {
|
|
|
190
192
|
{
|
|
191
193
|
...paymentLinkDto,
|
|
192
194
|
id: session.id,
|
|
193
|
-
amount: session.line_items?.data.reduce(
|
|
195
|
+
amount: paymentLinkDto.amount ?? session.line_items?.data.reduce(
|
|
194
196
|
(total, item) => total + item.amount_total,
|
|
195
197
|
0
|
|
196
|
-
)
|
|
198
|
+
)
|
|
197
199
|
},
|
|
198
200
|
this.em,
|
|
199
201
|
session,
|
|
@@ -259,24 +261,21 @@ var StripePaymentLinkService = class {
|
|
|
259
261
|
await this.basePaymentLinkService.handlePaymentFailure({ id });
|
|
260
262
|
}
|
|
261
263
|
async listPaymentLinks(idsDto) {
|
|
264
|
+
this.openTelemetryCollector.log("info", idsDto ?? "idsDto is undefined");
|
|
262
265
|
const stripePaymentLinks = await this.stripeClient.paymentLinks.list({
|
|
263
266
|
active: true
|
|
264
267
|
});
|
|
265
268
|
const databasePaymentLinks = await this.basePaymentLinkService.listPaymentLinks(idsDto);
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
paymentLink.stripeFields = stripePaymentLink;
|
|
277
|
-
return paymentLink;
|
|
278
|
-
})
|
|
279
|
-
);
|
|
269
|
+
return databasePaymentLinks.map((paymentLink) => {
|
|
270
|
+
const stripePaymentLink = stripePaymentLinks.data.find(
|
|
271
|
+
(sp) => sp.id === paymentLink.id
|
|
272
|
+
);
|
|
273
|
+
if (!stripePaymentLink) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
paymentLink.stripeFields = stripePaymentLink;
|
|
277
|
+
return paymentLink;
|
|
278
|
+
}).filter((paymentLink) => paymentLink !== null);
|
|
280
279
|
}
|
|
281
280
|
};
|
|
282
281
|
|
|
@@ -307,6 +306,7 @@ var StripePlanService = class {
|
|
|
307
306
|
async createPlan(planDto, em) {
|
|
308
307
|
const stripePlan = await this.stripeClient.plans.create({
|
|
309
308
|
...planDto.stripeFields,
|
|
309
|
+
amount: planDto.price,
|
|
310
310
|
interval: planDto.cadence,
|
|
311
311
|
product: planDto.name,
|
|
312
312
|
currency: planDto.currency
|
|
@@ -323,68 +323,76 @@ var StripePlanService = class {
|
|
|
323
323
|
return plan;
|
|
324
324
|
}
|
|
325
325
|
async getPlan(idDto, em) {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
this.options?.databaseTableName ?? "plan",
|
|
329
|
-
{ externalId: idDto.id }
|
|
330
|
-
))?.id;
|
|
331
|
-
if (!id) {
|
|
326
|
+
const planEntity = await this.basePlanService.getPlan(idDto, em);
|
|
327
|
+
if (!planEntity.externalId) {
|
|
332
328
|
throw new Error("Plan not found");
|
|
333
329
|
}
|
|
334
|
-
const
|
|
330
|
+
const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
|
|
335
331
|
planEntity.stripeFields = plan;
|
|
336
332
|
return planEntity;
|
|
337
333
|
}
|
|
338
334
|
async updatePlan(planDto, em) {
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
interval: planDto.cadence ?? existingPlan.interval,
|
|
344
|
-
product: planDto.name,
|
|
345
|
-
currency: planDto.currency ?? existingPlan.currency
|
|
346
|
-
})
|
|
347
|
-
);
|
|
348
|
-
const planEntity = await this.basePlanService.updatePlan(
|
|
349
|
-
await this.mappers.UpdatePlanMapper.toEntity(
|
|
350
|
-
{
|
|
351
|
-
...planDto,
|
|
352
|
-
externalId: plan.id,
|
|
353
|
-
billingProvider: "stripe"
|
|
354
|
-
},
|
|
355
|
-
em ?? this.em,
|
|
356
|
-
plan
|
|
357
|
-
),
|
|
335
|
+
const planEntity = await this.basePlanService.getPlan(
|
|
336
|
+
{
|
|
337
|
+
id: planDto.id
|
|
338
|
+
},
|
|
358
339
|
em
|
|
359
340
|
);
|
|
360
|
-
|
|
361
|
-
|
|
341
|
+
const existingPlan = await this.stripeClient.plans.retrieve(
|
|
342
|
+
planEntity.externalId
|
|
343
|
+
);
|
|
344
|
+
const existingProduct = existingPlan.product;
|
|
345
|
+
if (!existingProduct) {
|
|
346
|
+
throw new Error("Plan product not found");
|
|
347
|
+
}
|
|
348
|
+
const productId = typeof existingProduct === "string" ? existingProduct : existingProduct.id;
|
|
349
|
+
await this.stripeClient.plans.del(planEntity.externalId);
|
|
350
|
+
const updatedPlan = await this.stripeClient.plans.create({
|
|
351
|
+
...planDto.stripeFields,
|
|
352
|
+
interval: planDto.cadence ?? existingPlan.interval,
|
|
353
|
+
currency: planDto.currency ?? existingPlan.currency,
|
|
354
|
+
amount: planDto.price ?? existingPlan.amount ?? void 0,
|
|
355
|
+
product: productId
|
|
356
|
+
});
|
|
357
|
+
const updatedPlanEntity = await this.basePlanService.updatePlan(
|
|
358
|
+
{
|
|
359
|
+
...planDto,
|
|
360
|
+
externalId: updatedPlan.id,
|
|
361
|
+
name: planDto.name,
|
|
362
|
+
billingProvider: "stripe"
|
|
363
|
+
},
|
|
364
|
+
em,
|
|
365
|
+
updatedPlan
|
|
366
|
+
);
|
|
367
|
+
updatedPlanEntity.stripeFields = updatedPlan;
|
|
368
|
+
return updatedPlanEntity;
|
|
362
369
|
}
|
|
363
370
|
async deletePlan(idDto, em) {
|
|
364
|
-
await this.
|
|
371
|
+
const plan = await this.basePlanService.getPlan(idDto, em);
|
|
372
|
+
if (!plan.externalId) {
|
|
373
|
+
throw new Error("Plan not found");
|
|
374
|
+
}
|
|
375
|
+
await this.stripeClient.plans.del(plan.externalId);
|
|
365
376
|
await this.basePlanService.deletePlan(idDto, em);
|
|
366
377
|
}
|
|
367
378
|
async listPlans(idsDto, em) {
|
|
368
|
-
const plans = await this.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const planIds = (await em?.findAll(
|
|
372
|
-
this.options?.databaseTableName ?? "plan",
|
|
373
|
-
{ where: { externalId: { $in: plans.data.map((plan) => plan.id) } } }
|
|
374
|
-
))?.filter((s) => idsDto?.ids?.includes(s.id))?.map((s) => s.id);
|
|
375
|
-
if (!planIds) {
|
|
376
|
-
throw new Error("Plans not found");
|
|
379
|
+
const plans = await this.basePlanService.listPlans(idsDto, em);
|
|
380
|
+
if (!plans || plans.length === 0) {
|
|
381
|
+
return [];
|
|
377
382
|
}
|
|
378
|
-
|
|
379
|
-
(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
)
|
|
383
|
+
const stripePlans = await Promise.all(
|
|
384
|
+
plans.map(async (plan) => {
|
|
385
|
+
try {
|
|
386
|
+
return await this.stripeClient.plans.retrieve(plan.externalId);
|
|
387
|
+
} catch {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
})
|
|
387
391
|
);
|
|
392
|
+
return plans.map((plan, index) => ({
|
|
393
|
+
...plan,
|
|
394
|
+
stripeFields: stripePlans[index]
|
|
395
|
+
})).filter((plan) => plan.stripeFields !== null);
|
|
388
396
|
}
|
|
389
397
|
};
|
|
390
398
|
|
|
@@ -434,94 +442,127 @@ var StripeSubscriptionService = class {
|
|
|
434
442
|
}
|
|
435
443
|
async getSubscription(idDto, em) {
|
|
436
444
|
const subscriptionEntity = await this.baseSubscriptionService.getSubscription(idDto, em);
|
|
445
|
+
if (!subscriptionEntity.externalId) {
|
|
446
|
+
throw new Error("Subscription not found");
|
|
447
|
+
}
|
|
437
448
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
438
|
-
|
|
449
|
+
subscriptionEntity.externalId
|
|
439
450
|
);
|
|
440
451
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
441
452
|
return subscriptionEntity;
|
|
442
453
|
}
|
|
443
454
|
async getUserSubscription(idDto, em) {
|
|
444
455
|
const subscriptionEntity = await this.baseSubscriptionService.getUserSubscription(idDto, em);
|
|
456
|
+
if (!subscriptionEntity.externalId) {
|
|
457
|
+
throw new Error("Subscription not found");
|
|
458
|
+
}
|
|
445
459
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
446
|
-
|
|
460
|
+
subscriptionEntity.externalId
|
|
447
461
|
);
|
|
448
462
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
449
463
|
return subscriptionEntity;
|
|
450
464
|
}
|
|
451
465
|
async getOrganizationSubscription(idDto, em) {
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
{ externalId: idDto.id }
|
|
455
|
-
))?.id;
|
|
456
|
-
if (!id) {
|
|
466
|
+
const subscriptionEntity = await this.baseSubscriptionService.getOrganizationSubscription(idDto, em);
|
|
467
|
+
if (!subscriptionEntity.externalId) {
|
|
457
468
|
throw new Error("Subscription not found");
|
|
458
469
|
}
|
|
459
|
-
const subscriptionEntity = await this.baseSubscriptionService.getOrganizationSubscription(
|
|
460
|
-
{ id },
|
|
461
|
-
em
|
|
462
|
-
);
|
|
463
470
|
const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
464
|
-
|
|
471
|
+
subscriptionEntity.externalId
|
|
465
472
|
);
|
|
466
473
|
subscriptionEntity.stripeFields = stripeSubscription;
|
|
467
474
|
return subscriptionEntity;
|
|
468
475
|
}
|
|
469
476
|
async updateSubscription(subscriptionDto, em) {
|
|
470
|
-
const
|
|
471
|
-
|
|
477
|
+
const subscriptionEntity = await this.baseSubscriptionService.getSubscription(
|
|
478
|
+
{
|
|
479
|
+
id: subscriptionDto.id
|
|
480
|
+
},
|
|
481
|
+
em
|
|
482
|
+
);
|
|
483
|
+
if (!subscriptionEntity.externalId) {
|
|
484
|
+
throw new Error("Subscription not found");
|
|
485
|
+
}
|
|
486
|
+
const existingStripeSubscription = await this.stripeClient.subscriptions.retrieve(
|
|
487
|
+
subscriptionEntity.externalId
|
|
488
|
+
);
|
|
489
|
+
const updatedSubscription = await this.stripeClient.subscriptions.update(
|
|
490
|
+
subscriptionEntity.externalId,
|
|
472
491
|
{
|
|
473
492
|
...subscriptionDto.stripeFields,
|
|
474
|
-
items:
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
]
|
|
493
|
+
items: existingStripeSubscription.items.data.map((item) => ({
|
|
494
|
+
id: item.id,
|
|
495
|
+
plan: subscriptionDto.productId || item.plan?.id
|
|
496
|
+
}))
|
|
479
497
|
}
|
|
480
498
|
);
|
|
481
|
-
|
|
499
|
+
const updatedSubscriptionEntity = await this.baseSubscriptionService.updateSubscription(
|
|
482
500
|
{
|
|
483
501
|
...subscriptionDto,
|
|
484
|
-
externalId:
|
|
485
|
-
billingProvider: "stripe"
|
|
486
|
-
providerFields: subscription
|
|
502
|
+
externalId: updatedSubscription.id,
|
|
503
|
+
billingProvider: "stripe"
|
|
487
504
|
},
|
|
488
505
|
em ?? this.em,
|
|
489
|
-
|
|
506
|
+
updatedSubscription
|
|
490
507
|
);
|
|
508
|
+
updatedSubscriptionEntity.stripeFields = updatedSubscription;
|
|
509
|
+
return updatedSubscriptionEntity;
|
|
491
510
|
}
|
|
492
511
|
async deleteSubscription(idDto, em) {
|
|
493
|
-
await this.
|
|
512
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
513
|
+
idDto,
|
|
514
|
+
em
|
|
515
|
+
);
|
|
516
|
+
if (!subscription.externalId) {
|
|
517
|
+
throw new Error("Subscription not found");
|
|
518
|
+
}
|
|
519
|
+
await this.stripeClient.subscriptions.cancel(subscription.externalId);
|
|
494
520
|
await this.baseSubscriptionService.deleteSubscription(idDto, em);
|
|
495
521
|
}
|
|
496
522
|
async listSubscriptions(idsDto, em) {
|
|
497
|
-
const subscriptions =
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
))?.map((s) => s.id);
|
|
504
|
-
if (!ids) {
|
|
505
|
-
throw new Error("Subscriptions not found");
|
|
523
|
+
const subscriptions = await this.baseSubscriptionService.listSubscriptions(
|
|
524
|
+
idsDto,
|
|
525
|
+
em
|
|
526
|
+
);
|
|
527
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
528
|
+
return [];
|
|
506
529
|
}
|
|
507
|
-
|
|
508
|
-
(
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
530
|
+
const stripeSubscriptions = await Promise.all(
|
|
531
|
+
subscriptions.map(async (subscription) => {
|
|
532
|
+
try {
|
|
533
|
+
return await this.stripeClient.subscriptions.retrieve(
|
|
534
|
+
subscription.externalId
|
|
512
535
|
);
|
|
513
|
-
|
|
514
|
-
return
|
|
536
|
+
} catch {
|
|
537
|
+
return null;
|
|
515
538
|
}
|
|
516
|
-
)
|
|
539
|
+
})
|
|
517
540
|
);
|
|
541
|
+
return subscriptions.map((subscription, index) => ({
|
|
542
|
+
...subscription,
|
|
543
|
+
stripeFields: stripeSubscriptions[index]
|
|
544
|
+
})).filter((subscription) => subscription.stripeFields !== null);
|
|
518
545
|
}
|
|
519
546
|
async cancelSubscription(idDto, em) {
|
|
520
|
-
await this.
|
|
547
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
548
|
+
idDto,
|
|
549
|
+
em
|
|
550
|
+
);
|
|
551
|
+
if (!subscription.externalId) {
|
|
552
|
+
throw new Error("Subscription not found");
|
|
553
|
+
}
|
|
554
|
+
await this.stripeClient.subscriptions.cancel(subscription.externalId);
|
|
521
555
|
await this.baseSubscriptionService.cancelSubscription(idDto, em);
|
|
522
556
|
}
|
|
523
557
|
async resumeSubscription(idDto, em) {
|
|
524
|
-
await this.
|
|
558
|
+
const subscription = await this.baseSubscriptionService.getSubscription(
|
|
559
|
+
idDto,
|
|
560
|
+
em
|
|
561
|
+
);
|
|
562
|
+
if (!subscription.externalId) {
|
|
563
|
+
throw new Error("Subscription not found");
|
|
564
|
+
}
|
|
565
|
+
await this.stripeClient.subscriptions.resume(subscription.externalId);
|
|
525
566
|
await this.baseSubscriptionService.resumeSubscription(idDto, em);
|
|
526
567
|
}
|
|
527
568
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forklaunch/implementation-billing-stripe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Stripe implementation for forklaunch billing",
|
|
5
5
|
"homepage": "https://github.com/forklaunch/forklaunch-js#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
"lib/**"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@forklaunch/common": "^0.6.
|
|
46
|
-
"@forklaunch/core": "^0.15.
|
|
47
|
-
"@forklaunch/internal": "^0.3.
|
|
48
|
-
"@forklaunch/validator": "^0.10.
|
|
49
|
-
"@mikro-orm/core": "^6.5.
|
|
45
|
+
"@forklaunch/common": "^0.6.18",
|
|
46
|
+
"@forklaunch/core": "^0.15.7",
|
|
47
|
+
"@forklaunch/internal": "^0.3.18",
|
|
48
|
+
"@forklaunch/validator": "^0.10.18",
|
|
49
|
+
"@mikro-orm/core": "^6.5.7",
|
|
50
50
|
"@sinclair/typebox": "^0.34.41",
|
|
51
51
|
"ajv": "^8.17.1",
|
|
52
|
-
"stripe": "^
|
|
53
|
-
"zod": "^4.1.
|
|
54
|
-
"@forklaunch/implementation-billing-base": "0.
|
|
55
|
-
"@forklaunch/interfaces-billing": "0.
|
|
52
|
+
"stripe": "^19.1.0",
|
|
53
|
+
"zod": "^4.1.12",
|
|
54
|
+
"@forklaunch/implementation-billing-base": "0.8.1",
|
|
55
|
+
"@forklaunch/interfaces-billing": "0.8.1"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
58
|
+
"@typescript/native-preview": "7.0.0-dev.20251008.1",
|
|
59
59
|
"depcheck": "^1.4.7",
|
|
60
60
|
"prettier": "^3.6.2",
|
|
61
61
|
"typedoc": "^0.28.13"
|