@dbbs/strapi-stripe-payment
Version:
Strapi integration plugin for Stripe payment system
582 lines (581 loc) • 23.4 kB
JavaScript
"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
}
});
}
});