How the China 税后工资 (take-home pay) calculator is verified
Inputs
| Input | Field | Type |
|---|---|---|
| Gross monthly salary (CNY) | grossMonthly | number |
| Housing fund rate, % (5 to 12) | housingFundRate | number |
| Children you claim for (education or under-3 care) | children | number |
| Supporting a parent aged 60 or over | elderlySupport | string |
| Housing deduction | housing | string |
| Degree continuing education (400 a month) | continuingEducation | boolean |
The formula
Each derived value below is computed by the Valem engine from the expression shown, in this order. Nothing else runs — there is no hidden code path.
| Derived value | Field | Formula (JSONata) |
|---|---|---|
| Contribution base (social insurance) | siBase | ($g := $n(grossMonthly); $g <= 0 ? 0 : $clampFloorCap($g, $const.siBaseFloor, $const.siBaseCap)) |
| Pension 8% / month | pensionMonthly | $round(siBase * $const.pensionRate, 2) |
| Medical 2% + 3 / month | medicalMonthly | $round(siBase > 0 ? siBase * $const.medicalRate + $const.medicalMutualAidMonthly : 0, 2) |
| Unemployment 0.5% / month | unemploymentMonthly | $round(siBase * $const.unemploymentRate, 2) |
| Housing fund / month | housingFundMonthly | ($g := $n(grossMonthly); $b := $g <= 0 ? 0 : $clampFloorCap($g, $const.hfBaseFloor, $const.hfBaseCap); $floor($b * $clampFloorCap($n(housingFundRate) / 100, $const.hfRateMin, $const.hfRateMax) + 0.5)) |
| Your contributions / month | contributionsMonthly | $round(pensionMonthly + medicalMonthly + unemploymentMonthly + housingFundMonthly, 2) |
| Special additional deductions / month | specialAdditionalMonthly | $floor($n(children)) * $const.childDeductionMonthly + (elderlySupport = 'onlyChild' ? $const.elderlyOnlyChildMonthly : (elderlySupport = 'shared' ? $const.elderlySharedMaxMonthly : 0)) + (housing = 'rent' ? $const.housingRentMonthly : (housing = 'loan' ? $const.housingLoanMonthly : 0)) + (continuingEducation ? $const.continuingEducationMonthly : 0) |
| Taxable income for the year | taxableAnnual | ($v := $n(grossMonthly) * 12 - contributionsMonthly * 12 - $const.basicDeductionAnnual - specialAdditionalMonthly * 12; $round($v > 0 ? $v : 0, 2)) |
| Individual income tax for the year | iitAnnual | $round($bandedCharge(taxableAnnual, [{"upTo": $const.iitBand1, "rate": $const.iitRate1}, {"upTo": $const.iitBand2, "rate": $const.iitRate2}, {"upTo": $const.iitBand3, "rate": $const.iitRate3}, {"upTo": $const.iitBand4, "rate": $const.iitRate4}, {"upTo": $const.iitBand5, "rate": $const.iitRate5}, {"upTo": $const.iitBand6, "rate": $const.iitRate6}, {"upTo": null, "rate": $const.iitRate7}]), 2) |
| Net / year | netAnnual | $round($max([0, $n(grossMonthly) * 12 - contributionsMonthly * 12 - iitAnnual]), 2) |
| Net / month | netMonthly | $round(netAnnual / 12, 2) |
Rates and thresholds
The expressions above read these as $const. They are the figures the annual refresh replaces, so they live as data rather than being written into the formulas — which is what makes a year-on-year change a one-line diff instead of an edit to arithmetic.
| Constant | Value |
|---|---|
basicDeductionAnnual | 60000 |
iitBand1 | 36000 |
iitBand2 | 144000 |
iitBand3 | 300000 |
iitBand4 | 420000 |
iitBand5 | 660000 |
iitBand6 | 960000 |
iitRate1 | 0.03 |
iitRate2 | 0.1 |
iitRate3 | 0.2 |
iitRate4 | 0.25 |
iitRate5 | 0.3 |
iitRate6 | 0.35 |
iitRate7 | 0.45 |
childDeductionMonthly | 2000 |
elderlyOnlyChildMonthly | 3000 |
elderlySharedMaxMonthly | 1500 |
housingRentMonthly | 1500 |
housingLoanMonthly | 1000 |
continuingEducationMonthly | 400 |
siBaseFloor | 7270 |
siBaseCap | 36348 |
pensionRate | 0.08 |
medicalRate | 0.02 |
medicalMutualAidMonthly | 3 |
unemploymentRate | 0.005 |
hfBaseFloor | 2540 |
hfBaseCap | 36348 |
hfRateMin | 0.05 |
hfRateMax | 0.12 |
Test vectors (7)
Every case is executed against this model on each build, by the Java engine and independently by a JavaScript one. A mismatch of a single cent fails the build, so the page cannot ship advertising a figure the model no longer produces.
Case 1 — C1 20,000 a month, 12% housing fund, no deductions — 10,076.40 tax for the year
given grossMonthly | 20000 |
|---|---|
given housingFundRate | 12 |
given children | 0 |
given elderlySupport | none |
expect pensionMonthly | 1600 |
expect medicalMonthly | 403 |
expect unemploymentMonthly | 100 |
expect housingFundMonthly | 2400 |
expect contributionsMonthly | 4503 |
expect taxableAnnual | 125964 |
expect iitAnnual | 10076.4 |
expect netAnnual | 175887.6 |
expect netMonthly | 14657.3 |
Case 2 — C2 one child and an only child supporting a parent: 5,000 a month off
given grossMonthly | 20000 |
|---|---|
given housingFundRate | 12 |
given children | 1 |
given elderlySupport | onlyChild |
expect specialAdditionalMonthly | 5000 |
expect taxableAnnual | 65964 |
expect iitAnnual | 4076.4 |
expect netAnnual | 181887.6 |
Case 3 — C3 50,000 a month: both bases cap at 36,348, housing fund 4,362 as the notice’s table
given grossMonthly | 50000 |
|---|---|
given housingFundRate | 12 |
given children | 0 |
given elderlySupport | none |
expect siBase | 36348 |
expect pensionMonthly | 2907.84 |
expect medicalMonthly | 729.96 |
expect unemploymentMonthly | 181.74 |
expect housingFundMonthly | 4362 |
Case 4 — C4 5,000 a month: the social-insurance base floors at 7,270 and no tax is due
given grossMonthly | 5000 |
|---|---|
given housingFundRate | 12 |
given children | 0 |
given elderlySupport | none |
expect siBase | 7270 |
expect pensionMonthly | 581.6 |
expect medicalMonthly | 148.4 |
expect unemploymentMonthly | 36.35 |
expect housingFundMonthly | 600 |
expect taxableAnnual | 0 |
expect iitAnnual | 0 |
Case 5 — C5 housing fund at the 5% minimum
given grossMonthly | 20000 |
|---|---|
given housingFundRate | 5 |
given children | 0 |
given elderlySupport | none |
expect housingFundMonthly | 1000 |
expect contributionsMonthly | 3103 |
Case 6 — C6 sibling share of elderly support counts 1,500
given grossMonthly | 20000 |
|---|---|
given housingFundRate | 12 |
given children | 0 |
given elderlySupport | shared |
expect specialAdditionalMonthly | 1500 |
Case 7 — C7 rent in Beijing plus degree study: 1,900 a month off, 7,796.40 tax
given grossMonthly | 20000 |
|---|---|
given housingFundRate | 12 |
given children | 0 |
given elderlySupport | none |
given housing | rent |
given continuingEducation | yes |
expect specialAdditionalMonthly | 1900 |
expect taxableAnnual | 103164 |
expect iitAnnual | 7796.4 |
expect netAnnual | 178167.6 |
Sources
- 个人所得税法 (IIT Law 2018, 全文有效): art. 6 and Schedule 1 · checked 2026-10-31
- 国发〔2023〕13号: child and elderly-parent deductions · checked 2026-10-31
- 国发〔2018〕41号 arts. 8, 14, 17: study, home loan, rent · checked 2026-10-31
- 京人社发〔2026〕7号: base limits from July 2026 · checked 2026-10-31
- 北京市基本养老保险规定 (Order 183), art. 12: pension rate · checked 2026-10-31
- 北京市基本医疗保险规定 (Order 68), arts. 10, 38: medical rate and levy · checked 2026-10-31
- 北京市失业保险规定 (Order 38), art. 7(3): unemployment rate · checked 2026-10-31
- 京房公积金管委会办〔2026〕6号: housing fund rates and bases · checked 2026-10-31
Check it yourself — and reuse it
The whole model as JSON — inputs, outputs, formulas, vectors and sources in one re-runnable document.
This model is published under CC BY 4.0: use it, adapt it, build on it, with attribution. The licence covers the model — the expression of each rule and the arrangement of the catalog. The rates and thresholds are law, and nobody licenses those; the sources above are where they come from.
← Back to the China 税后工资 (take-home pay) calculator
Built with Valem — a live, verifiable calculator you can fork and embed.