Pure cart discount math for ecommerce. Percent and fixed discounts, per-item distribution by revenue share, and total computation. All amounts in cents (integer math, no floating point).
npm install @arraypress/discount-calculatorimport { computeCartTotals, distributeDiscountPerItem } from '@arraypress/discount-calculator';
// Compute cart total with a 20% discount + tax
const totals = computeCartTotals(
{ subtotal: 10000 }, // $100.00
{
discount: { id: 1, code: 'SAVE20', type: 'percent', percent_off: 20 },
taxAmount: 500,
shippingAmount: 800,
}
);
// totals.total === 9300 ($80.00 + $5.00 tax + $8.00 shipping)
// Distribute the discount across line items
const cart = {
lineItems: [
{ unitAmount: 7000, quantity: 1, isAddon: false, discountAmount: 0 },
{ unitAmount: 3000, quantity: 1, isAddon: false, discountAmount: 0 },
],
};
distributeDiscountPerItem(cart, totals.discountAmount);
// lineItems[0].discountAmount === 1400 (70% share)
// lineItems[1].discountAmount === 600 (30% share)Compute the final cart totals. Pure function.
Parameters:
cart.subtotal— Cart subtotal in centsoptions.discount— Discount object withid,code,type, andpercent_offoramount_offoptions.taxAmount— Tax in cents (default: 0)options.shippingAmount— Shipping in cents (default: 0)
Returns CartTotals:
subtotal— Original subtotaldiscountAmount— Computed discountdiscountId— Discount ID ornulldiscountCode— Discount code or''taxAmount,shippingAmount— Pass-throughtotal— Final total (minimum 0)
Distribute a discount across line items proportionally by revenue share. Mutates each line's discountAmount in place.
- Addon lines (
isAddon: true) are excluded - The last eligible line absorbs any rounding remainder
- Resets all
discountAmountvalues to 0 before distributing
Percent: discountAmount = Math.round(subtotal * percent_off / 100)
Fixed: discountAmount = Math.min(amount_off, subtotal) — capped at subtotal so the total never goes negative.
MIT