christoph-schaeffer / dhl-business-shipping

An unofficial library for the DHL business shipping soap API (Version 3.3) and the dhl shipment tracking rest API written in PHP.
MIT License
29 stars 8 forks source link

Error while Usage of the tracking api #28

Open Flaschenzug opened 1 year ago

Flaschenzug commented 1 year ago

Hey Christoph, I think it is not an error. But I am not able to use the tracking API.

I installed your API via composer. Included the autoloader.

$request = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail();
$request->pieceCode = 'VALID-TRACKINGID';
$response = $client->getPieceDetail($request);

Fatal error: Uncaught Error: Call to undefined method ChristophSchaeffer\Dhl\BusinessShipping\Client::getPieceDetail()

What am I missing? Thanks

christoph-schaeffer commented 1 year ago

Hello @Flaschenzug

you are using the wrong Client. The Client class is actually deprecated and will be removed soon. Its just there for compatibility.

It is the old class that used to be in this library, when it didn't have the ability to track shipments. That is why it doesn't have the tracking function.

Now there are 3 different client classes. TrackingClient, ShippingClient and the MultClient which combines the other clients into one client. I recommend using the MultiClient for ease of use.

Here is an example for how to initialize the MultiClient. The actual functions are structured the same as they always have been.

use ChristophSchaeffer\Dhl\BusinessShipping\Credentials\ShippingClientCredentials; // Autoload the ShippingClientCredentials class
use ChristophSchaeffer\Dhl\BusinessShipping\Credentials\TrackingClientCredentials; // Autoload the TrackingClientCredentials class
use ChristophSchaeffer\Dhl\BusinessShipping\MultiClient; // Autoload the MultiClient class

$shippingClientCredentials = new ShippingClientCredentials(
    'appID',        // In sandbox mode this is your developer ID.
    'apiToken',     // In sandbox mode this is your developer account password.
    'login',        // DHL business customer account name. This is optional and defaults to 2222222222_01 (sandbox)
    'password',     // DHL business customer password. This is optional and defaults to pass (sandbox)
);

$trackingClientCredentials = new TrackingClientCredentials(
    'appID',        // In sandbox mode this is your developer ID.
    'apiToken',     // In sandbox mode this is your developer account password.
    'ztToken',      // DHL business customer ZT Token. This is optional and defaults to zt12345 (sandbox)
    'password',     // DHL business customer ZT Token password. This is optional and defaults to geheim (sandbox)
);

$client = new MultiClient(
    $shippingClientCredentials
    $trackingClientCredentials
    TRUE,                              // isSandbox
    MultiClient::LANGUAGE_LOCALE_ENGLISH_GB // Set the status message language to english(default is german).
);

Please check the current readme for more details

I hope i could help!

Flaschenzug commented 1 year ago

Hmm, I don't really get it. I try to adapt my old code to the new one ... but get a notice like:

"required parameters $rawResponse, $rawRequest, $languageLocale missing"

for $request = new \ChristophSchaeffer\Dhl\BusinessShipping\Response\Shipping\validateShipment($shipmentOrders);

Confused..

christoph-schaeffer commented 1 year ago

Hello,

You're trying to create a response, but this class shouldn't be instantiated by you. This is the object for the... well response from dhl.

You need to use the corresponding request. This did not change though.

Just replace the word response with request in that path

Flaschenzug commented 1 year ago

Great, thanks a lot Christoph! Only one thing left: 'ztToken', // DHL business customer ZT Token. This is optional and defaults to zt12345 (sandbox) 'password', // DHL business customer ZT Token password. This is optional and defaults to geheim (sandbox)

Where do I find the ZT Token + password?

Um die Sendungsverfolgung um Produktiv-Modus zu verwenden wird zusätzlich noch ein ZT-Token mit DASS Freigabe, sowie ein Passwort für diesen benötigt. Den Token mitsamt Passwort erhalten Sie im Geschäftskundenportal unter Meine Daten ->Verfolgen.

That's not working anymore. If you don't know where to find it, I will contact the Support and send an update here.

