Skip to content

Instantly share code, notes, and snippets.

@reinvented
Last active May 7, 2024 12:49
Show Gist options
  • Save reinvented/22d173c7f0a607a7069ad7324b125f57 to your computer and use it in GitHub Desktop.
Save reinvented/22d173c7f0a607a7069ad7324b125f57 to your computer and use it in GitHub Desktop.
Automating the use of Canada Revenue's Payroll Deductions Online Calculator in PHP
<?php
/**
* get-pdoc.php - Scrape Canada Revenue Agency payroll deductions values
*
* This script takes an hourly amount, a number of hours, and a year,
* month and day of a pay period ending, and passes these to the
* Canada Revenue Agency Payroll Deductions Online Calculator
* (https://www.canada.ca/en/revenue-agency/services/e-services/e-services-businesses/payroll-deductions-online-calculator.html),
* returning the provincial tax, federal tax, CPP and EI amounts.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* @version 0.2, June 14, 2018
* @author Peter Rukavina <peter@rukavina.net>
* @copyright Copyright &copy; 2018, Reinvented Inc.
* @license http://www.fsf.org/licensing/licenses/gpl.txt GNU Public License
*/
// Get from http://simplehtmldom.sourceforge.net/
include('simple_html_dom.php');
$retval = array();
$retval['status'] = 'success';
$required = array('hours', 'hourly', 'year', 'month', 'day');
foreach($required as $key => $value) {
if (!array_key_exists($value, $_GET)) {
$retval['status'] = 'error';
$retval['errormessage'] = "Missing '" . $value . "' parameter.";
}
}
if ($retval['status'] != 'error') {
$param = array();
$param['income'] = ($_GET['hourly'] * $_GET['hours']);
$param['vacationPay'] = round($param['income'] * 0.04, 2);
$param['year'] = $_GET['year'];
$param['month'] = $_GET['month'];
$param['day'] = $_GET['day'];
$param['month'] = str_pad($param['month'], 2, '0', STR_PAD_LEFT);
$param['day'] = str_pad($param['day'], 2, '0', STR_PAD_LEFT);
$data = array(
"step0" => array(
"calculationType" => "SALARY", // Salary
"action:welcome_NextButton" => "Next",
"struts.token.name" => "166PXW9HKHFUX4B37TTDTLBYFEK2SGPC",
),
"step1" => array(
"employeeName" => "The Employee",
"employerName" => "The Employer",
"jurisdiction" => "PRINCE_EDWARD_ISLAND", // Prince Edward Island
"payPeriodFrequency" => "WEEKLY_52PP", // Weekly
"datePaidYear" => $param['year'],
"datePaidMonth" => $param['month'],
"datePaidDay" => $param['day'],
"action:payrollDeductionsStep1_NextButton" => "Next"
),
"step2" => array(
"incomeAmount" => $param['income'],
"vacationPay" => $param['vacationPay'],
"salaryType" => "NO_BONUS_PAY_NO_RETROACTIVE_PAY",
"clergyType" => "NOT_APPLICABLE",
"action:payrollDeductionsStep2a_NextButton" => "Next"
),
"step3" => array(
"federalClaimCode" => "CLAIM_CODE_1",
"provinceTerritoryClaimCode" => "CLAIM_CODE_1",
"requestedAdditionalTaxDeductions" => "0.00",
"pensionableEarningsYearToDate" => "0",
"cppOrQppContributionsDeductedYearToDate" => "0",
"cppQppType" => "CPP_QPP_YEAR_TO_DATE",
"insurableEarningsYearToDate" => "0",
"employmentInsuranceType" => "EI_YEAR_TO_DATE",
"employmentInsuranceDeductedYearToDate" => "0",
"reducedEIRate" => "0",
"employerEmploymentInsurancePremiumRate" => 1.4,
"action:payrollDeductionsStep3_CalculateButton" => "Calculate"
),
);
$triggers = array(
"federalTax" => array("Federal tax deduction", 3),
"provincialTax" => array("Provincial tax deduction", 4),
"CPP" => array("CPP deductions", 4),
"EI" => array("EI deductions", 4),
);
$result = fetch("https://apps.cra-arc.gc.ca/ebci/rhpd/prot/welcome.action", array('post' => false));
$hidden = getHiddenFormValueFromHTML($result);
$data['step0'] = array_merge($data['step0'], $hidden);
$result = fetch("https://apps.cra-arc.gc.ca/ebci/rhpd/prot/welcome.action", array('hidden' => $hidden, 'refer' => 'https://apps.cra-arc.gc.ca/ebci/rhpd/prot/welcome.action', 'post' => http_build_query($data['step0'], '', '&')));
$hidden = getHiddenFormValueFromHTML($result);
$data['step1'] = array_merge($data['step1'], $hidden);
$result = fetch("https://apps.cra-arc.gc.ca/ebci/rhpd/prot/payrollDeductionsStep1.action", array('refer' => 'https://apps.cra-arc.gc.ca/ebci/rhpd/prot/welcome.action', 'post' => http_build_query($data['step1'], '', '&')));
$hidden = getHiddenFormValueFromHTML($result);
$data['step2'] = array_merge($data['step2'], $hidden);
$result = fetch("https://apps.cra-arc.gc.ca/ebci/rhpd/prot/payrollDeductionsStep2a.action", array('refer' => 'https://apps.cra-arc.gc.ca/ebci/rhpd/prot/payrollDeductionsStep1.action', 'post' => http_build_query($data['step2'], '', '&')));
$hidden = getHiddenFormValueFromHTML($result);
$data['step3'] = array_merge($data['step3'], $hidden);
$result = fetch("https://apps.cra-arc.gc.ca/ebci/rhpd/prot/payrollDeductionsStep3.action", array('refer' => 'https://apps.cra-arc.gc.ca/ebci/rhpd/prot/payrollDeductionsStep2a.action', 'post' => http_build_query($data['step3'], '', '&')));
$lines = explode("\n", $result);
foreach ($lines as $linenumber => $line) {
foreach ($triggers as $key => $trigger) {
if (strpos($line, $trigger[0]) !== FALSE) {
$values[$key] = trim($lines[$linenumber + $trigger[1]]);
}
}
}
$retval['values'] = $values;
}
print json_encode($retval);
function fetch($url, $z=null) {
$ch = curl_init();
$useragent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2';
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_AUTOREFERER, true );
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $ch, CURLOPT_POST, isset($z['post']) );
if( isset($z['post']) ) curl_setopt( $ch, CURLOPT_POSTFIELDS, $z['post'] );
if( isset($z['refer']) ) curl_setopt( $ch, CURLOPT_REFERER, $z['refer'] );
curl_setopt( $ch, CURLOPT_USERAGENT, $useragent );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 5 );
curl_setopt( $ch, CURLOPT_COOKIEJAR, '/tmp/cra-cookies.txt' );
curl_setopt( $ch, CURLOPT_COOKIEFILE, '/tmp/cra-cookies.txt' );
$result = curl_exec( $ch );
curl_close( $ch );
return $result;
}
function getHiddenFormValueFromHTML($html) {
$html = str_get_html($html);
$nodes = $html->find("input[type=hidden]");
$hidden = array();
foreach ($nodes as $node) {
$hidden[$node->attr['name']] = $node->attr['value'];
}
return $hidden;
}
@educsis
Copy link

