Skip to content

Instantly share code, notes, and snippets.

@azagniotov
Last active April 16, 2024 22:08
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save azagniotov/210c31540712c10206484d5297616842 to your computer and use it in GitHub Desktop.
Save azagniotov/210c31540712c10206484d5297616842 to your computer and use it in GitHub Desktop.
Downloads all pay-slips from ADP website
/*
This has been tested in Chrome v55.0.2883.95 (64-bit)
1. Log into ADP website using the URL: https://my.adp.com/static/redbox/login.html (Make sure you are NOT in Incognito mode)
2. Open Developer Tools console
3. Run the following code in the console:
*/
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = function() {
if (xhr.status === 200) {
var rawData = JSON.parse(xhr.responseText);
for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
var entry = rawData.payStatements[index];
var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);
var a = document.createElement('a');
var trueIndex = (rawData.payStatements.length - index);
if (trueIndex < 10) {
trueIndex = "00" + trueIndex;
} else if (trueIndex < 100) {
trueIndex = "0" + trueIndex;
}
a.download = "payslip.no." + trueIndex + ".from." + entry.payDate + ".pdf";
a.href = url;
document.body.appendChild(a);
a.click();
delete a;
}
} else {
console.log('Request failed. Returned status of ' + xhr.status);
}
};
xhr.send();
@picaso
Copy link

picaso commented Jan 25, 2017

Nice!

@cplazuelo
Copy link

Oh, my! Thanks! This is awesome. 🎉

@bcotton
Copy link

bcotton commented Jan 14, 2018

Worked great. Thank you!

@wenboshen
Copy link

Worked very well, thanks so much

@vnl
Copy link

vnl commented Jan 17, 2019

Hi there, I was hoping I can get this to work with : https://myfreedom.adp.com/ukWelcome.asp and the worst part is, the link works in IE 🥇 any suggestions? After login it redirects to https://fress1.adp.com/coreMainFrame.asp

@Aatoz
Copy link

Aatoz commented Mar 14, 2019

In Vivaldi (chromium engine) this stops after 10. It looks like there's some sort of block to prevent excessive downloads. You need to await the downloads to get past this.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=999');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
    if (xhr.status === 200) {
        var rawData = JSON.parse(xhr.responseText);

        for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
            var entry = rawData.payStatements[index];
            var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);
            var a = document.createElement('a');

            var trueIndex = (rawData.payStatements.length - index);
            if (trueIndex < 10) {
                trueIndex = "00" + trueIndex;
            } else if (trueIndex < 100) {
                trueIndex = "0" + trueIndex;
            }

            a.download = "payslip.no." + trueIndex + ".from." + entry.payDate + ".pdf";
            a.href = url;
            document.body.appendChild(a);

            await sleep(500);
            a.click();
            delete a;
        }
    } else {
        console.log('Request failed.  Returned status of ' + xhr.status);
    }
};
xhr.send();

function sleep(ms) {
   return new Promise(resolve => setTimeout(resolve, ms));
}

@bb515526792
Copy link

Hi, I get this error, if anyone can help.

VM319:10 Uncaught (in promise) TypeError: Cannot read property 'href' of undefined
at XMLHttpRequest.xhr.onload [as __zone_symbol__ON_PROPERTYload] (:10:70)
at XMLHttpRequest.oe (zone.min.js:2)
at e.invokeTask (zone.min.js:1)
at r.runTask (zone.min.js:1)
at t.invokeTask [as invoke] (zone.min.js:1)
at h (zone.min.js:1)
at XMLHttpRequest.d (zone.min.js:1)

@Johndo
Copy link

Johndo commented Oct 7, 2019

Amazing stuff guys !!

@justifiablemeans
Copy link

