expire = new \DateTime('now'); $this->refreshExpire = new \DateTime('now'); } /** * {@inheritdoc} */ public function authLogin( string $login, string $password, ?string $client = null, ?string $payload = null ) : int { $this->apiKey = $client ?? $this->client; $this->login = $login; $this->password = $password; $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION; $request = new HttpRequest(new HttpUri($uri)); $request->setMethod(RequestMethod::GET); $request->header->set('Authorization', 'Basic ' . \base64_encode($this->login . ':' . $this->password)); $request->header->set('dhl-api-key', $this->apiKey); $this->expire = new \DateTime('now'); $response = Rest::request($request); switch ($response->header->status) { case 400: case 500: $status = AuthStatus::FAILED; break; case 403: $status = AuthStatus::BLOCKED; break; case 429: $status = AuthStatus::LIMIT_EXCEEDED; break; case 200: $status = AuthStatus::OK; break; default: $status = AuthStatus::FAILED; } return $status; } /** * {@inheritdoc} */ public function authRedirectLogin( string $client, ?string $redirect = null, array $payload = [] ) : HttpRequest { return new HttpRequest(); } /** * {@inheritdoc} */ public function tokenFromRedirect( string $login, string $password, HttpRequest $redirect ) : int { return AuthStatus::FAILED; } /** * {@inheritdoc} */ public function refreshToken() : int { return AuthStatus::FAILED; } /** * {@inheritdoc} */ public function authApiKey(string $key) : int { $this->apiKey = $key; $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION; $request = new HttpRequest(new HttpUri($uri)); $request->setMethod(RequestMethod::GET); $request->header->set('Accept', MimeType::M_JSON); $request->header->set('dhl-api-key', $key); $response = Rest::request($request); switch ($response->header->status) { case 400: case 500: $status = AuthStatus::FAILED; break; case 403: $status = AuthStatus::BLOCKED; break; case 429: $status = AuthStatus::LIMIT_EXCEEDED; break; case 200: $status = AuthStatus::OK; break; default: $status = AuthStatus::FAILED; } return $status; } /** * {@inheritdoc} */ public function ship( array $sender, array $shipFrom, array $receiver, array $package, array $data ) : array { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION .'/orders'; $httpUri = new HttpUri($uri); $httpUri->addQuery('validate', 'true'); // @todo implement docFormat $httpUri->addQuery('docFormat', 'PDF'); // @todo implement printFormat // Available values : A4, 910-300-600, 910-300-610, 910-300-700, 910-300-700-oz, 910-300-710, 910-300-300, 910-300-300-oz, 910-300-400, 910-300-410, 100x70mm // If not set, default specified in customer portal will be used // @todo implement as class setting //$request->setData('printFormat', ''); $request = new HttpRequest($httpUri); $request->setMethod(RequestMethod::POST); $request->header->set('Content-Type', MimeType::M_JSON); $request->header->set('Accept-Language', 'en-US'); $request->header->set('Authorization', 'Basic ' . \base64_encode($this->login . ':' . $this->password)); $request->setData('STANDARD_GRUPPENPROFIL', 'PDF'); $shipments = [ [ 'product' => 'V01PAK', // V53WPAK, V53WPAK 'billingNumber' => $data['costcenter'], // @todo maybe dhl number, check 'refNo' => $package['id'], 'shipper' => [ 'name1' => $sender['name'], 'addressStreet' => $sender['address'], 'additionalAddressInformation1' => $sender['address_addition'], 'postalCode' => $sender['zip'], 'city' => $sender['city'], 'country' => ISO3166CharEnum::getBy2Code($sender['country_code']), 'email' => $sender['email'], 'phone' => $sender['phone'], ], 'consignee' => [ 'name1' => $receiver['name'], 'addressStreet' => $receiver['address'], 'additionalAddressInformation1' => $receiver['address_addition'], 'postalCode' => $receiver['zip'], 'city' => $receiver['city'], 'country' => ISO3166CharEnum::getBy2Code($receiver['country_code']), 'email' => $receiver['email'], 'phone' => $receiver['phone'], ], 'details' => [ 'dim' => [ 'uom' => 'mm', 'height' => $package['height'], 'length' => $package['length'], 'width' => $package['width'], ], 'weight' => [ 'uom' => 'g', 'value' => $package['weight'], ], ], ], ]; $request->setData('shipments', $shipments); $response = Rest::request($request); if ($response->header->status !== 200) { return []; } $result = $response->getDataArray('items') ?? []; $labelUri = new HttpUri($result[0]['label']['url']); $label = $this->label($labelUri->getQuery('token')); return [ 'id' => $result[0]['shipmentNo'], 'label' => [ 'code' => $result[0]['label']['format'], 'url' => $result[0]['label']['url'], 'data' => $label['data'], ], 'packages' => [ 'id' => $result[0]['shipmentNo'], 'label' => [ 'code' => $result[0]['label']['format'], 'url' => $result[0]['label']['url'], 'data' => $label['data'], ], ], ]; } /** * {@inheritdoc} */ public function cancel(string $shipment, array $packages = []) : bool { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION .'/orders'; $request = new HttpRequest(new HttpUri($uri)); $request->setMethod(RequestMethod::DELETE); $request->header->set('Accept-Language', 'en-US'); $request->header->set('Authorization', 'Basic ' . \base64_encode($this->login . ':' . $this->password)); $request->setData('profile', 'STANDARD_GRUPPENPROFIL'); $request->setData('shipment', $shipment); $response = Rest::request($request); return $response->header->status === 200; } /** * Get shipment information (no tracking) * * This includes depending on service labels, shipping documents and general shipment information. * For some services this function simply re-creates the data from ship(). * * @param string $shipment Shipment id or token * * @return array * * @since 1.0.0 */ public function info(string $shipment) : array { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION .'/orders'; $httpUri = new HttpUri($uri); $httpUri->addQuery('shipment', $shipment); // @todo implement docFormat etc $httpUri->addQuery('docFormat', 'PDF'); $request = new HttpRequest($httpUri); $request->setMethod(RequestMethod::GET); $request->header->set('Accept-Language', 'en-US'); $request->header->set('Authorization', 'Basic ' . \base64_encode($this->login . ':' . $this->password)); $response = Rest::request($request); if ($response->header->status !== 200) { return []; } $result = $response->getDataArray('items') ?? []; $labelUri = new HttpUri($result[0]['label']['url']); $label = $this->label($labelUri->getQuery('token')); return [ 'id' => $result[0]['shipmentNo'], 'label' => [ 'code' => $result[0]['label']['format'], 'url' => $result[0]['label']['url'], 'data' => $label['data'], ], 'packages' => [ 'id' => $result[0]['shipmentNo'], 'label' => [ 'code' => $result[0]['label']['format'], 'url' => $result[0]['label']['url'], 'data' => $label['data'], ], ], ]; } /** * Get label information for shipment * * @param string $shipment Shipment id or token * * @return array * * @since 1.0.0 */ public function label(string $shipment) : array { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION .'/labels'; $httpUri = new HttpUri($uri); $httpUri->addQuery('token', $shipment); $request = new HttpRequest($httpUri); $request->setMethod(RequestMethod::GET); $request->header->set('Content-Type', MimeType::M_PDF); $response = Rest::request($request); if ($response->header->status !== 200) { return []; } return [ 'data' => $response->getData(), ]; } /** * {@inheritdoc} */ public function track(string $shipment) : array { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX2_URL; $uri = $base . '/track/shipments'; $httpUri = new HttpUri($uri); $httpUri->addQuery('trackingnumber', $shipment); $httpUri->addQuery('limit', '10'); // @todo implement: express, parcel-de, ecommerce, dgf, parcel-uk, post-de, sameday, freight, parcel-nl, parcel-pl, dsc, ecommerce-europe, svb //$httpUri->addQuery('service', ''); // @odo: implement //$httpUri->addQuery('requesterCountryCode', ''); //$httpUri->addQuery('originCountryCode', ''); //$httpUri->addQuery('recipientPostalCode', ''); $request = new HttpRequest($httpUri); $request->setMethod(RequestMethod::GET); $request->header->set('accept', MimeType::M_JSON); $request->header->set('dhl-api-key', $this->apiKey); $response = Rest::request($request); if ($response->header->status !== 200) { return []; } $shipments = $response->getDataArray('shipments') ?? []; $tracking = []; // @todo add general shipment status (not just for individual packages) foreach ($shipments as $shipment) { $packages = []; $package = $shipment; $activities = []; foreach ($package['events'] as $activity) { $activities[] = [ 'date' => new \DateTime($activity['timestamp']), 'description' => $activity['description'], 'location' => [ 'address' => [ $activity['location']['address']['streetAddress'], $activity['location']['address']['addressLocality'], ], 'city' => '', 'country' => '', 'country_code' => $activity['location']['address']['countryCode'], 'zip' => $activity['location']['address']['postalCode'], 'state' => '', ], 'status' => [ 'code' => $activity['statusCode'], 'statusCode' => $activity['statusCode'], 'description' => $activity['status'], ], ]; } $packages[] = [ 'status' => [ 'code' => $package['status']['statusCode'], 'statusCode' => $package['status']['statusCode'], 'description' => $package['status']['status'], ], 'deliveryDate' => new \DateTime($package['estimatedTimeOfDelivery']), 'count' => $package['details']['totalNumberOfPieces'], 'weight' => $package['details']['weight']['weight'], 'weight_unit' => 'g', 'activities' => $activities, 'received' => [ 'by' => $package['details']['proofOfDelivery']['familyName'], 'signature' => $package['details']['proofOfDelivery']['signatureUrl'], 'location' => '', 'date' => $package['details']['proofOfDelivery']['timestamp'], ], ]; $tracking[] = $packages; } return $tracking; } /** * Get daily manifest * * @param \DateTime $date Date of the manifest * * @return array * * @since 1.0.0 */ public function getManifest(?\DateTime $date = null) : array { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION .'/manifest'; $httpUri = new HttpUri($uri); if ($date !== null) { $httpUri->addQuery('date', $date->format('Y-m-d')); } $request = new HttpRequest($httpUri); $request->setMethod(RequestMethod::GET); $request->header->set('Accept-Language', 'en-US'); $request->header->set('Authorization', 'Basic ' . \base64_encode($this->login . ':' . $this->password)); $response = Rest::request($request); if ($response->header->status !== 200) { return []; } return [ 'date' => $response->getDataDateTime('manifestDate'), 'b64' => $response->getDataArray('manifest')['b64'] ?? '', 'zpl2' => $response->getDataArray('manifest')['zpl2'] ?? '', 'url' => $response->getDataArray('manifest')['url'] ?? '', 'format' => $response->getDataArray('manifest')['printFormat'] ?? '', ]; } /** * Finalize shipments. * * No further adjustments are possible. * * @param array $shipment Shipments to finalize. If empty = all * * @return bool * * @since 1.0.0 */ public function finalize(array $shipment = []) : bool { $base = self::$ENV === 'live' ? self::LIVE_URL : self::SANDBOX_URL; $uri = $base . '/parcel/de/shipping/' . self::API_VERSION .'/manifest'; $httpUri = new HttpUri($uri); $httpUri->addQuery('all', empty($shipment) ? 'true' : 'false'); $request = new HttpRequest($httpUri); $request->setMethod(RequestMethod::POST); $request->header->set('Content-Type', MimeType::M_JSON); $request->header->set('Authorization', 'Basic ' . \base64_encode($this->login . ':' . $this->password)); if (!empty($shipment)) { $request->setData('shipmentNumbers', $shipment); } $response = Rest::request($request); return $response->header->status === 200; } }