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/Service/FedExRegistrationService.php b/lib/EasyPost/Service/FedExRegistrationService.php new file mode 100644 index 00000000..dd0f29be --- /dev/null +++ b/lib/EasyPost/Service/FedExRegistrationService.php @@ -0,0 +1,172 @@ +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 unique ID 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'])) { + $array['name'] = uniqid(); + } + } +} diff --git a/test/EasyPost/FedExRegistrationTest.php b/test/EasyPost/FedExRegistrationTest.php new file mode 100644 index 00000000..afbbda6d --- /dev/null +++ b/test/EasyPost/FedExRegistrationTest.php @@ -0,0 +1,176 @@ + [ + '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(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); // @phpstan-ignore-line + } + + /** + * Test requesting a pin. + */ + public function testRequestPin(): void + { + $fedexAccountNumber = '123456789'; + + $response = self::$client->fedexRegistration->requestPin($fedexAccountNumber, 'SMS'); + + $this->assertInstanceOf(EasyPostObject::class, $response); + $this->assertEquals('sent secured Pin', $response->message); // @phpstan-ignore-line + } + + /** + * Test validating a pin. + */ + public function testValidatePin(): void + { + $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(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']); + } + + /** + * Test submitting details about an invoice. + */ + public function testSubmitInvoice(): void + { + $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(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']); + } +}