Does anyone know if this still works with the current version of Chrome? I am using ver. 79.0.3945.88 (Official Build) (64-bit) and can't seem to get it to work. Here is what I'm getting

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = function() {
if (xhr.status === 200) {
var rawData = JSON.parse(xhr.responseText);
for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
var entry = rawData.payStatements[index];
var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);
var a = document.createElement('a');

        var trueIndex = (rawData.payStatements.length - index);
        if (trueIndex < 10) {
            trueIndex = "00" + trueIndex;
        } else if (trueIndex < 100) {
            trueIndex = "0" + trueIndex;
        }

        a.download = "payslip.no." + trueIndex + ".from." + entry.payDate + ".pdf";
        a.href = url;
        document.body.appendChild(a);
        a.click();
        delete a;
    }
} else {
    console.log('Request failed.  Returned status of ' + xhr.status);
}

};
xhr.send();
undefined
VM797:1 Uncaught SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse ()
at XMLHttpRequest.xhr.onload (:6:28)

@azagniotov
Copy link
Author

@justifiablemeans, I have not used this script for a while, but it is not impossible that ADP could have changed the JSON format. Try to debug, add console.log(xhr.responseText) before var rawData = JSON.parse(xhr.responseText);, i.e.:

if (xhr.status === 200) {
   console.log(xhr.responseText);
   var rawData = JSON.parse(xhr.responseText);
...
...
...

@justifiablemeans
Copy link

@azagniotov I appreciate the response. I did what you said, but I can't make heads or tails of the log. I'll be the first to admit that I like to dabble with code from time to time but when it comes to really diving in, I'm lost.

<title>Login to MyADP</title>
  <link href="https://static.adp.com/static/redbox/login/login.min.css?4.7.55" rel="stylesheet" type="text/css"/>

  <script src="https://static.adp.com/static/redbox/ext/config/config.js?4.7.55"></script>
  <script src="https://static.adp.com/static/redbox/vendor/modernizr/modernizr.js?4.7.55"></script>
  <script src="https://static.adp.com/static/redbox/vendor/detectizr/dist/detectizr.min.js?4.7.55"></script>
  <script src="https://static.adp.com/static/redbox/vendor/core-js/client/shim.min.js?4.7.55"></script>
  <script src="https://static.adp.com/static/redbox/vendor/zone.js/dist/zone.min.js?4.7.55"></script>
  <script src="https://static.adp.com/static/redbox/vendor/web-animations-js/web-animations.min.js?4.7.55"></script>
  <script src="https://static.adp.com/static/redbox/vendor/log4javascript/log4javascript.js?4.7.55"></script>

  <!-- Google Digital Marketing Pixel -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=AW-1062739562"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){
      dataLayer.push(arguments);
    }
    gtag('js', new Date());
    gtag('config', 'AW-1062739562');
  </script>
  <script>
    gtag('event', 'conversion', {
      'send_to': 'AW-1062739562/IwQUCMSbtWgQ6rzg-gM',
      'aw_remarketing_only': true
    });
  </script>

  <style>
    .base-overlay {
      background-color: rgba(235,235,235,0.5);
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      /*hardcoding because I dont have access to espresso-base.scss file*/
      z-index: 98;
    }

    .activity-indicator {
      width: 94px;
      height: 94px;
      overflow: auto;
      margin: auto;
      position: absolute;
      top: 0;
      left: 0;
      bottom: 0;
      right: 0;
      z-index: 99;
      font-size: 10px;
      -webkit-transform: translateZ(0);
              transform: translateZ(0);
      -webkit-animation: loadIndicator 1s infinite linear;
              animation: loadIndicator 1s infinite linear;
      border-top: 0.4em solid rgba(26,128,153,0.2);
      border-right: 0.4em solid rgba(26,128,153,0.2);
      border-bottom: 0.4em solid rgba(26,128,153,0.2);
      border-left: 0.4em solid rgba(26,128,153,1);
    }

    .activity-indicator,
    .activity-indicator:after {
      border-radius: 50%;
    }

    @-webkit-keyframes loadIndicator {
      0% {
        -webkit-transform: rotate(0deg);
                transform: rotate(0deg);
      }
      100% {
        -webkit-transform: rotate(360deg);
                transform: rotate(360deg);
      }
    }

    @keyframes loadIndicator {
      0% {
        -webkit-transform: rotate(0deg);
                transform: rotate(0deg);
      }
      100% {
        -webkit-transform: rotate(360deg);
                transform: rotate(360deg);
      }
    }
</style>

