From 3af621ea9afc3f55affadf8222385a97529e20af Mon Sep 17 00:00:00 2001 From: Naz Date: Wed, 26 Feb 2020 12:42:41 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Added=20handling=20allowing=20membe?= =?UTF-8?q?rs=20to=20edit=20their=20billing=20info=20(#11571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no issue - This functionality allows member to update their billing information, like credit card information. - Adds handler to update Stripe billing when element with `data-members-edit-billing` attribute is present on the page. Additional `data-members-success` and `data-members-cancel` attributes could be used to control the redirects on billing update success or failure. They work in the same fission as for 'members-plan' (https://ghost.org/docs/members/checkout-buttons/#redirects) --- core/server/public/members.js | 69 +++++++++++++++++++++++ core/server/services/members/config.js | 7 +++ core/server/web/api/canary/members/app.js | 1 + package.json | 2 +- yarn.lock | 8 +-- 5 files changed, 82 insertions(+), 5 deletions(-) diff --git a/core/server/public/members.js b/core/server/public/members.js index f6de81a3f9..17f8fb5571 100644 --- a/core/server/public/members.js +++ b/core/server/public/members.js @@ -119,6 +119,75 @@ Array.prototype.forEach.call(document.querySelectorAll('[data-members-plan]'), f el.addEventListener('click', clickHandler); }); +Array.prototype.forEach.call(document.querySelectorAll('[data-members-edit-billing]'), function (el) { + var errorEl = el.querySelector('[data-members-error]'); + var membersSuccess = el.dataset.membersSuccess; + var membersCancel = el.dataset.membersCancel; + var successUrl; + var cancelUrl; + + if (membersSuccess) { + successUrl = (new URL(membersSuccess, window.location.href)).href; + } + + if (membersCancel) { + cancelUrl = (new URL(membersCancel, window.location.href)).href; + } + + function clickHandler(event) { + el.removeEventListener('click', clickHandler); + event.preventDefault(); + + if (errorEl) { + errorEl.innerText = ''; + } + el.classList.add('loading'); + fetch('{{blog-url}}/members/ssr', { + credentials: 'same-origin' + }).then(function (res) { + if (!res.ok) { + return null; + } + return res.text(); + }).then(function (identity) { + return fetch('{{admin-url}}/api/canary/members/create-stripe-setup-session/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + identity: identity, + successUrl: successUrl, + cancelUrl: cancelUrl + }) + }).then(function (res) { + if (!res.ok) { + throw new Error('Could not create stripe checkout session'); + } + return res.json(); + }); + }).then(function (result) { + var stripe = Stripe(result.publicKey); + return stripe.redirectToCheckout({ + sessionId: result.sessionId + }); + }).then(function (result) { + if (result.error) { + throw new Error(result.error.message); + } + }).catch(function (err) { + console.error(err); + el.addEventListener('click', clickHandler); + el.classList.remove('loading'); + if (errorEl) { + errorEl.innerText = err.message; + } + el.classList.add('error'); + }); + } + el.addEventListener('click', clickHandler); +}); + Array.prototype.forEach.call(document.querySelectorAll('[data-members-signout]'), function (el) { function clickHandler(event) { el.removeEventListener('click', clickHandler); diff --git a/core/server/services/members/config.js b/core/server/services/members/config.js index 95d29360b2..d09c485636 100644 --- a/core/server/services/members/config.js +++ b/core/server/services/members/config.js @@ -61,11 +61,18 @@ function getStripePaymentConfig() { const checkoutCancelUrl = new URL(siteUrl); checkoutCancelUrl.searchParams.set('stripe', 'cancel'); + const billingSuccessUrl = new URL(siteUrl); + billingSuccessUrl.searchParams.set('stripe', 'billing-update-success'); + const billingCancelUrl = new URL(siteUrl); + billingCancelUrl.searchParams.set('stripe', 'billing-update-cancel'); + return { publicKey: stripePaymentProcessor.config.public_token, secretKey: stripePaymentProcessor.config.secret_token, checkoutSuccessUrl: checkoutSuccessUrl.href, checkoutCancelUrl: checkoutCancelUrl.href, + billingSuccessUrl: billingSuccessUrl.href, + billingCancelUrl: billingCancelUrl.href, webhookHandlerUrl: webhookHandlerUrl.href, product: stripePaymentProcessor.config.product, plans: stripePaymentProcessor.config.plans, diff --git a/core/server/web/api/canary/members/app.js b/core/server/web/api/canary/members/app.js index ea84f9a4ec..2d180f2321 100644 --- a/core/server/web/api/canary/members/app.js +++ b/core/server/web/api/canary/members/app.js @@ -27,6 +27,7 @@ module.exports = function setupMembersApiApp() { // NOTE: this is wrapped in a function to ensure we always go via the getter apiApp.post('/send-magic-link', (req, res, next) => membersService.api.middleware.sendMagicLink(req, res, next)); apiApp.post('/create-stripe-checkout-session', (req, res, next) => membersService.api.middleware.createCheckoutSession(req, res, next)); + apiApp.post('/create-stripe-setup-session', (req, res, next) => membersService.api.middleware.createCheckoutSetupSession(req, res, next)); apiApp.put('/subscriptions/:id', (req, res, next) => membersService.api.middleware.updateSubscription(req, res, next)); // API error handling diff --git a/package.json b/package.json index 10d01475df..639f355998 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@nexes/nql": "0.3.0", "@sentry/node": "5.12.4", "@tryghost/helpers": "1.1.22", - "@tryghost/members-api": "0.15.1", + "@tryghost/members-api": "0.16.0", "@tryghost/members-ssr": "0.7.4", "@tryghost/social-urls": "0.1.5", "@tryghost/string": "^0.1.3", diff --git a/yarn.lock b/yarn.lock index f541106b0d..673804aefc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -330,10 +330,10 @@ jsonwebtoken "^8.5.1" lodash "^4.17.15" -"@tryghost/members-api@0.15.1": - version "0.15.1" - resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.15.1.tgz#65ea2722e200127a6f6604debbb77fa6c0c96dfd" - integrity sha512-Nlt7ZmZGHKJamrzu7hbS9OlSFifH3w7XKr7IYwWYaAMhkJpHTqCCMBJ7tuyiC6c5YgwGt9AT51+JsFvum29hnA== +"@tryghost/members-api@0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.16.0.tgz#429870aa5a35783f6e56bde00c9bae037b0c6a92" + integrity sha512-up51hixehvEDPyj1dP9gLdZtCr12rGLB/0yMPTxiBx2DN40Y0k3qvNBc8YdXmE9zuXTRUCgOMJV3BTZywYOW7g== dependencies: "@tryghost/magic-link" "^0.4.1" bluebird "^3.5.4"