Is it possible to access admin-ajax.php using PHP?

It returns a 0 because that AJAX handler is only meant for logged in requests, and no non_priv handler was registered. 0 is the final fallback.

Attempt #1

While you could register your own AJAX handler that calls the function that generates the PDF and put it in a mini plugin, there’s an alternative:

add_action("wp_ajax_no_priv_wcpn_export", function() {
    do_action( "wp_ajax_wcpn_export" );
} );

The code at the other end may not like this as it might be relying on the user being logged in, or maybe it doesn’t check, either way this will trigger the logged in handler for that AJAX when the logged out is requested. A simple workaround.

This leads to the next problem:

https://github.com/postnl/woocommerce/blob/master/includes/admin/class-wcpn-export.php#L232-L254

PostNL performs a number of checks, that you’re logged in, that the nonce is valid, that the logged in user has the appropriate access rights, etc.

Attempt #2

Lets use inheritance! Lets create a new class, lets call it Martin_Export:

class Martin_Export extends WPCN_Export {
...
}

Our class will do 2 things and only 2 things.

First it will have a brand new export function. We’ll copy paste the one from WCPN and remove those checks at the top. We won’t copy paste anything else, just the export function.

Second, I’d recommend adding a new check for security, lets say a password parameter with a secret value only your PHP file knows. E.g. if ( $_GET['foo'] !== 'bar' ) { wp_die('get outta here'); }

Then, we have a line at the end after the class or somewhere else that hooks the new version of export into admin-ajax.php for logged out users like this:

$martin_export = new Martin_Export();
add_action("wp_ajax_no_priv_wcpn_export", [ $martin_export, 'export' ] );

Now when you make an AJAX request and you aren’t logged in, it’ll run your slightly tweaked version instead. Just remember to add the secret parameter we added to your PHP script