educsis commented Sep 22, 2019

I think it doesn't work anymore, great stuff by the way. I think that it now uses session cookies, because if your token doesn't match, it gives an error.

@Meekcanadian
Copy link

Does anyone know of other api for Canadian payroll?

@bluntcoder
Copy link

Does anyone know of other api for Canadian payroll?

I'm also looking as well!

@DianaLozanoP
Copy link

Me too, looking for an API.

@vynci
Copy link

vynci commented Feb 8, 2024

I'm planning to build a micro saas that provides API that calculates the Canada payroll deduction using the T4127 formula.

Is this something that anyone would pay for?

@Meekcanadian
Copy link

Meekcanadian commented Feb 8, 2024 via email

@vynci
Copy link

vynci commented Feb 8, 2024

Nice to hear from you @Meekcanadian, I've worked on a project recently that needed to calculate payroll deductions, it wasn't an easy problem to solve as I didn't find any existing API services for it. Every province has its specific formulas and constants, and on top of that, things could change twice a year, so the code needs to be changed as well.

I think creating a micro saas for this would be a good idea.

@Meekcanadian
Copy link

Meekcanadian commented Feb 8, 2024 via email

@vynci
Copy link

vynci commented Feb 8, 2024

Awesome, I will keep you posted @Meekcanadian. Thanks!

@Meekcanadian
Copy link

Meekcanadian commented Feb 8, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment