mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 22:11:09 +03:00
Added WebhookManager and StripeService modules
no-issue
This commit is contained in:
parent
3a1c51672c
commit
635aa8aa3f
59
ghost/stripe/lib/StripeService.js
Normal file
59
ghost/stripe/lib/StripeService.js
Normal file
@ -0,0 +1,59 @@
|
||||
const WebhookManager = require('./WebhookManager');
|
||||
const StripeAPI = require('./StripeAPI');
|
||||
const StripeMigrations = require('./Migrations');
|
||||
|
||||
module.exports = class StripeService {
|
||||
constructor({
|
||||
StripeWebhook,
|
||||
models
|
||||
}) {
|
||||
const api = new StripeAPI();
|
||||
const webhookManager = new WebhookManager({
|
||||
StripeWebhook,
|
||||
api
|
||||
});
|
||||
const migrations = new StripeMigrations({
|
||||
models,
|
||||
api
|
||||
});
|
||||
|
||||
this.models = models;
|
||||
this.api = api;
|
||||
this.webhookManager = webhookManager;
|
||||
this.migrations = migrations;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
await this.models.Product.forge().query().update({
|
||||
monthly_price_id: null,
|
||||
yearly_price_id: null
|
||||
});
|
||||
await this.models.StripePrice.forge().query().del();
|
||||
await this.models.StripeProduct.forge().query().del();
|
||||
await this.models.MemberStripeCustomer.forge().query().del();
|
||||
await this.models.Offer.forge().query().update({
|
||||
stripe_coupon_id: null
|
||||
});
|
||||
await this.webhookManager.stop();
|
||||
}
|
||||
|
||||
async configure(config) {
|
||||
this.api.configure({
|
||||
secretKey: config.secretKey,
|
||||
publicKey: config.publicKey,
|
||||
enablePromoCodes: config.enablePromoCodes
|
||||
});
|
||||
|
||||
console.log('finna setup webhooks');
|
||||
console.log(config.webhookSecret, config.webhookHandlerUrl);
|
||||
await this.webhookManager.configure({
|
||||
webhookSecret: config.webhookSecret,
|
||||
webhookHandlerUrl: config.webhookHandlerUrl
|
||||
});
|
||||
await this.webhookManager.start();
|
||||
console.log('webhooks done');
|
||||
}
|
||||
};
|
168
ghost/stripe/lib/WebhookManager.js
Normal file
168
ghost/stripe/lib/WebhookManager.js
Normal file
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @typedef {import('stripe').Stripe.WebhookEndpointCreateParams.EnabledEvent} WebhookEvent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('stripe').Stripe.WebhookEndpoint} Webhook
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('./StripeAPI')} StripeAPI
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} StripeWebhookModel
|
||||
* @prop {string} webhook_id
|
||||
* @prop {string} secret
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} StripeWebhook
|
||||
* @prop {(data: StripeWebhookModel) => Promise<StripeWebhookModel>} save
|
||||
* @prop {() => Promise<StripeWebhookModel>} get
|
||||
*/
|
||||
|
||||
module.exports = class WebhookManager {
|
||||
/**
|
||||
* @param {object} deps
|
||||
* @param {StripeWebhook} deps.StripeWebhook
|
||||
* @param {StripeAPI} deps.api
|
||||
*/
|
||||
constructor({
|
||||
StripeWebhook,
|
||||
api
|
||||
}) {
|
||||
/** @private */
|
||||
this.StripeWebhook = StripeWebhook;
|
||||
/** @private */
|
||||
this.api = api;
|
||||
/** @private */
|
||||
this.config = null;
|
||||
/** @private */
|
||||
this.webhookSecret = null;
|
||||
/**
|
||||
* @private
|
||||
* @type {'network'|'local'}
|
||||
*/
|
||||
this.mode = 'network';
|
||||
}
|
||||
|
||||
/** @type {WebhookEvent[]} */
|
||||
static events = [
|
||||
'checkout.session.completed',
|
||||
'customer.subscription.deleted',
|
||||
'customer.subscription.updated',
|
||||
'customer.subscription.created',
|
||||
'invoice.payment_succeeded'
|
||||
];
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async stop() {
|
||||
if (this.mode !== 'network') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const existingWebhook = await this.StripeWebhook.get();
|
||||
if (existingWebhook.webhook_id) {
|
||||
await this.api.deleteWebhookEndpoint(existingWebhook.webhook_id);
|
||||
}
|
||||
await this.StripeWebhook.save({
|
||||
webhook_id: null,
|
||||
secret: null
|
||||
});
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async start() {
|
||||
if (this.mode !== 'network') {
|
||||
return;
|
||||
}
|
||||
const existingWebhook = await this.StripeWebhook.get();
|
||||
|
||||
const webhook = await this.setupWebhook(existingWebhook.webhook_id, existingWebhook.secret);
|
||||
|
||||
await this.StripeWebhook.save({
|
||||
webhook_id: webhook.id,
|
||||
secret: webhook.secret
|
||||
});
|
||||
|
||||
this.webhookSecret = webhook.secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} config
|
||||
* @param {string=} config.webhookSecret An optional webhook secret for use with stripe-cli, passing this will ensure a webhook is not created in Stripe
|
||||
* @param {string} config.webhookHandlerUrl The URL which the Webhook should hit
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async configure(config) {
|
||||
this.config = config;
|
||||
if (config.webhookSecret) {
|
||||
this.webhookSecret = config.webhookSecret;
|
||||
this.mode = 'local';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string=} id
|
||||
* @param {string=} secret
|
||||
* @param {object=} opts
|
||||
* @param {boolean} opts.forceCreate
|
||||
* @param {boolean} opts.skipDelete
|
||||
*
|
||||
* @returns {Promise<Webhook>}
|
||||
*/
|
||||
async setupWebhook(id, secret, opts) {
|
||||
if (!id || !secret || opts.forceCreate) {
|
||||
if (id && !opts.skipDelete) {
|
||||
try {
|
||||
await this.api.deleteWebhookEndpoint(id);
|
||||
} catch (err) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
const webhook = await this.api.createWebhookEndpoint(
|
||||
this.config.webhookHandlerUrl,
|
||||
WebhookManager.events
|
||||
);
|
||||
return {
|
||||
id: webhook.id,
|
||||
secret: webhook.secret
|
||||
};
|
||||
} else {
|
||||
try {
|
||||
await this.api.updateWebhookEndpoint(
|
||||
id,
|
||||
this.config.webhookHandlerUrl,
|
||||
WebhookManager.events
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
secret
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === 'resource_missing') {
|
||||
return this.setupWebhook(id, secret, {skipDelete: true, forceCreate: true});
|
||||
}
|
||||
return this.setupWebhook(id, secret, {skipDelete: false, forceCreate: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} body
|
||||
* @param {string} signature
|
||||
* @returns {import('stripe').Stripe.Event}
|
||||
*/
|
||||
parseWebhook(body, signature) {
|
||||
return this.api.parseWebhook(body, signature, this.webhookSecret);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user