Update: Never mind, ... https://github.com/christoph-schaeffer/dhl-business-shipping/issues/20 Long discussion about it in this closed topic.

Thanks!!!

Flaschenzug commented 1 year ago

If you need your ZT Token and password: Call the "DHL Business Kundensupport". They will send it to you via email.

Flaschenzug commented 1 year ago

They are quick - got my ZT token and password already. But now I get an error using the tracking API:

$request = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail(); $request->pieceCode = '00340434161094027318'; $response = $client->getPieceDetail($request);

And $client->getPieceDetail($request); results in the following error:

[31-Aug-2022 13:36:02 Europe/Berlin] PHP Fatal error:  Uncaught ChristophSchaeffer\Dhl\BusinessShipping\Exception\Tracking\DhlRestHttpException: 400 Bad Request - You have sent a request the dhl server could not understand in /vendor/christoph-schaeffer/dhl-business-shipping/src/Protocol/Rest.php:88
Stack trace:
#0 /vendor/christoph-schaeffer/dhl-business-shipping/src/Protocol/Rest.php(69): ChristophSchaeffer\Dhl\BusinessShipping\Protocol\Rest->handleHttpCodes(400, Object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail), '<?xml version="...')
#1 /vendor/christoph-schaeffer/dhl-business-shipping/src/TrackingClient.php(226): ChristophSchaeffer\Dhl\BusinessShipping\Protocol\Rest->callRestFunction('<?xml version="...', Object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail))
#2 /vendor/christoph-schaeffer/dhl-business-shipping/src/MultiClient.php(201): ChristophSchaeffer\Dhl\BusinessShipping\TrackingClient->getPieceDetail(Object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail))
#3 /testscript.php(521): ChristophSchaeffer\Dhl\BusinessShipping\MultiClient->getPieceDetail(Object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail))
#4 {main}
  thrown in /vendor/christoph-schaeffer/dhl-business-shipping/src/Protocol/Rest.php on line 88
christoph-schaeffer commented 1 year ago

Thats odd...

However i don't really know what could cause this just with this information... Could be a bug actually.

Could you please copy the string you get if you catch the exception and call the function getXmlRequest ?

Don't forget to replace your credentials with ... or something.

try {
    $response = $client->getPieceDetail($request);
} catch(\Exception $e) {
    echo $e->getXmlRequest();
}
Flaschenzug commented 1 year ago

Sure. But actually there is no output :-/

$request = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail();
$request->pieceCode = '00340431161094027318';
try {
    $response = $clientDhl->getPieceDetail($request);
} catch(\Exception $e) {
    echo "exception: ".$e->getXmlRequest()." !exception end";
}

output

exception: !exception end

christoph-schaeffer commented 1 year ago

What PHP Version are you using? And what version of this library?

christoph-schaeffer commented 1 year ago

Another way to get the xml request beforehand is doing this:

use ChristophSchaeffer\Dhl\BusinessShipping\Utility\XmlParser;

XmlParser::buildXmlRequest($request);

Flaschenzug commented 1 year ago

What PHP Version are you using? And what version of this library?

PHP 8.0.20 Library: 3.3.2 (the newest, updated via composer 2 days ago)

Flaschenzug commented 1 year ago

Another way to get the xml request beforehand is doing this:

use ChristophSchaeffer\Dhl\BusinessShipping\Utility\XmlParser; XmlParser::buildXmlRequest($request);

$test_request = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail();
$test_request->pieceCode = '00340434492098429045';

use ChristophSchaeffer\Dhl\BusinessShipping\Utility\XmlParser;
print_r(XmlParser::buildXmlRequest($test_request));
// empty output
echo XmlParser::buildXmlRequest($test_request);
// empty output

I tried something else:

$request2 = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail();
$request2->pieceCode = '00340434492098429045';
print_r($request2);

Is it right, that most of the object keys are empty?

ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail Object ( [pieceCode] => 00340434492098429045 [pieceCustomerReference] => [tasOrderNo] => [fromDate] => [toDate] => [contentObjects] => Array ( ) [request] => [appname] => [password] => [languageCode] => )

