ghost 5.115.0 → 5.115.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-adapter-cache-redis-5.115.1.tgz +0 -0
- package/components/tryghost-adapter-manager-5.115.1.tgz +0 -0
- package/components/{tryghost-announcement-bar-settings-5.115.0.tgz → tryghost-announcement-bar-settings-5.115.1.tgz} +0 -0
- package/components/{tryghost-api-framework-5.115.0.tgz → tryghost-api-framework-5.115.1.tgz} +0 -0
- package/components/tryghost-constants-5.115.1.tgz +0 -0
- package/components/tryghost-custom-fonts-5.115.1.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.115.0.tgz → tryghost-custom-theme-settings-service-5.115.1.tgz} +0 -0
- package/components/{tryghost-data-generator-5.115.0.tgz → tryghost-data-generator-5.115.1.tgz} +0 -0
- package/components/tryghost-domain-events-5.115.1.tgz +0 -0
- package/components/tryghost-donations-5.115.1.tgz +0 -0
- package/components/{tryghost-email-addresses-5.115.0.tgz → tryghost-email-addresses-5.115.1.tgz} +0 -0
- package/components/{tryghost-email-content-generator-5.115.0.tgz → tryghost-email-content-generator-5.115.1.tgz} +0 -0
- package/components/{tryghost-email-events-5.115.0.tgz → tryghost-email-events-5.115.1.tgz} +0 -0
- package/components/tryghost-email-service-5.115.1.tgz +0 -0
- package/components/{tryghost-email-suppression-list-5.115.0.tgz → tryghost-email-suppression-list-5.115.1.tgz} +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.115.0.tgz → tryghost-express-dynamic-redirects-5.115.1.tgz} +0 -0
- package/components/tryghost-ghost-5.115.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.115.1.tgz +0 -0
- package/components/tryghost-i18n-5.115.1.tgz +0 -0
- package/components/{tryghost-importer-handler-content-files-5.115.0.tgz → tryghost-importer-handler-content-files-5.115.1.tgz} +0 -0
- package/components/tryghost-in-memory-repository-5.115.1.tgz +0 -0
- package/components/{tryghost-job-manager-5.115.0.tgz → tryghost-job-manager-5.115.1.tgz} +0 -0
- package/components/{tryghost-link-redirects-5.115.0.tgz → tryghost-link-redirects-5.115.1.tgz} +0 -0
- package/components/tryghost-link-replacer-5.115.1.tgz +0 -0
- package/components/{tryghost-magic-link-5.115.0.tgz → tryghost-magic-link-5.115.1.tgz} +0 -0
- package/components/{tryghost-mailgun-client-5.115.0.tgz → tryghost-mailgun-client-5.115.1.tgz} +0 -0
- package/components/tryghost-member-attribution-5.115.1.tgz +0 -0
- package/components/{tryghost-member-events-5.115.0.tgz → tryghost-member-events-5.115.1.tgz} +0 -0
- package/components/{tryghost-members-api-5.115.0.tgz → tryghost-members-api-5.115.1.tgz} +0 -0
- package/components/{tryghost-members-csv-5.115.0.tgz → tryghost-members-csv-5.115.1.tgz} +0 -0
- package/components/{tryghost-members-offers-5.115.0.tgz → tryghost-members-offers-5.115.1.tgz} +0 -0
- package/components/{tryghost-members-payments-5.115.0.tgz → tryghost-members-payments-5.115.1.tgz} +0 -0
- package/components/{tryghost-milestones-5.115.0.tgz → tryghost-milestones-5.115.1.tgz} +0 -0
- package/components/{tryghost-minifier-5.115.0.tgz → tryghost-minifier-5.115.1.tgz} +0 -0
- package/components/{tryghost-mw-error-handler-5.115.0.tgz → tryghost-mw-error-handler-5.115.1.tgz} +0 -0
- package/components/{tryghost-mw-version-match-5.115.0.tgz → tryghost-mw-version-match-5.115.1.tgz} +0 -0
- package/components/tryghost-mw-vhost-5.115.1.tgz +0 -0
- package/components/tryghost-post-events-5.115.1.tgz +0 -0
- package/components/{tryghost-post-revisions-5.115.0.tgz → tryghost-post-revisions-5.115.1.tgz} +0 -0
- package/components/{tryghost-posts-service-5.115.0.tgz → tryghost-posts-service-5.115.1.tgz} +0 -0
- package/components/{tryghost-prometheus-metrics-5.115.0.tgz → tryghost-prometheus-metrics-5.115.1.tgz} +0 -0
- package/components/tryghost-recommendations-5.115.1.tgz +0 -0
- package/components/{tryghost-security-5.115.0.tgz → tryghost-security-5.115.1.tgz} +0 -0
- package/components/{tryghost-slack-notifications-5.115.0.tgz → tryghost-slack-notifications-5.115.1.tgz} +0 -0
- package/components/{tryghost-tiers-5.115.0.tgz → tryghost-tiers-5.115.1.tgz} +0 -0
- package/components/tryghost-webmentions-5.115.1.tgz +0 -0
- package/content/themes/casper/LICENSE +1 -1
- package/content/themes/casper/README.md +1 -1
- package/content/themes/source/LICENSE +1 -1
- package/content/themes/source/README.md +1 -1
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/screen.css.map +1 -1
- package/content/themes/source/assets/css/screen.css +11 -6
- package/content/themes/source/partials/feature-image.hbs +2 -2
- package/core/boot.js +3 -1
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +23497 -23041
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
- package/core/built/admin/assets/admin-x-demo/{index-0040480a.mjs → index-15df2af5.mjs} +4 -3
- package/core/built/admin/assets/admin-x-demo/{modals-fb35c86c.mjs → modals-8ca61d78.mjs} +67 -65
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-806ef39c.mjs → CodeEditorView-d2e6872f.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
- package/core/built/admin/assets/admin-x-settings/{index-376f847c.mjs → index-8e8821e5.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-8fa19303.mjs → index-f5cb3db3.mjs} +3104 -3094
- package/core/built/admin/assets/admin-x-settings/{modals-36775d71.mjs → modals-e8ae4d46.mjs} +3 -3
- package/core/built/admin/assets/{chunk.524.31419fdf6fb3859ecc1e.js → chunk.524.2439684964c164c598ab.js} +6 -6
- package/core/built/admin/assets/{chunk.582.08c816d5e4ab766486a7.js → chunk.582.bf5a2bbb2c4eb69ef1e7.js} +10 -10
- package/core/built/admin/assets/ghost-327b17ea23cb8c89bd7e6a51e18e8506.css +1 -0
- package/core/built/admin/assets/ghost-dark-f30a597ac19632a118939492591c531b.css +1 -0
- package/core/built/admin/assets/{ghost-938b3d9c29e3564a53a22f8c8f82d351.js → ghost-df7b9558260aa27d18b195ee895b487d.js} +181 -159
- package/core/built/admin/assets/stats/stats.js +11824 -0
- package/core/built/admin/index.html +4 -4
- package/core/frontend/helpers/ghost_head.js +3 -1
- package/core/frontend/src/cards/css/cta.css +1 -1
- package/core/server/api/endpoints/slugs.js +6 -2
- package/core/server/data/importer/import-manager.js +2 -2
- package/core/server/data/importer/importers/importer-revue.js +128 -0
- package/core/server/data/importer/importers/json-to-html.js +107 -0
- package/core/server/data/migrations/utils/tables.js +2 -4
- package/core/server/lib/bootstrap-socket.js +87 -0
- package/core/server/lib/package-json/index.js +1 -0
- package/core/server/lib/package-json/package-json.js +160 -0
- package/core/server/lib/package-json/parse.js +57 -0
- package/core/server/models/base/plugins/actions.js +44 -31
- package/core/server/models/base/plugins/generate-slug.js +6 -0
- package/core/server/notify.js +1 -1
- package/core/server/services/activitypub/ActivityPubService.ts +1 -1
- package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +99 -0
- package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +80 -0
- package/core/server/services/api-version-compatibility/extract-api-key.js +57 -0
- package/core/server/services/api-version-compatibility/index.js +2 -2
- package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +31 -0
- package/core/server/services/audience-feedback/AudienceFeedbackController.js +85 -0
- package/core/server/services/audience-feedback/AudienceFeedbackService.js +34 -0
- package/core/server/services/audience-feedback/Feedback.js +35 -0
- package/core/server/services/audience-feedback/index.js +4 -2
- package/core/server/services/auth/session/emails/signin.js +168 -0
- package/core/server/services/auth/session/index.js +2 -2
- package/core/server/services/auth/session/session-from-token.js +69 -0
- package/core/server/services/auth/session/session-service.js +364 -0
- package/core/server/services/email-analytics/EmailAnalyticsProviderMailgun.js +62 -0
- package/core/server/services/email-analytics/EmailAnalyticsService.js +552 -0
- package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +3 -3
- package/core/server/services/email-analytics/EventProcessingResult.js +66 -0
- package/core/server/services/explore-ping/ExplorePingService.js +106 -0
- package/core/server/services/explore-ping/index.js +31 -0
- package/core/server/services/identity-tokens/IdentityTokenService.js +30 -0
- package/core/server/services/identity-tokens/IdentityTokenService.ts +28 -0
- package/core/server/services/identity-tokens/IdentityTokenServiceWrapper.js +1 -1
- package/core/server/services/invitations/accept.js +5 -2
- package/core/server/services/mail-events/BookshelfMailEventRepository.js +2 -2
- package/core/server/services/mail-events/InMemoryMailEventRepository.js +10 -0
- package/core/server/services/mail-events/InMemoryMailEventRepository.ts +8 -0
- package/core/server/services/mail-events/MailEvent.js +20 -0
- package/core/server/services/mail-events/MailEvent.ts +10 -0
- package/core/server/services/mail-events/MailEventRepository.js +2 -0
- package/core/server/services/mail-events/MailEventRepository.ts +5 -0
- package/core/server/services/mail-events/MailEventService.js +124 -0
- package/core/server/services/mail-events/MailEventService.ts +169 -0
- package/core/server/services/mail-events/index.js +1 -1
- package/core/server/services/mail-events/libraries.d.ts +2 -0
- package/core/server/services/members/CaptchaService.js +80 -0
- package/core/server/services/members/api.js +1 -1
- package/core/server/services/members/importer/MembersCSVImporter.js +464 -0
- package/core/server/services/members/importer/MembersCSVImporterStripeUtils.js +194 -0
- package/core/server/services/members/importer/email-template.js +182 -0
- package/core/server/services/members/importer/index.js +30 -0
- package/core/server/services/members/members-ssr.js +333 -0
- package/core/server/services/members/service.js +2 -2
- package/core/server/services/posts/stats/PostStats.js +13 -0
- package/core/server/services/route-settings/SettingsPathManager.js +47 -0
- package/core/server/services/route-settings/index.js +1 -1
- package/core/server/services/stripe/README.md +63 -0
- package/core/server/services/stripe/StripeAPI.js +931 -0
- package/core/server/services/stripe/StripeMigrations.js +613 -0
- package/core/server/services/stripe/StripeService.js +175 -0
- package/core/server/services/stripe/WebhookController.js +100 -0
- package/core/server/services/stripe/WebhookManager.js +175 -0
- package/core/server/services/stripe/events/StripeLiveDisabledEvent.js +23 -0
- package/core/server/services/stripe/events/StripeLiveEnabledEvent.js +23 -0
- package/core/server/services/stripe/events/index.js +4 -0
- package/core/server/services/stripe/service.js +1 -1
- package/core/server/services/stripe/services/webhook/CheckoutSessionEventService.js +255 -0
- package/core/server/services/stripe/services/webhook/InvoiceEventService.js +70 -0
- package/core/server/services/stripe/services/webhook/SubscriptionEventService.js +54 -0
- package/core/server/services/themes/loader.js +1 -1
- package/core/server/services/themes/to-json.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/core/server/web/shared/middleware/cache-control.js +51 -0
- package/core/server/web/shared/middleware/index.js +1 -1
- package/core/server/web/well-known.js +1 -1
- package/core/shared/labs.js +3 -1
- package/core/shared/settings-cache/CacheManager.js +64 -6
- package/package.json +103 -134
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +7 -93
- package/components/tryghost-adapter-cache-redis-5.115.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.115.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.115.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.115.0.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.115.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.115.0.tgz +0 -0
- package/components/tryghost-captcha-service-5.115.0.tgz +0 -0
- package/components/tryghost-constants-5.115.0.tgz +0 -0
- package/components/tryghost-custom-fonts-5.115.0.tgz +0 -0
- package/components/tryghost-domain-events-5.115.0.tgz +0 -0
- package/components/tryghost-donations-5.115.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.115.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.115.0.tgz +0 -0
- package/components/tryghost-email-service-5.115.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.115.0.tgz +0 -0
- package/components/tryghost-ghost-5.115.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.115.0.tgz +0 -0
- package/components/tryghost-i18n-5.115.0.tgz +0 -0
- package/components/tryghost-identity-token-service-5.115.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.115.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.115.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.115.0.tgz +0 -0
- package/components/tryghost-mail-events-5.115.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.115.0.tgz +0 -0
- package/components/tryghost-members-importer-5.115.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.115.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.115.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.115.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.115.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.115.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.115.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.115.0.tgz +0 -0
- package/components/tryghost-package-json-5.115.0.tgz +0 -0
- package/components/tryghost-post-events-5.115.0.tgz +0 -0
- package/components/tryghost-recommendations-5.115.0.tgz +0 -0
- package/components/tryghost-referrers-5.115.0.tgz +0 -0
- package/components/tryghost-session-service-5.115.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.115.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.115.0.tgz +0 -0
- package/components/tryghost-webmentions-5.115.0.tgz +0 -0
- package/core/built/admin/assets/ghost-c2a7c4a1b76550c4219adb2ed4124ce0.css +0 -1
- package/core/built/admin/assets/ghost-dark-f91e4a479c6d38d94d5d1b14727871dc.css +0 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const WebhookManager = require('./WebhookManager');
|
|
2
|
+
const StripeAPI = require('./StripeAPI');
|
|
3
|
+
const StripeMigrations = require('./StripeMigrations');
|
|
4
|
+
const WebhookController = require('./WebhookController');
|
|
5
|
+
const DomainEvents = require('@tryghost/domain-events');
|
|
6
|
+
const {StripeLiveEnabledEvent, StripeLiveDisabledEvent} = require('./events');
|
|
7
|
+
const SubscriptionEventService = require('./services/webhook/SubscriptionEventService');
|
|
8
|
+
const InvoiceEventService = require('./services/webhook/InvoiceEventService');
|
|
9
|
+
const CheckoutSessionEventService = require('./services/webhook/CheckoutSessionEventService');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {object} IStripeServiceConfig
|
|
13
|
+
* @prop {string} secretKey The Stripe secret key
|
|
14
|
+
* @prop {string} publicKey The Stripe publishable key
|
|
15
|
+
* @prop {boolean} enablePromoCodes Whether to enable promo codes
|
|
16
|
+
* @prop {boolean} enableAutomaticTax Whether to enable automatic tax
|
|
17
|
+
* @prop {string} checkoutSessionSuccessUrl The URL to redirect to after successful checkout
|
|
18
|
+
* @prop {string} checkoutSessionCancelUrl The URL to redirect to if checkout is cancelled
|
|
19
|
+
* @prop {string} checkoutSetupSessionSuccessUrl The URL to redirect to after successful setup session
|
|
20
|
+
* @prop {string} checkoutSetupSessionCancelUrl The URL to redirect to if setup session is cancelled
|
|
21
|
+
* @prop {boolean} testEnv Whether this is a test environment
|
|
22
|
+
* @prop {string} webhookSecret The Stripe webhook secret
|
|
23
|
+
* @prop {string} webhookHandlerUrl The URL to handle Stripe webhooks
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The `StripeService` contains the core logic for Ghost's Stripe integration.
|
|
28
|
+
|
|
29
|
+
*/
|
|
30
|
+
module.exports = class StripeService {
|
|
31
|
+
/**
|
|
32
|
+
* @param {object} deps
|
|
33
|
+
* @param {*} deps.labs
|
|
34
|
+
* @param {*} deps.membersService
|
|
35
|
+
* @param {*} deps.donationService
|
|
36
|
+
* @param {*} deps.staffService
|
|
37
|
+
* @param {import('./WebhookManager').StripeWebhook} deps.StripeWebhook
|
|
38
|
+
* @param {object} deps.models
|
|
39
|
+
* @param {object} deps.models.Product
|
|
40
|
+
* @param {object} deps.models.StripePrice
|
|
41
|
+
* @param {object} deps.models.StripeCustomerSubscription
|
|
42
|
+
* @param {object} deps.models.StripeProduct
|
|
43
|
+
* @param {object} deps.models.MemberStripeCustomer
|
|
44
|
+
* @param {object} deps.models.Offer
|
|
45
|
+
* @param {object} deps.models.Settings
|
|
46
|
+
*/
|
|
47
|
+
constructor({
|
|
48
|
+
labs,
|
|
49
|
+
membersService,
|
|
50
|
+
donationService,
|
|
51
|
+
staffService,
|
|
52
|
+
StripeWebhook,
|
|
53
|
+
models
|
|
54
|
+
}) {
|
|
55
|
+
const api = new StripeAPI({labs});
|
|
56
|
+
const migrations = new StripeMigrations({
|
|
57
|
+
models,
|
|
58
|
+
api
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const webhookManager = new WebhookManager({
|
|
62
|
+
StripeWebhook,
|
|
63
|
+
api
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const subscriptionEventService = new SubscriptionEventService({
|
|
67
|
+
get memberRepository(){
|
|
68
|
+
return membersService.api.members;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const invoiceEventService = new InvoiceEventService({
|
|
73
|
+
api,
|
|
74
|
+
get memberRepository(){
|
|
75
|
+
return membersService.api.members;
|
|
76
|
+
},
|
|
77
|
+
get eventRepository(){
|
|
78
|
+
return membersService.api.events;
|
|
79
|
+
},
|
|
80
|
+
get productRepository(){
|
|
81
|
+
return membersService.api.productRepository;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const checkoutSessionEventService = new CheckoutSessionEventService({
|
|
86
|
+
api,
|
|
87
|
+
get memberRepository(){
|
|
88
|
+
return membersService.api.members;
|
|
89
|
+
},
|
|
90
|
+
get productRepository(){
|
|
91
|
+
return membersService.api.productRepository;
|
|
92
|
+
},
|
|
93
|
+
get eventRepository(){
|
|
94
|
+
return membersService.api.events;
|
|
95
|
+
},
|
|
96
|
+
get donationRepository(){
|
|
97
|
+
return donationService.repository;
|
|
98
|
+
},
|
|
99
|
+
get staffServiceEmails(){
|
|
100
|
+
return staffService.api.emails;
|
|
101
|
+
},
|
|
102
|
+
sendSignupEmail(email){
|
|
103
|
+
return membersService.api.sendEmailWithMagicLink({
|
|
104
|
+
email,
|
|
105
|
+
requestedType: 'signup-paid',
|
|
106
|
+
options: {
|
|
107
|
+
forceEmailType: true
|
|
108
|
+
},
|
|
109
|
+
tokenData: {}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const webhookController = new WebhookController({
|
|
115
|
+
webhookManager,
|
|
116
|
+
subscriptionEventService,
|
|
117
|
+
invoiceEventService,
|
|
118
|
+
checkoutSessionEventService
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.models = models;
|
|
122
|
+
this.api = api;
|
|
123
|
+
this.webhookManager = webhookManager;
|
|
124
|
+
this.migrations = migrations;
|
|
125
|
+
this.webhookController = webhookController;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async connect() {
|
|
129
|
+
DomainEvents.dispatch(StripeLiveEnabledEvent.create({message: 'Stripe Live Mode Enabled'}));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async disconnect() {
|
|
133
|
+
await this.models.Product.forge().query().update({
|
|
134
|
+
monthly_price_id: null,
|
|
135
|
+
yearly_price_id: null
|
|
136
|
+
});
|
|
137
|
+
await this.models.StripePrice.forge().query().del();
|
|
138
|
+
await this.models.StripeProduct.forge().query().del();
|
|
139
|
+
await this.models.MemberStripeCustomer.forge().query().del();
|
|
140
|
+
await this.models.Offer.forge().query().update({
|
|
141
|
+
stripe_coupon_id: null
|
|
142
|
+
});
|
|
143
|
+
await this.webhookManager.stop();
|
|
144
|
+
|
|
145
|
+
this.api.configure(null);
|
|
146
|
+
|
|
147
|
+
DomainEvents.dispatch(StripeLiveDisabledEvent.create({message: 'Stripe Live Mode Disabled'}));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Configures the Stripe API and registers the webhook with Stripe
|
|
152
|
+
* @param {IStripeServiceConfig} config
|
|
153
|
+
*/
|
|
154
|
+
async configure(config) {
|
|
155
|
+
this.api.configure({
|
|
156
|
+
secretKey: config.secretKey,
|
|
157
|
+
publicKey: config.publicKey,
|
|
158
|
+
enablePromoCodes: config.enablePromoCodes,
|
|
159
|
+
get enableAutomaticTax() {
|
|
160
|
+
return config.enableAutomaticTax;
|
|
161
|
+
},
|
|
162
|
+
checkoutSessionSuccessUrl: config.checkoutSessionSuccessUrl,
|
|
163
|
+
checkoutSessionCancelUrl: config.checkoutSessionCancelUrl,
|
|
164
|
+
checkoutSetupSessionSuccessUrl: config.checkoutSetupSessionSuccessUrl,
|
|
165
|
+
checkoutSetupSessionCancelUrl: config.checkoutSetupSessionCancelUrl,
|
|
166
|
+
testEnv: config.testEnv
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await this.webhookManager.configure({
|
|
170
|
+
webhookSecret: config.webhookSecret,
|
|
171
|
+
webhookHandlerUrl: config.webhookHandlerUrl
|
|
172
|
+
});
|
|
173
|
+
await this.webhookManager.start();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
|
|
3
|
+
module.exports = class WebhookController {
|
|
4
|
+
/**
|
|
5
|
+
* @param {object} deps
|
|
6
|
+
* @param {import('./WebhookManager')} deps.webhookManager
|
|
7
|
+
* @param {import('./services/webhook/CheckoutSessionEventService')} deps.checkoutSessionEventService
|
|
8
|
+
* @param {import('./services/webhook/SubscriptionEventService')} deps.subscriptionEventService
|
|
9
|
+
* @param {import('./services/webhook/InvoiceEventService')} deps.invoiceEventService
|
|
10
|
+
*/
|
|
11
|
+
constructor(deps) {
|
|
12
|
+
this.checkoutSessionEventService = deps.checkoutSessionEventService;
|
|
13
|
+
this.subscriptionEventService = deps.subscriptionEventService;
|
|
14
|
+
this.invoiceEventService = deps.invoiceEventService;
|
|
15
|
+
this.webhookManager = deps.webhookManager;
|
|
16
|
+
this.handlers = {
|
|
17
|
+
'customer.subscription.deleted': this.subscriptionEvent,
|
|
18
|
+
'customer.subscription.updated': this.subscriptionEvent,
|
|
19
|
+
'customer.subscription.created': this.subscriptionEvent,
|
|
20
|
+
'invoice.payment_succeeded': this.invoiceEvent,
|
|
21
|
+
'checkout.session.completed': this.checkoutSessionEvent
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handles a Stripe webhook event.
|
|
27
|
+
* - Parses the webhook event
|
|
28
|
+
* - Delegates the event to the appropriate handler
|
|
29
|
+
* - Returns a 200 response to Stripe to confirm receipt of the event, or an error response if the event is not handled or if an error occurs
|
|
30
|
+
* @param {import('express').Request} req
|
|
31
|
+
* @param {import('express').Response} res
|
|
32
|
+
* @returns {Promise<void>}
|
|
33
|
+
*/
|
|
34
|
+
async handle(req, res) {
|
|
35
|
+
if (!req.body || !req.headers['stripe-signature']) {
|
|
36
|
+
res.writeHead(400);
|
|
37
|
+
return res.end();
|
|
38
|
+
}
|
|
39
|
+
let event;
|
|
40
|
+
try {
|
|
41
|
+
event = this.webhookManager.parseWebhook(req.body, req.headers['stripe-signature']);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
logging.error(err);
|
|
44
|
+
res.writeHead(401);
|
|
45
|
+
return res.end();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
logging.info(`Handling webhook ${event.type}`);
|
|
49
|
+
try {
|
|
50
|
+
await this.handleEvent(event);
|
|
51
|
+
res.writeHead(200);
|
|
52
|
+
res.end();
|
|
53
|
+
} catch (err) {
|
|
54
|
+
logging.error(`Error handling webhook ${event.type}`, err);
|
|
55
|
+
res.writeHead(err.statusCode || 500);
|
|
56
|
+
res.end();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Accepts a webhook's event payload and delegates it to the appropriate handler based on the event type
|
|
62
|
+
* @private
|
|
63
|
+
* @param {import('stripe').Stripe.Event} event
|
|
64
|
+
* @returns {Promise<void>}
|
|
65
|
+
*/
|
|
66
|
+
async handleEvent(event) {
|
|
67
|
+
if (!this.handlers[event.type]) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await this.handlers[event.type].call(this, event.data.object);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Delegates any `customer.subscription.*` events to the `subscriptionEventService`
|
|
76
|
+
* @param {import('stripe').Stripe.Subscription} subscription
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
async subscriptionEvent(subscription) {
|
|
80
|
+
await this.subscriptionEventService.handleSubscriptionEvent(subscription);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Delegates any `invoice.*` events to the `invoiceEventService`
|
|
85
|
+
* @param {import('stripe').Stripe.Invoice} invoice
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
async invoiceEvent(invoice) {
|
|
89
|
+
await this.invoiceEventService.handleInvoiceEvent(invoice);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Delegates any `checkout.session.*` events to the `checkoutSessionEventService`
|
|
94
|
+
* @param {import('stripe').Stripe.Checkout.Session} session
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
async checkoutSessionEvent(session) {
|
|
98
|
+
await this.checkoutSessionEventService.handleEvent(session);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('stripe').Stripe.WebhookEndpointCreateParams.EnabledEvent} WebhookEvent
|
|
3
|
+
* @typedef {import('./StripeAPI')} StripeAPI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {object} StripeWebhookModel
|
|
8
|
+
* @prop {string} webhook_id
|
|
9
|
+
* @prop {string} secret
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {object} StripeWebhook
|
|
14
|
+
* @prop {(data: StripeWebhookModel) => Promise<void>} save
|
|
15
|
+
* @prop {() => Promise<StripeWebhookModel>} get
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
module.exports = class WebhookManager {
|
|
19
|
+
/**
|
|
20
|
+
* @param {object} deps
|
|
21
|
+
* @param {StripeWebhook} deps.StripeWebhook
|
|
22
|
+
* @param {StripeAPI} deps.api
|
|
23
|
+
*/
|
|
24
|
+
constructor({
|
|
25
|
+
StripeWebhook,
|
|
26
|
+
api
|
|
27
|
+
}) {
|
|
28
|
+
/** @private */
|
|
29
|
+
this.StripeWebhook = StripeWebhook;
|
|
30
|
+
/** @private */
|
|
31
|
+
this.api = api;
|
|
32
|
+
/** @private */
|
|
33
|
+
this.config = null;
|
|
34
|
+
/** @private */
|
|
35
|
+
this.webhookSecret = null;
|
|
36
|
+
/**
|
|
37
|
+
* @private
|
|
38
|
+
* @type {'network'|'local'}
|
|
39
|
+
*/
|
|
40
|
+
this.mode = 'network';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @type {WebhookEvent[]} */
|
|
44
|
+
static events = [
|
|
45
|
+
'checkout.session.completed',
|
|
46
|
+
'customer.subscription.deleted',
|
|
47
|
+
'customer.subscription.updated',
|
|
48
|
+
'customer.subscription.created',
|
|
49
|
+
'invoice.payment_succeeded'
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Deletes the Stripe Webhook Endpoint and saves null values for the webhook ID and secret.
|
|
54
|
+
*
|
|
55
|
+
* @returns {Promise<boolean>}
|
|
56
|
+
*/
|
|
57
|
+
async stop() {
|
|
58
|
+
if (this.mode !== 'network') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const existingWebhook = await this.StripeWebhook.get();
|
|
64
|
+
if (existingWebhook.webhook_id) {
|
|
65
|
+
await this.api.deleteWebhookEndpoint(existingWebhook.webhook_id);
|
|
66
|
+
}
|
|
67
|
+
await this.StripeWebhook.save({
|
|
68
|
+
webhook_id: null,
|
|
69
|
+
secret: null
|
|
70
|
+
});
|
|
71
|
+
return true;
|
|
72
|
+
} catch (err) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Starts the Stripe Webhook Endpoint and saves the webhook ID and secret.
|
|
79
|
+
*
|
|
80
|
+
* @returns {Promise<void>}
|
|
81
|
+
*/
|
|
82
|
+
async start() {
|
|
83
|
+
if (this.mode !== 'network') {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const existingWebhook = await this.StripeWebhook.get();
|
|
87
|
+
|
|
88
|
+
const webhook = await this.setupWebhook(existingWebhook.webhook_id, existingWebhook.secret);
|
|
89
|
+
|
|
90
|
+
await this.StripeWebhook.save({
|
|
91
|
+
webhook_id: webhook.id,
|
|
92
|
+
secret: webhook.secret
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
this.webhookSecret = webhook.secret;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Configures the Stripe Webhook Manager.
|
|
100
|
+
* @param {object} config
|
|
101
|
+
* @param {string} [config.webhookSecret] An optional webhook secret for use with stripe-cli, passing this will ensure a webhook is not created in Stripe
|
|
102
|
+
* @param {string} config.webhookHandlerUrl The URL which the Webhook should hit
|
|
103
|
+
*
|
|
104
|
+
* @returns {Promise<void>}
|
|
105
|
+
*/
|
|
106
|
+
async configure(config) {
|
|
107
|
+
this.config = config;
|
|
108
|
+
if (config.webhookSecret) {
|
|
109
|
+
this.webhookSecret = config.webhookSecret;
|
|
110
|
+
this.mode = 'local';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Setup a new Stripe Webhook Endpoint.
|
|
116
|
+
* - If the webhook exists, delete it and create a new one
|
|
117
|
+
* - If the webhook does not exist, create a new one
|
|
118
|
+
*
|
|
119
|
+
* @param {string} [id]
|
|
120
|
+
* @param {string} [secret]
|
|
121
|
+
* @param {object} [opts]
|
|
122
|
+
* @param {boolean} [opts.forceCreate]
|
|
123
|
+
* @param {boolean} [opts.skipDelete]
|
|
124
|
+
*
|
|
125
|
+
* @returns {Promise<{id: string, secret: string}>}
|
|
126
|
+
*/
|
|
127
|
+
async setupWebhook(id, secret, opts = {}) {
|
|
128
|
+
if (!id || !secret || opts.forceCreate) {
|
|
129
|
+
if (id && !opts.skipDelete) {
|
|
130
|
+
try {
|
|
131
|
+
await this.api.deleteWebhookEndpoint(id);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
// Continue
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const webhook = await this.api.createWebhookEndpoint(
|
|
137
|
+
this.config.webhookHandlerUrl,
|
|
138
|
+
WebhookManager.events
|
|
139
|
+
);
|
|
140
|
+
return {
|
|
141
|
+
id: webhook.id,
|
|
142
|
+
secret: webhook.secret
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
try {
|
|
146
|
+
await this.api.updateWebhookEndpoint(
|
|
147
|
+
id,
|
|
148
|
+
this.config.webhookHandlerUrl,
|
|
149
|
+
WebhookManager.events
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
id,
|
|
154
|
+
secret
|
|
155
|
+
};
|
|
156
|
+
} catch (err) {
|
|
157
|
+
if (err.code === 'resource_missing') {
|
|
158
|
+
return this.setupWebhook(id, secret, {skipDelete: true, forceCreate: true});
|
|
159
|
+
}
|
|
160
|
+
return this.setupWebhook(id, secret, {skipDelete: false, forceCreate: true});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Parse a Stripe Webhook event.
|
|
167
|
+
*
|
|
168
|
+
* @param {string} body
|
|
169
|
+
* @param {string} signature
|
|
170
|
+
* @returns {import('stripe').Stripe.Event}
|
|
171
|
+
*/
|
|
172
|
+
parseWebhook(body, signature) {
|
|
173
|
+
return this.api.parseWebhook(body, signature, this.webhookSecret);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} StripeLiveDisabledEventData
|
|
3
|
+
* @prop {string?} message
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = class StripeLiveDisabledEvent {
|
|
7
|
+
/**
|
|
8
|
+
* @param {StripeLiveDisabledEventData} data
|
|
9
|
+
* @param {Date} timestamp
|
|
10
|
+
*/
|
|
11
|
+
constructor(data, timestamp) {
|
|
12
|
+
this.data = data;
|
|
13
|
+
this.timestamp = timestamp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {StripeLiveDisabledEventData} data
|
|
18
|
+
* @param {Date} [timestamp]
|
|
19
|
+
*/
|
|
20
|
+
static create(data, timestamp) {
|
|
21
|
+
return new StripeLiveDisabledEvent(data, timestamp || new Date);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} StripeLiveEnabledEventData
|
|
3
|
+
* @prop {string} message
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = class StripeLiveEnabledEvent {
|
|
7
|
+
/**
|
|
8
|
+
* @param {StripeLiveEnabledEventData} data
|
|
9
|
+
* @param {Date} timestamp
|
|
10
|
+
*/
|
|
11
|
+
constructor(data, timestamp) {
|
|
12
|
+
this.data = data;
|
|
13
|
+
this.timestamp = timestamp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {StripeLiveEnabledEventData} data
|
|
18
|
+
* @param {Date} [timestamp]
|
|
19
|
+
*/
|
|
20
|
+
static create(data, timestamp) {
|
|
21
|
+
return new StripeLiveEnabledEvent(data, timestamp || new Date);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
-
const StripeService = require('
|
|
2
|
+
const StripeService = require('./StripeService');
|
|
3
3
|
const logging = require('@tryghost/logging');
|
|
4
4
|
const membersService = require('../members');
|
|
5
5
|
const config = require('../../../shared/config');
|