diff --git a/ghost/admin/app/components/gh-member-settings-form.hbs b/ghost/admin/app/components/gh-member-settings-form.hbs index f16e5f9289..dfcdd73ae1 100644 --- a/ghost/admin/app/components/gh-member-settings-form.hbs +++ b/ghost/admin/app/components/gh-member-settings-form.hbs @@ -170,16 +170,24 @@
Cancellation reason: {{sub.cancellationReason}}
{{/if}} {{#if sub.offer}} -
- {{sub.offer.name}} - offer - {{#if (eq sub.offer.type 'fixed')}} - ({{currency-symbol sub.offer.currency}}{{gh-price-amount sub.offer.amount}} off) - {{else}} - ({{sub.offer.amount}}% off) - {{/if}} - applied to subscription -
+ {{#if (eq sub.offer.type "trial")}} +
+ {{sub.offer.name}} + offer + ({{sub.offer.amount}} days free) +
+ {{else}} +
+ {{sub.offer.name}} + offer + {{#if (eq sub.offer.type 'fixed')}} + ({{currency-symbol sub.offer.currency}}{{gh-price-amount sub.offer.amount}} off) + {{else}} + ({{sub.offer.amount}}% off) + {{/if}} + applied to subscription +
+ {{/if}} {{/if}}
Created on {{sub.startDate}} diff --git a/ghost/members-api/lib/repositories/member.js b/ghost/members-api/lib/repositories/member.js index 80c9f8a4a9..ebc9a4bb01 100644 --- a/ghost/members-api/lib/repositories/member.js +++ b/ghost/members-api/lib/repositories/member.js @@ -327,7 +327,7 @@ module.exports = class MemberRepository { if (memberData.bio) { memberData.bio = memberData.bio.trim(); } - + // Determine if we need to fetch the initial member with relations const needsProducts = this._stripeAPIService.configured && data.products; const needsNewsletters = memberData.newsletters || typeof memberData.subscribed === 'boolean'; @@ -698,6 +698,7 @@ module.exports = class MemberRepository { * @param {Object} data * @param {String} data.id - member ID * @param {Object} data.subscription + * @param {String} data.offerId * @param {*} options * @returns */ @@ -785,7 +786,9 @@ module.exports = class MemberRepository { } let stripeCouponId = subscription.discount && subscription.discount.coupon ? subscription.discount.coupon.id : null; - let offerId = null; + + // For trial offers, offer id is passed from metadata as there is no stripe coupon + let offerId = data.offerId || null; if (stripeCouponId) { // Get the offer from our database @@ -831,6 +834,11 @@ module.exports = class MemberRepository { let eventData = {}; if (model) { + // CASE: Offer is already mapped against sub, don't overwrite it with NULL + // Needed for trial offers, which don't have a stripe coupon/discount attached to sub + if (!subscriptionData.offer_id) { + delete subscriptionData.offer_id; + } const updated = await this._StripeCustomerSubscription.edit(subscriptionData, { ...options, id: model.id diff --git a/ghost/stripe/lib/WebhookController.js b/ghost/stripe/lib/WebhookController.js index c9ada71f53..a71582107d 100644 --- a/ghost/stripe/lib/WebhookController.js +++ b/ghost/stripe/lib/WebhookController.js @@ -253,9 +253,12 @@ module.exports = class WebhookController { for (const subscription of customer.subscriptions.data) { try { + const offerId = session.metadata?.offer; + await this.deps.memberRepository.linkSubscription({ id: member.id, - subscription + subscription, + offerId }); } catch (err) { if (err.code !== 'ER_DUP_ENTRY' && err.code !== 'SQLITE_CONSTRAINT') {