Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/EasyPost/EasyPostClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
172 changes: 172 additions & 0 deletions lib/EasyPost/Service/FedExRegistrationService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php

namespace EasyPost\Service;

use EasyPost\Http\Requestor;
use EasyPost\Util\InternalUtil;

/**
* FedExRegistration service containing all the logic to make API calls.
*/
class FedExRegistrationService extends BaseService
{
/**
* Register the billing address for a FedEx account.
* Advanced method for custom parameter structures.
*
* @param string $fedexAccountNumber
* @param mixed $params
* @return mixed
*/
public function registerAddress(string $fedexAccountNumber, mixed $params = null): mixed
{
$wrappedParams = $this->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<string, mixed>
*/
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<string, mixed>
*/
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<string, mixed>
*/
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<string, mixed> &$array
* @return void
*/
private function ensureNameField(array &$array): void
{
if (!isset($array['name'])) {
$array['name'] = uniqid();
}
}
}
176 changes: 176 additions & 0 deletions test/EasyPost/FedExRegistrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

namespace EasyPost\Test;

use EasyPost\Constant\Constants;
use EasyPost\EasyPostClient;
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
{
private static EasyPostClient $client;

/**
* Setup the testing environment for this file.
*/
public static function setUpBeforeClass(): void
{
$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
);
}

/**
* Test registering a billing address.
*/
public function testRegisterAddress(): void
{
$fedexAccountNumber = '123456789';
$params = [
'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 = 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']);
}
}
Loading