mirror of
https://github.com/fleetdm/fleet
synced 2026-05-05 14:28:46 +00:00
https://github.com/fleetdm/fleet/issues/9172 Changes: - Added `website/api/controllers/webhooks/recieve-stripe-subscription-events.js` a webhook for receiving Stripe events. - If the stripe event received is from a user's subscription automatically renewing, A new license key is generated, the subscription record is updated, and a renewal confirmation email is sent. - If the stripe event received is from a user's subscription's upcoming renewal, a renewal notification email is sent. - If any other event type is received from Stripe, the webhook returns a 200 response. - Added new email templates: - `email-subscription-renewal-confirmation` - `email-upcoming-subscription-renewal` - Updated `website/api/controllers/admin/view-email-template-preview.js` to have fake data for the added email templates. - Updated `website/api/controllers/customers/view-dashboard.js` to set two boolean variables: `subscriptionExpiresSoon` and `subscriptionHasBeenRecentlyRenewed` - Updated the customer dashboard to display notifications on the top of the page if a user's subscription will renew in the next 30 days, or if the user's subscription has been renewed in the past 30 days. - `website/views/layouts/layout-email.ejs` - Updated the font, padding, and text color to match wireframes.
144 lines
5.9 KiB
JavaScript
Vendored
144 lines
5.9 KiB
JavaScript
Vendored
module.exports = {
|
|
|
|
|
|
friendlyName: 'Receive Stripe subscription events',
|
|
|
|
|
|
description: 'Receive events from Stripe about subscription renewals',
|
|
|
|
|
|
inputs: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'The unique identifier for this Stripe event.',
|
|
moreInfoUrl: 'https://stripe.com/docs/api/events/object#event_object-id',
|
|
required: true,
|
|
},
|
|
type: {
|
|
type: 'string',
|
|
description: 'The type of this Stripe event.',
|
|
moreInfoUrl: 'https://stripe.com/docs/api/events/object#event_object-type',
|
|
required: true,
|
|
},
|
|
data: {
|
|
type: {object: {}},
|
|
description: 'An object containing data associated with this Stripe event.',
|
|
moreInfoUrl: 'https://stripe.com/docs/api/events/object#event_object-data',
|
|
required: true,
|
|
},
|
|
webhookSecret: {
|
|
type: 'string',
|
|
description: 'Used to verify that requests are coming from Stripe.',
|
|
required: true,
|
|
},
|
|
},
|
|
|
|
|
|
exits: {
|
|
success: { description: 'A Stripe event has successfully been received' },
|
|
missingStripeHeader: { description: 'The webhook received a request with no stripe-signature header', responseType: 'unauthorized'},
|
|
},
|
|
|
|
|
|
fn: async function ({id, type, data, webhookSecret}) {
|
|
|
|
if(!this.req.get('stripe-signature')) {
|
|
throw 'missingStripeHeader';
|
|
}
|
|
|
|
if (!sails.config.custom.stripeSubscriptionWebhookSecret) {
|
|
throw new Error('No Stripe webhook secret configured! (Please set `sails.config.custom.stripeSubscriptionWebhookSecret`.)');
|
|
}
|
|
|
|
if (sails.config.custom.stripeSubscriptionWebhookSecret !== webhookSecret) {
|
|
throw new Error('Received unexpected Stripe webhook request with webhookSecret set to: '+webhookSecret);
|
|
}
|
|
|
|
let stripeEventData = data.object;
|
|
|
|
// If this event does not include a subscription ID, we'll ignore it and return a 200 response.
|
|
if(!stripeEventData.subscription) {
|
|
return;
|
|
}
|
|
|
|
// Find the subscription record for this event.
|
|
let subscriptionIdToFind = stripeEventData.subscription;
|
|
let subscriptionForThisEvent = await Subscription.findOne({stripeSubscriptionId: subscriptionIdToFind}).populate('user');
|
|
|
|
if(!subscriptionForThisEvent) {
|
|
throw new Error(`The Stripe subscription events webhook received a event for a subscription with stripeSubscriptionId: ${subscriptionIdToFind}, but no matching record was found in our database.`);
|
|
}
|
|
|
|
let userForThisSubscription = subscriptionForThisEvent.user;
|
|
|
|
// If stripe thinks this subscription renews in 7 days, we'll send the user an subscription reminder email.
|
|
if(type === 'invoice.upcoming' && stripeEventData.billing_reason === 'upcoming') {
|
|
// Get the subscription cost per host for the Subscription renewal notification email.
|
|
let subscriptionCostPerHost = Math.floor(subscriptionForThisEvent.subscriptionPrice / subscriptionForThisEvent.numberOfHosts / 12);
|
|
let upcomingBillingAt = stripeEventData.next_payment_attempt * 1000;
|
|
// Send a upcoming subscription renewal email.
|
|
await sails.helpers.sendTemplateEmail.with({
|
|
to: userForThisSubscription.emailAddress,
|
|
from: sails.config.custom.fromEmailAddress,
|
|
fromName: sails.config.custom.fromName,
|
|
subject: 'Your Fleet Premium subscription',
|
|
template: 'email-upcoming-subscription-renewal',
|
|
templateData: {
|
|
firstName: userForThisSubscription.firstName,
|
|
lastName: userForThisSubscription.lastName,
|
|
subscriptionPriceInWholeDollars: subscriptionForThisEvent.subscriptionPrice,
|
|
numberOfHosts: subscriptionForThisEvent.numberOfHosts,
|
|
subscriptionCostPerHost: subscriptionCostPerHost,
|
|
nextBillingAt: upcomingBillingAt,
|
|
}
|
|
});
|
|
|
|
} else if(type === 'invoice.paid' && stripeEventData.billing_reason === 'subscription_cycle') {
|
|
// If the event was triggered by a user's card successfully being charged by Stripe, we'll generate a new license key, update the subscription's database record, and send the user a renewal confirmation email.
|
|
|
|
if(!stripeEventData.lines || !stripeEventData.lines.data[0]) {
|
|
throw new Error(`When the Stripe subscription events webhook received an event for a paid invoice for subscription id: ${subscriptionIdToFind}, the event data object is missing information about the paid invoice. Check the Stripe dashboard to see the data for this event (Stripe event id: ${id})`);
|
|
}
|
|
|
|
// Get the information about the paid invoice from the stripe event.
|
|
let paidInvoiceInformation = stripeEventData.lines.data[0];
|
|
|
|
// Convert the new subscription cycle's period end timestamp from Stripe into a JS timestamp.
|
|
let nextBillingAt = paidInvoiceInformation.period.end * 1000;
|
|
|
|
// Generate a new license key for this subscription
|
|
let newLicenseKeyForThisSubscription = await sails.helpers.createLicenseKey.with({
|
|
numberOfHosts: subscriptionForThisEvent.numberOfHosts,
|
|
organization: subscriptionForThisEvent.user.organization,
|
|
expiresAt: nextBillingAt,
|
|
});
|
|
|
|
// Update the subscription record
|
|
await Subscription.updateOne({id: subscriptionForThisEvent.id}).set({
|
|
fleetLicenseKey: newLicenseKeyForThisSubscription,
|
|
nextBillingAt: nextBillingAt
|
|
});
|
|
|
|
// Send subscription renewal email
|
|
await sails.helpers.sendTemplateEmail.with({
|
|
to: userForThisSubscription.emailAddress,
|
|
from: sails.config.custom.fromEmailAddress,
|
|
fromName: sails.config.custom.fromName,
|
|
subject: 'Your Fleet Premium subscription',
|
|
template: 'email-subscription-renewal-confirmation',
|
|
templateData: {
|
|
firstName: userForThisSubscription.firstName,
|
|
lastName: userForThisSubscription.lastName,
|
|
}
|
|
});
|
|
|
|
}
|
|
// FUTURE: send emails about failed payments. (type === 'invoice.payment_failed' && stripeEventData.billing_reason === 'subscription_cycle')
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
};
|