christoph-schaeffer commented 1 year ago

Yes the empty ones will be removed before sending the request

christoph-schaeffer commented 1 year ago

All i can think of now is debugging it step by step to be honest.

Try to copy those functions and look at the var_dump outputs one by one maybe you find something there:

    public function getPieceDetail($request) {
        $request      = $this->sanitizeRequest($request);
        var_dump($request);
        $request      = $this->fillRequestData($request);
        var_dump($request);
        $xmlRequest = XmlParser::buildXmlRequest($request);
        var_dump($xmlRequest);
   }

    private function sanitizeRequest($request) {
        Sanitizer::sanitizeObjectRecursive($request);
        //maybe another var dump here. This might return null.
        Sanitizer::convertFloatToStringObjectRecursive($request);

        return $request;
    }

    private function fillRequestData($request) {
        $request->request = $request->getRequestString();

        $request->appname  = empty($request->appname) ? $this->credentials->ztToken : $request->appname;
        $request->password = empty($request->password) ? $this->credentials->password : $request->password;

        $request->languageCode = empty($request->languageCode) ? $this->languageLocaleAlpha2 : $request->languageCode;
        $request->languageCode = strtolower($request->languageCode);

        return $request;
    }

Please let me know if you find something, though so i can fix it. You could also fix it yourself and make a pull request its up to you

Flaschenzug commented 1 year ago
public function getPieceDetail($request) {
        $request      = $this->sanitizeRequest($request);
        var_dump($request);
        // returns -> object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail)#16 (1) { ["pieceCode"]=> string(20) "00340434492098429045" } X1

        $request      = $this->fillRequestData($request);
        var_dump($request);
        // returns -> object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail)#16 (5) { ["pieceCode"]=> string(20) "00340434492098429045" ["request"]=> string(18) "d-get-piece-detail" ["appname"]=> string(8) "REMOVED" ["password"]=> string(15) "REMOVED" ["languageCode"]=> string(2) "de" }

        $xmlRequest = XmlParser::buildXmlRequest($request);
        var_dump($xmlRequest);
        // returns -> string(184) "<!--?xml version="1.0" encoding="UTF-8"?--><data piece-code="00340434492098429045" request="d-get-piece-detail" appname="REMOVED" password="REMOVED" language-code="de"></data>"
   }

private function sanitizeRequest($request) {
        Sanitizer::sanitizeObjectRecursive($request);
        //maybe another var dump here. This might return null.
        // var_dump($request) -> returns: object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail)#16 (1) { ["pieceCode"]=> string(20) "00340434492098429045" }
        Sanitizer::convertFloatToStringObjectRecursive($request);

        return $request;
    }

private function fillRequestData($request) {
        $request->request = $request->getRequestString();

        $request->appname  = empty($request->appname) ? $this->credentials->ztToken : $request->appname;
        $request->password = empty($request->password) ? $this->credentials->password : $request->password;

        $request->languageCode = empty($request->languageCode) ? $this->languageLocaleAlpha2 : $request->languageCode;
        $request->languageCode = strtolower($request->languageCode);
        // var_dump($request) -> returns the same: object(ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail)#16 (5) { ["pieceCode"]=> string(20) "00340434492098429045" ["request"]=> string(18) "d-get-piece-detail" ["appname"]=> string(8) "REMOVED" ["password"]=> string(15) "REMOVED" ["languageCode"]=> string(2) "de" }
        return $request;
    }
christoph-schaeffer commented 1 year ago

It's kind of weird if it really is the problem, but could you try to change the line 54 in ChristophSchaeffer\Dhl\BusinessShipping\Protocol\Rest::callRestFunction?

$url .= '?xml='.rawurlencode($xml); to $url .= '?xml='.$xml;

and just to make sure the removed parts in appname are your ztToken and the password for your ztToken right? not tsomething else?

Flaschenzug commented 1 year ago

Sorry, I just realized: The exception is not empty - just plain html code and hidden:

