Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions src/infrastructure/adapters/stripe.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,14 @@ export class StripePaymentsAdapter implements PaymentsAdapter {
subscriptionId: string,
params: Partial<Stripe.SubscriptionUpdateParams>,
): Promise<Subscription> {
const subscription = await this.provider.subscriptions.update(subscriptionId, params);
const subscription = await this.newVersionProvider.subscriptions.update(subscriptionId, params);

return Subscription.toDomain({
id: subscription.id,
customer: subscription.customer as string,
status: subscription.status,
priceId: subscription.items.data[0].price.id,
currentPeriodEnd: subscription.current_period_end,
currentPeriodEnd: subscription.items.data[0].current_period_end,
metadata: subscription.metadata,
startDate: subscription.start_date,
trialEnd: subscription.trial_end ?? undefined,
Expand Down Expand Up @@ -219,11 +219,11 @@ export class StripePaymentsAdapter implements PaymentsAdapter {
}

async cancelSubscription(subscriptionId: string): Promise<void> {
await this.provider.subscriptions.cancel(subscriptionId);
await this.newVersionProvider.subscriptions.cancel(subscriptionId);
}

async createInvoice(params?: Partial<Stripe.InvoiceCreateParams>): Promise<Invoice> {
const invoice = await this.provider.invoices.create(params);
const invoice = await this.newVersionProvider.invoices.create(params);

return Invoice.toDomain({
id: invoice.id,
Expand All @@ -237,15 +237,19 @@ export class StripePaymentsAdapter implements PaymentsAdapter {
customerId: string,
params: Partial<Stripe.InvoiceItemCreateParams>,
): Promise<InvoiceItems> {
const invoice = await this.provider.invoiceItems.create({ invoice: invoiceId, customer: customerId, ...params });
const invoice = await this.newVersionProvider.invoiceItems.create({
invoice: invoiceId,
customer: customerId,
...params,
});

return InvoiceItems.toDomain({
id: invoice.id,
});
}

async finalizeInvoice(invoiceId: Invoice['id']): Promise<Invoice> {
const finalizedInvoice = await this.provider.invoices.finalizeInvoice(invoiceId, {
const finalizedInvoice = await this.newVersionProvider.invoices.finalizeInvoice(invoiceId, {
expand: ['payments', 'confirmation_secret'],
});

Expand Down
85 changes: 30 additions & 55 deletions src/services/payment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export class PaymentService {
couponId = await this.checkIfCouponIsAplicable(customerId, promoCodeId);
}

const subscription = await this.provider.subscriptions.create({
const subscription = await getStripeNewVersion().subscriptions.create({
customer: customerId,
currency: currencyValue,
items: [
Expand All @@ -187,7 +187,7 @@ export class PaymentService {
) as Stripe.SubscriptionCreateParams.PaymentSettings.PaymentMethodType[],
save_default_payment_method: 'on_subscription',
},
expand: ['latest_invoice.payment_intent', 'pending_setup_intent'],
expand: ['latest_invoice.confirmation_secret', 'latest_invoice.payments', 'pending_setup_intent'],
trial_end: trialEnd ? trialEnd : undefined,
...additionalOptions,
});
Expand All @@ -199,11 +199,14 @@ export class PaymentService {
subscriptionId: subscription.id,
};
} else {
const latestInvoice = subscription.latest_invoice as Stripe.Invoice;
const paymentIntent = latestInvoice.payments?.data[0]?.payment.payment_intent;

return {
type: 'payment',
clientSecret: (subscription.latest_invoice as any).payment_intent.client_secret,
clientSecret: latestInvoice.confirmation_secret?.client_secret as string,
subscriptionId: subscription.id,
paymentIntentId: (subscription.latest_invoice as any).payment_intent.id,
paymentIntentId: typeof paymentIntent === 'string' ? paymentIntent : paymentIntent?.id,
};
}
}
Expand Down Expand Up @@ -326,7 +329,7 @@ export class PaymentService {
const isRecurring = price.type === 'recurring';

if (isRecurring) {
await this.provider.subscriptions.create({
await getStripeNewVersion().subscriptions.create({
customer: customerId,
items: [
{
Expand All @@ -335,7 +338,7 @@ export class PaymentService {
],
});
} else {
const invoice = await this.provider.invoices.create({
const invoice = await getStripeNewVersion().invoices.create({
customer: customerId,
auto_advance: false,
pending_invoice_items_behavior: 'include',
Expand All @@ -345,15 +348,15 @@ export class PaymentService {
},
});

await this.provider.invoiceItems.create({
await getStripeNewVersion().invoiceItems.create({
customer: customerId,
price: priceId,
pricing: { price: priceId },
quantity: 0,
description: licenseCode?.provider ? `Affiliate sale via ${licenseCode.provider}` : 'One-time charge',
invoice: invoice.id,
});

await this.provider.invoices.pay(invoice.id, {
await getStripeNewVersion().invoices.pay(invoice.id as string, {
paid_out_of_band: true,
});
}
Expand Down Expand Up @@ -395,7 +398,7 @@ export class PaymentService {
const { isElegibleForCancellation, cancelAt } = this.getAnnualCommitmentCancellationInfo(subscription);

if (hasAnnualCommitment && !isElegibleForCancellation) {
await this.provider.subscriptions.update(subscriptionId, {
await getStripeNewVersion().subscriptions.update(subscriptionId, {
cancel_at: cancelAt,
});
return;
Expand Down Expand Up @@ -554,7 +557,7 @@ export class PaymentService {
throw new IncompatibleSubscriptionTypesError('The new price is not an individual price');
}

const updatedSubscription = await this.provider.subscriptions.update(individualActiveSubscription.id, {
const updatedSubscription = await getStripeNewVersion().subscriptions.update(individualActiveSubscription.id, {
cancel_at_period_end: false,
proration_behavior: 'none',
items: [
Expand Down Expand Up @@ -601,7 +604,7 @@ export class PaymentService {
throw new InvalidSeatNumberError('The new price does not allow the current amount of seats');
}

const updatedSubscription = await this.provider.subscriptions.update(businessActiveSubscription.id, {
const updatedSubscription = await getStripeNewVersion().subscriptions.update(businessActiveSubscription.id, {
cancel_at_period_end: false,
proration_behavior: additionalOptions?.proration_behavior ?? 'none',
items: [
Expand Down Expand Up @@ -657,20 +660,18 @@ export class PaymentService {
additionalOptions,
});

const getLatestInvoice = await this.provider.invoices.retrieve(updatedSubscription.latest_invoice as string);

if (getLatestInvoice.payment_intent) {
const getPaymentIntent: Stripe.PaymentIntent = await this.provider.paymentIntents.retrieve(
getLatestInvoice.payment_intent as string,
);
if (
getPaymentIntent.status === 'requires_action' &&
getPaymentIntent.next_action?.type === 'use_stripe_sdk' &&
getPaymentIntent.client_secret
) {
is3DSecureRequired = true;
clientSecret = getPaymentIntent.client_secret;
}
const latestInvoice = await getStripeNewVersion().invoices.retrieve(updatedSubscription.latest_invoice as string, {
expand: ['payments.data.payment.payment_intent'],
});
const paymentIntent = latestInvoice.payments?.data[0]?.payment.payment_intent as Stripe.PaymentIntent | undefined;

if (
paymentIntent?.status === 'requires_action' &&
paymentIntent.next_action?.type === 'use_stripe_sdk' &&
paymentIntent.client_secret
) {
is3DSecureRequired = true;
clientSecret = paymentIntent.client_secret;
}

return {
Expand All @@ -691,13 +692,13 @@ export class PaymentService {

if (!subscriptionId) throw new Error('Subscription not found');

const { id, customer } = await this.provider.paymentMethods.attach(paymentMethodId, {
const { id, customer } = await getStripeNewVersion().paymentMethods.attach(paymentMethodId, {
customer: customerId,
});

if (!id || !customer) throw new Error('Payment method not attached');

return this.provider.subscriptions.update(subscriptionId, {
return getStripeNewVersion().subscriptions.update(subscriptionId, {
default_payment_method: id,
});
}
Expand All @@ -708,32 +709,6 @@ export class PaymentService {
return res.data;
}

async getPlanIdFromLastPayment(
customerId: CustomerId,
pagination: { limit?: number; startingAfter?: string },
): Promise<string | null> {
const res = await this.provider.paymentIntents.list({
customer: customerId,
limit: pagination.limit,
starting_after: pagination.startingAfter,
});

const lastPaymentIntent = res.data
.filter((pi) => pi.status === 'succeeded')
.sort((a, b) => b.created - a.created)
.at(0);

if (!lastPaymentIntent) {
return null;
}

const checkout = await this.provider.checkout.sessions.list({ payment_intent: lastPaymentIntent.id });
const checkoutLines = await this.provider.checkout.sessions.listLineItems(checkout.data[0].id);
const productId = checkoutLines.data[0].price?.product;

return productId as string;
}

async getInvoicesFromUser(
customerId: CustomerId,
pagination: { limit?: number; startingAfter?: string },
Expand Down Expand Up @@ -1264,7 +1239,7 @@ export class PaymentService {
amount: number,
additionalOptions?: Partial<Stripe.PaymentIntentCreateParams>,
) {
return this.provider.paymentIntents.create({
return getStripeNewVersion().paymentIntents.create({
customer: customerId,
currency,
amount,
Expand Down
24 changes: 14 additions & 10 deletions tests/src/infrastructure/adapters/stripe.adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ describe('Stripe Adapter', () => {
const stripeSubscription = getCreatedSubscription({ status: 'active', trial_end: null });

const updateSpy = jest
.spyOn(stripePaymentsAdapter.provider.subscriptions, 'update')
.spyOn(stripePaymentsAdapter.newVersionProvider.subscriptions, 'update')
.mockResolvedValue(stripeSubscription as any);

const params = { cancel_at: 1800000000 };
Expand All @@ -467,7 +467,7 @@ describe('Stripe Adapter', () => {
customer: stripeSubscription.customer as string,
status: stripeSubscription.status,
priceId: stripeSubscription.items.data[0].price.id,
currentPeriodEnd: stripeSubscription.current_period_end,
currentPeriodEnd: stripeSubscription.items.data[0].current_period_end,
metadata: stripeSubscription.metadata,
startDate: stripeSubscription.start_date,
}),
Expand All @@ -478,7 +478,9 @@ describe('Stripe Adapter', () => {
const trialEnd = 1800000000;
const stripeSubscription = getCreatedSubscription({ status: 'trialing', trial_end: trialEnd });

jest.spyOn(stripePaymentsAdapter.provider.subscriptions, 'update').mockResolvedValue(stripeSubscription as any);
jest
.spyOn(stripePaymentsAdapter.newVersionProvider.subscriptions, 'update')
.mockResolvedValue(stripeSubscription as any);

const subscription = await stripePaymentsAdapter.updateSubscription(stripeSubscription.id, {});

Expand Down Expand Up @@ -530,9 +532,9 @@ describe('Stripe Adapter', () => {

describe('Creating an invoice', () => {
test('When creating an invoice, then the invoice is created and the entity is mapped and returned', async () => {
const mockedInvoice = getInvoice();
const mockedInvoice = getInvoice({ confirmation_secret: { client_secret: 'pi_secret', type: 'payment_intent' } });

jest.spyOn(stripePaymentsAdapter.provider.invoices, 'create').mockResolvedValue(mockedInvoice as any);
jest.spyOn(stripePaymentsAdapter.newVersionProvider.invoices, 'create').mockResolvedValue(mockedInvoice as any);

const invoice = await stripePaymentsAdapter.createInvoice({
customer: mockedInvoice.customer as string,
Expand All @@ -541,7 +543,7 @@ describe('Stripe Adapter', () => {
expect(invoice).toStrictEqual(
Invoice.toDomain({
id: mockedInvoice.id,
clientSecretId: mockedInvoice.confirmation_secret?.client_secret,
clientSecretId: 'pi_secret',
status: mockedInvoice.status as InvoiceStatus,
}),
);
Expand All @@ -550,16 +552,18 @@ describe('Stripe Adapter', () => {

describe('Finalizing an invoice', () => {
test('When finalizing an invoice, then the invoice is finalized and the entity is mapped and returned', async () => {
const mockedInvoice = getInvoice();
const mockedInvoice = getInvoice({ confirmation_secret: { client_secret: 'pi_secret', type: 'payment_intent' } });

jest.spyOn(stripePaymentsAdapter.provider.invoices, 'finalizeInvoice').mockResolvedValue(mockedInvoice as any);
jest
.spyOn(stripePaymentsAdapter.newVersionProvider.invoices, 'finalizeInvoice')
.mockResolvedValue(mockedInvoice as any);

const invoice = await stripePaymentsAdapter.finalizeInvoice(mockedInvoice.id);

expect(invoice).toStrictEqual(
Invoice.toDomain({
id: mockedInvoice.id,
clientSecretId: mockedInvoice.confirmation_secret?.client_secret,
clientSecretId: 'pi_secret',
status: mockedInvoice.status as InvoiceStatus,
}),
);
Expand All @@ -570,7 +574,7 @@ describe('Stripe Adapter', () => {
test('When adding invoice items, then the invoice items are added and the entity is mapped and returned', async () => {
const mockedInvoiceItems = getInvoiceItems();

jest.spyOn(stripePaymentsAdapter.provider.invoiceItems, 'create').mockResolvedValue({
jest.spyOn(stripePaymentsAdapter.newVersionProvider.invoiceItems, 'create').mockResolvedValue({
...mockedInvoiceItems,
lastResponse: {} as any,
});
Expand Down
Loading
Loading