зеркало из https://github.com/mozilla/fxa.git
chore(auth&payments): Remove feature flag
This commit is contained in:
Родитель
f05170cc21
Коммит
a39d69372b
|
@ -903,14 +903,6 @@ const convictConf = convict({
|
||||||
format: String,
|
format: String,
|
||||||
doc: 'Stripe API key for direct Stripe integration',
|
doc: 'Stripe API key for direct Stripe integration',
|
||||||
},
|
},
|
||||||
stripeInvoiceImmediately: {
|
|
||||||
enabled: {
|
|
||||||
default: false,
|
|
||||||
doc: 'Enables immediate invoicing for stripe in all subscription upgrades',
|
|
||||||
env: 'SUBSCRIPTIONS_STRIPE_INVOICE_IMMEDIATELY',
|
|
||||||
format: Boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
stripeWebhookPayloadLimit: {
|
stripeWebhookPayloadLimit: {
|
||||||
default: 1048576,
|
default: 1048576,
|
||||||
env: 'STRIPE_WEBHOOK_PAYLOAD_LIMIT',
|
env: 'STRIPE_WEBHOOK_PAYLOAD_LIMIT',
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import * as invoiceDTO from 'fxa-shared/dto/auth/payments/invoice';
|
import * as invoiceDTO from 'fxa-shared/dto/auth/payments/invoice';
|
||||||
import { InvoicePreview } from 'fxa-shared/subscriptions/types';
|
import { InvoicePreview } from 'fxa-shared/subscriptions/types';
|
||||||
import { Stripe } from 'stripe';
|
import { Stripe } from 'stripe';
|
||||||
import { config } from '../../config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a Stripe Invoice to the FirstInvoicePreview DTO format.
|
* Formats a Stripe Invoice to the FirstInvoicePreview DTO format.
|
||||||
|
@ -51,10 +50,7 @@ export function stripeInvoiceToFirstInvoicePreviewDTO(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (invoice[1]) {
|
||||||
invoice[1] &&
|
|
||||||
config.getProperties().subscriptions.stripeInvoiceImmediately
|
|
||||||
) {
|
|
||||||
const proration = invoice[1].lines.data.find(
|
const proration = invoice[1].lines.data.find(
|
||||||
(lineItem) => lineItem.proration
|
(lineItem) => lineItem.proration
|
||||||
);
|
);
|
||||||
|
|
|
@ -689,11 +689,7 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
);
|
);
|
||||||
|
|
||||||
let proratedInvoice;
|
let proratedInvoice;
|
||||||
if (
|
if (isUpgrade && requestObject.subscription_items?.length) {
|
||||||
isUpgrade &&
|
|
||||||
requestObject.subscription_items?.length &&
|
|
||||||
this.config.subscriptions.stripeInvoiceImmediately
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
requestObject.subscription_proration_behavior = 'always_invoice';
|
requestObject.subscription_proration_behavior = 'always_invoice';
|
||||||
requestObject.subscription_proration_date = Math.floor(
|
requestObject.subscription_proration_date = Math.floor(
|
||||||
|
@ -1454,7 +1450,10 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
expand: ['data.customer', 'data.subscription'],
|
expand: ['data.customer', 'data.subscription'],
|
||||||
})) {
|
})) {
|
||||||
const subscription = invoice.subscription as Stripe.Subscription;
|
const subscription = invoice.subscription as Stripe.Subscription;
|
||||||
if (subscription && ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status)) {
|
if (
|
||||||
|
subscription &&
|
||||||
|
ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status)
|
||||||
|
) {
|
||||||
yield invoice;
|
yield invoice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1974,11 +1973,6 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
plan_change_date: moment().unix(),
|
plan_change_date: moment().unix(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const prorationBehavior = this.config.subscriptions.stripeInvoiceImmediately
|
|
||||||
.enabled
|
|
||||||
? 'always_invoice'
|
|
||||||
: 'create_prorations';
|
|
||||||
|
|
||||||
const updatedSubscription = await this.updateSubscriptionAndBackfill(
|
const updatedSubscription = await this.updateSubscriptionAndBackfill(
|
||||||
subscription,
|
subscription,
|
||||||
{
|
{
|
||||||
|
@ -1989,7 +1983,7 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
plan: newPlanId,
|
plan: newPlanId,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
proration_behavior: prorationBehavior,
|
proration_behavior: 'always_invoice',
|
||||||
metadata: updatedMetadata,
|
metadata: updatedMetadata,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -3137,7 +3131,6 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
id: invoiceId,
|
id: invoiceId,
|
||||||
number: invoiceNumber,
|
number: invoiceNumber,
|
||||||
currency: paymentProratedCurrency,
|
currency: paymentProratedCurrency,
|
||||||
total: invoiceTotalNewInCents,
|
|
||||||
amount_due: invoiceAmountDue,
|
amount_due: invoiceAmountDue,
|
||||||
} = invoice;
|
} = invoice;
|
||||||
|
|
||||||
|
@ -3164,87 +3157,8 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
subscriptionId: subscription.id,
|
subscriptionId: subscription.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const { total: nextInvoiceTotal, currency: nextInvoiceCurrency } =
|
||||||
total: nextInvoiceTotal,
|
nextInvoice || {};
|
||||||
currency: nextInvoiceCurrency,
|
|
||||||
lines: nextInvoiceLines,
|
|
||||||
} = nextInvoice || {};
|
|
||||||
|
|
||||||
// https://stripe.com/docs/api/invoices/object#invoice_object-lines
|
|
||||||
// as per the Stripe docs, one of the individual line items includes
|
|
||||||
// new plan pricing and tax information without additional fees
|
|
||||||
const upcomingInvoiceLineItem = nextInvoiceLines?.data?.find(
|
|
||||||
(line) => line.type === 'subscription'
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
|
||||||
amount: upcomingInvoiceAmount,
|
|
||||||
discount_amounts: upcomingInvoiceDiscountAmounts,
|
|
||||||
tax_amounts: upcomingInvoiceTaxAmounts,
|
|
||||||
} = upcomingInvoiceLineItem || {};
|
|
||||||
|
|
||||||
const {
|
|
||||||
productPaymentCycleNew: productPaymentCycleNew,
|
|
||||||
invoiceTotalOldInCents: invoiceTotalOldInCents,
|
|
||||||
} = baseDetails;
|
|
||||||
|
|
||||||
// remove everything between asterisk in FXA-7796
|
|
||||||
// **************** //
|
|
||||||
const sameBillingCycle = productPaymentCycleOld === productPaymentCycleNew;
|
|
||||||
const sameBillingCondition = sameBillingCycle && !!upcomingInvoiceLineItem;
|
|
||||||
|
|
||||||
const invoiceImmediately =
|
|
||||||
this.config.subscriptions.stripeInvoiceImmediately.enabled;
|
|
||||||
|
|
||||||
// used for newTotalInCents
|
|
||||||
const newListPriceTotalWithoutTax =
|
|
||||||
sameBillingCondition && !!upcomingInvoiceAmount
|
|
||||||
? upcomingInvoiceAmount
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
// used for newTotalInCents
|
|
||||||
const newListPriceDiscounts =
|
|
||||||
(sameBillingCondition &&
|
|
||||||
upcomingInvoiceDiscountAmounts?.reduce(
|
|
||||||
(acc, discount) =>
|
|
||||||
discount.amount ? acc + discount.amount : acc + 0,
|
|
||||||
0
|
|
||||||
)) ||
|
|
||||||
0;
|
|
||||||
|
|
||||||
// used for newTotalInCents
|
|
||||||
const newListPriceTotalTaxes = sameBillingCondition
|
|
||||||
? upcomingInvoiceTaxAmounts?.reduce(
|
|
||||||
(acc, tax) => (tax.inclusive ? acc + 0 : acc + tax.amount),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
// if same billing cycle and line item exists, add amount and tax for new total
|
|
||||||
// else return the next invoice total
|
|
||||||
const newTotalInCents =
|
|
||||||
sameBillingCycle &&
|
|
||||||
!!newListPriceTotalWithoutTax &&
|
|
||||||
!!newListPriceTotalTaxes
|
|
||||||
? newListPriceTotalWithoutTax -
|
|
||||||
newListPriceDiscounts +
|
|
||||||
newListPriceTotalTaxes
|
|
||||||
: nextInvoiceTotal;
|
|
||||||
|
|
||||||
// calculated proration
|
|
||||||
// if same billing cycle, return next invoice total minus new plan price
|
|
||||||
// else return amount due on current invoice
|
|
||||||
const paymentProratedInCents = sameBillingCycle
|
|
||||||
? nextInvoiceTotal - newTotalInCents
|
|
||||||
: invoiceAmountDue;
|
|
||||||
|
|
||||||
// calculated old total
|
|
||||||
// if same billing cycle, get new invoiceTotal
|
|
||||||
// else get current invoiceTotal
|
|
||||||
const oldTotalInCents = sameBillingCycle
|
|
||||||
? invoiceTotalNewInCents
|
|
||||||
: invoiceTotalOldInCents;
|
|
||||||
// **************** //
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseDetails,
|
...baseDetails,
|
||||||
|
@ -3253,25 +3167,14 @@ export class StripeHelper extends StripeHelperBase {
|
||||||
productNameOld,
|
productNameOld,
|
||||||
productIconURLOld,
|
productIconURLOld,
|
||||||
productPaymentCycleOld,
|
productPaymentCycleOld,
|
||||||
// remove condition and keep invoiceTotalOldInCents as value in FXA-7796
|
paymentAmountOldInCents: baseDetails.invoiceTotalOldInCents,
|
||||||
paymentAmountOldInCents: !invoiceImmediately
|
|
||||||
? oldTotalInCents
|
|
||||||
: invoiceTotalOldInCents,
|
|
||||||
paymentAmountOldCurrency: planOld.currency,
|
paymentAmountOldCurrency: planOld.currency,
|
||||||
// remove condition and keep nextInvoiceTotal as value in FXA-7796
|
paymentAmountNewInCents: nextInvoiceTotal,
|
||||||
paymentAmountNewInCents: !invoiceImmediately
|
|
||||||
? newTotalInCents
|
|
||||||
: nextInvoiceTotal,
|
|
||||||
paymentAmountNewCurrency: nextInvoiceCurrency,
|
paymentAmountNewCurrency: nextInvoiceCurrency,
|
||||||
invoiceNumber,
|
invoiceNumber,
|
||||||
invoiceId,
|
invoiceId,
|
||||||
// remove condition and keep invoiceAmountDue as value in FXA-7796
|
paymentProratedInCents: invoiceAmountDue,
|
||||||
paymentProratedInCents: !invoiceImmediately
|
|
||||||
? paymentProratedInCents
|
|
||||||
: invoiceAmountDue,
|
|
||||||
paymentProratedCurrency,
|
paymentProratedCurrency,
|
||||||
// remove in FXA-7796
|
|
||||||
invoiceImmediately: invoiceImmediately,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,6 @@ subscriptionUpgrade-upgrade-info = You have successfully upgraded from { $produc
|
||||||
# $productPaymentCycleNew (String) - The interval of time from the end of one payment statement date to the next payment statement date of the new subscription, e.g. month
|
# $productPaymentCycleNew (String) - The interval of time from the end of one payment statement date to the next payment statement date of the new subscription, e.g. month
|
||||||
# $productPaymentCycleOld (String) - The interval of time from the end of one payment statement date to the next payment statement date of the old subscription, e.g. month
|
# $productPaymentCycleOld (String) - The interval of time from the end of one payment statement date to the next payment statement date of the old subscription, e.g. month
|
||||||
# $paymentProrated (String) - The one time fee to reflect the higher charge for the remainder of the payment cycle, including currency, e.g. $10.00
|
# $paymentProrated (String) - The one time fee to reflect the higher charge for the remainder of the payment cycle, including currency, e.g. $10.00
|
||||||
# remove subscriptionUpgrade-content-charge-info in FXA-7796; additionally remove in subscriptionUpgrade/index.txt
|
|
||||||
subscriptionUpgrade-content-charge-info = Starting with your next bill, your charge will change from { $paymentAmountOld } per { $productPaymentCycleOld } to { $paymentAmountNew } per { $productPaymentCycleNew }. At that time you will also be charged a one-time fee of { $paymentProrated } to reflect the higher charge for the remainder of this { $productPaymentCycleOld }.
|
|
||||||
subscriptionUpgrade-content-charge-info-different-cycle = You will be charged a one-time fee of { $paymentProrated } to reflect your subscription’s higher price for the remainder of this { $productPaymentCycleOld }. Starting with your next bill, your charge will change from { $paymentAmountOld } per { $productPaymentCycleOld } to { $paymentAmountNew } per { $productPaymentCycleNew }.
|
subscriptionUpgrade-content-charge-info-different-cycle = You will be charged a one-time fee of { $paymentProrated } to reflect your subscription’s higher price for the remainder of this { $productPaymentCycleOld }. Starting with your next bill, your charge will change from { $paymentAmountOld } per { $productPaymentCycleOld } to { $paymentAmountNew } per { $productPaymentCycleNew }.
|
||||||
|
|
||||||
# Variables:
|
# Variables:
|
||||||
|
|
|
@ -9,22 +9,15 @@
|
||||||
<mj-text css-class="text-header">
|
<mj-text css-class="text-header">
|
||||||
<span data-l10n-id="subscriptionUpgrade-title">Thank you for upgrading!</span>
|
<span data-l10n-id="subscriptionUpgrade-title">Thank you for upgrading!</span>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
</mj-column>
|
|
||||||
</mj-section>
|
|
||||||
|
|
||||||
<mj-section>
|
|
||||||
<mj-column>
|
|
||||||
<mj-text css-class="text-body">
|
<mj-text css-class="text-body">
|
||||||
<span data-l10n-id="subscriptionUpgrade-upgrade-info" data-l10n-args="<%= JSON.stringify({productName, productNameOld}) %>">You have successfully upgraded from <%- productNameOld %> to <%- productName %>.</span>
|
<span data-l10n-id="subscriptionUpgrade-upgrade-info" data-l10n-args="<%= JSON.stringify({productName, productNameOld}) %>">You have successfully upgraded from <%- productNameOld %> to <%- productName %>.</span>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
|
|
||||||
<mj-text css-class="text-body">
|
<mj-text css-class="text-body">
|
||||||
<% if (!locals.invoiceImmediately && locals.productPaymentCycleOld === locals.productPaymentCycleNew) { %>
|
<span data-l10n-id="subscriptionUpgrade-content-charge-info-different-cycle" data-l10n-args="<%= JSON.stringify({paymentAmountNew, paymentAmountOld, paymentProrated, productPaymentCycleNew, productPaymentCycleOld}) %>">You will be charged a one-time fee of <%- paymentProrated %> to reflect your subscription’s higher price for the remainder of this <%- productPaymentCycleOld %>. Starting with your next bill, your charge will change from <%- paymentAmountOld %> per <%- productPaymentCycleOld %> to <%- paymentAmountNew %> per <%- productPaymentCycleNew %>.</span>
|
||||||
<span data-l10n-id="subscriptionUpgrade-content-charge-info" data-l10n-args="<%= JSON.stringify({paymentAmountNew, paymentAmountOld, paymentProrated, productPaymentCycleNew, productPaymentCycleOld}) %>">Starting with your next bill, your charge will change from <%- paymentAmountOld %> per <%- productPaymentCycleOld %> to <%- paymentAmountNew %> per <%- productPaymentCycleNew %>. At that time you will also be charged a one-time fee of <%- paymentProrated %> to reflect the higher charge for the remainder of this <%- productPaymentCycleOld %>.</span>
|
|
||||||
<% } else { %>
|
|
||||||
<span data-l10n-id="subscriptionUpgrade-content-charge-info-different-cycle" data-l10n-args="<%= JSON.stringify({paymentAmountNew, paymentAmountOld, paymentProrated, productPaymentCycleNew, productPaymentCycleOld}) %>">You will be charged a one-time fee of <%- paymentProrated %> to reflect your subscription’s higher price for the remainder of this <%- productPaymentCycleOld %>. Starting with your next bill, your charge will change from <%- paymentAmountOld %> per <%- productPaymentCycleOld %> to <%- paymentAmountNew %> per <%- productPaymentCycleNew %>.</span>
|
|
||||||
<% } %>
|
|
||||||
</mj-text>
|
</mj-text>
|
||||||
|
|
||||||
<mj-text css-class="text-body">
|
<mj-text css-class="text-body">
|
||||||
<span data-l10n-id="subscriptionUpgrade-install" data-l10n-args="<%= JSON.stringify({productName}) %>">If there is new software for you to install in order to use <%- productName %>, you will receive a separate email with download instructions.</span>
|
<span data-l10n-id="subscriptionUpgrade-install" data-l10n-args="<%= JSON.stringify({productName}) %>">If there is new software for you to install in order to use <%- productName %>, you will receive a separate email with download instructions.</span>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
|
|
|
@ -29,20 +29,3 @@ const createStory = subplatStoryWithProps(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Default = createStory();
|
export const Default = createStory();
|
||||||
|
|
||||||
// remove in FXA-7796
|
|
||||||
export const SubscriptionUpgradeDifferentBillingCycle = createStory(
|
|
||||||
{},
|
|
||||||
'Subscription upgrade - differnt billing cycle'
|
|
||||||
);
|
|
||||||
|
|
||||||
// remove in FXA-7796
|
|
||||||
export const SubscriptionUpgradeSameBillingCycle = createStory(
|
|
||||||
{
|
|
||||||
paymentAmountNew: '£123,121.00',
|
|
||||||
paymentAmountOld: '¥99,991',
|
|
||||||
productPaymentCycleNew: 'month',
|
|
||||||
paymentProrated: '$5,231.00',
|
|
||||||
},
|
|
||||||
'Subscription upgrade - same billing cycle'
|
|
||||||
);
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ subscriptionUpgrade-title = "Thank you for upgrading!"
|
||||||
|
|
||||||
subscriptionUpgrade-upgrade-info = "You have successfully upgraded from <%- productNameOld %> to <%- productName %>."
|
subscriptionUpgrade-upgrade-info = "You have successfully upgraded from <%- productNameOld %> to <%- productName %>."
|
||||||
|
|
||||||
<% if (locals.productPaymentCycleOld === locals.productPaymentCycleNew) { %>subscriptionUpgrade-content-charge-info = "Starting with your next bill, your charge will change from <%- paymentAmountOld %> per <%- productPaymentCycleOld %> to <%- paymentAmountNew %> per <%- productPaymentCycleNew %>. At that time you will also be charged a one-time fee of <%- paymentProrated %> to reflect the higher charge for the remainder of this <%- productPaymentCycleOld %>."<% } else { %>subscriptionUpgrade-content-charge-info-different-cycle = "You will be charged a one-time fee of <%- paymentProrated %> to reflect your subscription’s higher price for the remainder of this <%- productPaymentCycleOld %>. Starting with your next bill, your charge will change from <%- paymentAmountOld %> per <%- productPaymentCycleOld %> to <%- paymentAmountNew %> per <%- productPaymentCycleNew %>."<% } %>
|
subscriptionUpgrade-content-charge-info-different-cycle = "You will be charged a one-time fee of <%- paymentProrated %> to reflect your subscription’s higher price for the remainder of this <%- productPaymentCycleOld %>. Starting with your next bill, your charge will change from <%- paymentAmountOld %> per <%- productPaymentCycleOld %> to <%- paymentAmountNew %> per <%- productPaymentCycleNew %>."
|
||||||
|
|
||||||
subscriptionUpgrade-install = "If there is new software for you to install in order to use <%- productName %>, you will receive a separate email with download instructions."
|
subscriptionUpgrade-install = "If there is new software for you to install in order to use <%- productName %>, you will receive a separate email with download instructions."
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,6 @@ const mockConfig = {
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
cacheTtlSeconds: 10,
|
cacheTtlSeconds: 10,
|
||||||
productConfigsFirestore: { enabled: true },
|
productConfigsFirestore: { enabled: true },
|
||||||
stripeInvoiceImmediately: { enabled: false },
|
|
||||||
stripeApiKey: 'sk_test_4eC39HqLyjWDarjtT1zdp7dc',
|
stripeApiKey: 'sk_test_4eC39HqLyjWDarjtT1zdp7dc',
|
||||||
},
|
},
|
||||||
subhub: {
|
subhub: {
|
||||||
|
@ -2093,7 +2092,6 @@ describe('#integration - StripeHelper', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('retrieves both upcoming invoices with and without proration info', async () => {
|
it('retrieves both upcoming invoices with and without proration info', async () => {
|
||||||
stripeHelper.config.subscriptions.stripeInvoiceImmediately.enabled = true;
|
|
||||||
const stripeStub = sandbox
|
const stripeStub = sandbox
|
||||||
.stub(stripeHelper.stripe.invoices, 'retrieveUpcoming')
|
.stub(stripeHelper.stripe.invoices, 'retrieveUpcoming')
|
||||||
.resolves();
|
.resolves();
|
||||||
|
@ -2134,8 +2132,6 @@ describe('#integration - StripeHelper', () => {
|
||||||
],
|
],
|
||||||
expand: ['total_tax_amounts.tax_rate'],
|
expand: ['total_tax_amounts.tax_rate'],
|
||||||
});
|
});
|
||||||
|
|
||||||
stripeHelper.config.subscriptions.stripeInvoiceImmediately.enabled = false;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3633,7 +3629,7 @@ describe('#integration - StripeHelper', () => {
|
||||||
plan: 'plan_G93mMKnIFCjZek',
|
plan: 'plan_G93mMKnIFCjZek',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
proration_behavior: 'create_prorations',
|
proration_behavior: 'always_invoice',
|
||||||
metadata: {
|
metadata: {
|
||||||
key: 'value',
|
key: 'value',
|
||||||
previous_plan_id: subscription1.items.data[0].plan.id,
|
previous_plan_id: subscription1.items.data[0].plan.id,
|
||||||
|
@ -3659,49 +3655,6 @@ describe('#integration - StripeHelper', () => {
|
||||||
assert.equal(thrown.errno, error.ERRNO.SUBSCRIPTION_ALREADY_CHANGED);
|
assert.equal(thrown.errno, error.ERRNO.SUBSCRIPTION_ALREADY_CHANGED);
|
||||||
sinon.assert.notCalled(stripeHelper.updateSubscriptionAndBackfill);
|
sinon.assert.notCalled(stripeHelper.updateSubscriptionAndBackfill);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`uses 'always_invoice' when the config is enabled`, async () => {
|
|
||||||
stripeHelper.config.subscriptions.stripeInvoiceImmediately.enabled = true;
|
|
||||||
|
|
||||||
const unixTimestamp = moment().unix();
|
|
||||||
const subscription = deepCopy(subscription1);
|
|
||||||
subscription.metadata = {
|
|
||||||
key: 'value',
|
|
||||||
previous_plan_id: 'plan_123',
|
|
||||||
plan_change_date: 12345678,
|
|
||||||
};
|
|
||||||
|
|
||||||
sandbox.stub(moment, 'unix').returns(unixTimestamp);
|
|
||||||
sandbox
|
|
||||||
.stub(stripeHelper, 'updateSubscriptionAndBackfill')
|
|
||||||
.resolves(subscription2);
|
|
||||||
|
|
||||||
const actual = await stripeHelper.changeSubscriptionPlan(
|
|
||||||
subscription,
|
|
||||||
'plan_G93mMKnIFCjZek'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(actual, subscription2);
|
|
||||||
sinon.assert.calledWithExactly(
|
|
||||||
stripeHelper.updateSubscriptionAndBackfill,
|
|
||||||
subscription,
|
|
||||||
{
|
|
||||||
cancel_at_period_end: false,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: subscription1.items.data[0].id,
|
|
||||||
plan: 'plan_G93mMKnIFCjZek',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
proration_behavior: 'always_invoice',
|
|
||||||
metadata: {
|
|
||||||
key: 'value',
|
|
||||||
previous_plan_id: subscription1.items.data[0].plan.id,
|
|
||||||
plan_change_date: unixTimestamp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('cancelSubscriptionForCustomer', () => {
|
describe('cancelSubscriptionForCustomer', () => {
|
||||||
|
@ -5963,8 +5916,6 @@ describe('#integration - StripeHelper', () => {
|
||||||
const event = deepCopy(eventCustomerSubscriptionUpdated);
|
const event = deepCopy(eventCustomerSubscriptionUpdated);
|
||||||
const productIdOld = event.data.previous_attributes.plan.product;
|
const productIdOld = event.data.previous_attributes.plan.product;
|
||||||
const productIdNew = event.data.object.plan.product;
|
const productIdNew = event.data.object.plan.product;
|
||||||
const invoiceImmediately =
|
|
||||||
stripeHelper.config.subscriptions.stripeInvoiceImmediately.enabled;
|
|
||||||
|
|
||||||
const baseDetails = {
|
const baseDetails = {
|
||||||
...expectedBaseUpdateDetails,
|
...expectedBaseUpdateDetails,
|
||||||
|
@ -6038,29 +5989,13 @@ describe('#integration - StripeHelper', () => {
|
||||||
event.data.previous_attributes.plan.interval,
|
event.data.previous_attributes.plan.interval,
|
||||||
paymentAmountOldCurrency:
|
paymentAmountOldCurrency:
|
||||||
event.data.previous_attributes.plan.currency,
|
event.data.previous_attributes.plan.currency,
|
||||||
paymentAmountOldInCents:
|
paymentAmountOldInCents: baseDetails.invoiceTotalOldInCents,
|
||||||
upcomingInvoice && upcomingInvoice.total && !invoiceImmediately
|
paymentAmountNewCurrency: upcomingInvoice.currency,
|
||||||
? upcomingInvoice.total
|
paymentAmountNewInCents: upcomingInvoice.total,
|
||||||
: baseDetails.invoiceTotalOldInCents,
|
paymentProratedCurrency: mockInvoice.currency,
|
||||||
paymentAmountNewCurrency:
|
paymentProratedInCents: mockInvoice.amount_due,
|
||||||
upcomingInvoice && upcomingInvoice.currency && !invoiceImmediately
|
|
||||||
? upcomingInvoice.currency
|
|
||||||
: mockInvoice.currency,
|
|
||||||
paymentAmountNewInCents:
|
|
||||||
upcomingInvoice && upcomingInvoice.total && !invoiceImmediately
|
|
||||||
? upcomingInvoice.total
|
|
||||||
: mockInvoice.total,
|
|
||||||
paymentProratedCurrency:
|
|
||||||
upcomingInvoice && upcomingInvoice.currency && !invoiceImmediately
|
|
||||||
? upcomingInvoice.currency
|
|
||||||
: mockInvoice.currency,
|
|
||||||
paymentProratedInCents:
|
|
||||||
upcomingInvoice && !invoiceImmediately
|
|
||||||
? expectedPaymentProratedInCents
|
|
||||||
: mockInvoice.amount_due,
|
|
||||||
invoiceNumber: mockInvoice.number,
|
invoiceNumber: mockInvoice.number,
|
||||||
invoiceId: mockInvoice.id,
|
invoiceId: mockInvoice.id,
|
||||||
invoiceImmediately: invoiceImmediately,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2300,7 +2300,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
|
||||||
]), {updateTemplateValues: x => (
|
]), {updateTemplateValues: x => (
|
||||||
{...x, discountType: 'repeating', discountDuration: 3})}
|
{...x, discountType: 'repeating', discountDuration: 3})}
|
||||||
],
|
],
|
||||||
// subscription upgrade email for different billing cycle
|
|
||||||
['subscriptionUpgradeEmail', new Map<string, Test | any>([
|
['subscriptionUpgradeEmail', new Map<string, Test | any>([
|
||||||
['subject', { test: 'equal', expected: `You have upgraded to ${MESSAGE.productNameNew}` }],
|
['subject', { test: 'equal', expected: `You have upgraded to ${MESSAGE.productNameNew}` }],
|
||||||
['headers', new Map([
|
['headers', new Map([
|
||||||
|
@ -2333,39 +2333,6 @@ const TESTS: [string, any, Record<string, any>?][] = [
|
||||||
]), {updateTemplateValues: x => (
|
]), {updateTemplateValues: x => (
|
||||||
{...x, productName: MESSAGE.productNameNew })}
|
{...x, productName: MESSAGE.productNameNew })}
|
||||||
],
|
],
|
||||||
// subscription upgrade email for same billing cycle
|
|
||||||
['subscriptionUpgradeEmail', new Map<string, Test | any>([
|
|
||||||
['subject', { test: 'equal', expected: `You have upgraded to ${MESSAGE.productNameNew}` }],
|
|
||||||
['headers', new Map([
|
|
||||||
['X-SES-MESSAGE-TAGS', { test: 'equal', expected: sesMessageTagsHeaderValue('subscriptionUpgrade') }],
|
|
||||||
['X-Template-Name', { test: 'equal', expected: 'subscriptionUpgrade' }],
|
|
||||||
['X-Template-Version', { test: 'equal', expected: TEMPLATE_VERSIONS.subscriptionUpgrade }],
|
|
||||||
])],
|
|
||||||
['html', [
|
|
||||||
{ test: 'include', expected: `You have upgraded to ${MESSAGE.productNameNew}` },
|
|
||||||
{ test: 'include', expected: 'Thank you for upgrading!' },
|
|
||||||
{ test: 'include', expected: decodeUrl(configHref('subscriptionSettingsUrl', 'subscription-upgrade', 'cancel-subscription', 'plan_id', 'product_id', 'uid', 'email')) },
|
|
||||||
{ test: 'include', expected: decodeUrl(configHref('subscriptionTermsUrl', 'subscription-upgrade', 'subscription-terms')) },
|
|
||||||
{ test: 'include', expected: `from ${MESSAGE.productNameOld} to ${MESSAGE.productNameNew}.` },
|
|
||||||
{ test: 'include', expected: `from ${MESSAGE_FORMATTED.paymentAmountOld} per ${MESSAGE.productPaymentCycleOld} to ${MESSAGE_FORMATTED.paymentAmountNew} per ${MESSAGE.productPaymentCycleOld}.` },
|
|
||||||
{ test: 'include', expected: `one-time fee of ${MESSAGE_FORMATTED.paymentProrated} to reflect the higher charge for the remainder of this ${MESSAGE.productPaymentCycleOld}.` },
|
|
||||||
{ test: 'include', expected: `to use ${MESSAGE.productNameNew},` },
|
|
||||||
{ test: 'notInclude', expected: `one-time fee of ${MESSAGE_FORMATTED.paymentProrated} to reflect your subscription’s higher price for the remainder of this ${MESSAGE.productPaymentCycleOld}.` },
|
|
||||||
{ test: 'notInclude', expected: 'utm_source=email' },
|
|
||||||
]],
|
|
||||||
['text', [
|
|
||||||
{ test: 'include', expected: `You have upgraded to ${MESSAGE.productNameNew}` },
|
|
||||||
{ test: 'include', expected: 'Thank you for upgrading!' },
|
|
||||||
{ test: 'include', expected: `from ${MESSAGE.productNameOld} to ${MESSAGE.productNameNew}.` },
|
|
||||||
{ test: 'include', expected: `from ${MESSAGE_FORMATTED.paymentAmountOld} per ${MESSAGE.productPaymentCycleOld} to ${MESSAGE_FORMATTED.paymentAmountNew} per ${MESSAGE.productPaymentCycleOld}.` },
|
|
||||||
{ test: 'include', expected: `one-time fee of ${MESSAGE_FORMATTED.paymentProrated} to reflect the higher charge for the remainder of this ${MESSAGE.productPaymentCycleOld}.` },
|
|
||||||
{ test: 'include', expected: `to use ${MESSAGE.productNameNew},` },
|
|
||||||
{ test: 'notInclude', expected: `one-time fee of ${MESSAGE_FORMATTED.paymentProrated} to reflect your subscription’s higher price for the remainder of this ${MESSAGE.productPaymentCycleOld}.` },
|
|
||||||
{ test: 'notInclude', expected: 'utm_source=email' },
|
|
||||||
]]
|
|
||||||
]), {updateTemplateValues: x => (
|
|
||||||
{...x, productName: MESSAGE.productNameNew, productPaymentCycleNew: MESSAGE.productPaymentCycleOld })}
|
|
||||||
],
|
|
||||||
|
|
||||||
// Template partial specific tests (choose a template containing the partial)
|
// Template partial specific tests (choose a template containing the partial)
|
||||||
['verifyLoginEmail', new Map<string, Test | any>([
|
['verifyLoginEmail', new Map<string, Test | any>([
|
||||||
|
|
|
@ -24,12 +24,6 @@ const conf = convict({
|
||||||
env: 'SUBSCRIPTIONS_STRIPE_TAX_ENABLED',
|
env: 'SUBSCRIPTIONS_STRIPE_TAX_ENABLED',
|
||||||
format: Boolean,
|
format: Boolean,
|
||||||
},
|
},
|
||||||
useStripeInvoiceImmediately: {
|
|
||||||
default: false,
|
|
||||||
doc: 'Enables immediate invoicing for stripe in all subscription upgrades',
|
|
||||||
env: 'SUBSCRIPTIONS_STRIPE_INVOICE_IMMEDIATELY',
|
|
||||||
format: Boolean,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
amplitude: {
|
amplitude: {
|
||||||
enabled: {
|
enabled: {
|
||||||
|
|
|
@ -44,7 +44,6 @@ export const PlanUpgradeDetails = ({
|
||||||
const role = isMobile ? undefined : 'complementary';
|
const role = isMobile ? undefined : 'complementary';
|
||||||
|
|
||||||
const showTax = config.featureFlags.useStripeAutomaticTax;
|
const showTax = config.featureFlags.useStripeAutomaticTax;
|
||||||
const invoiceImmediately = config.featureFlags.useStripeInvoiceImmediately;
|
|
||||||
|
|
||||||
const exclusiveTaxRates =
|
const exclusiveTaxRates =
|
||||||
invoicePreview.tax?.filter(
|
invoicePreview.tax?.filter(
|
||||||
|
@ -77,26 +76,20 @@ export const PlanUpgradeDetails = ({
|
||||||
{showTax && !!subTotal && !!exclusiveTaxRates.length && (
|
{showTax && !!subTotal && !!exclusiveTaxRates.length && (
|
||||||
<>
|
<>
|
||||||
<div className="plan-details-item">
|
<div className="plan-details-item">
|
||||||
{invoiceImmediately ? (
|
<Localized
|
||||||
<Localized
|
id={`sub-update-new-plan-${formattedInterval}`}
|
||||||
id={`sub-update-new-plan-${formattedInterval}`}
|
vars={{
|
||||||
vars={{
|
productName: productDetails.name || product_name,
|
||||||
productName: productDetails.name || product_name,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div data-testid={`sub-update-new-plan-${formattedInterval}`}>
|
||||||
<div>
|
{productDetails.name || product_name} (
|
||||||
{productDetails.name || product_name} (
|
{formattedInterval.replace(/\w/, (firstLetter) =>
|
||||||
{formattedInterval.replace(/\w/, (firstLetter) =>
|
firstLetter.toUpperCase()
|
||||||
firstLetter.toUpperCase()
|
)}
|
||||||
)}
|
)
|
||||||
)
|
</div>
|
||||||
</div>
|
</Localized>
|
||||||
</Localized>
|
|
||||||
) : (
|
|
||||||
<Localized id="plan-details-list-price">
|
|
||||||
<div>List Price</div>
|
|
||||||
</Localized>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<PriceDetails
|
<PriceDetails
|
||||||
total={subTotal}
|
total={subTotal}
|
||||||
|
@ -150,13 +143,18 @@ export const PlanUpgradeDetails = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{invoiceImmediately && oneTimeCharge && (
|
{oneTimeCharge && (
|
||||||
<>
|
<>
|
||||||
<hr className="m-0 my-5 unit-row-hr" />
|
<hr className="m-0 my-5 unit-row-hr" />
|
||||||
|
|
||||||
<div className="plan-details-item font-semibold mt-5">
|
<div className="plan-details-item font-semibold mt-5">
|
||||||
<Localized id="sub-update-prorated-upgrade">
|
<Localized id="sub-update-prorated-upgrade">
|
||||||
<div className="total-label">Prorated Upgrade</div>
|
<div
|
||||||
|
className="total-label"
|
||||||
|
data-testid="sub-update-prorated-upgrade"
|
||||||
|
>
|
||||||
|
Prorated Upgrade
|
||||||
|
</div>
|
||||||
</Localized>
|
</Localized>
|
||||||
|
|
||||||
<PriceDetails
|
<PriceDetails
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
product-plan-change-heading = Review your change
|
product-plan-change-heading = Review your change
|
||||||
sub-change-failed = Plan change failed
|
sub-change-failed = Plan change failed
|
||||||
sub-update-copy =
|
|
||||||
Your plan will change immediately, and you’ll be charged an adjusted
|
|
||||||
amount for the rest of your billing cycle. Starting { $startingDate }
|
|
||||||
you’ll be charged the full amount.
|
|
||||||
sub-update-acknowledgment =
|
sub-update-acknowledgment =
|
||||||
Your plan will change immediately, and you’ll be charged a prorated
|
Your plan will change immediately, and you’ll be charged a prorated
|
||||||
amount today for the rest of this billing cycle. Starting { $startingDate }
|
amount today for the rest of this billing cycle. Starting { $startingDate }
|
||||||
|
|
|
@ -148,7 +148,6 @@ export const Default = storyWithContext({
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
useStripeAutomaticTax: true,
|
useStripeAutomaticTax: true,
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -167,7 +166,6 @@ export const DefaultWithInclusiveTax = storyWithContext({
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
useStripeAutomaticTax: true,
|
useStripeAutomaticTax: true,
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -186,7 +184,6 @@ export const DefaultWithExclusiveTax = storyWithContext({
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
useStripeAutomaticTax: true,
|
useStripeAutomaticTax: true,
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -205,7 +202,6 @@ export const MultipleWithExclusiveTax = storyWithContext({
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
useStripeAutomaticTax: true,
|
useStripeAutomaticTax: true,
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -221,9 +217,6 @@ export const LocalizedToPirate = storyWithContext({
|
||||||
navigatorLanguages: ['xx-pirate'],
|
navigatorLanguages: ['xx-pirate'],
|
||||||
config: {
|
config: {
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -241,9 +234,6 @@ export const Submitting = storyWithContext({
|
||||||
...defaultAppContext,
|
...defaultAppContext,
|
||||||
config: {
|
config: {
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -265,9 +255,6 @@ export const InternalServerError = storyWithContext({
|
||||||
...defaultAppContext,
|
...defaultAppContext,
|
||||||
config: {
|
config: {
|
||||||
...defaultAppContext.config,
|
...defaultAppContext.config,
|
||||||
featureFlags: {
|
|
||||||
useStripeInvoiceImmediately: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,7 +44,7 @@ import {
|
||||||
getLocalizedDateString,
|
getLocalizedDateString,
|
||||||
} from '../../../lib/formats';
|
} from '../../../lib/formats';
|
||||||
import { WebSubscription } from 'fxa-shared/subscriptions/types';
|
import { WebSubscription } from 'fxa-shared/subscriptions/types';
|
||||||
import { config, updateConfig } from '../../../lib/config';
|
import { updateConfig } from '../../../lib/config';
|
||||||
import { deepCopy } from '../../../lib/test-utils';
|
import { deepCopy } from '../../../lib/test-utils';
|
||||||
|
|
||||||
jest.mock('../../../lib/sentry');
|
jest.mock('../../../lib/sentry');
|
||||||
|
@ -100,30 +100,29 @@ async function rendersAsExpected(
|
||||||
expect(queryByTestId('plan-upgrade-subtotal')).not.toBeInTheDocument();
|
expect(queryByTestId('plan-upgrade-subtotal')).not.toBeInTheDocument();
|
||||||
expect(queryByTestId('plan-upgrade-tax-amount')).not.toBeInTheDocument();
|
expect(queryByTestId('plan-upgrade-tax-amount')).not.toBeInTheDocument();
|
||||||
|
|
||||||
if (config.featureFlags.useStripeInvoiceImmediately) {
|
expect(queryByTestId('sub-update-acknowledgment')).toHaveTextContent(
|
||||||
expect(queryByTestId('sub-update-acknowledgment')).toHaveTextContent(
|
expectedInvoiceDate
|
||||||
expectedInvoiceDate
|
);
|
||||||
);
|
|
||||||
|
// <Product Name (Interval)> (e.g. Better Upgrade Product (Monthly))
|
||||||
|
// only appears when there is exclusive tax
|
||||||
|
if (
|
||||||
|
invoicePreview?.subtotal_excluding_tax ||
|
||||||
|
invoicePreview?.total_excluding_tax
|
||||||
|
) {
|
||||||
expect(
|
expect(
|
||||||
queryByTestId(`sub-update-new-plan-${selectedPlan.interval}`)
|
queryByTestId(`sub-update-new-plan-${selectedPlan.interval}`)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (!!invoicePreview.one_time_charge && invoicePreview.one_time_charge > 0) {
|
||||||
!!invoicePreview.one_time_charge &&
|
expect(queryByTestId('sub-update-prorated-upgrade')).toBeInTheDocument();
|
||||||
invoicePreview.one_time_charge > 0
|
expect(queryByTestId('prorated-amount')).toBeInTheDocument();
|
||||||
) {
|
|
||||||
expect(queryByTestId('sub-update-prorated-upgrade')).toBeInTheDocument();
|
|
||||||
expect(queryByTestId('prorated-amount')).toBeInTheDocument();
|
|
||||||
} else {
|
|
||||||
expect(
|
|
||||||
queryByTestId('sub-update-prorated-upgrade')
|
|
||||||
).not.toBeInTheDocument();
|
|
||||||
expect(queryByTestId('prorated-amount')).not.toBeInTheDocument();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
expect(queryByTestId('sub-update-copy')).toHaveTextContent(
|
expect(
|
||||||
expectedInvoiceDate
|
queryByTestId('sub-update-prorated-upgrade')
|
||||||
);
|
).not.toBeInTheDocument();
|
||||||
|
expect(queryByTestId('prorated-amount')).not.toBeInTheDocument();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import React, { useCallback, useEffect, useState, useContext } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { Localized } from '@fluent/react';
|
import { Localized } from '@fluent/react';
|
||||||
|
|
||||||
import { Plan, Customer, Profile } from '../../../store/types';
|
import { Plan, Customer, Profile } from '../../../store/types';
|
||||||
import { SelectorReturns } from '../../../store/selectors';
|
import { SelectorReturns } from '../../../store/selectors';
|
||||||
|
|
||||||
import * as Amplitude from '../../../lib/amplitude';
|
import * as Amplitude from '../../../lib/amplitude';
|
||||||
import AppContext from '../../../lib/AppContext';
|
|
||||||
|
|
||||||
import { getLocalizedDate, getLocalizedDateString } from '../../../lib/formats';
|
import { getLocalizedDate, getLocalizedDateString } from '../../../lib/formats';
|
||||||
import { useCallbackOnce } from '../../../lib/hooks';
|
import { useCallbackOnce } from '../../../lib/hooks';
|
||||||
|
@ -61,7 +60,6 @@ export const SubscriptionUpgrade = ({
|
||||||
resetUpdateSubscriptionPlan,
|
resetUpdateSubscriptionPlan,
|
||||||
discount,
|
discount,
|
||||||
}: SubscriptionUpgradeProps) => {
|
}: SubscriptionUpgradeProps) => {
|
||||||
const { config } = useContext(AppContext);
|
|
||||||
const ariaLabelledBy = 'error-plan-change-failed-header';
|
const ariaLabelledBy = 'error-plan-change-failed-header';
|
||||||
const ariaDescribedBy = 'error-plan-change-failed-description';
|
const ariaDescribedBy = 'error-plan-change-failed-description';
|
||||||
const validator = useValidatorState();
|
const validator = useValidatorState();
|
||||||
|
@ -201,35 +199,21 @@ export const SubscriptionUpgrade = ({
|
||||||
{...{ validator, onSubmit }}
|
{...{ validator, onSubmit }}
|
||||||
>
|
>
|
||||||
<hr className="my-6" />
|
<hr className="my-6" />
|
||||||
{config.featureFlags.useStripeInvoiceImmediately ? (
|
|
||||||
<Localized
|
<Localized
|
||||||
id="sub-update-acknowledgment"
|
id="sub-update-acknowledgment"
|
||||||
vars={{
|
vars={{
|
||||||
startingDate: getLocalizedDate(nextInvoiceDate),
|
startingDate: getLocalizedDate(nextInvoiceDate),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p data-testid="sub-update-acknowledgment">
|
<p data-testid="sub-update-acknowledgment">
|
||||||
Your plan will change immediately, and you’ll be charged a
|
Your plan will change immediately, and you’ll be charged a
|
||||||
prorated amount today for the rest of this billing cycle.
|
prorated amount today for the rest of this billing cycle.
|
||||||
Starting {getLocalizedDateString(nextInvoiceDate)} you’ll be
|
Starting {getLocalizedDateString(nextInvoiceDate)} you’ll be
|
||||||
charged the full amount.
|
charged the full amount.
|
||||||
</p>
|
</p>
|
||||||
</Localized>
|
</Localized>
|
||||||
) : (
|
|
||||||
<Localized
|
|
||||||
id="sub-update-copy"
|
|
||||||
vars={{
|
|
||||||
startingDate: getLocalizedDate(nextInvoiceDate),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p data-testid="sub-update-copy">
|
|
||||||
Your plan will change immediately, and you’ll be charged an
|
|
||||||
adjusted amount for the rest of your billing cycle. Starting
|
|
||||||
{getLocalizedDateString(nextInvoiceDate)} you’ll be charged
|
|
||||||
the full amount.
|
|
||||||
</p>
|
|
||||||
</Localized>
|
|
||||||
)}
|
|
||||||
<hr className="my-6" />
|
<hr className="my-6" />
|
||||||
|
|
||||||
<PaymentConsentCheckbox
|
<PaymentConsentCheckbox
|
||||||
|
|
|
@ -82,9 +82,6 @@ export type StripeHelperConfig = {
|
||||||
productConfigsFirestore: {
|
productConfigsFirestore: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
stripeInvoiceImmediately: {
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
authFirestore: {
|
authFirestore: {
|
||||||
prefix: string;
|
prefix: string;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче