UNPKG

@dbbs/strapi-stripe-payment

Version:
582 lines (581 loc) 23.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const http_errors_1 = __importDefault(require("http-errors")); const enums_1 = require("../enums"); exports.default = ({ strapi }) => ({ async handleEvent(event) { switch (event.type) { case enums_1.StripeEventType.CheckoutSessionCompleted: await this.handleCheckoutSessionCompleted(event); break; case enums_1.StripeEventType.InvoicePaymentSucceeded: await this.handleInvoicePaymentSucceeded(event); break; case enums_1.StripeEventType.InvoicePaymentFailed: await this.handleInvoicePaymentFailed(event); break; case enums_1.StripeEventType.CustomerSubscriptionUpdated: await this.handleSubscriptionUpdated(event); break; case enums_1.StripeEventType.PaymentMethodAttached: await this.handlePaymentMethodAttached(event); break; case enums_1.StripeEventType.PriceDeleted: await this.handlePriceDeleted(event); break; case enums_1.StripeEventType.PriceUpdated: await this.handlePriceUpdated(event); break; case enums_1.StripeEventType.PriceCreated: await this.handlePriceCreated(event); break; case enums_1.StripeEventType.ProductCreated: await this.handleProductCreated(event); break; case enums_1.StripeEventType.ProductUpdated: await this.handleProductUpdated(event); break; case enums_1.StripeEventType.ProductDeleted: await this.handleProductDeleted(event); break; case enums_1.StripeEventType.SetupIntentSucceeded: await this.handleSetupIntentSucceeded(event); break; default: break; } }, async handleCheckoutSessionCompleted(event) { var _a; const checkoutSession = event.data.object; if (checkoutSession.mode === enums_1.PaymentMode.Setup) { return; } try { const { metadata } = checkoutSession; if (!(metadata === null || metadata === void 0 ? void 0 : metadata.organizationName) || !(metadata === null || metadata === void 0 ? void 0 : metadata.userId) || !(metadata === null || metadata === void 0 ? void 0 : metadata.planId) || !(metadata === null || metadata === void 0 ? void 0 : metadata.quantity)) { throw new http_errors_1.default.BadRequest('Metadata is missing required fields'); } const { organizationName, userId, planId, quantity } = metadata; const isSubscription = checkoutSession.mode === enums_1.PaymentMode.Subscription; const stripeCustomerId = checkoutSession.customer; const stripeSubscriptionId = checkoutSession.subscription; const stripeInvoiceId = checkoutSession.invoice; const stripePaymentIntentId = checkoutSession.payment_intent; let stripeCustomer; let stripeTransaction; if (isSubscription) { stripeTransaction = await strapi.plugin('stripe-payment').service('stripe').invoices.retrieve(stripeInvoiceId); } else { stripeTransaction = await strapi .plugin('stripe-payment') .service('stripe') .paymentIntents.retrieve(stripePaymentIntentId); } const paymentTransaction = await strapi.query('plugin::stripe-payment.transaction').create({ data: { status: enums_1.PaymentTransactionStatus.COMPLETED, externalTransaction: stripeTransaction } }); let organization = await strapi.query('plugin::stripe-payment.organization').findOne({ where: stripeCustomerId ? { customer_id: stripeCustomerId } : { name: organizationName }, populate: { subscription: true, transactions: true, purchases: true } }); if (!organization) { if (!stripeCustomerId) { const owner = await strapi.query('plugin::users-permissions.user').findOne({ where: { id: userId } }); stripeCustomer = await strapi.plugin('stripe-payment').service('stripe').customers.create({ name: organizationName, email: owner.email }); } organization = await strapi.query('plugin::stripe-payment.organization').create({ data: { name: organizationName, customer_id: (_a = stripeCustomer === null || stripeCustomer === void 0 ? void 0 : stripeCustomer.id) !== null && _a !== void 0 ? _a : stripeCustomerId, owner_id: userId, users: [userId], quantity: parseInt(quantity, 10) } }); } if (isSubscription) { const subscription = await strapi.query('plugin::stripe-payment.subscription').create({ data: { status: enums_1.SubscriptionStatus.TRIALING, stripe_id: stripeSubscriptionId, organization: organization.id, plan: planId } }); await strapi.query('plugin::stripe-payment.organization').update({ where: { id: organization.id }, data: { subscription: subscription.id, transactions: organization.transactions ? [...organization.transactions, paymentTransaction.id] : [paymentTransaction.id] } }); await strapi.query('plugin::stripe-payment.transaction').update({ where: { id: paymentTransaction.id }, data: { subscriptionId: subscription.id, organization: organization.id } }); } else { const purchase = await strapi.query('plugin::stripe-payment.purchase').create({ data: { plan: planId, organization: organization.id, stripe_id: stripeTransaction.id } }); await strapi.query('plugin::stripe-payment.organization').update({ where: { id: organization.id }, data: { purchases: organization.purchases ? [...organization.purchases, purchase.id] : [purchase.id], transactions: organization.transactions ? [...organization.transactions, paymentTransaction.id] : [paymentTransaction.id] } }); await strapi.query('plugin::stripe-payment.transaction').update({ where: { id: paymentTransaction.id }, data: { purchaseId: purchase.id, organization: organization.id } }); } } catch (e) { console.error(e); throw new Error('Could not create subscription or organization'); } }, async handleInvoicePaymentSucceeded(event) { if (event.data.object.billing_reason === enums_1.BillingReason.Subscription_create) { return null; } const subscription = await strapi.query('plugin::stripe-payment.subscription').findOne({ where: { stripe_id: event.data.object.subscription }, populate: { organization: true } }); if (!subscription) { throw new http_errors_1.default.NotFound(`Subscription with stripe id ${event.data.object.subscription} was not found`); } if (subscription.status === enums_1.SubscriptionStatus.ACTIVE) { return subscription; } await strapi.query('plugin::stripe-payment.transaction').create({ data: { subscriptionId: subscription.id, organization: subscription.organization, status: enums_1.PaymentTransactionStatus.COMPLETED, externalTransaction: event.data.object } }); return strapi.query('plugin::stripe-payment.subscription').update({ where: { id: subscription.id }, data: { status: enums_1.SubscriptionStatus.ACTIVE } }); }, async handleInvoicePaymentFailed(event) { if (event.data.object.billing_reason === enums_1.BillingReason.Subscription_create) { return null; } const subscription = await strapi.query('plugin::stripe-payment.subscription').findOne({ where: { stripe_id: event.data.object.subscription }, populate: { organization: true } }); if (!subscription) { throw new http_errors_1.default.NotFound(`Subscription with stripe id ${event.data.object.subscription} was not found`); } if (subscription.status === enums_1.SubscriptionStatus.UNPAID) { return subscription; } await strapi.query('plugin::stripe-payment.transaction').create({ data: { subscriptionId: subscription.id, organization: subscription.organization, status: enums_1.PaymentTransactionStatus.COMPLETED, externalTransaction: event.data.object } }); return strapi.query('plugin::stripe-payment.subscription').update({ where: { id: subscription.id }, data: { status: enums_1.SubscriptionStatus.UNPAID } }); }, async handleSubscriptionUpdated(event) { if (event.data.object.status === enums_1.SubscriptionStatus.TRIALING) { return; } const organization = await strapi.query('plugin::stripe-payment.organization').findOne({ where: { customer_id: event.data.object.customer } }); const { quantity, price } = event.data.object.items.data[0]; if (!organization) { return; } if (quantity && quantity > organization.quantity) { await strapi.query('plugin::stripe-payment.organization').update({ where: { id: organization.id }, data: { quantity } }); } if (price) { const plan = await strapi.query('plugin::stripe-payment.plan').findOne({ where: { stripe_id: price.id } }); if (plan) { await strapi.query('plugin::stripe-payment.subscription').update({ where: { id: event.data.object.id }, data: { plan: plan.id, status: event.data.object.status } }); } } }, async handlePaymentMethodAttached(event) { const customerId = event.data.object.customer; const paymentMethodId = event.data.object.id; await strapi .plugin('stripe-payment') .service('stripe') .customers.update(customerId, { invoice_settings: { default_payment_method: paymentMethodId } }); const organization = await strapi.query('plugin::stripe-payment.organization').findOne({ where: { customer_id: customerId } }); if (!organization) { await strapi.query('plugin::stripe-payment.organization').create({ data: { customer_id: customerId, payment_method_id: paymentMethodId } }); } else { await strapi.query('plugin::stripe-payment.organization').update({ where: { customer_id: customerId }, data: { payment_method_id: paymentMethodId } }); } }, async handlePriceDeleted(event) { const plan = await strapi.query('plugin::stripe-payment.plan').findOne({ where: { stripe_id: event.data.object.id } }); if (!plan) { throw new http_errors_1.default.NotFound(`Plan with stripe id ${event.data.object.id} was not found`); } await strapi.query('plugin::stripe-payment.plan').delete({ where: { id: plan.id } }); }, async handlePriceUpdated(event) { var _a; const existedPlan = await strapi.query('plugin::stripe-payment.plan').findOne({ where: { stripe_id: event.data.object.id } }); if (!existedPlan) { throw new http_errors_1.default.NotFound(`Plan with stripe id ${event.data.object.id} was not found`); } const product = await strapi.query('plugin::stripe-payment.product').findOne({ where: { stripe_id: event.data.object.product }, populate: { plans: true } }); if (!product) { throw new http_errors_1.default.NotFound(`Product with stripe id ${event.data.object.product} was not found`); } const planInterval = (_a = event.data.object.recurring) === null || _a === void 0 ? void 0 : _a.interval; const updatedPlan = await strapi.query('plugin::stripe-payment.plan').update({ where: { stripe_id: event.data.object.id }, data: { price: event.data.object.unit_amount / 100, interval: planInterval || null, stripe_id: event.data.object.id, currency: event.data.object.currency, type: planInterval ? enums_1.PlanType.RECURRING : enums_1.PlanType.ONE_TIME, product: product.id } }); const newPlansList = product.plans.filter((plan) => plan.stripe_id !== event.data.object.id); if (event.data.object.active) { newPlansList.push(updatedPlan); } await strapi.query('plugin::stripe-payment.product').update({ where: { id: product.id }, data: { plans: newPlansList } }); }, async handlePriceCreated(event) { var _a; const plan = await strapi.query('plugin::stripe-payment.plan').findOne({ where: { stripe_id: event.data.object.id } }); if (plan) { return; } const product = await strapi.query('plugin::stripe-payment.product').findOne({ where: { stripe_id: event.data.object.product }, populate: { plans: true } }); if (!product) { throw new http_errors_1.default.NotFound(`Product with stripe id ${event.data.object.product} was not found`); } const planInterval = (_a = event.data.object.recurring) === null || _a === void 0 ? void 0 : _a.interval; const savedPlan = await strapi.query('plugin::stripe-payment.plan').create({ data: { price: event.data.object.unit_amount / 100, interval: planInterval, stripe_id: event.data.object.id, currency: event.data.object.currency, type: planInterval ? enums_1.PlanType.RECURRING : enums_1.PlanType.ONE_TIME, product: product.id } }); await strapi.query('plugin::stripe-payment.product').update({ where: { stripe_id: event.data.object.product }, data: { plans: [...product.plans, savedPlan.id] } }); }, async handleProductCreated(event) { var _a; const product = await strapi.query('plugin::stripe-payment.product').findOne({ where: { stripe_id: event.data.object.id } }); if (product) { return; } const savedProduct = await strapi.query('plugin::stripe-payment.product').create({ data: { name: event.data.object.name, stripe_id: event.data.object.id } }); const prices = await strapi .plugin('stripe-payment') .service('stripe') .prices.list({ product: event.data.object.id }); const price = prices.data[0]; let savedPlan = await strapi.query('plugin::stripe-payment.plan').findOne({ where: { stripe_id: price.id } }); if (!savedPlan) { const planInterval = (_a = price.recurring) === null || _a === void 0 ? void 0 : _a.interval; savedPlan = await strapi.query('plugin::stripe-payment.plan').create({ data: { price: price.unit_amount / 100, interval: planInterval, stripe_id: price.id, currency: price.currency, type: planInterval ? enums_1.PlanType.RECURRING : enums_1.PlanType.ONE_TIME, product: savedProduct.id } }); } await strapi.query('plugin::stripe-payment.product').update({ where: { id: savedProduct.id }, data: { plans: [savedPlan.id] } }); }, async deleteProduct(product) { const planStripeIds = product.plans.map((plan) => plan.stripe_id); await strapi.query('plugin::stripe-payment.plan').deleteMany({ where: { stripe_id: planStripeIds } }); await strapi.query('plugin::stripe-payment.product').delete({ where: { id: product.id } }); }, async handleProductUpdated(event) { const product = await strapi.query('plugin::stripe-payment.product').findOne({ where: { stripe_id: event.data.object.id }, populate: { plans: true } }); if (!product) { throw new http_errors_1.default.NotFound(`Product with stripe id ${event.data.object.id} was not found`); } const isActive = event.data.object.active; if (!isActive) { await this.deleteProduct(product); return; } const prices = await strapi .plugin('stripe-payment') .service('stripe') .prices.list({ product: event.data.object.id }); const existingPlans = product.plans.map((plan) => plan.stripe_id); const newPlansData = []; prices.data.forEach((price) => { var _a; if (!existingPlans.includes(price.id)) { const planInterval = (_a = price.recurring) === null || _a === void 0 ? void 0 : _a.interval; newPlansData.push({ price: price.unit_amount / 100, interval: planInterval, stripe_id: price.id, currency: price.currency, type: planInterval ? enums_1.PlanType.RECURRING : enums_1.PlanType.ONE_TIME, product: product.id }); } }); if (newPlansData.length > 0) { const newPlans = await strapi.query('plugin::stripe-payment.plan').createMany({ data: newPlansData }); await strapi.query('plugin::stripe-payment.product').update({ where: { id: product.id }, data: { name: event.data.object.name, plans: [...product.plans, ...newPlans.ids] }, populate: { plans: true } }); } else { await strapi.query('plugin::stripe-payment.product').update({ where: { id: product.id }, data: { name: event.data.object.name } }); } }, async handleProductDeleted(event) { const product = await strapi.query('plugin::stripe-payment.product').findOne({ where: { stripe_id: event.data.object.id }, populate: { plans: true } }); if (!product) { throw new http_errors_1.default.NotFound(`Product with stripe id ${event.data.object.id} was not found`); } await this.deleteProduct(product); }, // TODO (#1113): Wrap payment method update with transaction async handleSetupIntentSucceeded(event) { const setupIntent = event.data.object; const organization = await strapi .query('plugin::stripe-payment.organization') .findOne({ where: { customer_id: setupIntent.customer } }); if (!organization) { throw new http_errors_1.default.NotFound(`Organization with customer_id ${setupIntent.customer} was not found`); } const newPaymentMethod = await strapi .plugin('stripe-payment') .service('stripe') .paymentMethods.attach(setupIntent.payment_method, { customer: setupIntent.customer }); await strapi .plugin('stripe-payment') .service('stripe') .customers.update(organization.customer_id, { invoice_settings: { default_payment_method: newPaymentMethod.id } }); await strapi.query('plugin::stripe-payment.organization').update({ where: { id: organization.id }, data: { payment_method_id: newPaymentMethod.id } }); } });