<script src='/inc/js/lib/6a8f894227f41759348779077d1e5851.js'></script>
<script>window.module = 'aot';</script>
<redbox-login>
  <div class="base-overlay"></div>
  <div class="activity-indicator"></div>
</redbox-login>

<script>
  function verifyBrowserSupport() {
    if (navigator.userAgent.indexOf('MSIE') > -1) {
      if (document.all) {
        window.location.href = 'ext/framework/views/unsupported-browser.html';
      }
    }
  }

  (function loadAppBundle() {
    var preferredLocale = window.MYADP_CONFIG.LOCALE,
        LANG_REGEX = /[a-z]{2}/,
        REGION_REGEX = /[A-Z]{2}/,
        locale = getLocale();

    if (locale.indexOf('_') > -1) {
      var regex = new RegExp('_', 'g');
      // replace any underscores with dashes via regex
      locale = locale.replace(regex, '-');
      normalizeLanguageCookie(locale);
    }

    var login = document.createElement('script');
    login.setAttribute('src', window.MYADP_CONFIG.STATIC_DOMAIN + 'login/login.' + locale + '.js?4.7.55');
    document.head.appendChild(login);

    /////////////////////////////////////////

    function getLocale() {
      var _locale = getLanguageCookieValue() || preferredLocale,
        language = _locale.match(LANG_REGEX).join(),
        region = _locale.match(REGION_REGEX) ? _locale.match(REGION_REGEX).join() : undefined;
      if (region) {
        return _locale;
      } else {
        // No region (e.g., es vs. es-US)? Find the default local for the language.
        return getDefaultLocal(language) || preferredLocale;
      }
    }

    function getDefaultLocal(language) {
      var defaultLocales = ['en-US', 'es-US', 'fr-FR', 'zh-cn', 'nl-NL', 'pt-br', 'de-DE', 'it-IT'],
        defaultLocale;
      for (var i = 0; i < defaultLocales.length; i++) {
        if (language === defaultLocales[i].match(LANG_REGEX).join()) {
          defaultLocale = defaultLocales[i];
          break;
        }
      }
      return defaultLocale;
    }

    function getLanguageCookieValue() {
      var value = document.cookie.match('(^|;)\\s*ADPLangLocaleCookie\\s*=\\s*([^;]+)');
      return value ? value.pop() : undefined;
    }

    function normalizeLanguageCookie(locale) {
      var expires = new Date();
      expires.setFullYear(expires.getFullYear() + 1);
      document.cookie = 'ADPLangLocaleCookie=' + locale + ';domain=' +  window.MYADP_CONFIG.COOKIE_DOMAIN + ';path=/;expires=' + expires.toUTCString();
    }
  })();

  (function ensureCorrectLoginUrl() {
    // Session timeouts can result in this page being included inside of the single page app.
    // If the login page is ever loaded, ensure we are at the correct URL.
    if (window.location.href.indexOf('/login.html') === -1) {
      window.location.href = 'login.html?REASON=ERROR_TIMEOUT';
    }
    if (window.location.href.indexOf('#/') > 0) {
      let param = window.location.href.match(/#\/(.*)/)[1];
      param = encodeURIComponent(param);
      window.location.href = 'login.html?route=' + param;
    }
  })();
</script>

@azagniotov
Copy link
Author

@justifiablemeans you are not logged in. First you need to log into the ADP website using the URL https://my.adp.com/static/redbox/login.html as per the comment in the script header:

/*
   This has been tested in Chrome v55.0.2883.95 (64-bit)
1. Log into ADP website using the URL: https://my.adp.com/static/redbox/login.html (Make sure you are NOT in Incognito mode)
2. Open Developer Tools console
3. Run the following code in the console:
*/

I just tested the script using the same Chrome version as you mentioned and it works fine.

@janpolach
Copy link

janpolach commented Feb 24, 2020

@vnl did you ever make this work on that site?

@justifiablemeans
Copy link

I did, yes. My problem was that I went directly to my ADP profile as I normally would. Once I signed in using the link above, it worked.

@LukeHandle
Copy link

The Gist'd script did not work for me. It appears there are 2 issues.

  1. Chrome won't bulk download more than 10 things
  2. ADP rate limits if you try and async every page at once

https://gist.github.com/azagniotov/210c31540712c10206484d5297616842#gistcomment-2862672 is probably what I should have done and looked no further.

But, with my lacking Javascript skills, I took a random stab and went the route of creating a Zip with folders based on year.

Load in the JSZip library:

var script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.0/jszip.min.js';
document.head.appendChild(script); 

Then run this block to synchronously download every slip and then download the payslips:

var zip = new JSZip();
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
    if (xhr.status === 200) {
        var rawData = JSON.parse(xhr.responseText);
        for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
            var entry = rawData.payStatements[index];
            var date = entry.payDate
            var year = entry.payDate.split("-")[0]
            var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);

            var trueIndex = (rawData.payStatements.length - index);
            if (trueIndex < 10) {
                trueIndex = "00" + trueIndex;
            } else if (trueIndex < 100) {
                trueIndex = "0" + trueIndex;
            }

            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if(this.readyState == 4 && this.status == 200) {
                    zip.folder(year).file(date + ".pdf", this.response)
                }
            };
            xhttp.open('GET', url, true);
            xhttp.responseType = "blob";
            xhttp.send();
            while (xhttp.readyState != 4) {
                console.log("Waiting for download " + trueIndex + " from " + date);
                await new Promise(r => setTimeout(r, 500));
            }
        }
    } else {
        console.log('Request failed.  Returned status of ' + xhr.status);
    }

    zip.generateAsync({type : 'base64'})
        .then(function (base64) {
            window.location = "data:application/zip;base64," + base64;
        });
};
xhr.send();

