@automattic/wpcom-checkout
Version:
Functions and components used by WordPress.com checkout.
406 lines • 17.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTotalLineItemFromCart = getTotalLineItemFromCart;
exports.getCouponLineItemFromCart = getCouponLineItemFromCart;
exports.getTaxLineItemFromCart = getTaxLineItemFromCart;
exports.getTaxBreakdownLineItemsFromCart = getTaxBreakdownLineItemsFromCart;
exports.getCreditsLineItemFromCart = getCreditsLineItemFromCart;
exports.isUserVisibleCostOverride = isUserVisibleCostOverride;
exports.doesIntroductoryOfferHaveDifferentTermLengthThanProduct = doesIntroductoryOfferHaveDifferentTermLengthThanProduct;
exports.doesIntroductoryOfferHavePriceIncrease = doesIntroductoryOfferHavePriceIncrease;
exports.filterCostOverridesForLineItem = filterCostOverridesForLineItem;
exports.getSubtotalWithoutDiscountsForProduct = getSubtotalWithoutDiscountsForProduct;
exports.getSubtotalWithoutDiscounts = getSubtotalWithoutDiscounts;
exports.getTotalDiscountsWithoutCredits = getTotalDiscountsWithoutCredits;
exports.doesPurchaseHaveFullCredits = doesPurchaseHaveFullCredits;
exports.isOverrideCodeIntroductoryOffer = isOverrideCodeIntroductoryOffer;
exports.isBillingInfoEmpty = isBillingInfoEmpty;
const number_formatters_1 = require("@automattic/number-formatters");
const i18n_calypso_1 = require("i18n-calypso");
const get_contact_details_type_1 = require("./get-contact-details-type");
const introductory_offer_1 = require("./introductory-offer");
function getTotalLineItemFromCart(cart) {
return {
id: 'total',
type: 'total',
// translators: The label of the total line item in checkout
label: String((0, i18n_calypso_1.translate)('Total')),
formattedAmount: (0, number_formatters_1.formatCurrency)(cart.total_cost_integer, cart.currency, {
isSmallestUnit: true,
stripZeros: true,
}),
};
}
function getCouponLineItemFromCart(responseCart) {
if (!responseCart.coupon || !responseCart.coupon_savings_total_integer) {
return null;
}
return {
id: 'coupon-line-item',
hasDeleteButton: !responseCart.has_auto_renew_coupon_been_automatically_applied,
// translators: The label of the coupon line item in checkout, including the coupon code
label: String((0, i18n_calypso_1.translate)('Coupon: %(couponCode)s', { args: { couponCode: responseCart.coupon } })),
type: 'coupon',
// translators: The displayed discount of the coupon line item in checkout
formattedAmount: String((0, i18n_calypso_1.translate)('- %(discountAmount)s', {
args: {
discountAmount: (0, number_formatters_1.formatCurrency)(responseCart.coupon_savings_total_integer, responseCart.currency, { isSmallestUnit: true, stripZeros: true }),
},
})),
};
}
function getTaxLineItemFromCart(responseCart) {
if (!responseCart.tax.display_taxes) {
return null;
}
return {
id: 'tax-line-item',
// translators: The label of the taxes line item in checkout
label: String((0, i18n_calypso_1.translate)('Tax', {
context: "Shortened form of 'Sales Tax', not a country-specific tax name",
})),
type: 'tax',
formattedAmount: (0, number_formatters_1.formatCurrency)(responseCart.total_tax_integer, responseCart.currency, {
isSmallestUnit: true,
stripZeros: true,
}),
};
}
function getTaxBreakdownLineItemsFromCart(responseCart) {
if (!responseCart.tax.display_taxes) {
return [];
}
if (!Array.isArray(responseCart.total_tax_breakdown) ||
responseCart.total_tax_breakdown.length === 0) {
const lineItem = getTaxLineItemFromCart(responseCart);
return lineItem ? [lineItem] : [];
}
return responseCart.total_tax_breakdown.map((taxBreakdownItem) => {
const id = `tax-line-item-${taxBreakdownItem.label ?? taxBreakdownItem.rate}`;
const label = taxBreakdownItem.label
? `${taxBreakdownItem.label} (${taxBreakdownItem.rate_display})`
: String((0, i18n_calypso_1.translate)('Tax', {
context: "Shortened form of 'Sales Tax', not a country-specific tax name",
}));
return {
id,
label,
type: 'tax',
formattedAmount: (0, number_formatters_1.formatCurrency)(taxBreakdownItem.tax_collected_integer, responseCart.currency, {
isSmallestUnit: true,
stripZeros: true,
}),
};
});
}
function getCreditsLineItemFromCart(responseCart) {
const credits = getCreditsUsedByCart(responseCart);
if (credits === 0) {
return null;
}
return {
id: 'credits',
// translators: The label of the credits line item in checkout
label: String((0, i18n_calypso_1.translate)('Credits')),
type: 'credits',
// translators: The discount amount of the credits line item in checkout
formattedAmount: String((0, i18n_calypso_1.translate)('- %(discountAmount)s', {
args: {
discountAmount: (0, number_formatters_1.formatCurrency)(credits, responseCart.currency, {
isSmallestUnit: true,
stripZeros: true,
}),
},
})),
};
}
function getDiscountReasonForIntroductoryOffer(product, terms, translate, allowFreeText, isPriceIncrease, isStreamlinedPrice) {
return (0, introductory_offer_1.getIntroductoryOfferIntervalDisplay)({
translate,
intervalUnit: terms.interval_unit,
intervalCount: terms.interval_count,
isFreeTrial: product.item_subtotal_integer === 0 && allowFreeText,
isPriceIncrease,
context: 'checkout',
remainingRenewalsUsingOffer: terms.transition_after_renewal_count,
isStreamlinedPrice,
});
}
function isUserVisibleCostOverride(costOverride) {
if (costOverride.does_override_original_cost) {
// We won't display original cost overrides since they are
// included in the original cost that's being displayed. They
// are not discounts.
return false;
}
return true;
}
function makeSaleCostOverrideUnique(costOverride, product, translate) {
// Separate out Sale Coupons because we want to show the name
// of each item on sale.
if (costOverride.override_code === 'sale-coupon-discount-1') {
return {
...costOverride,
human_readable_reason: translate('Sale: %(productName)s', {
textOnly: true,
args: { productName: product.product_name },
}),
};
}
return costOverride;
}
/**
* Replace introductory offer cost override text with wording specific to that
* offer, like "Discount for first 3 months" instead of "Introductory offer".
*/
function makeIntroductoryOfferCostOverrideUnique(costOverride, product, translate, allowFreeText, isStreamlinedPrice) {
if ('introductory-offer' !== costOverride.override_code || !product.introductory_offer_terms) {
return costOverride;
}
const isPriceIncrease = costOverride.old_subtotal_integer < costOverride.new_subtotal_integer;
// Renewals get generic text because an introductory offer manual renewal
// can be hard to explain simply and saying "Discount for first 3 months"
// may not be accurate.
if (product.is_renewal) {
return {
...costOverride,
human_readable_reason: isPriceIncrease
? translate('Prorated renewal')
: translate('Prorated renewal discount'),
};
}
return {
...costOverride,
human_readable_reason: getDiscountReasonForIntroductoryOffer(product, product.introductory_offer_terms, translate, allowFreeText, isPriceIncrease, isStreamlinedPrice),
};
}
function getDiscountForCostOverrideForDisplay(costOverride) {
return costOverride.old_subtotal_integer - costOverride.new_subtotal_integer;
}
function getBillPeriodMonthsForIntroductoryOfferInterval(interval) {
switch (interval) {
case 'month':
return 1;
case 'year':
return 12;
default:
return 0;
}
}
/**
* Returns true if the product has an introductory offer which is for a
* different term length than the term length of the product (eg: a 3 month
* discount for an annual plan).
*/
function doesIntroductoryOfferHaveDifferentTermLengthThanProduct(costOverrides, introductoryOfferTerms, monthsPerBillPeriodForProduct) {
if (costOverrides?.some((costOverride) => {
!isOverrideCodeIntroductoryOffer(costOverride.override_code);
})) {
return false;
}
if (!introductoryOfferTerms?.enabled) {
return false;
}
if (getBillPeriodMonthsForIntroductoryOfferInterval(introductoryOfferTerms.interval_unit) ===
monthsPerBillPeriodForProduct) {
return false;
}
return true;
}
function doesIntroductoryOfferCostOverrideHavePriceIncrease(product, costOverride) {
if (!isOverrideCodeIntroductoryOffer(costOverride.override_code)) {
return false;
}
if (!product.introductory_offer_terms?.enabled) {
return false;
}
const priceBeforeOverride = costOverride.old_subtotal_integer;
const priceAfterOverride = costOverride.new_subtotal_integer;
const monthsForProductBeforeOverride = product.months_per_bill_period ?? 1;
const priceBeforeOverrideMonthly = priceBeforeOverride / monthsForProductBeforeOverride;
const monthsForProductAfterOverride = getBillPeriodMonthsForIntroductoryOfferInterval(product.introductory_offer_terms.interval_unit);
const priceAfterOverrideMonthly = priceAfterOverride / monthsForProductAfterOverride;
// If you will pay less per month for the subscription in the cart with the
// offer than without the offer, this is not a price increase.
if (priceBeforeOverrideMonthly >= priceAfterOverrideMonthly) {
return false;
}
return true;
}
function doesIntroductoryOfferHavePriceIncrease(product) {
if (!product.introductory_offer_terms?.enabled) {
return false;
}
const hasIntroOfferOverrideWithPriceIncrease = product.cost_overrides?.some((override) => doesIntroductoryOfferCostOverrideHavePriceIncrease(product, override));
if (!hasIntroOfferOverrideWithPriceIncrease) {
return false;
}
return true;
}
function filterCostOverridesForLineItem(product, translate, isStreamlinedPrice) {
const costOverrides = product?.cost_overrides ?? [];
return (costOverrides
.filter((costOverride) => isUserVisibleCostOverride(costOverride))
// Hide coupon overrides because they will be displayed separately.
.filter((costOverride) => costOverride.override_code !== 'coupon-discount')
.map((costOverride) => makeSaleCostOverrideUnique(costOverride, product, translate))
.map((costOverride) => makeIntroductoryOfferCostOverrideUnique(costOverride, product, translate, true, isStreamlinedPrice))
.map((costOverride) => {
// Introductory offers which are renewals may have a prorated
// discount amount which is hard to display as a simple
// discount, so we will hide the discounted amount here.
if (costOverride.override_code === 'introductory-offer' && product.is_renewal) {
return {
humanReadableReason: costOverride.human_readable_reason,
overrideCode: costOverride.override_code,
};
}
// Introductory offer discounts with term lengths that differ from
// the term length of the product (eg: a 3 month discount for an
// annual plan) need to be displayed differently because the
// discount is only temporary and the user will still be charged
// the remainder before the next renewal.
if (isOverrideCodeIntroductoryOffer(costOverride.override_code) &&
doesIntroductoryOfferHaveDifferentTermLengthThanProduct(product.cost_overrides, product.introductory_offer_terms, product.months_per_bill_period)) {
return {
humanReadableReason: costOverride.human_readable_reason,
overrideCode: costOverride.override_code,
};
}
// Introductory offer discounts which are price increases
// should have their full details displayed because displaying
// them as a simple price change can be confusing. We therefore
// hide the price change amount.
if (doesIntroductoryOfferHavePriceIncrease(product)) {
return {
humanReadableReason: costOverride.human_readable_reason,
overrideCode: costOverride.override_code,
};
}
return {
humanReadableReason: costOverride.human_readable_reason,
overrideCode: costOverride.override_code,
discountAmount: getDiscountForCostOverrideForDisplay(costOverride),
};
}));
}
/**
* Even though a user might have a number of credits available, that number may
* be greater than the cart's total. This function returns the number of
* credits actually being used by a cart.
*/
function getCreditsUsedByCart(responseCart) {
if (responseCart.credits_integer <= 0) {
return 0;
}
const isFullCredits = doesPurchaseHaveFullCredits(responseCart);
return isFullCredits ? responseCart.sub_total_with_taxes_integer : responseCart.credits_integer;
}
/**
* Return a shopping-cart line item's cost before any discounts are applied.
*
* Note that this function returns the cost in the currency's smallest unit.
*/
function getSubtotalWithoutDiscountsForProduct(product) {
// Return the last original cost override's new price.
const originalCostOverrides = product.cost_overrides?.filter((override) => override.does_override_original_cost) ?? [];
if (originalCostOverrides.length > 0) {
const lastOriginalCostOverride = originalCostOverrides.pop();
if (lastOriginalCostOverride) {
return lastOriginalCostOverride.new_subtotal_integer;
}
}
// If there is an introductory offer override that increases the price,
// consider that part of the base price because it's confusing to show
// "Subtotal before discounts" as lower than the "Subtotal". The details of
// the price increase will be displayed elsewhere.
if (doesIntroductoryOfferHavePriceIncrease(product)) {
const introOffer = product.cost_overrides?.find((offer) => isOverrideCodeIntroductoryOffer(offer.override_code));
if (introOffer) {
return introOffer.new_subtotal_integer;
}
}
// If there are no original cost overrides, return the first cost override's
// old price.
if (product.cost_overrides && product.cost_overrides.length > 0) {
const firstOverride = product.cost_overrides[0];
if (firstOverride) {
return firstOverride.old_subtotal_integer;
}
}
// If there are no cost overrides, return the item's cost, since it has no
// discounts.
return product.item_subtotal_integer;
}
/**
* Return the shopping-cart subtotal before any discounts have been applied to
* any cart item.
*
* This does not include credits which are not a discount; they reduce the
* final price after taxes.
*
* Note that this function returns the cost in the currency's smallest unit.
*/
function getSubtotalWithoutDiscounts(responseCart) {
return responseCart.products.reduce((total, product) => {
return total + getSubtotalWithoutDiscountsForProduct(product);
}, 0);
}
/**
* Return the total savings from shopping-cart item discounts.
*
* This does not include credits which are not a discount; they reduce the
* final price after taxes.
*
* Note that this function returns the cost in the currency's smallest unit.
*/
function getTotalDiscountsWithoutCredits(responseCart) {
const filteredOverrides = responseCart.products.reduce((overrides, product) => {
product.cost_overrides.forEach((override) => {
if (override.does_override_original_cost) {
return;
}
if (doesIntroductoryOfferCostOverrideHavePriceIncrease(product, override)) {
return;
}
overrides.push(override);
});
return overrides;
}, []);
return -filteredOverrides.reduce((total, override) => {
total = total + (override.old_subtotal_integer - override.new_subtotal_integer);
return total;
}, 0);
}
function doesPurchaseHaveFullCredits(cart) {
const credits = cart.credits_integer;
const subtotal = cart.sub_total_integer;
const taxes = cart.total_tax_integer;
const totalBeforeCredits = subtotal + taxes;
return credits > 0 && totalBeforeCredits > 0 && credits >= totalBeforeCredits;
}
function isOverrideCodeIntroductoryOffer(overrideCode) {
switch (overrideCode) {
case 'introductory-offer':
return true;
case 'prorated-introductory-offer':
return true;
case 'quantity-upgrade-introductory-offer':
return true;
}
return false;
}
/**
* True if the billing/contact info is not filled in on a shopping cart (and it
* needs to be filled in).
*/
function isBillingInfoEmpty(responseCart) {
if ((0, get_contact_details_type_1.getContactDetailsType)(responseCart) === 'none') {
return false;
}
if (responseCart.tax.location.country_code) {
return false;
}
return true;
}
//# sourceMappingURL=transformations.js.map