From db66b4363afb732612f126bbb438ff98c943bb12 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Thu, 2 Oct 2025 16:41:38 +0100
Subject: [PATCH 01/13] Set type of InvoiceDetails.amount to a string as it
requires trailing zeros.
---
resources/metadata/modifications.json | 8 ++++++++
resources/models/account-registration/v1.json | 2 +-
src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php | 4 ++--
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/resources/metadata/modifications.json b/resources/metadata/modifications.json
index 9d460253..31bb6cbb 100644
--- a/resources/metadata/modifications.json
+++ b/resources/metadata/modifications.json
@@ -36,6 +36,14 @@
"value": {
"type": "string"
}
+ },
+ {
+ "comment": "Set type of InvoiceDetails.amount to a string as it requires trailing zeros",
+ "action": "merge",
+ "path": "components.schemas.InvoiceDetails.properties.amount",
+ "value": {
+ "type": "string"
+ }
}
]
},
diff --git a/resources/models/account-registration/v1.json b/resources/models/account-registration/v1.json
index c6fc5685..ba4da4f4 100644
--- a/resources/models/account-registration/v1.json
+++ b/resources/models/account-registration/v1.json
@@ -1107,7 +1107,7 @@
"example": "2022-10-09"
},
"amount": {
- "type": "number",
+ "type": "string",
"description": "Used to validate Invoice details whether invoice is related to user or not. It accepts upto 2 digit of decimal.",
"example": 2323.22
}
diff --git a/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php b/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php
index 60f19309..4726b045 100644
--- a/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php
+++ b/src/Api/AccountRegistrationV1/Dto/InvoiceDetails.php
@@ -18,12 +18,12 @@ final class InvoiceDetails extends Dto
* @param int $number Used to validate invoice details .It accepts no decimal.
* @param string $currency Used to validate Invoice details whether invoice is related to user or not.It accepts upto 4 digits currency.
Click here to see Currency Codes
* @param string $date Used to validate Invoice details whether invoice is related to user or not. Date format is YYYY-MM-DD.
- * @param float $amount Used to validate Invoice details whether invoice is related to user or not. It accepts upto 2 digit of decimal.
+ * @param string $amount Used to validate Invoice details whether invoice is related to user or not. It accepts upto 2 digit of decimal.
*/
public function __construct(
public int $number,
public string $currency,
public string $date,
- public float $amount,
+ public string $amount,
) {}
}
From 8051cc01153b4d17aa1182252a49ba908739faa8 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 6 Oct 2025 11:48:10 +0100
Subject: [PATCH 02/13] Check auth token before each request. Refs DEV-1447
---
src/FedEx.php | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/FedEx.php b/src/FedEx.php
index b9fb235f..4f0bb6d7 100644
--- a/src/FedEx.php
+++ b/src/FedEx.php
@@ -78,6 +78,20 @@ public function resolveBaseUrl(): string
public function boot(PendingRequest $pendingRequest): void
{
+ // Check auth token before each request
+ $request = $pendingRequest->getRequest();
+ if (! $request instanceof AuthorizationV1\Requests\ApiAuthorization
+ && ! $this->getAuthenticator()?->hasNotExpired()
+ ) {
+ $cacheKey = $this->tokenCacheKey();
+ $authenticator = $this->tokenCache::get($cacheKey);
+ if ( ! $authenticator) {
+ $authenticator = $this->getAccessToken();
+ $this->tokenCache::set($cacheKey, $authenticator);
+ }
+ $this->authenticate($authenticator);
+ }
+
if ($this->transactionIdGenerator) {
$transactionId = ($this->transactionIdGenerator)($pendingRequest);
if (! is_string($transactionId)) {
From 375e26eb67873f689c74b8a1ce1839a6eca10cdf Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 6 Oct 2025 18:09:57 +0100
Subject: [PATCH 03/13] Add missing 429 responses.
---
README.md | 2 +-
resources/models/account-registration/v1.json | 40 +++++++
resources/models/address-validation/v1.json | 10 ++
resources/models/authorization/v1.json | 10 ++
resources/models/consolidation/v1.json | 90 ++++++++++++++++
resources/models/freight-ltl/v1.json | 50 +++++++++
resources/models/global-trade/v1.json | 10 ++
resources/models/ground-eod-close/v1.json | 20 ++++
resources/models/locations-search/v1.json | 10 ++
resources/models/open-ship/v1.json | 100 ++++++++++++++++++
resources/models/pickup-request/v1.json | 30 ++++++
.../models/postal-code-validation/v1.json | 10 ++
resources/models/rates-transit-times/v1.json | 10 ++
resources/models/service-availability/v1.json | 30 ++++++
resources/models/ship-dg-hazmat/v1.json | 30 ++++++
resources/models/ship/v1.json | 60 +++++++++++
resources/models/track/v1.json | 60 +++++++++++
.../models/trade-documents-upload/v1.json | 40 +++++++
.../Requests/CreateCustomerKey.php | 2 +-
.../Requests/SendPin.php | 2 +-
.../Requests/ValidateInvoice.php | 2 +-
.../Requests/ValidatePin.php | 2 +-
.../Requests/ValidateAddress.php | 2 +-
.../Requests/ApiAuthorization.php | 2 +-
.../Requests/ConfirmConsolidationResults.php | 2 +-
.../Requests/ConfirmConsolidations.php | 2 +-
.../Requests/ConsolidationShipmentResults.php | 2 +-
.../Requests/CreateConsolidation.php | 2 +-
.../Requests/CreateConsolidationShipment.php | 2 +-
.../Requests/DeleteConsolidation.php | 2 +-
.../Requests/DeleteConsolidationShipments.php | 2 +-
.../Requests/ModifyConsolidation.php | 2 +-
.../Requests/RetrieveConsolidation.php | 2 +-
.../Requests/CancelFreightPickup.php | 2 +-
.../CheckFreightPickupAvailability.php | 2 +-
.../Requests/CreateFreightPickup.php | 2 +-
.../Requests/FreightRateQuote.php | 2 +-
.../FreightLTLV1/Requests/FreightShipment.php | 2 +-
.../Requests/ShipmentRegulatoryDetails.php | 2 +-
.../Requests/GroundPerformEndOfDayClose.php | 2 +-
.../Requests/PerformEndOfDayClose.php | 2 +-
.../Requests/FindLocation.php | 2 +-
.../Requests/AddOpenShipmentPackages.php | 2 +-
.../Requests/ConfirmOpenShipment.php | 2 +-
.../Requests/CreateOpenShipmentt.php | 2 +-
.../Requests/DeleteOpenShipment.php | 2 +-
.../Requests/DeleteOpenShipmentPackages.php | 2 +-
.../Requests/GetOpenShipmentResults.php | 2 +-
.../Requests/ModifyOpenShipment.php | 2 +-
.../Requests/ModifyOpenShipmentPackages.php | 2 +-
.../Requests/RetrieveOpenShipment.php | 2 +-
.../Requests/RetrieveOpenShipmentPackages.php | 2 +-
.../PickupRequestV1/Requests/CancelPickup.php | 2 +-
.../Requests/CheckPickupAvailability.php | 2 +-
.../PickupRequestV1/Requests/CreatePickup.php | 2 +-
.../Requests/ValidatePostal.php | 2 +-
.../Requests/RateAndTransitTimes.php | 2 +-
.../Requests/GetPackageAndServiceOptions.php | 2 +-
.../Requests/GetSpecialServiceOptions.php | 2 +-
.../RetrieveServicesAndTransitTimes.php | 2 +-
.../Requests/CancelShipment.php | 2 +-
.../ShipDGHazmatV1/Requests/DgHazShipment.php | 2 +-
.../Requests/ShipmentPackageValidate.php | 2 +-
src/Api/ShipV1/Requests/CancelShipment.php | 2 +-
src/Api/ShipV1/Requests/CancelTag.php | 2 +-
src/Api/ShipV1/Requests/CreateShipment.php | 2 +-
src/Api/ShipV1/Requests/CreateTag.php | 2 +-
.../GetConfirmedShipmentAsyncResults.php | 2 +-
.../Requests/ShipmentPackageValidate.php | 2 +-
src/Api/TrackV1/Requests/SendNotification.php | 2 +-
.../TrackV1/Requests/TrackByReferences.php | 2 +-
src/Api/TrackV1/Requests/TrackByTcn.php | 2 +-
.../Requests/TrackByTrackingNumber.php | 2 +-
.../Requests/TrackMultiplePieceShipment.php | 2 +-
.../TrackV1/Requests/TrackingDocuments.php | 2 +-
.../Requests/ImageUploadServiceInfo.php | 2 +-
.../Requests/UploadEtdFiles.php | 2 +-
.../Requests/UploadMultiEncodedEtDfiles.php | 2 +-
.../Requests/UploadMultiEtDfiles.php | 2 +-
src/Generator/Schema/Refactorer.php | 13 ++-
80 files changed, 683 insertions(+), 64 deletions(-)
diff --git a/README.md b/README.md
index 722f89c4..c59e90cc 100644
--- a/README.md
+++ b/README.md
@@ -138,7 +138,7 @@ A wide variety of refactoring processes are run on the raw schemas to get them r
* **Combine raw schemas:** If a given API segment has multiple OpenAPI model files, they are combined into a single file. Routes, methods, and component schemas are aggregated across original OpenAPI files, and combined into a single model object. So far, we have not found any OpenAPI model files from the same API segment with overlapping routes, nor conflicting component schemas, so we're not handling those cases.
* **Clean combined schema:** There are often inconsistencies, missing elements, and duplicated or unnecessary component schemas in the OpenAPI models, so this step takes care of those issues. Some specific current steps that are taken:
- * Filling in missing `404` response schemas
+ * Filling in missing `404` and `429` response schemas
* Removing unnecessary `allOf` and `oneOf` definitions in requests, responses, and component schemas
* Setting the types of schemas and properties that are missing explicit types
* Flattening multi-level `$ref`s, where a component or property is a `$ref` to another `$ref`.
diff --git a/resources/models/account-registration/v1.json b/resources/models/account-registration/v1.json
index ba4da4f4..4582a29f 100644
--- a/resources/models/account-registration/v1.json
+++ b/resources/models/account-registration/v1.json
@@ -142,6 +142,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -365,6 +375,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_2"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -546,6 +566,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_3"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -767,6 +797,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_3"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/resources/models/address-validation/v1.json b/resources/models/address-validation/v1.json
index 0365f437..cca19ace 100644
--- a/resources/models/address-validation/v1.json
+++ b/resources/models/address-validation/v1.json
@@ -142,6 +142,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/authorization/v1.json b/resources/models/authorization/v1.json
index 2c14c277..2d4e08b5 100644
--- a/resources/models/authorization/v1.json
+++ b/resources/models/authorization/v1.json
@@ -123,6 +123,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/resources/models/consolidation/v1.json b/resources/models/consolidation/v1.json
index 0687834f..57d96db8 100644
--- a/resources/models/consolidation/v1.json
+++ b/resources/models/consolidation/v1.json
@@ -181,6 +181,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -397,6 +407,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"tags": [
@@ -585,6 +605,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -803,6 +833,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -1021,6 +1061,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -1239,6 +1289,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -1457,6 +1517,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -1648,6 +1718,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_6"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -1866,6 +1946,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/resources/models/freight-ltl/v1.json b/resources/models/freight-ltl/v1.json
index 5444e1c6..dda892bb 100644
--- a/resources/models/freight-ltl/v1.json
+++ b/resources/models/freight-ltl/v1.json
@@ -134,6 +134,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -337,6 +347,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -549,6 +569,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -752,6 +782,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -955,6 +995,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/global-trade/v1.json b/resources/models/global-trade/v1.json
index bf0183fe..05841000 100644
--- a/resources/models/global-trade/v1.json
+++ b/resources/models/global-trade/v1.json
@@ -84,6 +84,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/ground-eod-close/v1.json b/resources/models/ground-eod-close/v1.json
index 74e7d659..f7ad6ae3 100644
--- a/resources/models/ground-eod-close/v1.json
+++ b/resources/models/ground-eod-close/v1.json
@@ -140,6 +140,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -357,6 +367,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/locations-search/v1.json b/resources/models/locations-search/v1.json
index fd3ac164..214e213e 100644
--- a/resources/models/locations-search/v1.json
+++ b/resources/models/locations-search/v1.json
@@ -182,6 +182,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/resources/models/open-ship/v1.json b/resources/models/open-ship/v1.json
index bd3677d0..e1d6ac29 100644
--- a/resources/models/open-ship/v1.json
+++ b/resources/models/open-ship/v1.json
@@ -124,6 +124,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -343,6 +353,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -561,6 +581,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -751,6 +781,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -969,6 +1009,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1159,6 +1209,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1379,6 +1439,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1599,6 +1669,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1799,6 +1879,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -2019,6 +2109,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/pickup-request/v1.json b/resources/models/pickup-request/v1.json
index 8386db72..b17168f2 100644
--- a/resources/models/pickup-request/v1.json
+++ b/resources/models/pickup-request/v1.json
@@ -141,6 +141,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -361,6 +371,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -581,6 +601,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/postal-code-validation/v1.json b/resources/models/postal-code-validation/v1.json
index cd1a5fd6..4ed2b10c 100644
--- a/resources/models/postal-code-validation/v1.json
+++ b/resources/models/postal-code-validation/v1.json
@@ -141,6 +141,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/rates-transit-times/v1.json b/resources/models/rates-transit-times/v1.json
index 232fcc5b..6aeb0aea 100644
--- a/resources/models/rates-transit-times/v1.json
+++ b/resources/models/rates-transit-times/v1.json
@@ -141,6 +141,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/service-availability/v1.json b/resources/models/service-availability/v1.json
index f0a0f533..2bbeaf6c 100644
--- a/resources/models/service-availability/v1.json
+++ b/resources/models/service-availability/v1.json
@@ -155,6 +155,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -333,6 +343,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_2"
+ }
+ }
+ }
}
},
"parameters": [
@@ -553,6 +573,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/ship-dg-hazmat/v1.json b/resources/models/ship-dg-hazmat/v1.json
index 462cc8e5..723e27aa 100644
--- a/resources/models/ship-dg-hazmat/v1.json
+++ b/resources/models/ship-dg-hazmat/v1.json
@@ -176,6 +176,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -394,6 +404,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_2"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -610,6 +630,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/resources/models/ship/v1.json b/resources/models/ship/v1.json
index 4699c860..d33c32dd 100644
--- a/resources/models/ship/v1.json
+++ b/resources/models/ship/v1.json
@@ -141,6 +141,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -361,6 +371,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -625,6 +645,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -767,6 +797,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -987,6 +1027,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1258,6 +1308,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/resources/models/track/v1.json b/resources/models/track/v1.json
index 372a7351..009de13d 100644
--- a/resources/models/track/v1.json
+++ b/resources/models/track/v1.json
@@ -141,6 +141,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -362,6 +372,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -583,6 +603,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -804,6 +834,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1025,6 +1065,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
@@ -1246,6 +1296,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"parameters": [
diff --git a/resources/models/trade-documents-upload/v1.json b/resources/models/trade-documents-upload/v1.json
index 2009be19..77647082 100644
--- a/resources/models/trade-documents-upload/v1.json
+++ b/resources/models/trade-documents-upload/v1.json
@@ -175,6 +175,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"servers": [
@@ -377,6 +387,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO"
+ }
+ }
+ }
}
},
"servers": [
@@ -637,6 +657,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_2"
+ }
+ }
+ }
}
},
"x-code-samples": [
@@ -1019,6 +1049,16 @@
}
}
}
+ },
+ "429": {
+ "description": "Too many requests.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponseVO_2"
+ }
+ }
+ }
}
},
"x-code-samples": [
diff --git a/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php b/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php
index 494e78fd..e55b9bf6 100644
--- a/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php
+++ b/src/Api/AccountRegistrationV1/Requests/CreateCustomerKey.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): IrcpResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => IrcpResponseVo::class,
- 400, 401, 403, 404, 500 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/AccountRegistrationV1/Requests/SendPin.php b/src/Api/AccountRegistrationV1/Requests/SendPin.php
index 27c01ffa..0ab6081a 100644
--- a/src/Api/AccountRegistrationV1/Requests/SendPin.php
+++ b/src/Api/AccountRegistrationV1/Requests/SendPin.php
@@ -54,7 +54,7 @@ public function createDtoFromResponse(Response $response): PinGenerationOutputVo
$status = $response->status();
$responseCls = match ($status) {
200 => PinGenerationOutputVo::class,
- 400, 401, 403, 404, 500 => ErrorResponseVo3::class,
+ 400, 401, 403, 404, 500, 429 => ErrorResponseVo3::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php b/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php
index 579398f2..f627f8bc 100644
--- a/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php
+++ b/src/Api/AccountRegistrationV1/Requests/ValidateInvoice.php
@@ -55,7 +55,7 @@ public function createDtoFromResponse(Response $response): IrcpResponseVo2|Error
$status = $response->status();
$responseCls = match ($status) {
200 => IrcpResponseVo2::class,
- 400, 404, 500 => ErrorResponseVo3::class,
+ 400, 404, 500, 429 => ErrorResponseVo3::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/AccountRegistrationV1/Requests/ValidatePin.php b/src/Api/AccountRegistrationV1/Requests/ValidatePin.php
index 07d43322..f368f7fb 100644
--- a/src/Api/AccountRegistrationV1/Requests/ValidatePin.php
+++ b/src/Api/AccountRegistrationV1/Requests/ValidatePin.php
@@ -54,7 +54,7 @@ public function createDtoFromResponse(Response $response): IrcpResponseVo2|Error
$status = $response->status();
$responseCls = match ($status) {
200 => IrcpResponseVo2::class,
- 400, 404, 500 => ErrorResponseVo2::class,
+ 400, 404, 500, 429 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/AddressValidationV1/Requests/ValidateAddress.php b/src/Api/AddressValidationV1/Requests/ValidateAddress.php
index 2953376b..a5365f4d 100644
--- a/src/Api/AddressValidationV1/Requests/ValidateAddress.php
+++ b/src/Api/AddressValidationV1/Requests/ValidateAddress.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): AdvcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => AdvcResponseVo::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
index a7194cdf..3cfb1e49 100644
--- a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
+++ b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(Response1 $response): Response|ErrorRespon
$status = $response->status();
$responseCls = match ($status) {
200 => Response::class,
- 401, 500, 503 => ErrorResponseVo::class,
+ 401, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php b/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php
index a21ac046..e91e6951 100644
--- a/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php
+++ b/src/Api/ConsolidationV1/Requests/ConfirmConsolidationResults.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ConfirmConsolidationResultsResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php b/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php
index a34d007d..a2d2df5f 100644
--- a/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php
+++ b/src/Api/ConsolidationV1/Requests/ConfirmConsolidations.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ConfirmConsolidationResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php b/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php
index c5ca0a85..0a20d3d4 100644
--- a/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php
+++ b/src/Api/ConsolidationV1/Requests/ConsolidationShipmentResults.php
@@ -53,7 +53,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ConsolidationShipmentResultsResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/CreateConsolidation.php b/src/Api/ConsolidationV1/Requests/CreateConsolidation.php
index 029f7004..b7fea3ec 100644
--- a/src/Api/ConsolidationV1/Requests/CreateConsolidation.php
+++ b/src/Api/ConsolidationV1/Requests/CreateConsolidation.php
@@ -56,7 +56,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => CreateConsolidationResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php b/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php
index dd6a86d4..eb643320 100644
--- a/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php
+++ b/src/Api/ConsolidationV1/Requests/CreateConsolidationShipment.php
@@ -55,7 +55,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => CreateConsolidationShipmentResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php b/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php
index 7e3fab83..393060d5 100644
--- a/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php
+++ b/src/Api/ConsolidationV1/Requests/DeleteConsolidation.php
@@ -56,7 +56,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => DeleteConsolidationResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php b/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php
index f24d96b3..ed8e5946 100644
--- a/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php
+++ b/src/Api/ConsolidationV1/Requests/DeleteConsolidationShipments.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVo::class,
- 400, 401, 403, 500 => ErrorResponseVo6::class,
+ 400, 401, 403, 500, 429 => ErrorResponseVo6::class,
404 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php b/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php
index c9f8f306..5b354a5f 100644
--- a/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php
+++ b/src/Api/ConsolidationV1/Requests/ModifyConsolidation.php
@@ -56,7 +56,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ModifyConsolidationResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php b/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php
index 6c2d68ce..195575e1 100644
--- a/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php
+++ b/src/Api/ConsolidationV1/Requests/RetrieveConsolidation.php
@@ -55,7 +55,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => RetrieveConsolidationResponse::class,
- 400 => ErrorResponseVo::class,
+ 400, 429 => ErrorResponseVo::class,
401 => ErrorResponseVo2::class,
403 => ErrorResponseVo3::class,
404 => ErrorResponseVo4::class,
diff --git a/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php b/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php
index dcf007df..4b14f562 100644
--- a/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php
+++ b/src/Api/FreightLTLV1/Requests/CancelFreightPickup.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): PudcResponseVoCancelP
$status = $response->status();
$responseCls = match ($status) {
200 => PudcResponseVoCancelPickup::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php b/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php
index 9e51f762..132610b2 100644
--- a/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php
+++ b/src/Api/FreightLTLV1/Requests/CheckFreightPickupAvailability.php
@@ -53,7 +53,7 @@ public function createDtoFromResponse(Response $response): PudcResponseVoPickupA
$status = $response->status();
$responseCls = match ($status) {
200 => PudcResponseVoPickupAvailaibility::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php b/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php
index a1ccf576..c85597b8 100644
--- a/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php
+++ b/src/Api/FreightLTLV1/Requests/CreateFreightPickup.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(Response $response): PudcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => PudcResponseVo::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/FreightLTLV1/Requests/FreightRateQuote.php b/src/Api/FreightLTLV1/Requests/FreightRateQuote.php
index c5dd454b..10f2a6a0 100644
--- a/src/Api/FreightLTLV1/Requests/FreightRateQuote.php
+++ b/src/Api/FreightLTLV1/Requests/FreightRateQuote.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): RatcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => RatcResponseVo::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/FreightLTLV1/Requests/FreightShipment.php b/src/Api/FreightLTLV1/Requests/FreightShipment.php
index ce993809..cd39a45d 100644
--- a/src/Api/FreightLTLV1/Requests/FreightShipment.php
+++ b/src/Api/FreightLTLV1/Requests/FreightShipment.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVo::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php b/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php
index baa828a9..46804d88 100644
--- a/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php
+++ b/src/Api/GlobalTradeV1/Requests/ShipmentRegulatoryDetails.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): GticResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => GticResponseVo::class,
- 400, 401, 404, 422 => ErrorResponseVo::class,
+ 400, 401, 404, 422, 429 => ErrorResponseVo::class,
500 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php b/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php
index fb040ef9..f91abd5e 100644
--- a/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php
+++ b/src/Api/GroundEODCloseV1/Requests/GroundPerformEndOfDayClose.php
@@ -47,7 +47,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVo::class,
- 400, 401, 403, 404, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 503, 429 => ErrorResponseVo::class,
500 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php b/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php
index b7a19a94..a318e115 100644
--- a/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php
+++ b/src/Api/GroundEODCloseV1/Requests/PerformEndOfDayClose.php
@@ -47,7 +47,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVo::class,
- 400, 401, 403, 404, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 503, 429 => ErrorResponseVo::class,
500 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/LocationsSearchV1/Requests/FindLocation.php b/src/Api/LocationsSearchV1/Requests/FindLocation.php
index 36f760b6..dfd2f629 100644
--- a/src/Api/LocationsSearchV1/Requests/FindLocation.php
+++ b/src/Api/LocationsSearchV1/Requests/FindLocation.php
@@ -53,7 +53,7 @@ public function createDtoFromResponse(Response $response): LoccResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => LoccResponseVo::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php
index 63794534..1fbb185d 100644
--- a/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php
+++ b/src/Api/OpenShipV1/Requests/AddOpenShipmentPackages.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoAddPackagesToOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php b/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php
index bce2d6e4..5c41a116 100644
--- a/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php
+++ b/src/Api/OpenShipV1/Requests/ConfirmOpenShipment.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoConfirmOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php b/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php
index f6d5a901..7e176c23 100644
--- a/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php
+++ b/src/Api/OpenShipV1/Requests/CreateOpenShipmentt.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoCreateOpenShipment::class,
- 400, 401, 403, 404, 500 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 429 => ErrorResponseVo::class,
503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php b/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php
index d21a9365..35dd4ea7 100644
--- a/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php
+++ b/src/Api/OpenShipV1/Requests/DeleteOpenShipment.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoDeleteOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php
index d7457d8f..2f9451ee 100644
--- a/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php
+++ b/src/Api/OpenShipV1/Requests/DeleteOpenShipmentPackages.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoDeletePackages::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php b/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php
index 58bf86b4..f5c36f24 100644
--- a/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php
+++ b/src/Api/OpenShipV1/Requests/GetOpenShipmentResults.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoGetOpenShipmentResults::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php b/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php
index 5c712cb3..2e93c02d 100644
--- a/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php
+++ b/src/Api/OpenShipV1/Requests/ModifyOpenShipment.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoModifyOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php
index 04a691ba..e9a8e1bd 100644
--- a/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php
+++ b/src/Api/OpenShipV1/Requests/ModifyOpenShipmentPackages.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoModifyPackageInOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php b/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php
index 9417bbc6..4ae6e10e 100644
--- a/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php
+++ b/src/Api/OpenShipV1/Requests/RetrieveOpenShipment.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoRetrieveOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php b/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php
index 2f5117fc..8ee0aaa5 100644
--- a/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php
+++ b/src/Api/OpenShipV1/Requests/RetrieveOpenShipmentPackages.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoRetrievePackageInOpenShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/PickupRequestV1/Requests/CancelPickup.php b/src/Api/PickupRequestV1/Requests/CancelPickup.php
index 9cbe2284..a19f9771 100644
--- a/src/Api/PickupRequestV1/Requests/CancelPickup.php
+++ b/src/Api/PickupRequestV1/Requests/CancelPickup.php
@@ -53,7 +53,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => PudcResponseVoCancelPickup::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php b/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php
index 4e83cf1c..951ca97c 100644
--- a/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php
+++ b/src/Api/PickupRequestV1/Requests/CheckPickupAvailability.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => PudcResponseVoPickupAvailability::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/PickupRequestV1/Requests/CreatePickup.php b/src/Api/PickupRequestV1/Requests/CreatePickup.php
index f3c23055..e544f0a5 100644
--- a/src/Api/PickupRequestV1/Requests/CreatePickup.php
+++ b/src/Api/PickupRequestV1/Requests/CreatePickup.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => PudcResponseVoCreatePickup::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php b/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php
index 0c1c518b..0aa7a4a9 100644
--- a/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php
+++ b/src/Api/PostalCodeValidationV1/Requests/ValidatePostal.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): CountryCxsResponseVo|
$status = $response->status();
$responseCls = match ($status) {
200 => CountryCxsResponseVo::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php b/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php
index 5991eb79..dcb2a985 100644
--- a/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php
+++ b/src/Api/RatesAndTransitTimesV1/Requests/RateAndTransitTimes.php
@@ -54,7 +54,7 @@ public function createDtoFromResponse(Response $response): RatcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => RatcResponseVo::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php b/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php
index 4e5d5b4f..0e71703b 100644
--- a/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php
+++ b/src/Api/ServiceAvailabilityV1/Requests/GetPackageAndServiceOptions.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): CmdcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => CmdcResponseVo::class,
- 400, 401, 403, 404, 503 => ErrorResponseVo2::class,
+ 400, 401, 403, 404, 503, 429 => ErrorResponseVo2::class,
500 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php b/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php
index 304a3d84..c3426476 100644
--- a/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php
+++ b/src/Api/ServiceAvailabilityV1/Requests/GetSpecialServiceOptions.php
@@ -53,7 +53,7 @@ public function createDtoFromResponse(Response $response): AvailabilitycxsRespon
$status = $response->status();
$responseCls = match ($status) {
200 => AvailabilitycxsResponseVo::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php b/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php
index 7ab51b20..6bd1a6cd 100644
--- a/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php
+++ b/src/Api/ServiceAvailabilityV1/Requests/RetrieveServicesAndTransitTimes.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): TransitTimeOutputVo|E
$status = $response->status();
$responseCls = match ($status) {
200 => TransitTimeOutputVo::class,
- 400, 401, 404 => ErrorResponseVo::class,
+ 400, 401, 404, 429 => ErrorResponseVo::class,
403, 500 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php b/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php
index a7572f6a..5d2a9053 100644
--- a/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php
+++ b/src/Api/ShipDGHazmatV1/Requests/CancelShipment.php
@@ -56,7 +56,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoCancelShipment::class,
- 400 => ErrorResponseVo2::class,
+ 400, 429 => ErrorResponseVo2::class,
401 => ErrorResponseVo3::class,
403 => ErrorResponseVo4::class,
404 => ErrorResponseVo5::class,
diff --git a/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php b/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php
index fa9d654d..dc03ecd6 100644
--- a/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php
+++ b/src/Api/ShipDGHazmatV1/Requests/DgHazShipment.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVo::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php b/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php
index 1f45f1ef..d8bc6aae 100644
--- a/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php
+++ b/src/Api/ShipDGHazmatV1/Requests/ShipmentPackageValidate.php
@@ -57,7 +57,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVoValidat
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoValidate::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipV1/Requests/CancelShipment.php b/src/Api/ShipV1/Requests/CancelShipment.php
index f8df0d82..658a6466 100644
--- a/src/Api/ShipV1/Requests/CancelShipment.php
+++ b/src/Api/ShipV1/Requests/CancelShipment.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoCancelShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipV1/Requests/CancelTag.php b/src/Api/ShipV1/Requests/CancelTag.php
index f53ab19b..374f20f7 100644
--- a/src/Api/ShipV1/Requests/CancelTag.php
+++ b/src/Api/ShipV1/Requests/CancelTag.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVo|ErrorR
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVo::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipV1/Requests/CreateShipment.php b/src/Api/ShipV1/Requests/CreateShipment.php
index 3311de49..8d95e24b 100644
--- a/src/Api/ShipV1/Requests/CreateShipment.php
+++ b/src/Api/ShipV1/Requests/CreateShipment.php
@@ -53,7 +53,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoShipShipment::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipV1/Requests/CreateTag.php b/src/Api/ShipV1/Requests/CreateTag.php
index fd33d650..c67bc03f 100644
--- a/src/Api/ShipV1/Requests/CreateTag.php
+++ b/src/Api/ShipV1/Requests/CreateTag.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVoCreateT
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoCreateTag::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php b/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php
index b1b8ee17..006bb415 100644
--- a/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php
+++ b/src/Api/ShipV1/Requests/GetConfirmedShipmentAsyncResults.php
@@ -49,7 +49,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoGetOpenShipmentResults::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/ShipV1/Requests/ShipmentPackageValidate.php b/src/Api/ShipV1/Requests/ShipmentPackageValidate.php
index 6838e7b3..815c8259 100644
--- a/src/Api/ShipV1/Requests/ShipmentPackageValidate.php
+++ b/src/Api/ShipV1/Requests/ShipmentPackageValidate.php
@@ -57,7 +57,7 @@ public function createDtoFromResponse(Response $response): ShpcResponseVoValidat
$status = $response->status();
$responseCls = match ($status) {
200 => ShpcResponseVoValidate::class,
- 400, 401, 403, 404, 500 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TrackV1/Requests/SendNotification.php b/src/Api/TrackV1/Requests/SendNotification.php
index b66d9893..fdc6f2ab 100644
--- a/src/Api/TrackV1/Requests/SendNotification.php
+++ b/src/Api/TrackV1/Requests/SendNotification.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => TrkcResponseVoNotifications::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TrackV1/Requests/TrackByReferences.php b/src/Api/TrackV1/Requests/TrackByReferences.php
index f5b76043..4aea9b78 100644
--- a/src/Api/TrackV1/Requests/TrackByReferences.php
+++ b/src/Api/TrackV1/Requests/TrackByReferences.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => TrkcResponseVoReferenceNumber::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TrackV1/Requests/TrackByTcn.php b/src/Api/TrackV1/Requests/TrackByTcn.php
index 9084c472..5b519d80 100644
--- a/src/Api/TrackV1/Requests/TrackByTcn.php
+++ b/src/Api/TrackV1/Requests/TrackByTcn.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(Response $response): TrkcResponseVoTcn|Err
$status = $response->status();
$responseCls = match ($status) {
200 => TrkcResponseVoTcn::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TrackV1/Requests/TrackByTrackingNumber.php b/src/Api/TrackV1/Requests/TrackByTrackingNumber.php
index c0069b3b..246e5b68 100644
--- a/src/Api/TrackV1/Requests/TrackByTrackingNumber.php
+++ b/src/Api/TrackV1/Requests/TrackByTrackingNumber.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
200 => TrkcResponseVoTrackingNumber::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php b/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php
index cfd6bde4..ddd7066a 100644
--- a/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php
+++ b/src/Api/TrackV1/Requests/TrackMultiplePieceShipment.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): TrkcResponseVoAssocia
$status = $response->status();
$responseCls = match ($status) {
200 => TrkcResponseVoAssociated::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TrackV1/Requests/TrackingDocuments.php b/src/Api/TrackV1/Requests/TrackingDocuments.php
index 3d6beed6..f3b31b3e 100644
--- a/src/Api/TrackV1/Requests/TrackingDocuments.php
+++ b/src/Api/TrackV1/Requests/TrackingDocuments.php
@@ -47,7 +47,7 @@ public function createDtoFromResponse(Response $response): TrkcResponseVoSpod|Er
$status = $response->status();
$responseCls = match ($status) {
200 => TrkcResponseVoSpod::class,
- 400, 500 => ErrorResponseVo::class,
+ 400, 500, 429 => ErrorResponseVo::class,
401, 403, 404, 503 => ErrorResponseVo2::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php b/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php
index 3623fb6b..7e5ed47b 100644
--- a/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php
+++ b/src/Api/TradeDocumentsUploadV1/Requests/ImageUploadServiceInfo.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): ImageUploadServiceOut
$status = $response->status();
$responseCls = match ($status) {
201 => ImageUploadServiceOutputVo::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php b/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php
index 87e92302..ecd10bb8 100644
--- a/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php
+++ b/src/Api/TradeDocumentsUploadV1/Requests/UploadEtdFiles.php
@@ -51,7 +51,7 @@ public function createDtoFromResponse(Response $response): DocumentResponseOutpu
$status = $response->status();
$responseCls = match ($status) {
201 => DocumentResponseOutputVo::class,
- 400, 401, 403, 404, 500, 503 => ErrorResponseVo::class,
+ 400, 401, 403, 404, 500, 503, 429 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
};
diff --git a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php
index e155b53c..550a0151 100644
--- a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php
+++ b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEncodedEtDfiles.php
@@ -52,7 +52,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
201 => MultiBasePreResponse2::class,
- 400 => ErrorResponseVo2::class,
+ 400, 429 => ErrorResponseVo2::class,
401, 403, 500, 503 => ErrorResponseVo3::class,
404 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
diff --git a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php
index fc217e34..22c0fc7c 100644
--- a/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php
+++ b/src/Api/TradeDocumentsUploadV1/Requests/UploadMultiEtDfiles.php
@@ -50,7 +50,7 @@ public function createDtoFromResponse(
$status = $response->status();
$responseCls = match ($status) {
201 => MultiBasePreResponse::class,
- 400 => ErrorResponseVo2::class,
+ 400, 429 => ErrorResponseVo2::class,
401, 403, 500, 503 => ErrorResponseVo3::class,
404 => ErrorResponseVo::class,
default => throw new Exception("Unhandled response status: {$status}")
diff --git a/src/Generator/Schema/Refactorer.php b/src/Generator/Schema/Refactorer.php
index 8b55a7fe..eff1c59e 100644
--- a/src/Generator/Schema/Refactorer.php
+++ b/src/Generator/Schema/Refactorer.php
@@ -26,8 +26,7 @@ class Refactorer
public function __construct(
private SchemaVersion $schemaVersion,
private array $schemas
- ) {
- }
+ ) {}
public function combine(): void
{
@@ -107,6 +106,16 @@ public function clean(): stdClass
$operation->responses->{'404'}->content->{'application/json'} = $mediaType;
}
+ // All FedEx endpoints are missing responses schemas for 429 responses
+ if (! isset($operation->responses->{'429'})) {
+ $operation->responses->{'429'} = new stdClass;
+ $operation->responses->{'429'}->description = 'Too many requests.';
+ $operation->responses->{'429'}->content = new stdClass;
+ $operation->responses->{'429'}->content->{'application/json'} = new stdClass;
+ $operation->responses->{'429'}->content->{'application/json'}->schema = new stdClass;
+ $operation->responses->{'429'}->content->{'application/json'}->schema->{'$ref'} = $operation->responses->{'400'}->content->{'application/json'}->schema->{'$ref'} ?? '#/components/schemas/ErrorResponseVO';
+ }
+
if (isset($operation->requestBody)) {
foreach ($operation->requestBody->content as $contentType => $mediaType) {
if (isset($mediaType->schema->oneOf)) {
From c01e45a28aaf26b994a991ec79cd693816c95589 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 6 Oct 2025 18:28:33 +0100
Subject: [PATCH 04/13] Add support for locking during authentication.
---
src/Contracts/TokenLock.php | 18 ++++++++++++++++++
src/FedEx.php | 19 +++++++++++++++----
2 files changed, 33 insertions(+), 4 deletions(-)
create mode 100644 src/Contracts/TokenLock.php
diff --git a/src/Contracts/TokenLock.php b/src/Contracts/TokenLock.php
new file mode 100644
index 00000000..9f3db576
--- /dev/null
+++ b/src/Contracts/TokenLock.php
@@ -0,0 +1,18 @@
+tokenCacheKey();
$authenticator = $this->tokenCache::get($cacheKey);
- if ( ! $authenticator) {
- $authenticator = $this->getAccessToken();
- $this->tokenCache::set($cacheKey, $authenticator);
+ if (! $authenticator) {
+ try {
+ $this->tokenLock?->lock($cacheKey);
+ // Re-check cache after acquiring lock in case another process already fetched the token
+ $authenticator = $this->tokenCache::get($cacheKey);
+ if (! $authenticator) {
+ $authenticator = $this->getAccessToken();
+ $this->tokenCache::set($cacheKey, $authenticator);
+ }
+ } finally {
+ $this->tokenLock?->unlock($cacheKey);
+ }
+ $this->authenticate($authenticator);
}
- $this->authenticate($authenticator);
}
if ($this->transactionIdGenerator) {
From 21ea8d7bbd01a312ffe12b0b310dce2deaff6b43 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 6 Oct 2025 19:57:36 +0100
Subject: [PATCH 05/13] Add rate limiting for authorization API to avoid 10min
lockout.
---
composer.json | 3 +-
.../Requests/ApiAuthorization.php | 23 +++++++++++-
src/FedEx.php | 7 ++--
src/Generator/Generators/RequestGenerator.php | 35 +++++++++++++++++++
4 files changed, 64 insertions(+), 4 deletions(-)
diff --git a/composer.json b/composer.json
index 2b9611ed..db2d0786 100644
--- a/composer.json
+++ b/composer.json
@@ -9,7 +9,8 @@
],
"license": "Apache-2.0",
"require": {
- "saloonphp/saloon": "^3.8"
+ "saloonphp/saloon": "^3.8",
+ "saloonphp/rate-limit-plugin": "^2.1"
},
"require-dev": {
"highsidelabs/saloon-sdk-generator": "^2.1",
diff --git a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
index 3cfb1e49..84f796e8 100644
--- a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
+++ b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
@@ -14,6 +14,10 @@
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Response as Response1;
+use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
+use Saloon\RateLimitPlugin\Limit;
+use Saloon\RateLimitPlugin\Stores\MemoryStore;
+use Saloon\RateLimitPlugin\Traits\HasRateLimits;
use Saloon\Traits\Body\HasFormBody;
use ShipStream\FedEx\Api\AuthorizationV1\Dto\FullSchema;
use ShipStream\FedEx\Api\AuthorizationV1\Responses\ErrorResponseVo;
@@ -30,6 +34,7 @@
class ApiAuthorization extends Request implements HasBody
{
use HasFormBody;
+ use HasRateLimits;
protected Method $method = Method::POST;
@@ -38,7 +43,10 @@ class ApiAuthorization extends Request implements HasBody
*/
public function __construct(
public FullSchema $fullSchema,
- ) {}
+ ?RateLimitStore $rateLimitStore = null,
+ ) {
+ $this->rateLimitStore = $rateLimitStore;
+ }
public function resolveEndpoint(): string
{
@@ -61,4 +69,17 @@ public function defaultBody(): array
{
return $this->fullSchema->toArray();
}
+
+ public function resolveLimits(): array
+ {
+ return [
+ Limit::allow(requests: 14)->everySeconds(5)->name('burst-threshold'),
+ Limit::allow(requests: 119)->everySeconds(120)->name('average-threshold'),
+ ];
+ }
+
+ public function resolveRateLimitStore(): RateLimitStore
+ {
+ return new MemoryStore;
+ }
}
diff --git a/src/FedEx.php b/src/FedEx.php
index 2019cec2..03c5343f 100644
--- a/src/FedEx.php
+++ b/src/FedEx.php
@@ -10,6 +10,8 @@
use Saloon\Http\Connector;
use Saloon\Http\PendingRequest;
use Saloon\Http\Request;
+use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
+use Saloon\RateLimitPlugin\Stores\MemoryStore;
use Saloon\Traits\OAuth2\ClientCredentialsGrant;
use Saloon\Traits\Plugins\AlwaysThrowOnErrors;
use ShipStream\FedEx\Api\AccountRegistrationV1;
@@ -25,8 +27,8 @@
use ShipStream\FedEx\Api\PickupRequestV1;
use ShipStream\FedEx\Api\PostalCodeValidationV1;
use ShipStream\FedEx\Api\RatesAndTransitTimesV1;
-use ShipStream\FedEx\Api\ShipV1;
use ShipStream\FedEx\Api\ShipDGHazmatV1;
+use ShipStream\FedEx\Api\ShipV1;
use ShipStream\FedEx\Api\TrackV1;
use ShipStream\FedEx\Api\TradeDocumentsUploadV1;
use ShipStream\FedEx\Auth\MemoryCache;
@@ -53,6 +55,7 @@ public function __construct(
public ?bool $proprietaryChild = false,
public ?TokenCache $tokenCache = new MemoryCache(),
public ?TokenLock $tokenLock = null,
+ public ?RateLimitStore $rateLimitStore = new MemoryStore(),
public ?Closure $transactionIdGenerator = null,
public ?string $locale = null,
) {
@@ -221,6 +224,6 @@ protected function resolveAccessTokenRequest(): Request
$args['grantType'] = $args['grantType']->value;
- return new ApiAuthorization(new FullSchema(...$args));
+ return new ApiAuthorization(new FullSchema(...$args), $this->rateLimitStore);
}
}
diff --git a/src/Generator/Generators/RequestGenerator.php b/src/Generator/Generators/RequestGenerator.php
index 1ea947a4..b9197e46 100644
--- a/src/Generator/Generators/RequestGenerator.php
+++ b/src/Generator/Generators/RequestGenerator.php
@@ -10,6 +10,10 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Nette\PhpGenerator\PhpFile;
+use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
+use Saloon\RateLimitPlugin\Limit;
+use Saloon\RateLimitPlugin\Stores\MemoryStore;
+use Saloon\RateLimitPlugin\Traits\HasRateLimits;
class RequestGenerator extends SDKRequestGenerator
{
@@ -40,6 +44,37 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile
);
}
+ // Add rate limit to authorization API to avoid triggering a 10min lockout
+ if ($endpoint->name === 'API Authorization') {
+ $namespaces = $classFile->getNamespaces();
+ $namespace = reset($namespaces);
+ $namespace->addUse(HasRateLimits::class);
+ $namespace->addUse(Limit::class);
+ $namespace->addUse(RateLimitStore::class);
+ $namespace->addUse(MemoryStore::class);
+ $classes = $classFile->getClasses();
+ $class = reset($classes);
+ $class->addTrait(HasRateLimits::class);
+ $class->addMethod('resolveLimits')
+ ->setPublic()
+ ->setReturnType('array')
+ ->addBody('return [')
+ ->addBody(" Limit::allow(requests: 14)->everySeconds(5)->name('burst-threshold'),")
+ ->addBody(" Limit::allow(requests: 119)->everySeconds(120)->name('average-threshold'),")
+ ->addBody('];');
+ $class->addMethod('resolveRateLimitStore')
+ ->setPublic()
+ ->setReturnType(RateLimitStore::class)
+ ->addBody('return new MemoryStore();');
+
+ $constructor = $class->getMethod('__construct');
+ $constructor->addParameter('rateLimitStore')
+ ->setDefaultValue(null)
+ ->setNullable(true)
+ ->setType(RateLimitStore::class);
+ $constructor->addBody('$this->rateLimitStore = $rateLimitStore;');
+ }
+
return $classFile;
}
}
From ed70faa71aa6cbe9f756e07a2af18df9b605982c Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Tue, 7 Oct 2025 11:56:12 +0100
Subject: [PATCH 06/13] Add sandbox flag to auth token cache key.
---
src/Enums/Endpoint.php | 29 +++++++++++++++++++++++++++++
src/FedEx.php | 8 +++++++-
2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/src/Enums/Endpoint.php b/src/Enums/Endpoint.php
index c6db0b82..7d250b64 100644
--- a/src/Enums/Endpoint.php
+++ b/src/Enums/Endpoint.php
@@ -19,4 +19,33 @@ public static function host(Endpoint $endpoint): string
{
return parse_url($endpoint->value, PHP_URL_HOST) ?: '';
}
+
+ public function getProductionEndpoint(): self
+ {
+ return $this->_getEndpointPair($this, false);
+ }
+
+ public function getSandboxEndpoint(): self
+ {
+ return $this->_getEndpointPair($this, true);
+ }
+
+ public function isProduction(): bool
+ {
+ return $this->_getEndpointPair($this, false) === $this;
+ }
+
+ public function isSandbox(): bool
+ {
+ return $this->_getEndpointPair($this, true) === $this;
+ }
+
+ private function _getEndpointPair(Endpoint $endpoint, bool $sandbox): Endpoint
+ {
+ return match ($endpoint) {
+ Endpoint::PROD, Endpoint::SANDBOX => $sandbox ? Endpoint::SANDBOX : Endpoint::PROD,
+ Endpoint::PROD_DOCUMENTS_UPLOAD, Endpoint::SANDBOX_DOCUMENTS_UPLOAD => $sandbox ? Endpoint::SANDBOX_DOCUMENTS_UPLOAD : Endpoint::PROD_DOCUMENTS_UPLOAD,
+ default => throw new \Exception('Unknown FedEx endpoint.')
+ };
+ }
}
diff --git a/src/FedEx.php b/src/FedEx.php
index 03c5343f..74e2d08f 100644
--- a/src/FedEx.php
+++ b/src/FedEx.php
@@ -203,7 +203,13 @@ public function tokenCacheKey(): string
{
$childKeyStr = $this->childKey ? '.'.$this->childKey : '';
- return $this->clientId.$childKeyStr;
+ $key = $this->clientId.$childKeyStr;
+
+ if ($this->endpoint->isSandbox()) {
+ $key .= '.sandbox';
+ }
+
+ return $key;
}
protected function resolveAccessTokenRequest(): Request
From 7fdf2c3cabb58d986d8bdea27349e0221d2058df Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Tue, 7 Oct 2025 15:08:34 +0100
Subject: [PATCH 07/13] Fix false positive php-compatibility check by switching
to develop branch (no recent tagged release available).
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index db2d0786..76c866b0 100644
--- a/composer.json
+++ b/composer.json
@@ -16,7 +16,7 @@
"highsidelabs/saloon-sdk-generator": "^2.1",
"psy/psysh": "^0.12.3",
"symfony/console": "^7.0",
- "phpcompatibility/php-compatibility": "^9.3",
+ "phpcompatibility/php-compatibility": "dev-develop",
"laravel/pint": "^1.15",
"composer/semver": "^3.4",
"ramsey/uuid": "^4.7",
From 97234bf82432dddfb5a28ae324bfcbdfe0406e5e Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Tue, 7 Oct 2025 16:14:41 +0100
Subject: [PATCH 08/13] Remove auth token code from connector constructor.
---
src/FedEx.php | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/src/FedEx.php b/src/FedEx.php
index 74e2d08f..c4dba3f8 100644
--- a/src/FedEx.php
+++ b/src/FedEx.php
@@ -65,15 +65,6 @@ public function __construct(
$this->oauthConfig()->setClientId($clientId);
$this->oauthConfig()->setClientSecret($clientSecret);
-
- $cacheKey = $this->tokenCacheKey();
- $authenticator = $tokenCache::get($cacheKey);
- if (! $authenticator) {
- $authenticator = $this->getAccessToken();
- $tokenCache::set($cacheKey, $authenticator);
- }
-
- $this->authenticate($authenticator);
}
public function resolveBaseUrl(): string
From 595907c9f87dbf66a21eccc3a080a658314546ee Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Tue, 7 Oct 2025 17:04:53 +0100
Subject: [PATCH 09/13] Fix authentication.
---
src/FedEx.php | 42 ++++++++++++++++++++++--------------------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/src/FedEx.php b/src/FedEx.php
index c4dba3f8..5eaf6eb4 100644
--- a/src/FedEx.php
+++ b/src/FedEx.php
@@ -7,6 +7,7 @@
use Closure;
use InvalidArgumentException;
use RuntimeException;
+use Saloon\Contracts\Authenticator;
use Saloon\Http\Connector;
use Saloon\Http\PendingRequest;
use Saloon\Http\Request;
@@ -72,31 +73,32 @@ public function resolveBaseUrl(): string
return $this->endpoint->value;
}
- public function boot(PendingRequest $pendingRequest): void
+ protected function defaultAuth(): Authenticator
{
- // Check auth token before each request
- $request = $pendingRequest->getRequest();
- if (! $request instanceof AuthorizationV1\Requests\ApiAuthorization
- && ! $this->getAuthenticator()?->hasNotExpired()
- ) {
- $cacheKey = $this->tokenCacheKey();
- $authenticator = $this->tokenCache::get($cacheKey);
- if (! $authenticator) {
- try {
- $this->tokenLock?->lock($cacheKey);
- // Re-check cache after acquiring lock in case another process already fetched the token
- $authenticator = $this->tokenCache::get($cacheKey);
- if (! $authenticator) {
- $authenticator = $this->getAccessToken();
- $this->tokenCache::set($cacheKey, $authenticator);
- }
- } finally {
- $this->tokenLock?->unlock($cacheKey);
+ $cacheKey = $this->tokenCacheKey();
+ $authenticator = $this->tokenCache::get($cacheKey);
+ if (! $authenticator) {
+ try {
+ $this->tokenLock?->lock($cacheKey);
+ // Re-check cache after acquiring lock in case another process already fetched the token
+ $authenticator = $this->tokenCache::get($cacheKey);
+ if (! $authenticator) {
+ $authenticator = $this->getAccessToken();
+ $this->tokenCache::set($cacheKey, $authenticator);
}
- $this->authenticate($authenticator);
+ } finally {
+ $this->tokenLock?->unlock($cacheKey);
}
}
+ return $authenticator;
+ }
+
+ public function boot(PendingRequest $pendingRequest): void
+ {
+ // Clear cached authenticator to force token to be rechecked before the next request
+ $this->authenticator = null;
+
if ($this->transactionIdGenerator) {
$transactionId = ($this->transactionIdGenerator)($pendingRequest);
if (! is_string($transactionId)) {
From 94727ffcc76a49a67bfc6c89440ab9725dba9451 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 13 Oct 2025 14:33:09 +0100
Subject: [PATCH 10/13] Fix authentication.
---
.../Requests/ApiAuthorization.php | 7 +++++++
src/Auth/NullAuthenticator.php | 18 ------------------
src/FedEx.php | 6 ------
src/Generator/Generators/RequestGenerator.php | 12 +++++++++++-
tests/FedExConnectorTest.php | 10 +++-------
5 files changed, 21 insertions(+), 32 deletions(-)
delete mode 100644 src/Auth/NullAuthenticator.php
diff --git a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
index 84f796e8..95570097 100644
--- a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
+++ b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
@@ -11,8 +11,10 @@
namespace ShipStream\FedEx\Api\AuthorizationV1\Requests;
use Exception;
+use Saloon\Contracts\Authenticator;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
+use Saloon\Http\Auth\NullAuthenticator;
use Saloon\Http\Response as Response1;
use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
use Saloon\RateLimitPlugin\Limit;
@@ -82,4 +84,9 @@ public function resolveRateLimitStore(): RateLimitStore
{
return new MemoryStore;
}
+
+ public function defaultAuth(): Authenticator
+ {
+ return new NullAuthenticator;
+ }
}
diff --git a/src/Auth/NullAuthenticator.php b/src/Auth/NullAuthenticator.php
deleted file mode 100644
index dfb94c87..00000000
--- a/src/Auth/NullAuthenticator.php
+++ /dev/null
@@ -1,18 +0,0 @@
-authenticator = null;
-
if ($this->transactionIdGenerator) {
$transactionId = ($this->transactionIdGenerator)($pendingRequest);
if (! is_string($transactionId)) {
@@ -127,8 +123,6 @@ public function addressValidationV1(): AddressValidationV1\Api
public function authorizationV1(): AuthorizationV1\Api
{
- $this->authenticate(new NullAuthenticator());
-
return new AuthorizationV1\Api($this);
}
diff --git a/src/Generator/Generators/RequestGenerator.php b/src/Generator/Generators/RequestGenerator.php
index b9197e46..b2a38746 100644
--- a/src/Generator/Generators/RequestGenerator.php
+++ b/src/Generator/Generators/RequestGenerator.php
@@ -10,6 +10,8 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Nette\PhpGenerator\PhpFile;
+use Saloon\Contracts\Authenticator;
+use Saloon\Http\Auth\NullAuthenticator;
use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
use Saloon\RateLimitPlugin\Limit;
use Saloon\RateLimitPlugin\Stores\MemoryStore;
@@ -44,8 +46,8 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile
);
}
- // Add rate limit to authorization API to avoid triggering a 10min lockout
if ($endpoint->name === 'API Authorization') {
+ // Add rate limit to authorization API to avoid triggering a 10min lockout
$namespaces = $classFile->getNamespaces();
$namespace = reset($namespaces);
$namespace->addUse(HasRateLimits::class);
@@ -73,6 +75,14 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile
->setNullable(true)
->setType(RateLimitStore::class);
$constructor->addBody('$this->rateLimitStore = $rateLimitStore;');
+
+ // Disable authorization for authorization API
+ $namespace->addUse(Authenticator::class);
+ $namespace->addUse(NullAuthenticator::class);
+ $class->addMethod('defaultAuth')
+ ->setPublic()
+ ->setReturnType(Authenticator::class)
+ ->addBody('return new NullAuthenticator();');
}
return $classFile;
diff --git a/tests/FedExConnectorTest.php b/tests/FedExConnectorTest.php
index cc4853bd..cec76510 100644
--- a/tests/FedExConnectorTest.php
+++ b/tests/FedExConnectorTest.php
@@ -48,11 +48,13 @@ public function testThrowsUnauthorizedExceptionIfCredentialsAreInvalid(): void
{
$this->expectException(UnauthorizedException::class);
- new FedEx(
+ $connector = new FedEx(
clientId: 'invalidId',
clientSecret: 'invalidSecret',
endpoint: Endpoint::SANDBOX,
);
+
+ $connector->getAccessToken();
}
public function testFetchesNewAccessToken(): void
@@ -103,12 +105,6 @@ public function testFetchesNewAccessTokenIfCacheExpired(): void
$expiredAuthenticator = new AccessTokenAuthenticator($accessToken, expiresAt: new DateTimeImmutable('-10 seconds'));
MemoryCache::set($connector->tokenCacheKey(), $expiredAuthenticator);
- $connector = new FedEx(
- clientId: $this->clientId,
- clientSecret: $this->clientSecret,
- endpoint: Endpoint::SANDBOX,
- );
-
$newAccessToken = $connector->getAuthenticator()->accessToken;
$this->assertNotEquals($accessToken, $newAccessToken);
From 1bb8cf3f4827c26c6b63441bc0c759fb5c4c7574 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 13 Oct 2025 17:09:59 +0100
Subject: [PATCH 11/13] Fix authentication for connectors using a different
base url.
---
.../Requests/ApiAuthorization.php | 17 +++++++++++++++++
tests/FedExConnectorTest.php | 12 ++++++++++++
2 files changed, 29 insertions(+)
diff --git a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
index 95570097..29e417fb 100644
--- a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
+++ b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
@@ -14,7 +14,9 @@
use Saloon\Contracts\Authenticator;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
+use Saloon\Helpers\URLHelper;
use Saloon\Http\Auth\NullAuthenticator;
+use Saloon\Http\PendingRequest;
use Saloon\Http\Response as Response1;
use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
use Saloon\RateLimitPlugin\Limit;
@@ -24,6 +26,7 @@
use ShipStream\FedEx\Api\AuthorizationV1\Dto\FullSchema;
use ShipStream\FedEx\Api\AuthorizationV1\Responses\ErrorResponseVo;
use ShipStream\FedEx\Api\AuthorizationV1\Responses\Response;
+use ShipStream\FedEx\Enums\Endpoint;
use ShipStream\FedEx\Request;
/**
@@ -50,6 +53,20 @@ public function __construct(
$this->rateLimitStore = $rateLimitStore;
}
+ public function boot(PendingRequest $pendingRequest): void
+ {
+ // Ensure that authorization always uses the correct base url so that connectors with a different base url can still authenticate
+ /** @var Endpoint $endpoint */
+ if ($endpoint = ($pendingRequest->getConnector()->endpoint ?? null)) {
+ $pendingRequest->setUrl(
+ URLHelper::join(
+ $endpoint->isProduction() ? Endpoint::PROD->value : Endpoint::SANDBOX->value,
+ $pendingRequest->getRequest()->resolveEndpoint()
+ )
+ );
+ }
+ }
+
public function resolveEndpoint(): string
{
return '/oauth/token';
diff --git a/tests/FedExConnectorTest.php b/tests/FedExConnectorTest.php
index cec76510..18a40a36 100644
--- a/tests/FedExConnectorTest.php
+++ b/tests/FedExConnectorTest.php
@@ -110,6 +110,18 @@ public function testFetchesNewAccessTokenIfCacheExpired(): void
$this->assertNotEquals($accessToken, $newAccessToken);
}
+ public function testEndpointIsOverriddenForAuthorization(): void
+ {
+ $connector = new FedEx(
+ clientId: $this->clientId,
+ clientSecret: $this->clientSecret,
+ endpoint: Endpoint::SANDBOX_DOCUMENTS_UPLOAD, // Incorrect for authorization
+ );
+
+ $authenticator = $connector->getAccessToken();
+ $this->assertInstanceOf(AccessTokenAuthenticator::class, $authenticator);
+ }
+
public function testGeneratesTransactionIds(): void
{
$mockClient = new MockClient([
From 07586432890c58683faec4e5abdcc7c201b687f6 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Mon, 13 Oct 2025 17:29:21 +0100
Subject: [PATCH 12/13] Fix authentication for connectors using a different
base url.
---
.../Requests/ApiAuthorization.php | 23 ++++++++-----------
src/Generator/Generators/RequestGenerator.php | 17 ++++++++++++++
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
index 29e417fb..4577a726 100644
--- a/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
+++ b/src/Api/AuthorizationV1/Requests/ApiAuthorization.php
@@ -53,20 +53,6 @@ public function __construct(
$this->rateLimitStore = $rateLimitStore;
}
- public function boot(PendingRequest $pendingRequest): void
- {
- // Ensure that authorization always uses the correct base url so that connectors with a different base url can still authenticate
- /** @var Endpoint $endpoint */
- if ($endpoint = ($pendingRequest->getConnector()->endpoint ?? null)) {
- $pendingRequest->setUrl(
- URLHelper::join(
- $endpoint->isProduction() ? Endpoint::PROD->value : Endpoint::SANDBOX->value,
- $pendingRequest->getRequest()->resolveEndpoint()
- )
- );
- }
- }
-
public function resolveEndpoint(): string
{
return '/oauth/token';
@@ -106,4 +92,13 @@ public function defaultAuth(): Authenticator
{
return new NullAuthenticator;
}
+
+ public function boot(PendingRequest $pendingRequest): void
+ {
+ // Ensure that authorization always uses the correct base url so that connectors with a different base url can still authenticate
+ /** @var Endpoint $endpoint */
+ if ($endpoint = ($pendingRequest->getConnector()->endpoint ?? null)) {
+ $pendingRequest->setUrl(URLHelper::join($endpoint->isProduction() ? Endpoint::PROD->value : Endpoint::SANDBOX->value, $pendingRequest->getRequest()->resolveEndpoint()));
+ }
+ }
}
diff --git a/src/Generator/Generators/RequestGenerator.php b/src/Generator/Generators/RequestGenerator.php
index b2a38746..056025e7 100644
--- a/src/Generator/Generators/RequestGenerator.php
+++ b/src/Generator/Generators/RequestGenerator.php
@@ -11,7 +11,9 @@
use Illuminate\Support\Str;
use Nette\PhpGenerator\PhpFile;
use Saloon\Contracts\Authenticator;
+use Saloon\Helpers\URLHelper;
use Saloon\Http\Auth\NullAuthenticator;
+use Saloon\Http\PendingRequest;
use Saloon\RateLimitPlugin\Contracts\RateLimitStore;
use Saloon\RateLimitPlugin\Limit;
use Saloon\RateLimitPlugin\Stores\MemoryStore;
@@ -83,6 +85,21 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile
->setPublic()
->setReturnType(Authenticator::class)
->addBody('return new NullAuthenticator();');
+
+ // Ensure correct base url is used for authorization
+ $namespace->addUse(URLHelper::class);
+ $namespace->addUse(PendingRequest::class);
+ $namespace->addUse(\ShipStream\FedEx\Enums\Endpoint::class);
+ $class->addMethod('boot')
+ ->setPublic()
+ ->setReturnType('void')
+ ->addBody('// Ensure that authorization always uses the correct base url so that connectors with a different base url can still authenticate')
+ ->addBody('/** @var Endpoint $endpoint */')
+ ->addBody('if ($endpoint = ($pendingRequest->getConnector()->endpoint ?? null)) {')
+ ->addBody(' $pendingRequest->setUrl(URLHelper::join($endpoint->isProduction() ? Endpoint::PROD->value : Endpoint::SANDBOX->value, $pendingRequest->getRequest()->resolveEndpoint()));')
+ ->addBody('}')
+ ->addParameter('pendingRequest')
+ ->setType(PendingRequest::class);
}
return $classFile;
From 64b5d4c6b0ff3414449cfa6a70b2548ae74b39e8 Mon Sep 17 00:00:00 2001
From: Rob Wigginton <8102829+bob2021@users.noreply.github.com>
Date: Wed, 15 Oct 2025 16:32:02 +0100
Subject: [PATCH 13/13] Add missing header parameters from registration API.
---
src/Api/AccountRegistrationV1/Api.php | 33 ++++++++++++++-----
.../Generators/ResourceGenerator.php | 10 +++++-
2 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/src/Api/AccountRegistrationV1/Api.php b/src/Api/AccountRegistrationV1/Api.php
index ae68054c..ecdc8ae1 100644
--- a/src/Api/AccountRegistrationV1/Api.php
+++ b/src/Api/AccountRegistrationV1/Api.php
@@ -33,25 +33,40 @@ public function createCustomerKey(CustomerKeyInputVo $customerKeyInputVo): Respo
/**
* @param ValidatePinInputVo $validatePinInputVo This field indicates INPUT model
+ * @param string $accountAuthToken Address validation jwt
+ * @param ?string $xClientid This indicates the Client who is consuming this endpoint
*/
- public function validatePin(ValidatePinInputVo $validatePinInputVo): Response
- {
- return $this->connector->send(new ValidatePin($validatePinInputVo));
+ public function validatePin(
+ ValidatePinInputVo $validatePinInputVo,
+ string $accountAuthToken,
+ ?string $xClientid = null,
+ ): Response {
+ return $this->connector->send(new ValidatePin($validatePinInputVo, $accountAuthToken, $xClientid));
}
/**
* @param ValidateInvoiceInputVo $validateInvoiceInputVo This field indicates Invoice Information ( Invoice number, Date, currency and amount) and locale.
+ * @param string $accountAuthToken Address validation jwt
+ * @param ?string $xClientid This indicates the Client who is consuming this endpoint
*/
- public function validateInvoice(ValidateInvoiceInputVo $validateInvoiceInputVo): Response
- {
- return $this->connector->send(new ValidateInvoice($validateInvoiceInputVo));
+ public function validateInvoice(
+ ValidateInvoiceInputVo $validateInvoiceInputVo,
+ string $accountAuthToken,
+ ?string $xClientid = null,
+ ): Response {
+ return $this->connector->send(new ValidateInvoice($validateInvoiceInputVo, $accountAuthToken, $xClientid));
}
/**
* @param IssuePinInputVo $issuePinInputVo This field indicates INPUT model
+ * @param string $accountAuthToken Address validation jwt
+ * @param ?string $xClientid This indicates the client who is using this endpoint.
*/
- public function sendPin(IssuePinInputVo $issuePinInputVo): Response
- {
- return $this->connector->send(new SendPin($issuePinInputVo));
+ public function sendPin(
+ IssuePinInputVo $issuePinInputVo,
+ string $accountAuthToken,
+ ?string $xClientid = null,
+ ): Response {
+ return $this->connector->send(new SendPin($issuePinInputVo, $accountAuthToken, $xClientid));
}
}
diff --git a/src/Generator/Generators/ResourceGenerator.php b/src/Generator/Generators/ResourceGenerator.php
index 0561f373..f3cc410a 100644
--- a/src/Generator/Generators/ResourceGenerator.php
+++ b/src/Generator/Generators/ResourceGenerator.php
@@ -80,7 +80,15 @@ public function generateResourceClass(string $resourceName, array $endpoints): ?
}
foreach ($endpoint->queryParameters as $parameter) {
- if (in_array($parameter->name, $this->config->ignoredQueryParams)) {
+ if (in_array(strtolower($parameter->rawName), $this->config->ignoredParams['query'])) {
+ continue;
+ }
+ MethodGeneratorHelper::addParameterToMethod($method, $parameter);
+ $args[] = new Literal(sprintf('$%s', NameHelper::safeVariableName($parameter->name)));
+ }
+
+ foreach ($endpoint->headerParameters as $parameter) {
+ if (in_array(strtolower($parameter->rawName), $this->config->ignoredParams['header'])) {
continue;
}
MethodGeneratorHelper::addParameterToMethod($method, $parameter);