While this was more reliable with the downloads / avoided the server 504s, it fails to actually save the zip... So I went with the previous method and instead used Aatoz's adjustments. Make sure to check your downloads list for failed ones needing to be retried.

@dwt12777
Copy link

Thanks guys, this worked great (used Aatoz's update)!

@b-steel
Copy link

b-steel commented Nov 19, 2020

Just used Aatoz's version on Firefox 82.0.3 (64bit) and it worked like a charm.

Many thanks!

@raugustin93
Copy link

Thank you so much!

Still working!

@iampranavsethi
Copy link

iampranavsethi commented Feb 9, 2021

I modified the code that @LukeHandle posted to create the zip. I append the base64 zip contents to a href tag and then trigger the click.
Saves the files with only one download / save dialog.

Load in the JSZip library:

var script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.0/jszip.min.js';
document.head.appendChild(script); 

Then run this block to synchronously download every slip and then download the payslips:

var zip = new JSZip();
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
    if (xhr.status === 200) {
        var rawData = JSON.parse(xhr.responseText);
        for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
            var entry = rawData.payStatements[index];
            var date = entry.payDate
            var year = entry.payDate.split("-")[0]
            var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);

            var trueIndex = (rawData.payStatements.length - index);
            if (trueIndex < 10) {
                trueIndex = "00" + trueIndex;
            } else if (trueIndex < 100) {
                trueIndex = "0" + trueIndex;
            }

            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if(this.readyState == 4 && this.status == 200) {
                    zip.folder(year).file(date + ".pdf", this.response)
                }
            };
            xhttp.open('GET', url, true);
            xhttp.responseType = "blob";
            xhttp.send();
            while (xhttp.readyState != 4) {
                console.log("Waiting for download " + trueIndex + " from " + date);
                await new Promise(r => setTimeout(r, 500));
            }
        }
    } else {
        console.log('Request failed.  Returned status of ' + xhr.status);
    }
};
xhr.send();

After the downloads have finished, paste this code in the console later -

zip.generateAsync({type : 'base64'}).then(function (base64) {
    var a = document.createElement('a');
    a.download = 'paystubs.zip'; 
    a.href = "data:application/zip;base64," + base64;
    document.body.appendChild(a);
    a.click();
});

@theSuda
Copy link

theSuda commented Feb 19, 2021

Just used this in Firefox 85.0.2.
Great script.
Had to change setting to Always Automatically Download files in Firefox settings because it just prompted a lot of Download File windows.
Please add this to the instructions.

