From 30410fd8ca47873a170701f9125e4e66f7d62e61 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:49:34 -0700 Subject: [PATCH 1/3] feat(fedex): add FedEx Multi-Factor Authentication endpoints Adds native support for FedEx 2FA registration endpoints, eliminating the need for customers to use curl commands. This implementation enables automated FedEx registration workflows through the PHP client library. Co-Authored-By: Claude Sonnet 4.5 --- lib/EasyPost/EasyPostClient.php | 3 + .../FedExAccountValidationResponse.php | 17 ++ lib/EasyPost/FedExRequestPinResponse.php | 11 ++ .../Service/FedExRegistrationService.php | 185 ++++++++++++++++++ lib/EasyPost/Util/InternalUtil.php | 28 +-- test/EasyPost/FedExRegistrationTest.php | 137 +++++++++++++ .../fedex_registration/registerAddress.yml | 25 +++ .../fedex_registration/requestPin.yml | 25 +++ .../fedex_registration/submitInvoice.yml | 25 +++ .../fedex_registration/validatePin.yml | 25 +++ 10 files changed, 469 insertions(+), 12 deletions(-) create mode 100644 lib/EasyPost/FedExAccountValidationResponse.php create mode 100644 lib/EasyPost/FedExRequestPinResponse.php create mode 100644 lib/EasyPost/Service/FedExRegistrationService.php create mode 100644 test/EasyPost/FedExRegistrationTest.php create mode 100644 test/cassettes/fedex_registration/registerAddress.yml create mode 100644 test/cassettes/fedex_registration/requestPin.yml create mode 100644 test/cassettes/fedex_registration/submitInvoice.yml create mode 100644 test/cassettes/fedex_registration/validatePin.yml diff --git a/lib/EasyPost/EasyPostClient.php b/lib/EasyPost/EasyPostClient.php index f0d8db68..e5d14889 100644 --- a/lib/EasyPost/EasyPostClient.php +++ b/lib/EasyPost/EasyPostClient.php @@ -22,6 +22,7 @@ use EasyPost\Service\EmbeddableService; use EasyPost\Service\EndShipperService; use EasyPost\Service\EventService; +use EasyPost\Service\FedExRegistrationService; use EasyPost\Service\InsuranceService; use EasyPost\Service\LumaService; use EasyPost\Service\OrderService; @@ -58,6 +59,7 @@ * @property EmbeddableService $embeddable * @property EndShipperService $endShipper * @property EventService $event + * @property FedExRegistrationService $fedexRegistration * @property InsuranceService $insurance * @property LumaService $luma * @property OrderService $order @@ -134,6 +136,7 @@ public function __get(string $serviceName) 'embeddable' => EmbeddableService::class, 'endShipper' => EndShipperService::class, 'event' => EventService::class, + 'fedexRegistration' => FedExRegistrationService::class, 'insurance' => InsuranceService::class, 'luma' => LumaService::class, 'order' => OrderService::class, diff --git a/lib/EasyPost/FedExAccountValidationResponse.php b/lib/EasyPost/FedExAccountValidationResponse.php new file mode 100644 index 00000000..fbe03728 --- /dev/null +++ b/lib/EasyPost/FedExAccountValidationResponse.php @@ -0,0 +1,17 @@ +|null $credentials + */ +class FedExAccountValidationResponse extends EasyPostObject +{ +} diff --git a/lib/EasyPost/FedExRequestPinResponse.php b/lib/EasyPost/FedExRequestPinResponse.php new file mode 100644 index 00000000..a741406e --- /dev/null +++ b/lib/EasyPost/FedExRequestPinResponse.php @@ -0,0 +1,11 @@ +wrapAddressValidation($params); + $url = "/fedex_registrations/{$fedexAccountNumber}/address"; + + $response = Requestor::request($this->client, 'post', $url, $wrappedParams); + + return InternalUtil::convertToEasyPostObject($this->client, $response); + } + + /** + * Request a PIN for FedEx account verification. + * + * @param string $fedexAccountNumber + * @param string $pinMethodOption + * @return mixed + */ + public function requestPin(string $fedexAccountNumber, string $pinMethodOption): mixed + { + $wrappedParams = [ + 'pin_method' => [ + 'option' => $pinMethodOption, + ], + ]; + $url = "/fedex_registrations/{$fedexAccountNumber}/pin"; + + $response = Requestor::request($this->client, 'post', $url, $wrappedParams); + + return InternalUtil::convertToEasyPostObject($this->client, $response); + } + + /** + * Validate the PIN entered by the user for FedEx account verification. + * + * @param string $fedexAccountNumber + * @param mixed $params + * @return mixed + */ + public function validatePin(string $fedexAccountNumber, mixed $params = null): mixed + { + $wrappedParams = $this->wrapPinValidation($params); + $url = "/fedex_registrations/{$fedexAccountNumber}/pin/validate"; + + $response = Requestor::request($this->client, 'post', $url, $wrappedParams); + + return InternalUtil::convertToEasyPostObject($this->client, $response); + } + + /** + * Submit invoice information to complete FedEx account registration. + * + * @param string $fedexAccountNumber + * @param mixed $params + * @return mixed + */ + public function submitInvoice(string $fedexAccountNumber, mixed $params = null): mixed + { + $wrappedParams = $this->wrapInvoiceValidation($params); + $url = "/fedex_registrations/{$fedexAccountNumber}/invoice"; + + $response = Requestor::request($this->client, 'post', $url, $wrappedParams); + + return InternalUtil::convertToEasyPostObject($this->client, $response); + } + + /** + * Wraps address validation parameters and ensures the "name" field exists. + * If not present, generates a UUID (with hyphens removed) as the name. + * + * @param mixed $params + * @return array + */ + private function wrapAddressValidation(mixed $params): array + { + $wrappedParams = []; + + if (isset($params['address_validation'])) { + $addressValidation = $params['address_validation']; + $this->ensureNameField($addressValidation); + $wrappedParams['address_validation'] = $addressValidation; + } + + if (isset($params['easypost_details'])) { + $wrappedParams['easypost_details'] = $params['easypost_details']; + } + + return $wrappedParams; + } + + /** + * Wraps PIN validation parameters and ensures the "name" field exists. + * If not present, generates a UUID (with hyphens removed) as the name. + * + * @param mixed $params + * @return array + */ + private function wrapPinValidation(mixed $params): array + { + $wrappedParams = []; + + if (isset($params['pin_validation'])) { + $pinValidation = $params['pin_validation']; + $this->ensureNameField($pinValidation); + $wrappedParams['pin_validation'] = $pinValidation; + } + + if (isset($params['easypost_details'])) { + $wrappedParams['easypost_details'] = $params['easypost_details']; + } + + return $wrappedParams; + } + + /** + * Wraps invoice validation parameters and ensures the "name" field exists. + * If not present, generates a UUID (with hyphens removed) as the name. + * + * @param mixed $params + * @return array + */ + private function wrapInvoiceValidation(mixed $params): array + { + $wrappedParams = []; + + if (isset($params['invoice_validation'])) { + $invoiceValidation = $params['invoice_validation']; + $this->ensureNameField($invoiceValidation); + $wrappedParams['invoice_validation'] = $invoiceValidation; + } + + if (isset($params['easypost_details'])) { + $wrappedParams['easypost_details'] = $params['easypost_details']; + } + + return $wrappedParams; + } + + /** + * Ensures the "name" field exists in the provided array. + * If not present, generates a UUID (with hyphens removed) as the name. + * This follows the pattern used in the web UI implementation. + * + * @param array &$array + * @return void + */ + private function ensureNameField(array &$array): void + { + if (!isset($array['name'])) { + $uuid = sprintf( + '%04x%04x%04x%04x%04x%04x%04x%04x', + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff) + ); + $array['name'] = $uuid; + } + } +} diff --git a/lib/EasyPost/Util/InternalUtil.php b/lib/EasyPost/Util/InternalUtil.php index a0ff86be..f7e817bf 100644 --- a/lib/EasyPost/Util/InternalUtil.php +++ b/lib/EasyPost/Util/InternalUtil.php @@ -18,6 +18,8 @@ use EasyPost\Event; use EasyPost\Exception\General\EasyPostException; use EasyPost\Exception\General\FilteringException; +use EasyPost\FedExAccountValidationResponse; +use EasyPost\FedExRequestPinResponse; use EasyPost\Fee; use EasyPost\Insurance; use EasyPost\Order; @@ -38,18 +40,20 @@ use EasyPost\Webhook; const OBJECT_MAPPING = [ - 'Address' => Address::class, - 'ApiKey' => ApiKey::class, - 'Batch' => Batch::class, - 'Brand' => Brand::class, - 'CarrierAccount' => CarrierAccount::class, - 'CarrierDetail' => CarrierDetail::class, - 'Claim' => Claim::class, - 'CustomsInfo' => CustomsInfo::class, - 'CustomsItem' => CustomsItem::class, - 'EndShipper' => EndShipper::class, - 'Event' => Event::class, - 'Fee' => Fee::class, + 'Address' => Address::class, + 'ApiKey' => ApiKey::class, + 'Batch' => Batch::class, + 'Brand' => Brand::class, + 'CarrierAccount' => CarrierAccount::class, + 'CarrierDetail' => CarrierDetail::class, + 'Claim' => Claim::class, + 'CustomsInfo' => CustomsInfo::class, + 'CustomsItem' => CustomsItem::class, + 'EndShipper' => EndShipper::class, + 'Event' => Event::class, + 'FedExAccountValidationResponse' => FedExAccountValidationResponse::class, + 'FedExRequestPinResponse' => FedExRequestPinResponse::class, + 'Fee' => Fee::class, 'Insurance' => Insurance::class, 'Order' => Order::class, 'Parcel' => Parcel::class, diff --git a/test/EasyPost/FedExRegistrationTest.php b/test/EasyPost/FedExRegistrationTest.php new file mode 100644 index 00000000..17e28bcc --- /dev/null +++ b/test/EasyPost/FedExRegistrationTest.php @@ -0,0 +1,137 @@ + [ + 'name' => 'BILLING NAME', + 'street1' => '1234 BILLING STREET', + 'city' => 'BILLINGCITY', + 'state' => 'ST', + 'postal_code' => '12345', + 'country_code' => 'US', + ], + 'easypost_details' => [ + 'carrier_account_id' => 'ca_123', + ], + ]; + + $response = self::$client->fedexRegistration->registerAddress($fedexAccountNumber, $params); + + $this->assertInstanceOf(FedExAccountValidationResponse::class, $response); + $this->assertNull($response->email_address); + $this->assertNotNull($response->options); + $this->assertContains('SMS', $response->options); + $this->assertContains('CALL', $response->options); + $this->assertContains('INVOICE', $response->options); + $this->assertEquals('***-***-9721', $response->phone_number); + } + + /** + * Test requesting a pin. + */ + public function testRequestPin(): void + { + TestUtil::setupCassette('fedex_registration/requestPin.yml'); + + $fedexAccountNumber = '123456789'; + + $response = self::$client->fedexRegistration->requestPin($fedexAccountNumber, 'SMS'); + + $this->assertInstanceOf(FedExRequestPinResponse::class, $response); + $this->assertEquals('sent secured Pin', $response->message); + } + + /** + * Test validating a pin. + */ + public function testValidatePin(): void + { + TestUtil::setupCassette('fedex_registration/validatePin.yml'); + + $fedexAccountNumber = '123456789'; + $params = [ + 'pin_validation' => [ + 'pin_code' => '123456', + 'name' => 'BILLING NAME', + ], + 'easypost_details' => [ + 'carrier_account_id' => 'ca_123', + ], + ]; + + $response = self::$client->fedexRegistration->validatePin($fedexAccountNumber, $params); + + $this->assertInstanceOf(FedExAccountValidationResponse::class, $response); + $this->assertEquals('ca_123', $response->id); + $this->assertEquals('FedexAccount', $response->type); + $this->assertNotNull($response->credentials); + $this->assertEquals('123456789', $response->credentials['account_number']); + $this->assertEquals('123456789-XXXXX', $response->credentials['mfa_key']); + } + + /** + * Test submitting details about an invoice. + */ + public function testSubmitInvoice(): void + { + TestUtil::setupCassette('fedex_registration/submitInvoice.yml'); + + $fedexAccountNumber = '123456789'; + $params = [ + 'invoice_validation' => [ + 'name' => 'BILLING NAME', + 'invoice_number' => 'INV-12345', + 'invoice_date' => '2025-12-08', + 'invoice_amount' => '100.00', + 'invoice_currency' => 'USD', + ], + 'easypost_details' => [ + 'carrier_account_id' => 'ca_123', + ], + ]; + + $response = self::$client->fedexRegistration->submitInvoice($fedexAccountNumber, $params); + + $this->assertInstanceOf(FedExAccountValidationResponse::class, $response); + $this->assertEquals('ca_123', $response->id); + $this->assertEquals('FedexAccount', $response->type); + $this->assertNotNull($response->credentials); + $this->assertEquals('123456789', $response->credentials['account_number']); + $this->assertEquals('123456789-XXXXX', $response->credentials['mfa_key']); + } +} diff --git a/test/cassettes/fedex_registration/registerAddress.yml b/test/cassettes/fedex_registration/registerAddress.yml new file mode 100644 index 00000000..26f13996 --- /dev/null +++ b/test/cassettes/fedex_registration/registerAddress.yml @@ -0,0 +1,25 @@ + +- + request: + method: POST + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/address' + headers: + Host: api.easypost.com + Expect: '' + Accept-Encoding: '' + Accept: application/json + Authorization: '' + Content-Type: application/json + User-Agent: '' + body: '{"address_validation":{"name":"BILLING NAME","street1":"1234 BILLING STREET","city":"BILLINGCITY","state":"ST","postal_code":"12345","country_code":"US"},"easypost_details":{"carrier_account_id":"ca_123"}}' + response: + status: + code: 200 + message: OK + headers: + content-type: 'application/json; charset=utf-8' + body: '{"object":"FedExAccountValidationResponse","email_address":null,"options":["SMS","CALL","INVOICE"],"phone_number":"***-***-9721"}' + curl_info: + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/address' + content_type: 'application/json; charset=utf-8' + http_code: 200 diff --git a/test/cassettes/fedex_registration/requestPin.yml b/test/cassettes/fedex_registration/requestPin.yml new file mode 100644 index 00000000..14cb87af --- /dev/null +++ b/test/cassettes/fedex_registration/requestPin.yml @@ -0,0 +1,25 @@ + +- + request: + method: POST + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin' + headers: + Host: api.easypost.com + Expect: '' + Accept-Encoding: '' + Accept: application/json + Authorization: '' + Content-Type: application/json + User-Agent: '' + body: '{"pin_method":{"option":"SMS"}}' + response: + status: + code: 200 + message: OK + headers: + content-type: 'application/json; charset=utf-8' + body: '{"object":"FedExRequestPinResponse","message":"sent secured Pin"}' + curl_info: + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin' + content_type: 'application/json; charset=utf-8' + http_code: 200 diff --git a/test/cassettes/fedex_registration/submitInvoice.yml b/test/cassettes/fedex_registration/submitInvoice.yml new file mode 100644 index 00000000..1fba7f4e --- /dev/null +++ b/test/cassettes/fedex_registration/submitInvoice.yml @@ -0,0 +1,25 @@ + +- + request: + method: POST + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/invoice' + headers: + Host: api.easypost.com + Expect: '' + Accept-Encoding: '' + Accept: application/json + Authorization: '' + Content-Type: application/json + User-Agent: '' + body: '{"invoice_validation":{"name":"BILLING NAME","invoice_number":"INV-12345","invoice_date":"2025-12-08","invoice_amount":"100.00","invoice_currency":"USD"},"easypost_details":{"carrier_account_id":"ca_123"}}' + response: + status: + code: 200 + message: OK + headers: + content-type: 'application/json; charset=utf-8' + body: '{"object":"FedExAccountValidationResponse","id":"ca_123","type":"FedexAccount","credentials":{"account_number":"123456789","mfa_key":"123456789-XXXXX"}}' + curl_info: + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/invoice' + content_type: 'application/json; charset=utf-8' + http_code: 200 diff --git a/test/cassettes/fedex_registration/validatePin.yml b/test/cassettes/fedex_registration/validatePin.yml new file mode 100644 index 00000000..a0c5ceba --- /dev/null +++ b/test/cassettes/fedex_registration/validatePin.yml @@ -0,0 +1,25 @@ + +- + request: + method: POST + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin/validate' + headers: + Host: api.easypost.com + Expect: '' + Accept-Encoding: '' + Accept: application/json + Authorization: '' + Content-Type: application/json + User-Agent: '' + body: '{"pin_validation":{"pin_code":"123456","name":"BILLING NAME"},"easypost_details":{"carrier_account_id":"ca_123"}}' + response: + status: + code: 200 + message: OK + headers: + content-type: 'application/json; charset=utf-8' + body: '{"object":"FedExAccountValidationResponse","id":"ca_123","type":"FedexAccount","credentials":{"account_number":"123456789","mfa_key":"123456789-XXXXX"}}' + curl_info: + url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin/validate' + content_type: 'application/json; charset=utf-8' + http_code: 200 From ac774003067f33670969ddca1cce2f69196de197 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:57:17 -0700 Subject: [PATCH 2/3] chore: cleanup --- .../FedExAccountValidationResponse.php | 17 ----------- lib/EasyPost/FedExRequestPinResponse.php | 11 -------- .../Service/FedExRegistrationService.php | 17 ++--------- lib/EasyPost/Util/InternalUtil.php | 28 ++++++++----------- .../fedex_registration/registerAddress.yml | 25 ----------------- .../fedex_registration/requestPin.yml | 25 ----------------- .../fedex_registration/submitInvoice.yml | 25 ----------------- .../fedex_registration/validatePin.yml | 25 ----------------- 8 files changed, 14 insertions(+), 159 deletions(-) delete mode 100644 lib/EasyPost/FedExAccountValidationResponse.php delete mode 100644 lib/EasyPost/FedExRequestPinResponse.php delete mode 100644 test/cassettes/fedex_registration/registerAddress.yml delete mode 100644 test/cassettes/fedex_registration/requestPin.yml delete mode 100644 test/cassettes/fedex_registration/submitInvoice.yml delete mode 100644 test/cassettes/fedex_registration/validatePin.yml diff --git a/lib/EasyPost/FedExAccountValidationResponse.php b/lib/EasyPost/FedExAccountValidationResponse.php deleted file mode 100644 index fbe03728..00000000 --- a/lib/EasyPost/FedExAccountValidationResponse.php +++ /dev/null @@ -1,17 +0,0 @@ -|null $credentials - */ -class FedExAccountValidationResponse extends EasyPostObject -{ -} diff --git a/lib/EasyPost/FedExRequestPinResponse.php b/lib/EasyPost/FedExRequestPinResponse.php deleted file mode 100644 index a741406e..00000000 --- a/lib/EasyPost/FedExRequestPinResponse.php +++ /dev/null @@ -1,11 +0,0 @@ - &$array @@ -168,18 +166,7 @@ private function wrapInvoiceValidation(mixed $params): array private function ensureNameField(array &$array): void { if (!isset($array['name'])) { - $uuid = sprintf( - '%04x%04x%04x%04x%04x%04x%04x%04x', - mt_rand(0, 0xffff), - mt_rand(0, 0xffff), - mt_rand(0, 0xffff), - mt_rand(0, 0x0fff) | 0x4000, - mt_rand(0, 0x3fff) | 0x8000, - mt_rand(0, 0xffff), - mt_rand(0, 0xffff), - mt_rand(0, 0xffff) - ); - $array['name'] = $uuid; + $array['name'] = uniqid(); } } } diff --git a/lib/EasyPost/Util/InternalUtil.php b/lib/EasyPost/Util/InternalUtil.php index f7e817bf..a0ff86be 100644 --- a/lib/EasyPost/Util/InternalUtil.php +++ b/lib/EasyPost/Util/InternalUtil.php @@ -18,8 +18,6 @@ use EasyPost\Event; use EasyPost\Exception\General\EasyPostException; use EasyPost\Exception\General\FilteringException; -use EasyPost\FedExAccountValidationResponse; -use EasyPost\FedExRequestPinResponse; use EasyPost\Fee; use EasyPost\Insurance; use EasyPost\Order; @@ -40,20 +38,18 @@ use EasyPost\Webhook; const OBJECT_MAPPING = [ - 'Address' => Address::class, - 'ApiKey' => ApiKey::class, - 'Batch' => Batch::class, - 'Brand' => Brand::class, - 'CarrierAccount' => CarrierAccount::class, - 'CarrierDetail' => CarrierDetail::class, - 'Claim' => Claim::class, - 'CustomsInfo' => CustomsInfo::class, - 'CustomsItem' => CustomsItem::class, - 'EndShipper' => EndShipper::class, - 'Event' => Event::class, - 'FedExAccountValidationResponse' => FedExAccountValidationResponse::class, - 'FedExRequestPinResponse' => FedExRequestPinResponse::class, - 'Fee' => Fee::class, + 'Address' => Address::class, + 'ApiKey' => ApiKey::class, + 'Batch' => Batch::class, + 'Brand' => Brand::class, + 'CarrierAccount' => CarrierAccount::class, + 'CarrierDetail' => CarrierDetail::class, + 'Claim' => Claim::class, + 'CustomsInfo' => CustomsInfo::class, + 'CustomsItem' => CustomsItem::class, + 'EndShipper' => EndShipper::class, + 'Event' => Event::class, + 'Fee' => Fee::class, 'Insurance' => Insurance::class, 'Order' => Order::class, 'Parcel' => Parcel::class, diff --git a/test/cassettes/fedex_registration/registerAddress.yml b/test/cassettes/fedex_registration/registerAddress.yml deleted file mode 100644 index 26f13996..00000000 --- a/test/cassettes/fedex_registration/registerAddress.yml +++ /dev/null @@ -1,25 +0,0 @@ - -- - request: - method: POST - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/address' - headers: - Host: api.easypost.com - Expect: '' - Accept-Encoding: '' - Accept: application/json - Authorization: '' - Content-Type: application/json - User-Agent: '' - body: '{"address_validation":{"name":"BILLING NAME","street1":"1234 BILLING STREET","city":"BILLINGCITY","state":"ST","postal_code":"12345","country_code":"US"},"easypost_details":{"carrier_account_id":"ca_123"}}' - response: - status: - code: 200 - message: OK - headers: - content-type: 'application/json; charset=utf-8' - body: '{"object":"FedExAccountValidationResponse","email_address":null,"options":["SMS","CALL","INVOICE"],"phone_number":"***-***-9721"}' - curl_info: - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/address' - content_type: 'application/json; charset=utf-8' - http_code: 200 diff --git a/test/cassettes/fedex_registration/requestPin.yml b/test/cassettes/fedex_registration/requestPin.yml deleted file mode 100644 index 14cb87af..00000000 --- a/test/cassettes/fedex_registration/requestPin.yml +++ /dev/null @@ -1,25 +0,0 @@ - -- - request: - method: POST - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin' - headers: - Host: api.easypost.com - Expect: '' - Accept-Encoding: '' - Accept: application/json - Authorization: '' - Content-Type: application/json - User-Agent: '' - body: '{"pin_method":{"option":"SMS"}}' - response: - status: - code: 200 - message: OK - headers: - content-type: 'application/json; charset=utf-8' - body: '{"object":"FedExRequestPinResponse","message":"sent secured Pin"}' - curl_info: - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin' - content_type: 'application/json; charset=utf-8' - http_code: 200 diff --git a/test/cassettes/fedex_registration/submitInvoice.yml b/test/cassettes/fedex_registration/submitInvoice.yml deleted file mode 100644 index 1fba7f4e..00000000 --- a/test/cassettes/fedex_registration/submitInvoice.yml +++ /dev/null @@ -1,25 +0,0 @@ - -- - request: - method: POST - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/invoice' - headers: - Host: api.easypost.com - Expect: '' - Accept-Encoding: '' - Accept: application/json - Authorization: '' - Content-Type: application/json - User-Agent: '' - body: '{"invoice_validation":{"name":"BILLING NAME","invoice_number":"INV-12345","invoice_date":"2025-12-08","invoice_amount":"100.00","invoice_currency":"USD"},"easypost_details":{"carrier_account_id":"ca_123"}}' - response: - status: - code: 200 - message: OK - headers: - content-type: 'application/json; charset=utf-8' - body: '{"object":"FedExAccountValidationResponse","id":"ca_123","type":"FedexAccount","credentials":{"account_number":"123456789","mfa_key":"123456789-XXXXX"}}' - curl_info: - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/invoice' - content_type: 'application/json; charset=utf-8' - http_code: 200 diff --git a/test/cassettes/fedex_registration/validatePin.yml b/test/cassettes/fedex_registration/validatePin.yml deleted file mode 100644 index a0c5ceba..00000000 --- a/test/cassettes/fedex_registration/validatePin.yml +++ /dev/null @@ -1,25 +0,0 @@ - -- - request: - method: POST - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin/validate' - headers: - Host: api.easypost.com - Expect: '' - Accept-Encoding: '' - Accept: application/json - Authorization: '' - Content-Type: application/json - User-Agent: '' - body: '{"pin_validation":{"pin_code":"123456","name":"BILLING NAME"},"easypost_details":{"carrier_account_id":"ca_123"}}' - response: - status: - code: 200 - message: OK - headers: - content-type: 'application/json; charset=utf-8' - body: '{"object":"FedExAccountValidationResponse","id":"ca_123","type":"FedexAccount","credentials":{"account_number":"123456789","mfa_key":"123456789-XXXXX"}}' - curl_info: - url: 'https://api.easypost.com/v2/fedex_registrations/123456789/pin/validate' - content_type: 'application/json; charset=utf-8' - http_code: 200 From 1cd777d616d8bbb559c2322ccd52fd1a9a9ef8d4 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:06:18 -0700 Subject: [PATCH 3/3] refactor: mock fedex tests instead of using cassettes --- test/EasyPost/FedExRegistrationTest.php | 107 ++++++++++++++++-------- 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/test/EasyPost/FedExRegistrationTest.php b/test/EasyPost/FedExRegistrationTest.php index 17e28bcc..afbbda6d 100644 --- a/test/EasyPost/FedExRegistrationTest.php +++ b/test/EasyPost/FedExRegistrationTest.php @@ -2,9 +2,13 @@ namespace EasyPost\Test; +use EasyPost\Constant\Constants; use EasyPost\EasyPostClient; -use EasyPost\FedExAccountValidationResponse; -use EasyPost\FedExRequestPinResponse; +use EasyPost\EasyPostObject; +use EasyPost\Test\Mocking\MockingUtility; +use EasyPost\Test\Mocking\MockRequest; +use EasyPost\Test\Mocking\MockRequestMatchRule; +use EasyPost\Test\Mocking\MockRequestResponseInfo; use PHPUnit\Framework\TestCase; class FedExRegistrationTest extends TestCase @@ -16,16 +20,59 @@ class FedExRegistrationTest extends TestCase */ public static function setUpBeforeClass(): void { - TestUtil::setupVcrTests(); - self::$client = new EasyPostClient((string)getenv('EASYPOST_PROD_API_KEY')); - } - - /** - * Cleanup the testing environment once finished. - */ - public static function tearDownAfterClass(): void - { - TestUtil::teardownVcrTests(); + $mockingUtility = new MockingUtility( + [ + new MockRequest( + new MockRequestMatchRule( + 'post', + '/v2\/fedex_registrations\/\S*\/address$/' + ), + new MockRequestResponseInfo( + 200, + '{"email_address":null,"options":["SMS","CALL","INVOICE"],"phone_number":"***-***-9721"}' + ) + ), + new MockRequest( + new MockRequestMatchRule( + 'post', + '/v2\/fedex_registrations\/\S*\/pin$/' + ), + new MockRequestResponseInfo( + 200, + '{"message":"sent secured Pin"}' + ) + ), + new MockRequest( + new MockRequestMatchRule( + 'post', + '/v2\/fedex_registrations\/\S*\/pin\/validate$/' + ), + new MockRequestResponseInfo( + 200, + '{"id":"ca_123","type":"FedexAccount",' . + '"credentials":{"account_number":"123456789","mfa_key":"123456789-XXXXX"}}' + ) + ), + new MockRequest( + new MockRequestMatchRule( + 'post', + '/v2\/fedex_registrations\/\S*\/invoice$/' + ), + new MockRequestResponseInfo( + 200, + '{"id":"ca_123","type":"FedexAccount",' . + '"credentials":{"account_number":"123456789","mfa_key":"123456789-XXXXX"}}' + ) + ), + ] + ); + + self::$client = new EasyPostClient( + (string)getenv('EASYPOST_TEST_API_KEY'), + Constants::TIMEOUT, + Constants::API_BASE, + $mockingUtility + ); } /** @@ -33,8 +80,6 @@ public static function tearDownAfterClass(): void */ public function testRegisterAddress(): void { - TestUtil::setupCassette('fedex_registration/registerAddress.yml'); - $fedexAccountNumber = '123456789'; $params = [ 'address_validation' => [ @@ -52,13 +97,13 @@ public function testRegisterAddress(): void $response = self::$client->fedexRegistration->registerAddress($fedexAccountNumber, $params); - $this->assertInstanceOf(FedExAccountValidationResponse::class, $response); - $this->assertNull($response->email_address); - $this->assertNotNull($response->options); + $this->assertInstanceOf(EasyPostObject::class, $response); + $this->assertNull($response->email_address); // @phpstan-ignore-line + $this->assertNotNull($response->options); // @phpstan-ignore-line $this->assertContains('SMS', $response->options); $this->assertContains('CALL', $response->options); $this->assertContains('INVOICE', $response->options); - $this->assertEquals('***-***-9721', $response->phone_number); + $this->assertEquals('***-***-9721', $response->phone_number); // @phpstan-ignore-line } /** @@ -66,14 +111,12 @@ public function testRegisterAddress(): void */ public function testRequestPin(): void { - TestUtil::setupCassette('fedex_registration/requestPin.yml'); - $fedexAccountNumber = '123456789'; $response = self::$client->fedexRegistration->requestPin($fedexAccountNumber, 'SMS'); - $this->assertInstanceOf(FedExRequestPinResponse::class, $response); - $this->assertEquals('sent secured Pin', $response->message); + $this->assertInstanceOf(EasyPostObject::class, $response); + $this->assertEquals('sent secured Pin', $response->message); // @phpstan-ignore-line } /** @@ -81,8 +124,6 @@ public function testRequestPin(): void */ public function testValidatePin(): void { - TestUtil::setupCassette('fedex_registration/validatePin.yml'); - $fedexAccountNumber = '123456789'; $params = [ 'pin_validation' => [ @@ -96,10 +137,10 @@ public function testValidatePin(): void $response = self::$client->fedexRegistration->validatePin($fedexAccountNumber, $params); - $this->assertInstanceOf(FedExAccountValidationResponse::class, $response); - $this->assertEquals('ca_123', $response->id); - $this->assertEquals('FedexAccount', $response->type); - $this->assertNotNull($response->credentials); + $this->assertInstanceOf(EasyPostObject::class, $response); + $this->assertEquals('ca_123', $response->id); // @phpstan-ignore-line + $this->assertEquals('FedexAccount', $response->type); // @phpstan-ignore-line + $this->assertNotNull($response->credentials); // @phpstan-ignore-line $this->assertEquals('123456789', $response->credentials['account_number']); $this->assertEquals('123456789-XXXXX', $response->credentials['mfa_key']); } @@ -109,8 +150,6 @@ public function testValidatePin(): void */ public function testSubmitInvoice(): void { - TestUtil::setupCassette('fedex_registration/submitInvoice.yml'); - $fedexAccountNumber = '123456789'; $params = [ 'invoice_validation' => [ @@ -127,10 +166,10 @@ public function testSubmitInvoice(): void $response = self::$client->fedexRegistration->submitInvoice($fedexAccountNumber, $params); - $this->assertInstanceOf(FedExAccountValidationResponse::class, $response); - $this->assertEquals('ca_123', $response->id); - $this->assertEquals('FedexAccount', $response->type); - $this->assertNotNull($response->credentials); + $this->assertInstanceOf(EasyPostObject::class, $response); + $this->assertEquals('ca_123', $response->id); // @phpstan-ignore-line + $this->assertEquals('FedexAccount', $response->type); // @phpstan-ignore-line + $this->assertNotNull($response->credentials); // @phpstan-ignore-line $this->assertEquals('123456789', $response->credentials['account_number']); $this->assertEquals('123456789-XXXXX', $response->credentials['mfa_key']); }