$request = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail();
$request->pieceCode = '00340431161094027318';
try {
    $response = $clientDhl->getPieceDetail($request);
} catch(\Exception $e) {
    echo "exception: ".$e->getXmlRequest()." !exception end";
}

<?xml version="1.0" encoding="UTF-8"?><data piece-code="00340434492098429045" request="d-get-piece-detail" appname="REMOVED" password="REMOVED" language-code="de"></data>

$url .= '?xml='.rawurlencode($xml); to $url .= '?xml='.$xml;

Doesn't change anything.

and just to make sure the removed parts in appname are your ztToken and the password for your ztToken right? not tsomething else?

I guess I do. Username starts with zt and then numbers. Password ist the one I use to login into https://nolb.dhl.de/

I could try to change the password and remove special chars ...

Flaschenzug commented 1 year ago

Does the result of the exception change anything?

$request = new \ChristophSchaeffer\Dhl\BusinessShipping\Request\Tracking\getPieceDetail();
$request->pieceCode = '00340431161094027318';
try {
    $response = $clientDhl->getPieceDetail($request);
} catch(\Exception $e) {
    echo "exception:<br>".$e->getXmlRequest()."<br>!exception end";
}

exception:
<?xml version="1.0" encoding="UTF-8"?><data piece-code="00340434492098429045" request="d-get-piece-detail" appname="REMOVED" password="REMOVED" language-code="de"></data>
!exception end
christoph-schaeffer commented 1 year ago

The xml looks actually fine to me... have you tried removing the special character from your password?

You could try to create a support ticket on entwickler.dhl.de please include the xml there. Thats what they need.

You could also try out the testsuite they have: https://entwickler.dhl.de/group/ep/test-suite you could enter your xml there and see what it gives you. Or cross-check the test value they have there for d-get-piece-detail.

by the way one thing that doesn't work aswell is using a shipment number that you haven't sent yourself. At lest in production mode. in sandbox mode only the sandbox ztToken and password is allowed aswell. However i think this would result in another error. and in sandbox mode the only allow specific shipment numbers

The one you have been using is one of those shipment numbers: 00340434161094027318 The sandbox ztToken and password are the default values, so if you only enter 2 parameters for the TrackingClientCredentials it'll use those.

ztToken: zt12345 password: geheim

Flaschenzug commented 1 year ago

I created a Ticket on entwickler.dhl.de , I'll keep you updated.

I also installed SoapUI and imported the demo files of DHL. After adding my username and password, the demo files work and give the result I am looking for. Changing the login details, I get a 401 - wrong login data, so I guess they are not the problem.

have you tried removing the special character from your password?

Dhl does not provide a way to change the password ... would be to simple. But I checked and they allow the following special chars: z.B. +, $, /, !, ? I use #& and they are not listed, but I asked about the password on entwickler.dhl.de

The one you have been using is one of those shipment numbers: 00340434161094027318

I changed the number to one that I created. Same problem.

Flaschenzug commented 1 year ago

To set a new password they ask for "mindestens eine Ziffer (0-9) und ein Sonderzeichen (z.B. +, $, /, !, ?)"

The support confirmed: The problem was one of the special chars.

bitte nutzen sie wenige Sonderzeichen und vor allem kein + ,&, ? oder /

So I updated my password and should wait 24 hours before I try it again ... let's see

Flaschenzug commented 1 year ago

Everything works fine now. Thanks for your incredible help Christoph!

If you ever need to set up an account, prevent special chars (even if the DHL asks for them). ! is allowed and one in the password is enough.

I asked DHL to change the description for picking a password. But I doubt that they change it.

christoph-schaeffer commented 1 year ago

Great im glad it works for you now.

But didn't your password work in soapUI?

So it might be that my lib just cant handle the special chars but im not sure

I could try to fix that if its the case.

Flaschenzug commented 1 year ago

I had the same problem in SoapUI too. So not your lib but a DHL thing.

christoph-schaeffer commented 1 year ago

Allright. I think i'll add a new Exception for that so its easier to find out.