@ph-dev-utils/payroll
v0.3.0
Published
Filipino payroll calculators — SSS, PhilHealth, Pag-IBIG, 13th-month, BIR withholding tax for all 4 payroll periods (TRAIN), de minimis caps (RR 29-2025), ₱90k 13th-month/bonus exemption, net take-home. Versioned tables.
Downloads
231
Maintainers
Readme
@ph-dev-utils/payroll
Filipino payroll calculators for JavaScript / TypeScript — SSS, PhilHealth, Pag-IBIG monthly contributions, 13th-month pay, BIR monthly withholding tax (TRAIN), and pre-tax / post-tax net take-home. Tables versioned by effective date.
⚠️ READ FIRST
This package handles real money math. Wrong outputs cause underpayment / overpayment in actual payroll runs. Verify outputs against official agency calculators before production use. Pin your dependency version — tables change every few years. BIR WT in v0.2 is monthly-only and operates on taxable income, not raw gross. De minimis caps and the ₱90k 13th-month annual exemption are caller-handled in v0.2.
Full disclaimer: project README.
Install
npm install @ph-dev-utils/payrollRequires Node 20+.
Quick start
import { netTakeHome } from '@ph-dev-utils/payroll';
netTakeHome(30000);
// pre-tax (v0.1 shape — unchanged):
// {
// gross: 30000,
// sss: { msc: 30000, employeeShare: 1500, employerShare: 3030, ... },
// philHealth: { total: 1500, employee: 750, employer: 750 },
// pagIbig: { mfs: 10000, employee: 200, employer: 200, total: 400 },
// totalDeductions: 2450,
// net: 27550
// }
netTakeHome(30000, { includeWT: true });
// adds: taxableIncome: 27550, withholdingTax: 1007.55, netAfterTax: 26542.45API Reference
sssContribution(monthlyCompensation, opts?)
Source: SSS Circular 2024-006 (effective 2025-01-01, applies to 2025–2026).
function sssContribution(
monthlyCompensation: number,
opts?: { year?: number }
): SSSContribution
interface SSSContribution {
msc: number; // Monthly Salary Credit (₱500 increments, clamped 5,000–35,000)
regular: { employee: number; employer: number; total: number }; // MSC capped at 20,000
mpf: { employee: number; employer: number; total: number }; // MySSS Pension Booster — portion above 20,000
ec: number; // Employees' Compensation (employer-paid)
employeeShare: number; // regular.employee + mpf.employee
employerShare: number; // regular.employer + mpf.employer + ec
total: number;
}Math: Total rate 15% = 10% employer + 5% employee. EC is ₱10 if MSC ≤ 14,500, ₱30 if MSC ≥ 15,000.
sssContribution(20000);
// { msc: 20000, regular: { employee: 1000, employer: 2000, total: 3000 },
// mpf: { employee: 0, employer: 0, total: 0 }, ec: 30,
// employeeShare: 1000, employerShare: 2030, total: 3030 }
sssContribution(30000);
// MPF kicks in: regular at MSC=20k, MPF at MSC=10k
// { msc: 30000, regular: { employee: 1000, ... }, mpf: { employee: 500, employer: 1000, total: 1500 },
// ec: 30, employeeShare: 1500, employerShare: 3030, total: 4530 }
sssContribution(50000).msc; // 35000 (capped at MSC max)
sssContribution(3000).msc; // 5000 (floored at MSC min)philHealthContribution(monthlySalary, opts?)
Source: RA 11223 (UHC Law) final 5% rate (applies 2024+; no further increases scheduled).
function philHealthContribution(
monthlySalary: number,
opts?: { year?: number }
): { total: number; employee: number; employer: number }Math: 5% of monthly salary, split equally (2.5% each). Floor ₱500 total (salary ≤ ₱10,000). Cap ₱5,000 total (salary ≥ ₱100,000). Each share is rounded to the centavo; the remitted total is the sum of remitted shares (real-payroll convention).
philHealthContribution(25000); // { total: 1250, employee: 625, employer: 625 }
philHealthContribution(5000); // floor: { total: 500, employee: 250, employer: 250 }
philHealthContribution(150000); // ceiling: { total: 5000, employee: 2500, employer: 2500 }pagIbigContribution(monthlySalary, opts?)
Source: HDMF Circular 460 (effective 2024-02-01). MFS ceiling raised from ₱5,000 to ₱10,000 in Feb 2024.
function pagIbigContribution(
monthlySalary: number,
opts?: { year?: number }
): { mfs: number; employee: number; employer: number; total: number }Math:
- MFS = min(monthlySalary, 10,000)
- If MFS ≤ ₱1,500: employee 1%, employer 2%
- If MFS > ₱1,500: employee 2%, employer 2%
- Max ₱200 each side (at MFS = ₱10,000)
Mandatory contribution only. Voluntary higher contributions are not computed here.
pagIbigContribution(1000); // low bracket: { mfs: 1000, employee: 10, employer: 20, total: 30 }
pagIbigContribution(5000); // high bracket: { mfs: 5000, employee: 100, employer: 100, total: 200 }
pagIbigContribution(50000); // MFS capped: { mfs: 10000, employee: 200, employer: 200, total: 400 }thirteenthMonthPay(totalBasicEarnings) / thirteenthMonthFromMonthly(monthlyBasicSalary, monthsWorked?)
Source: PD 851 (Presidential Decree on 13th-month pay).
function thirteenthMonthPay(totalBasicEarnings: number): number
function thirteenthMonthFromMonthly(monthlyBasicSalary: number, monthsWorked?: number): numberMath: DOLE canonical formula is totalBasicEarnings / 12. The "proration" for partial-year employees is captured by passing actual earnings — not by post-multiplying.
thirteenthMonthFromMonthly is a convenience for fixed-salary employees: equivalent to thirteenthMonthPay(monthlyBasicSalary × monthsWorked).
"Basic earnings" excludes allowances, overtime, holiday pay, night-shift differential, and other non-base monetary benefits. Caller is responsible for passing the right figure.
thirteenthMonthPay(360000); // 30000 (full year @ 30k/month)
thirteenthMonthPay(180000); // 15000 (6 months @ 30k/month)
thirteenthMonthFromMonthly(30000); // 30000 (defaults to 12 months)
thirteenthMonthFromMonthly(30000, 6); // 15000
thirteenthMonthFromMonthly(30000, 1); // 2500withholdingTaxMonthly(taxableIncome, opts?) (v0.2+)
Source: BIR RR 11-2018 monthly table (TRAIN Law, second-phase rates effective 2023-01-01).
function withholdingTaxMonthly(
taxableIncome: number,
opts?: { year?: number }
): { wt: number; bracket: 1|2|3|4|5|6; marginalRate: number }Input is taxable income, not gross. To derive taxable income from gross, use taxableIncomeMonthly (or compute manually as gross − mandatories − nonTaxableAllowances).
BIR monthly brackets (TRAIN 2023+):
| Bracket | Monthly taxable income | Tax | | --- | --- | --- | | 1 | ≤ ₱20,833 | 0 | | 2 | ₱20,833 – ₱33,333 | 15% of excess over ₱20,833 | | 3 | ₱33,333 – ₱66,667 | ₱1,875 + 20% of excess over ₱33,333 | | 4 | ₱66,667 – ₱166,667 | ₱8,541.80 + 25% of excess over ₱66,667 | | 5 | ₱166,667 – ₱666,667 | ₱33,541.80 + 30% of excess over ₱166,667 | | 6 | > ₱666,667 | ₱183,541.80 + 35% of excess over ₱666,667 |
withholdingTaxMonthly(15000); // { wt: 0, bracket: 1, marginalRate: 0 }
withholdingTaxMonthly(25000); // { wt: 625.05, bracket: 2, marginalRate: 0.15 }
withholdingTaxMonthly(50000); // { wt: 5208.4, bracket: 3, marginalRate: 0.20 }
withholdingTaxMonthly(100000); // { wt: 16875.05, bracket: 4, marginalRate: 0.25 }
withholdingTaxMonthly(200000); // { wt: 43541.7, bracket: 5, marginalRate: 0.30 }
withholdingTaxMonthly(1000000); // { wt: 300208.35, bracket: 6, marginalRate: 0.35 }taxableIncomeMonthly(gross, opts?) (v0.2+)
Derive monthly taxable income from gross. Formula:
taxableIncome = gross − mandatoryDeductions − nonTaxableAllowancesfunction taxableIncomeMonthly(
gross: number,
opts?: {
year?: number;
mandatoryDeductions?: number; // override SSS+PH+Pag-IBIG; default: auto-computed
nonTaxableAllowances?: number; // de minimis within caps + tax-exempt allowances; default: 0
}
): numberCaller-handled (not done here):
- De minimis caps — pass only the non-taxable portion of each benefit. Rice subsidy is exempt up to ₱2,000/mo, uniform ₱6,000/yr, medical ₱10,000/yr (employee), etc. If a benefit exceeds its cap, the excess is taxable and should NOT appear in
nonTaxableAllowances. - 13th-month / bonus ₱90k annual exemption — applies at year level, not month. Excess goes in
grossfor the month received.
taxableIncomeMonthly(30000); // 27550 (auto: gross − 2,450 mandatories − 0)
taxableIncomeMonthly(30000, { nonTaxableAllowances: 2000 }); // 25550
taxableIncomeMonthly(50000, { mandatoryDeductions: 3200 }); // 46800netTakeHome(monthlySalary, opts?)
Net take-home: gross salary minus mandatory SSS / PhilHealth / Pag-IBIG employee contributions, and (optionally, v0.2+) BIR withholding tax.
function netTakeHome(
monthlySalary: number,
opts?: {
year?: number;
includeWT?: boolean; // v0.2+: also compute WT and netAfterTax
nonTaxableAllowances?: number; // v0.2+: only used when includeWT is true
}
): NetTakeHome
interface NetTakeHome {
gross: number;
sss: SSSContribution;
philHealth: PhilHealthContribution;
pagIbig: PagIbigContribution;
totalDeductions: number; // sum of employee shares
net: number; // gross - totalDeductions (pre-tax)
taxableIncome?: number; // populated only when includeWT
withholdingTax?: number; // populated only when includeWT
netAfterTax?: number; // populated only when includeWT
}By default (no includeWT), this preserves the v0.1 pre-tax shape. The includeWT: true opt-in adds three fields without removing any.
// v0.1 shape (default) — pre-tax:
netTakeHome(30000).net; // 27550
netTakeHome(8000).net; // 7190
netTakeHome(150000).net; // 145550
// v0.2+ with WT:
netTakeHome(30000, { includeWT: true }).withholdingTax; // 1007.55
netTakeHome(30000, { includeWT: true }).netAfterTax; // 26542.45
// v0.2+ with non-taxable allowances:
netTakeHome(30000, { includeWT: true, nonTaxableAllowances: 2000 }).withholdingTax;
// 707.55 (lower because TI dropped from 27,550 → 25,550)Versioning & rate changes
Tables change. The package follows this contract:
- Within a major+minor version (e.g.
0.1.x), tables won't change. - A new table ships in a new minor version (
0.2.0). - Old tables stay accessible via the
{ year }option until a major version cleanup.
Pin your dependency to a specific minor (~0.1.0 or 0.1.x) to avoid silent table changes during your payroll cycle.
When a circular drops, file an issue with the source link — patches ship quickly.
License
MIT