@benblasco
Copy link

Just used this in Firefox 85.0.2.
Great script.
Had to change setting to Always Automatically Download files in Firefox settings because it just prompted a lot of Download File windows.
Please add this to the instructions.

I wasn't able to find this setting in Firefox, and the pasted code from @iampranavthesi generated the following errors:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300. (Reason: header ‘access-control-allow-origin’ is not allowed according to header ‘Access-Control-Allow-Headers’ from CORS preflight response).

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300. (Reason: CORS request did not succeed).

Any ideas?

@iampranavsethi
Copy link

Just used this in Firefox 85.0.2.
Great script.
Had to change setting to Always Automatically Download files in Firefox settings because it just prompted a lot of Download File windows.
Please add this to the instructions.

I wasn't able to find this setting in Firefox, and the pasted code from @iampranavthesi generated the following errors:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300. (Reason: header ‘access-control-allow-origin’ is not allowed according to header ‘Access-Control-Allow-Headers’ from CORS preflight response).

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300. (Reason: CORS request did not succeed).

Any ideas?

@eraser215 were you already logged in to my.adp.com?
You can try these steps if it helps you out -

  1. After login to my.adp.com open this link https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300 in a new tab. (This will be a JSON response body)
    (You will not get CORS issue if you try on this link)
  2. Open Dev tools -> Console and then paste the first code block to load up the JSZip library
  3. Then paste the second code block to start the download of every payslip. (It is going to take a while depending on how many are there to download)
  4. Lastly, paste the last code block to trigger the download of the zip file.

Paste the 3 code blocks one at a time and not as one script together.

Tested in Chrome v89 can't say about Firefox but should be similar.

@r0guenj
Copy link

r0guenj commented Apr 23, 2021

This was helpful and worked as described after using @iampranavsethi modified instructions.

@Krthkyns
Copy link

Updated Script as of this date(Tested in chrome Version 94.0.4606.81 (Official Build) (64-bit))

var script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.0/jszip.min.js';
document.head.appendChild(script);

var zip = new JSZip();
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
if (xhr.status === 200) {
var rawData = JSON.parse(xhr.responseText);
for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
var entry = rawData.payStatements[index];
var date = entry.payDate
var year = entry.payDate.split("-")[0]
var url = "https://my.adp.com/myadp_prefix/" + entry.statementImageUri.href;

        var trueIndex = (rawData.payStatements.length - index);
        if (trueIndex < 10) {
            trueIndex = "00" + trueIndex;
        } else if (trueIndex < 100) {
            trueIndex = "0" + trueIndex;
        }

        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
            if(this.readyState == 4 && this.status == 200) {
                zip.folder(year).file(date + ".pdf", this.response)
            }
        };
        xhttp.open('GET', url, true);
        xhttp.responseType = "blob";
        xhttp.send();
        while (xhttp.readyState != 4) {
            console.log("Waiting for download " + trueIndex + " from " + date);
            await new Promise(r => setTimeout(r, 500));
        }
    }
} else {
    console.log('Request failed.  Returned status of ' + xhr.status);
}

};
xhr.send();

zip.generateAsync({type : 'base64'}).then(function (base64) {
var a = document.createElement('a');
a.download = 'paystubs.zip';
a.href = "data:application/zip;base64," + base64;
document.body.appendChild(a);
a.click();
});

@cwlls
Copy link

cwlls commented Jan 6, 2022

This worked very nicely, thanks!

@pfoneill
Copy link

Works great after adding the sleep step - thank you!

@rcrocker
Copy link

rcrocker commented Jan 4, 2023

Thanks for this. I found that the Zip version didn't quite work if you had more than one paystub on the same day, so I made a small modification to the callback to append an index to the filename and now that works.

var zip = new JSZip();
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
    if (xhr.status === 200) {
        var rawData = JSON.parse(xhr.responseText);
        var lastPaydate = ""
        var consecutiveEntries = 0
        for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
            var entry = rawData.payStatements[index];
            var date = entry.payDate
            // Handle multiple paystubs on the same day
            var dateSuffix = ""
            if (date === lastPaydate) {
                consecutiveEntries++
                dateSuffix = `-${consecutiveEntries}`
            } else {
                lastPaydate = date
                consecutiveEntries = 0
            }
            var year = entry.payDate.split("-")[0]
            var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);

            var trueIndex = (rawData.payStatements.length - index);
            if (trueIndex < 10) {
                trueIndex = "00" + trueIndex;
            } else if (trueIndex < 100) {
                trueIndex = "0" + trueIndex;
            }

            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if(this.readyState == 4 && this.status == 200) {
                    zip.folder(year).file(date + dateSuffix + ".pdf", this.response)
                }
            };
            xhttp.open('GET', url, true);
            xhttp.responseType = "blob";
            xhttp.send();
            while (xhttp.readyState != 4) {
                console.log("Waiting for download " + trueIndex + " from " + date);
                await new Promise(r => setTimeout(r, 500));
            }
        }
    } else {
        console.log('Request failed.  Returned status of ' + xhr.status);
    }
};
xhr.send();

@FrankTaylorLieder
Copy link

Hi, I've just used the original script in Chrome and as before it stops after 10 downloads. I added a simple fix to sleep between downloads and it then was able to download all 159 of my payslips without any problems. (Note I also added some logging so I can see progress.)

Here is the updated code:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
    if (xhr.status === 200) {
        var rawData = JSON.parse(xhr.responseText);
        console.log('Number of items:', rawData.payStatements.length);
        for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
            var entry = rawData.payStatements[index];
            var url = "https://my.adp.com" + entry.statementImageUri.href.substring(3);
            console.log("Downloading:", url);
            var a = document.createElement('a');

            var trueIndex = (rawData.payStatements.length - index);
            if (trueIndex < 10) {
                trueIndex = "00" + trueIndex;
            } else if (trueIndex < 100) {
                trueIndex = "0" + trueIndex;
            }

            a.download = "payslip.no." + trueIndex + ".from." + entry.payDate + ".pdf";
            a.href = url;
            document.body.appendChild(a);
            a.click();
            delete a;

            await new Promise(r => setTimeout(r, 1000));
        }
    } else {
        console.log('Request failed.  Returned status of ' + xhr.status);
    }
};
xhr.send();

@corymayer
Copy link

Updated version for 2024:

You need to get your OID by inspecting web requests, and replace YOUR_OID_HERE. I tried to make it generic by calling this API: https://my.adp.com/myadp_prefix/myadpapi/core/v1/version which returns the OID, but the API has some special CORs protection I couldn't figure out.

function sleep(ms) {
   return new Promise(resolve => setTimeout(resolve, ms));
}

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://my.adp.com/myadp_prefix/payroll/v1/workers/YOUR_OID_HERE/pay-statements?adjustments=yes&numberoflastpaydates=300');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); // CORS policy
xhr.onload = async function() {
    if (xhr.status === 200) {
        var rawData = JSON.parse(xhr.responseText);
        var lastPaydate = ""
        var consecutiveEntries = 0
        for (var index = rawData.payStatements.length - 1; index >= 0; --index) {
            var entry = rawData.payStatements[index];
            var date = entry.payDate
            // Handle multiple paystubs on the same day
            var dateSuffix = ""
            if (date === lastPaydate) {
                consecutiveEntries++
                dateSuffix = `-${consecutiveEntries}`
            } else {
                lastPaydate = date
                consecutiveEntries = 0
            }
            var year = entry.payDate.split("-")[0]
            var url = "https://my.adp.com/myadp_prefix" + entry.statementImageUri.href + "?rolecode=employee";

            var a = document.createElement('a');

            var trueIndex = (rawData.payStatements.length - index);
            if (trueIndex < 10) {
                trueIndex = "00" + trueIndex;
            } else if (trueIndex < 100) {
                trueIndex = "0" + trueIndex;
            }

            a.download = "payslip.no." + trueIndex + ".from." + entry.payDate + ".pdf";
            a.href = url;
            document.body.appendChild(a);

            await sleep(500);
            a.click();
            delete a;
        }
    } else {
        console.log('Request failed.  Returned status of ' + xhr.status);
    }
};
xhr.send();

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