From 96d474c933c769bde1e6e4c749c23505bbaafbae Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 09:45:50 +0530 Subject: [PATCH 01/10] Fix Name.com error handling for invalid domains and unsupported TLDs - Return false instead of throwing when checking availability for invalid domains - Add UnsupportedTldException for unsupported TLDs in purchase and transfer - Change error checks in transfer to use && instead of || for stricter matching - Add test for transferring unsupported TLD (.in) --- src/Domains/Registrar/Adapter/NameCom.php | 32 +++++++++++++++---- .../Exception/UnsupportedTldException.php | 9 ++++++ tests/Registrar/NameComTest.php | 9 ++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 src/Domains/Registrar/Exception/UnsupportedTldException.php diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 530ee63..64df279 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -12,6 +12,7 @@ use Utopia\Domains\Registrar\Exception\AuthException; use Utopia\Domains\Registrar\Exception\PriceNotFoundException; use Utopia\Domains\Registrar\Exception\DomainNotFoundException; +use Utopia\Domains\Registrar\Exception\UnsupportedTldException; use Utopia\Domains\Registrar\Adapter; use Utopia\Domains\Registrar\Renewal; use Utopia\Domains\Registrar\TransferStatus; @@ -30,6 +31,8 @@ class NameCom extends Adapter public const ERROR_MESSAGE_DOMAIN_NOT_TRANSFERABLE = 'we were unable to get authoritative domain information from the registry. this usually means that the domain name or auth code provided was not correct.'; public const ERROR_MESSAGE_INVALID_CONTACT = 'invalid value for $country when calling'; public const ERROR_MESSAGE_INVALID_DOMAIN = 'Invalid Domain Name'; + public const ERROR_MESSAGE_INVALID_DOMAINS = 'None of the submitted domains are valid'; + public const ERROR_MESSAGE_UNSUPPORTED_TLD = 'unsupported tld'; /** * Contact Types @@ -89,9 +92,18 @@ public function getName(): string */ public function available(string $domain): bool { - $result = $this->send('POST', '/core/v1/domains:checkAvailability', [ - 'domainNames' => [$domain], - ]); + try { + $result = $this->send('POST', '/core/v1/domains:checkAvailability', [ + 'domainNames' => [$domain], + ]); + } catch (Exception $e) { + $errorLower = strtolower($e->getMessage()); + if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_DOMAINS))) { + return false; + } + + throw $e; + } return $result['results'][0]['purchasable'] ?? false; } @@ -165,6 +177,9 @@ public function purchase(string $domain, array|Contact $contacts, int $periodYea if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_CONTACT))) { throw new InvalidContactException($message, $e->getCode(), $e); } + if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD))) { + throw new UnsupportedTldException($message, $e->getCode(), $e); + } throw new DomainsException($message, $code, $e); } } @@ -209,12 +224,17 @@ public function transfer(string $domain, string $authCode, array|Contact $contac $code = $e->getCode(); $errorLower = strtolower($e->getMessage()); - if ($code === 422 || - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_CONTACT)) + if ($code === 422 && + str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_CONTACT)) ) { throw new InvalidContactException($message, $e->getCode(), $e); } - if ($code === 409 || + if ($code === 422 && + str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD)) + ) { + throw new UnsupportedTldException($message, $e->getCode(), $e); + } + if ($code === 409 && str_contains($errorLower, strtolower(self::ERROR_MESSAGE_DOMAIN_NOT_TRANSFERABLE)) ) { throw new DomainNotTransferableException($message, $code, $e); diff --git a/src/Domains/Registrar/Exception/UnsupportedTldException.php b/src/Domains/Registrar/Exception/UnsupportedTldException.php new file mode 100644 index 0000000..b65ed04 --- /dev/null +++ b/src/Domains/Registrar/Exception/UnsupportedTldException.php @@ -0,0 +1,9 @@ +generateRandomString() . '.in'; + + $this->expectException(UnsupportedTldException::class); + $this->registrar->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); + } + public function testCheckTransferStatus(): void { $this->markTestSkipped('Name.com for some reason always returning 404 (Not Found) for transfer status check. Investigate later.'); From 33158232bae343f18954ffbe68d929b8fbb781a4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 09:50:06 +0530 Subject: [PATCH 02/10] unsupported tld --- src/Domains/Registrar/Adapter/NameCom.php | 9 ++++++--- tests/Registrar/NameComTest.php | 10 +++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 64df279..0f15310 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -33,6 +33,7 @@ class NameCom extends Adapter public const ERROR_MESSAGE_INVALID_DOMAIN = 'Invalid Domain Name'; public const ERROR_MESSAGE_INVALID_DOMAINS = 'None of the submitted domains are valid'; public const ERROR_MESSAGE_UNSUPPORTED_TLD = 'unsupported tld'; + public const ERROR_MESSAGE_UNSUPPORTED_TRANSFER = 'do not support transfers for'; /** * Contact Types @@ -177,7 +178,9 @@ public function purchase(string $domain, array|Contact $contacts, int $periodYea if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_CONTACT))) { throw new InvalidContactException($message, $e->getCode(), $e); } - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD))) { + if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD)) || + str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TRANSFER)) + ) { throw new UnsupportedTldException($message, $e->getCode(), $e); } throw new DomainsException($message, $code, $e); @@ -229,8 +232,8 @@ public function transfer(string $domain, string $authCode, array|Contact $contac ) { throw new InvalidContactException($message, $e->getCode(), $e); } - if ($code === 422 && - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD)) + if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD)) || + str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TRANSFER)) ) { throw new UnsupportedTldException($message, $e->getCode(), $e); } diff --git a/tests/Registrar/NameComTest.php b/tests/Registrar/NameComTest.php index aa5427f..93ac4c4 100644 --- a/tests/Registrar/NameComTest.php +++ b/tests/Registrar/NameComTest.php @@ -158,7 +158,7 @@ public function testSuggestWithFilter(): void } } - public function testTransferUnsupportedTld(): void + public function testTransferUnsupportedTldDotIn(): void { $domain = $this->generateRandomString() . '.in'; @@ -166,6 +166,14 @@ public function testTransferUnsupportedTld(): void $this->registrar->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); } + public function testTransferUnsupportedTldDotXYZ(): void + { + $domain = $this->generateRandomString() . '.xyz'; + + $this->expectException(UnsupportedTldException::class); + $this->registrar->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); + } + public function testCheckTransferStatus(): void { $this->markTestSkipped('Name.com for some reason always returning 404 (Not Found) for transfer status check. Investigate later.'); From b7454d1538278fb6019e94c80e748570f13fa3e1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 10:14:05 +0530 Subject: [PATCH 03/10] improve error handling --- src/Domains/Registrar/Adapter/NameCom.php | 163 +++++++++++------- .../Exception/InvalidAuthCodeException.php | 9 + tests/Registrar/Base.php | 6 +- tests/Registrar/NameComTest.php | 2 +- 4 files changed, 114 insertions(+), 66 deletions(-) create mode 100644 src/Domains/Registrar/Exception/InvalidAuthCodeException.php diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 0f15310..a667bf4 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -7,7 +7,7 @@ use Utopia\Domains\Registrar\Contact; use Utopia\Domains\Exception as DomainsException; use Utopia\Domains\Registrar\Exception\DomainTakenException; -use Utopia\Domains\Registrar\Exception\DomainNotTransferableException; +use Utopia\Domains\Registrar\Exception\InvalidAuthCodeException; use Utopia\Domains\Registrar\Exception\InvalidContactException; use Utopia\Domains\Registrar\Exception\AuthException; use Utopia\Domains\Registrar\Exception\PriceNotFoundException; @@ -24,16 +24,32 @@ class NameCom extends Adapter { /** - * Name.com API Error Messages + * Name.com API Error Keys */ - public const ERROR_MESSAGE_NOT_FOUND = 'Not Found'; - public const ERROR_MESSAGE_DOMAIN_TAKEN = 'Domain is not available'; - public const ERROR_MESSAGE_DOMAIN_NOT_TRANSFERABLE = 'we were unable to get authoritative domain information from the registry. this usually means that the domain name or auth code provided was not correct.'; - public const ERROR_MESSAGE_INVALID_CONTACT = 'invalid value for $country when calling'; - public const ERROR_MESSAGE_INVALID_DOMAIN = 'Invalid Domain Name'; - public const ERROR_MESSAGE_INVALID_DOMAINS = 'None of the submitted domains are valid'; - public const ERROR_MESSAGE_UNSUPPORTED_TLD = 'unsupported tld'; - public const ERROR_MESSAGE_UNSUPPORTED_TRANSFER = 'do not support transfers for'; + public const ERROR_NOT_FOUND = 'Not Found'; + public const ERROR_DOMAIN_TAKEN = 'Domain is not available'; + public const ERROR_INVALID_AUTH_CODE = 'we were unable to get authoritative domain information from the registry. this usually means that the domain name or auth code provided was not correct.'; + public const ERROR_INVALID_CONTACT = 'invalid value for'; + public const ERROR_INVALID_DOMAIN = 'Invalid Domain Name'; + public const ERROR_INVALID_DOMAINS = 'None of the submitted domains are valid'; + public const ERROR_UNSUPPORTED_TLD = 'unsupported tld'; + public const ERROR_UNSUPPORTED_TRANSFER = 'do not support transfers for'; + public const ERROR_UNAUTHORIZED = 'Unauthorized'; + + /** + * Name.com API Error Map: [message => code] + */ + public const ERROR_MAP = [ + self::ERROR_NOT_FOUND => 404, + self::ERROR_DOMAIN_TAKEN => null, + self::ERROR_INVALID_AUTH_CODE => 500, + self::ERROR_INVALID_CONTACT => null, + self::ERROR_INVALID_DOMAIN => null, + self::ERROR_INVALID_DOMAINS => null, + self::ERROR_UNSUPPORTED_TLD => 422, + self::ERROR_UNSUPPORTED_TRANSFER => 400, + self::ERROR_UNAUTHORIZED => 401, + ]; /** * Contact Types @@ -98,12 +114,12 @@ public function available(string $domain): bool 'domainNames' => [$domain], ]); } catch (Exception $e) { - $errorLower = strtolower($e->getMessage()); - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_DOMAINS))) { - return false; + switch ($this->matchError($e)) { + case self::ERROR_INVALID_DOMAINS: + return false; + default: + throw $e; } - - throw $e; } return $result['results'][0]['purchasable'] ?? false; @@ -164,26 +180,27 @@ public function purchase(string $domain, array|Contact $contacts, int $periodYea $result = $this->send('POST', '/core/v1/domains', $data); return (string) ($result['order'] ?? ''); - } catch (AuthException $e) { - throw $e; - } catch (Exception $e) { $message = 'Failed to purchase domain: ' . $e->getMessage(); $code = $e->getCode(); - $errorLower = strtolower($e->getMessage()); - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_DOMAIN_TAKEN))) { - throw new DomainTakenException($message, $e->getCode(), $e); - } - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_CONTACT))) { - throw new InvalidContactException($message, $e->getCode(), $e); - } - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD)) || - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TRANSFER)) - ) { - throw new UnsupportedTldException($message, $e->getCode(), $e); + switch ($this->matchError($e)) { + case self::ERROR_UNAUTHORIZED: + throw new AuthException($message, $code, $e); + + case self::ERROR_DOMAIN_TAKEN: + throw new DomainTakenException($message, $code, $e); + + case self::ERROR_INVALID_CONTACT: + throw new InvalidContactException($message, $code, $e); + + case self::ERROR_UNSUPPORTED_TLD: + case self::ERROR_UNSUPPORTED_TRANSFER: + throw new UnsupportedTldException($message, $code, $e); + + default: + throw new DomainsException($message, $code, $e); } - throw new DomainsException($message, $code, $e); } } @@ -219,33 +236,30 @@ public function transfer(string $domain, string $authCode, array|Contact $contac $result = $this->send('POST', '/core/v1/transfers', $data); return (string) ($result['order'] ?? ''); - } catch (AuthException $e) { - throw $e; - } catch (Exception $e) { $message = 'Failed to transfer domain: ' . $e->getMessage(); $code = $e->getCode(); - $errorLower = strtolower($e->getMessage()); - if ($code === 422 && - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_CONTACT)) - ) { - throw new InvalidContactException($message, $e->getCode(), $e); - } - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TLD)) || - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_UNSUPPORTED_TRANSFER)) - ) { - throw new UnsupportedTldException($message, $e->getCode(), $e); - } - if ($code === 409 && - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_DOMAIN_NOT_TRANSFERABLE)) - ) { - throw new DomainNotTransferableException($message, $code, $e); - } - if (str_contains($errorLower, strtolower(self::ERROR_MESSAGE_DOMAIN_TAKEN))) { - throw new DomainTakenException($message, $e->getCode(), $e); + switch ($this->matchError($e)) { + case self::ERROR_UNAUTHORIZED: + throw new AuthException($message, $code, $e); + + case self::ERROR_INVALID_CONTACT: + throw new InvalidContactException($message, $code, $e); + + case self::ERROR_UNSUPPORTED_TLD: + case self::ERROR_UNSUPPORTED_TRANSFER: + throw new UnsupportedTldException($message, $code, $e); + + case self::ERROR_INVALID_AUTH_CODE: + throw new InvalidAuthCodeException($message, $code, $e); + + case self::ERROR_DOMAIN_TAKEN: + throw new DomainTakenException($message, $code, $e); + + default: + throw new DomainsException($message, $code, $e); } - throw new DomainsException($message, $code, $e); } } @@ -386,16 +400,16 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = } catch (Exception $e) { $message = 'Failed to get price for domain: ' . $domain . ' - ' . $e->getMessage(); - $errorLower = strtolower($e->getMessage()); + $code = $e->getCode(); - if ( - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_NOT_FOUND)) || - str_contains($errorLower, strtolower(self::ERROR_MESSAGE_INVALID_DOMAIN)) - ) { - throw new PriceNotFoundException($message, $e->getCode(), $e); - } + switch ($this->matchError($e)) { + case self::ERROR_NOT_FOUND: + case self::ERROR_INVALID_DOMAIN: + throw new PriceNotFoundException($message, $code, $e); - throw new DomainsException($message, $e->getCode(), $e); + default: + throw new DomainsException($message, $code, $e); + } } } @@ -646,16 +660,37 @@ private function send(string $method, string $path, ?array $data = null): array $message .= '(' . $details . ')'; } - if ($httpCode === 401 && $message === 'Unauthorized') { - throw new AuthException('Failed to send request to Name.com: ' . $message, $httpCode); - } - throw new Exception($message, $httpCode); } return $response ?? []; } + /** + * Match an exception against the error map. + * Returns the matched error key, or null if no match is found. + * + * @param Exception $e The exception to check + * @return string|null The matched error key from ERROR_MAP, or null + */ + private function matchError(Exception $e): ?string + { + $errorLower = strtolower($e->getMessage()); + $code = $e->getCode(); + + foreach (self::ERROR_MAP as $message => $expectedCode) { + if ($expectedCode !== null && $code !== $expectedCode) { + continue; + } + + if (str_contains($errorLower, strtolower($message))) { + return $message; + } + } + + return null; + } + /** * Sanitize contacts array to Name.com format * diff --git a/src/Domains/Registrar/Exception/InvalidAuthCodeException.php b/src/Domains/Registrar/Exception/InvalidAuthCodeException.php new file mode 100644 index 0000000..74ff590 --- /dev/null +++ b/src/Domains/Registrar/Exception/InvalidAuthCodeException.php @@ -0,0 +1,9 @@ +assertIsString($result); $this->assertNotEmpty($result); } catch (\Exception $e) { - $this->assertInstanceOf(DomainNotTransferableException::class, $e); + $this->assertTrue( + $e instanceof InvalidAuthCodeException || $e instanceof DomainNotTransferableException, + 'Expected InvalidAuthCodeException or DomainNotTransferableException, got ' . get_class($e) + ); } } diff --git a/tests/Registrar/NameComTest.php b/tests/Registrar/NameComTest.php index 93ac4c4..613d804 100644 --- a/tests/Registrar/NameComTest.php +++ b/tests/Registrar/NameComTest.php @@ -115,7 +115,7 @@ public function testPurchaseWithInvalidCredentials(): void $domain = $this->generateRandomString() . '.com'; $this->expectException(AuthException::class); - $this->expectExceptionMessage("Failed to send request to Name.com: Unauthorized"); + $this->expectExceptionMessage("Failed to purchase domain: Unauthorized"); $registrar->purchase($domain, $this->getPurchaseContact(), 1); } From b2d91f4cf69fa673ba42821a056aa4ea0f6655e0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 10:33:42 +0530 Subject: [PATCH 04/10] fix transfer method handler --- src/Domains/Registrar.php | 7 ++-- src/Domains/Registrar/Adapter.php | 6 ++-- src/Domains/Registrar/Adapter/Mock.php | 9 ++--- src/Domains/Registrar/Adapter/NameCom.php | 20 +++--------- src/Domains/Registrar/Adapter/OpenSRS.php | 22 ++++--------- tests/Registrar/Base.php | 2 +- tests/Registrar/MockTest.php | 40 +---------------------- tests/Registrar/NameComTest.php | 4 +-- tests/Registrar/OpenSRSTest.php | 4 +-- 9 files changed, 24 insertions(+), 90 deletions(-) diff --git a/src/Domains/Registrar.php b/src/Domains/Registrar.php index 58cbf58..685608e 100644 --- a/src/Domains/Registrar.php +++ b/src/Domains/Registrar.php @@ -178,13 +178,12 @@ public function renew(string $domain, int $periodYears): Renewal * * @param string $domain * @param string $authCode - * @param array|Contact $contacts - * @param array $nameservers + * @param float|null $purchasePrice Required if domain is premium * @return string Order ID */ - public function transfer(string $domain, string $authCode, array|Contact $contacts, int $periodYears = 1, array $nameservers = []): string + public function transfer(string $domain, string $authCode, ?float $purchasePrice = null): string { - return $this->adapter->transfer($domain, $authCode, $contacts, $periodYears, $nameservers); + return $this->adapter->transfer($domain, $authCode, $purchasePrice); } /** diff --git a/src/Domains/Registrar/Adapter.php b/src/Domains/Registrar/Adapter.php index f18beb2..35d3aa4 100644 --- a/src/Domains/Registrar/Adapter.php +++ b/src/Domains/Registrar/Adapter.php @@ -173,12 +173,10 @@ abstract public function renew(string $domain, int $periodYears): Renewal; * * @param string $domain * @param string $authCode - * @param array|Contact $contacts - * @param int $periodYears - * @param array $nameservers + * @param float|null $purchasePrice Required if domain is premium * @return string Order ID */ - abstract public function transfer(string $domain, string $authCode, array|Contact $contacts, int $periodYears = 1, array $nameservers = []): string; + abstract public function transfer(string $domain, string $authCode, ?float $purchasePrice = null): string; /** * Get the authorization code for an EPP domain diff --git a/src/Domains/Registrar/Adapter/Mock.php b/src/Domains/Registrar/Adapter/Mock.php index 6dae736..27b54e2 100644 --- a/src/Domains/Registrar/Adapter/Mock.php +++ b/src/Domains/Registrar/Adapter/Mock.php @@ -356,21 +356,16 @@ public function updateDomain(string $domain, UpdateDetails $details): bool * * @param string $domain * @param string $authCode - * @param array|Contact $contacts - * @param int $periodYears - * @param array $nameservers + * @param float|null $purchasePrice Required if domain is premium * @return string Order ID * @throws DomainTakenException - * @throws InvalidContactException */ - public function transfer(string $domain, string $authCode, array|Contact $contacts, int $periodYears = 1, array $nameservers = []): string + public function transfer(string $domain, string $authCode, ?float $purchasePrice = null): string { if (in_array($domain, $this->purchasedDomains)) { throw new DomainTakenException("Domain {$domain} is already in this account", self::RESPONSE_CODE_DOMAIN_TAKEN); } - $this->validateContacts($contacts); - $this->transferredDomains[] = $domain; $this->purchasedDomains[] = $domain; diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index a667bf4..835c05d 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -209,28 +209,19 @@ public function purchase(string $domain, array|Contact $contacts, int $periodYea * * @param string $domain The domain name to transfer * @param string $authCode Authorization code for the transfer - * @param array|Contact $contacts Contact information - * @param int $periodYears Transfer period in years - * @param array $nameservers Nameservers to use + * @param float|null $purchasePrice Required if domain is premium * @return string Order ID */ - public function transfer(string $domain, string $authCode, array|Contact $contacts, int $periodYears = 1, array $nameservers = []): string + public function transfer(string $domain, string $authCode, ?float $purchasePrice = null): string { try { - $contacts = is_array($contacts) ? $contacts : [$contacts]; - $nameservers = empty($nameservers) ? $this->defaultNameservers : $nameservers; - - $contactData = $this->sanitizeContacts($contacts); - $data = [ 'domainName' => $domain, 'authCode' => $authCode, - 'years' => $periodYears, - 'contacts' => $contactData, ]; - if (!empty($nameservers)) { - $data['nameservers'] = $nameservers; + if ($purchasePrice !== null) { + $data['purchasePrice'] = $purchasePrice; } $result = $this->send('POST', '/core/v1/transfers', $data); @@ -244,9 +235,6 @@ public function transfer(string $domain, string $authCode, array|Contact $contac case self::ERROR_UNAUTHORIZED: throw new AuthException($message, $code, $e); - case self::ERROR_INVALID_CONTACT: - throw new InvalidContactException($message, $code, $e); - case self::ERROR_UNSUPPORTED_TLD: case self::ERROR_UNSUPPORTED_TRANSFER: throw new UnsupportedTldException($message, $code, $e); diff --git a/src/Domains/Registrar/Adapter/OpenSRS.php b/src/Domains/Registrar/Adapter/OpenSRS.php index 1fcd6fd..9c3dfea 100644 --- a/src/Domains/Registrar/Adapter/OpenSRS.php +++ b/src/Domains/Registrar/Adapter/OpenSRS.php @@ -136,7 +136,7 @@ public function updateNameservers(string $domain, array $nameservers): array ]; } - private function register(string $domain, string $regType, array $user, array $contacts, array $nameservers = [], int $periodYears = 1, ?string $authCode = null): string + private function register(string $domain, string $regType, array $user, array $contacts, array $nameservers = [], int $periodYears = 1, ?string $authCode = null, ?float $purchasePrice = null): string { $hasNameservers = empty($nameservers) ? 0 : 1; @@ -166,6 +166,10 @@ private function register(string $domain, string $regType, array $user, array $c $message['attributes']['nameserver_list'] = $nameservers; } + if ($purchasePrice !== null) { + $message['attributes']['premium_price_to_display'] = $purchasePrice; + } + $result = $this->send($message); return $result; @@ -205,21 +209,12 @@ public function purchase(string $domain, array|Contact $contacts, int $periodYea } } - public function transfer(string $domain, string $authCode, array|Contact $contacts, int $periodYears = 1, array $nameservers = []): string + public function transfer(string $domain, string $authCode, ?float $purchasePrice = null): string { - $contacts = is_array($contacts) ? $contacts : [$contacts]; - - $nameservers = - empty($nameservers) - ? $this->defaultNameservers - : $nameservers; - - $contacts = $this->sanitizeContacts($contacts); - $regType = Registrar::REG_TYPE_TRANSFER; try { - $result = $this->register($domain, $regType, $this->user, $contacts, $nameservers, $periodYears, $authCode); + $result = $this->register($domain, $regType, $this->user, [], [], 1, $authCode, $purchasePrice); $result = $this->response($result); return $result['id']; @@ -230,9 +225,6 @@ public function transfer(string $domain, string $authCode, array|Contact $contac $reason = $parts[1] ?? $parts[0]; throw new DomainNotTransferableException('Domain is not transferable: ' . $reason, $e->getCode(), $e); } - if ($code === self::RESPONSE_CODE_INVALID_CONTACT) { - throw new InvalidContactException('Failed to transfer domain: ' . $e->getMessage(), $code, $e); - } if ($code === self::RESPONSE_CODE_DOMAIN_TAKEN) { throw new DomainTakenException('Domain is already in this account', $code, $e); } diff --git a/tests/Registrar/Base.php b/tests/Registrar/Base.php index af052ee..755e0a4 100644 --- a/tests/Registrar/Base.php +++ b/tests/Registrar/Base.php @@ -297,7 +297,7 @@ public function testTransfer(): void $domain = $this->generateRandomString() . '.' . $this->getDefaultTld(); try { - $result = $this->getRegistrar()->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); + $result = $this->getRegistrar()->transfer($domain, 'test-auth-code'); $this->assertIsString($result); $this->assertNotEmpty($result); diff --git a/tests/Registrar/MockTest.php b/tests/Registrar/MockTest.php index 342c508..ba17e47 100644 --- a/tests/Registrar/MockTest.php +++ b/tests/Registrar/MockTest.php @@ -6,9 +6,7 @@ use Utopia\Cache\Adapter\None as NoneAdapter; use Utopia\Domains\Cache; use Utopia\Domains\Registrar; -use Utopia\Domains\Registrar\Contact; use Utopia\Domains\Registrar\Exception\DomainTakenException; -use Utopia\Domains\Registrar\Exception\InvalidContactException; use Utopia\Domains\Registrar\Adapter\Mock; use Utopia\Domains\Registrar\UpdateDetails; @@ -83,19 +81,6 @@ public function testPurchaseWithNameservers(): void $this->assertNotEmpty($result); } - public function testTransferWithNameservers(): void - { - $domain = 'transferdomain.com'; - $contact = $this->getPurchaseContact(); - $authCode = 'test-auth-code-12345'; - $nameservers = ['ns1.example.com', 'ns2.example.com']; - - $result = $this->registrar->transfer($domain, $authCode, $contact, 1, $nameservers); - - $this->assertIsString($result); - $this->assertNotEmpty($result); - } - public function testTransferAlreadyExists(): void { $domain = 'alreadyexists.com'; @@ -106,30 +91,7 @@ public function testTransferAlreadyExists(): void $this->expectException(DomainTakenException::class); $this->expectExceptionMessage('Domain ' . $domain . ' is already in this account'); - $this->registrar->transfer($domain, $authCode, $contact); - } - - public function testTransferWithInvalidContact(): void - { - $this->expectException(InvalidContactException::class); - $this->expectExceptionMessage('missing required field'); - - $invalidContact = new Contact( - 'John', - 'Doe', - '+1.5551234567', - 'john.doe@example.com', - '123 Main St', - 'Suite 100', - '', - '', // Empty city - 'CA', - 'US', - '94105', - 'Test Inc' - ); - - $this->registrar->transfer('transfer.com', 'auth-code', [$invalidContact]); + $this->registrar->transfer($domain, $authCode); } public function testCheckTransferStatusWithRequestAddress(): void diff --git a/tests/Registrar/NameComTest.php b/tests/Registrar/NameComTest.php index 613d804..654aee2 100644 --- a/tests/Registrar/NameComTest.php +++ b/tests/Registrar/NameComTest.php @@ -163,7 +163,7 @@ public function testTransferUnsupportedTldDotIn(): void $domain = $this->generateRandomString() . '.in'; $this->expectException(UnsupportedTldException::class); - $this->registrar->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); + $this->registrar->transfer($domain, 'test-auth-code'); } public function testTransferUnsupportedTldDotXYZ(): void @@ -171,7 +171,7 @@ public function testTransferUnsupportedTldDotXYZ(): void $domain = $this->generateRandomString() . '.xyz'; $this->expectException(UnsupportedTldException::class); - $this->registrar->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); + $this->registrar->transfer($domain, 'test-auth-code'); } public function testCheckTransferStatus(): void diff --git a/tests/Registrar/OpenSRSTest.php b/tests/Registrar/OpenSRSTest.php index 480e9bd..4547f6b 100644 --- a/tests/Registrar/OpenSRSTest.php +++ b/tests/Registrar/OpenSRSTest.php @@ -174,7 +174,7 @@ public function testTransferNotRegistered(): void $domain = $this->generateRandomString() . '.net'; try { - $result = $this->registrar->transfer($domain, 'test-auth-code', $this->getPurchaseContact()); + $result = $this->registrar->transfer($domain, 'test-auth-code'); $this->assertIsString($result); $this->assertNotEmpty($result); } catch (DomainNotTransferableException $e) { @@ -186,7 +186,7 @@ public function testTransferNotRegistered(): void public function testTransferAlreadyExists(): void { try { - $result = $this->registrar->transfer($this->testDomain, 'test-auth-code', $this->getPurchaseContact()); + $result = $this->registrar->transfer($this->testDomain, 'test-auth-code'); $this->assertIsString($result); $this->assertNotEmpty($result); } catch (DomainNotTransferableException $e) { From d36455e6f12ecfe41696763db144cf645663e870 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 10:59:38 +0530 Subject: [PATCH 05/10] adjust getprice method --- src/Domains/Registrar.php | 5 +-- src/Domains/Registrar/Adapter.php | 4 +-- src/Domains/Registrar/Adapter/Mock.php | 27 +++++++++-------- src/Domains/Registrar/Adapter/NameCom.php | 37 ++++++++++++----------- src/Domains/Registrar/Adapter/OpenSRS.php | 17 +++++------ src/Domains/Registrar/Price.php | 12 ++++++++ tests/Registrar/Base.php | 16 +++++++--- 7 files changed, 69 insertions(+), 49 deletions(-) create mode 100644 src/Domains/Registrar/Price.php diff --git a/src/Domains/Registrar.php b/src/Domains/Registrar.php index 685608e..8ee7ab8 100644 --- a/src/Domains/Registrar.php +++ b/src/Domains/Registrar.php @@ -6,6 +6,7 @@ use Utopia\Domains\Registrar\Domain; use Utopia\Domains\Registrar\Renewal; use Utopia\Domains\Registrar\Contact; +use Utopia\Domains\Registrar\Price; use Utopia\Domains\Registrar\TransferStatus; use Utopia\Domains\Registrar\UpdateDetails; @@ -154,9 +155,9 @@ public function updateNameservers(string $domain, array $nameservers): array * @param int $periodYears * @param string $regType * @param int $ttl - * @return float + * @return Price */ - public function getPrice(string $domain, int $periodYears = 1, string $regType = self::REG_TYPE_NEW, int $ttl = 3600): float + public function getPrice(string $domain, int $periodYears = 1, string $regType = self::REG_TYPE_NEW, int $ttl = 3600): Price { return $this->adapter->getPrice($domain, $periodYears, $regType, $ttl); } diff --git a/src/Domains/Registrar/Adapter.php b/src/Domains/Registrar/Adapter.php index 35d3aa4..08fe0c7 100644 --- a/src/Domains/Registrar/Adapter.php +++ b/src/Domains/Registrar/Adapter.php @@ -155,9 +155,9 @@ public function updateNameservers(string $domain, array $nameservers): array * @param int $periodYears * @param string $regType * @param int $ttl - * @return float + * @return Price */ - abstract public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): float; + abstract public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): Price; /** * Renew a domain diff --git a/src/Domains/Registrar/Adapter/Mock.php b/src/Domains/Registrar/Adapter/Mock.php index 27b54e2..47149b4 100644 --- a/src/Domains/Registrar/Adapter/Mock.php +++ b/src/Domains/Registrar/Adapter/Mock.php @@ -14,6 +14,7 @@ use Utopia\Domains\Registrar\Adapter; use Utopia\Domains\Registrar\TransferStatusEnum; use Utopia\Domains\Registrar; +use Utopia\Domains\Registrar\Price; use Utopia\Domains\Registrar\UpdateDetails; class Mock extends Adapter @@ -254,24 +255,25 @@ public function getDomain(string $domain): Domain * @param int $periodYears * @param string $regType * @param int $ttl Time to live for the cache (if set) in seconds - * @return float + * @return Price * @throws PriceNotFoundException */ - public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): float + public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): Price { if ($this->cache) { $cached = $this->cache->load($domain, $ttl); - if ($cached !== null && is_array($cached)) { - return $cached['price']; + if (is_array($cached) && isset($cached['price'])) { + return new Price($cached['price'], $cached['premium'] ?? false); } } - if (isset($this->premiumDomains[$domain])) { - $result = $this->premiumDomains[$domain] * $periodYears; + $isPremium = isset($this->premiumDomains[$domain]); + + if ($isPremium) { + $price = $this->premiumDomains[$domain] * $periodYears; + $result = new Price($price, true); if ($this->cache) { - $this->cache->save($domain, [ - 'price' => $result, - ]); + $this->cache->save($domain, ['price' => $result->price, 'premium' => $result->premium]); } return $result; @@ -296,11 +298,10 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = default => 1.0, }; - $result = $basePrice * $periodYears * $multiplier; + $price = $basePrice * $periodYears * $multiplier; + $result = new Price($price, false); if ($this->cache) { - $this->cache->save($domain, [ - 'price' => $result, - ]); + $this->cache->save($domain, ['price' => $result->price, 'premium' => $result->premium]); } return $result; diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 835c05d..8f7b13d 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -20,6 +20,7 @@ use Utopia\Domains\Registrar\TransferStatusEnum; use Utopia\Domains\Registrar\UpdateDetails; use Utopia\Domains\Registrar; +use Utopia\Domains\Registrar\Price; class NameCom extends Adapter { @@ -345,15 +346,15 @@ public function suggest(array|string $query, array $tlds = [], int|null $limit = * @param int $periodYears Registration period in years * @param string $regType Type of registration * @param int $ttl Time to live for the cache - * @return float The price of the domain + * @return Price The price and premium status of the domain */ - public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): float + public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): Price { if ($this->cache) { $cacheKey = $domain . '_' . $periodYears; $cached = $this->cache->load($cacheKey, $ttl); - if ($cached !== null && is_array($cached) && isset($cached[$regType])) { - return (float) $cached[$regType]; + if (is_array($cached) && isset($cached[$regType]) && is_array($cached[$regType])) { + return new Price($cached[$regType]['price'], $cached[$regType]['premium']); } } @@ -362,37 +363,37 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = $purchasePrice = (float) ($result['purchasePrice'] ?? 0); $renewalPrice = (float) ($result['renewalPrice'] ?? 0); $transferPrice = (float) ($result['transferPrice'] ?? 0); + $isPremium = isset($result['premium']) && $result['premium'] === true; if ($this->cache) { $cacheKey = $domain . '_' . $periodYears; $this->cache->save($cacheKey, [ - Registrar::REG_TYPE_NEW => $purchasePrice, - Registrar::REG_TYPE_RENEWAL => $renewalPrice, - Registrar::REG_TYPE_TRANSFER => $transferPrice, + Registrar::REG_TYPE_NEW => ['price' => $purchasePrice, 'premium' => $isPremium], + Registrar::REG_TYPE_RENEWAL => ['price' => $renewalPrice, 'premium' => $isPremium], + Registrar::REG_TYPE_TRANSFER => ['price' => $transferPrice, 'premium' => $isPremium], ]); } switch ($regType) { case Registrar::REG_TYPE_NEW: - return $purchasePrice; + return new Price($purchasePrice, $isPremium); case Registrar::REG_TYPE_RENEWAL: - return $renewalPrice; + return new Price($renewalPrice, $isPremium); case Registrar::REG_TYPE_TRANSFER: - return $transferPrice; + return new Price($transferPrice, $isPremium); + default: + throw new PriceNotFoundException('Price not found for domain: ' . $domain, 400); } - throw new PriceNotFoundException('Price not found for domain: ' . $domain, 400); - - } catch (PriceNotFoundException $e) { - throw $e; - } catch (Exception $e) { $message = 'Failed to get price for domain: ' . $domain . ' - ' . $e->getMessage(); $code = $e->getCode(); - switch ($this->matchError($e)) { - case self::ERROR_NOT_FOUND: - case self::ERROR_INVALID_DOMAIN: + switch (true) { + case $e instanceof PriceNotFoundException: + throw $e; + + case in_array($this->matchError($e), [self::ERROR_NOT_FOUND, self::ERROR_INVALID_DOMAIN]): throw new PriceNotFoundException($message, $code, $e); default: diff --git a/src/Domains/Registrar/Adapter/OpenSRS.php b/src/Domains/Registrar/Adapter/OpenSRS.php index 9c3dfea..f3d7f85 100644 --- a/src/Domains/Registrar/Adapter/OpenSRS.php +++ b/src/Domains/Registrar/Adapter/OpenSRS.php @@ -18,6 +18,7 @@ use Utopia\Domains\Registrar\TransferStatusEnum; use Utopia\Domains\Registrar\UpdateDetails; use Utopia\Domains\Registrar; +use Utopia\Domains\Registrar\Price; class OpenSRS extends Adapter { @@ -444,16 +445,16 @@ public function suggest(array|string $query, array $tlds = [], int|null $limit = * @param int $periodYears Registration periodYears in years (default 1) * @param string $regType Type of registration: 'new', 'renewal', 'transfer', or 'trade' * @param int $ttl Time to live for the cache (if set) in seconds (default 3600 seconds = 1 hour) - * @return float The price of the domain + * @return Price The price and premium status of the domain * @throws PriceNotFoundException When pricing information is not found or unavailable for the domain * @throws DomainsException When other errors occur during price retrieval */ - public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): float + public function getPrice(string $domain, int $periodYears = 1, string $regType = Registrar::REG_TYPE_NEW, int $ttl = 3600): Price { if ($this->cache) { $cached = $this->cache->load($domain, $ttl); - if ($cached !== null && is_array($cached)) { - return $cached['price']; + if (is_array($cached) && isset($cached['price'])) { + return new Price($cached['price'], $cached['premium'] ?? false); } } @@ -479,14 +480,12 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = throw new PriceNotFoundException('Price not found for domain: ' . $domain, self::RESPONSE_CODE_DOMAIN_PRICE_NOT_FOUND); } - $result = $price; + $priceObj = new Price($price, false); if ($this->cache) { - $this->cache->save($domain, [ - 'price' => $result, - ]); + $this->cache->save($domain, ['price' => $priceObj->price, 'premium' => $priceObj->premium]); } - return $result; + return $priceObj; } catch (Exception $e) { $message = 'Failed to get price for domain: ' . $e->getMessage(); diff --git a/src/Domains/Registrar/Price.php b/src/Domains/Registrar/Price.php new file mode 100644 index 0000000..170dba6 --- /dev/null +++ b/src/Domains/Registrar/Price.php @@ -0,0 +1,12 @@ +getRegistrar()->getPrice($domain, 1, Registrar::REG_TYPE_NEW); $this->assertNotNull($result); - $this->assertIsFloat($result); - $this->assertGreaterThan(0, $result); + $this->assertInstanceOf(Price::class, $result); + $this->assertIsFloat($result->price); + $this->assertGreaterThan(0, $result->price); + $this->assertIsBool($result->premium); } public function testGetPriceWithInvalidDomain(): void @@ -238,7 +241,8 @@ public function testGetPriceWithCache(): void $result1 = $registrar->getPrice($domain, 1, Registrar::REG_TYPE_NEW, 3600); $this->assertNotNull($result1); - $this->assertIsFloat($result1); + $this->assertInstanceOf(Price::class, $result1); + $this->assertIsFloat($result1->price); $result2 = $registrar->getPrice($domain, 1, Registrar::REG_TYPE_NEW, 3600); $this->assertEquals($result1, $result2); @@ -249,8 +253,10 @@ public function testGetPriceWithCustomTtl(): void $domain = $this->getPricingTestDomain(); $result = $this->getRegistrarWithCache()->getPrice($domain, 1, Registrar::REG_TYPE_NEW, 7200); - $this->assertIsFloat($result); - $this->assertGreaterThan(0, $result); + $this->assertInstanceOf(Price::class, $result); + $this->assertIsFloat($result->price); + $this->assertGreaterThan(0, $result->price); + $this->assertIsBool($result->premium); } public function testUpdateNameservers(): void From a49c6bcd02e64196bf945126aa2bc66003f10dc8 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 11:28:00 +0530 Subject: [PATCH 06/10] reuse unsupported tld for getprice --- src/Domains/Registrar/Adapter/Mock.php | 3 ++- src/Domains/Registrar/Adapter/NameCom.php | 3 +++ tests/Registrar/Base.php | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Domains/Registrar/Adapter/Mock.php b/src/Domains/Registrar/Adapter/Mock.php index 47149b4..8abf057 100644 --- a/src/Domains/Registrar/Adapter/Mock.php +++ b/src/Domains/Registrar/Adapter/Mock.php @@ -8,6 +8,7 @@ use Utopia\Domains\Registrar\Exception\DomainTakenException; use Utopia\Domains\Registrar\Exception\InvalidContactException; use Utopia\Domains\Registrar\Exception\PriceNotFoundException; +use Utopia\Domains\Registrar\Exception\UnsupportedTldException; use Utopia\Domains\Registrar\Domain; use Utopia\Domains\Registrar\Renewal; use Utopia\Domains\Registrar\TransferStatus; @@ -287,7 +288,7 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = $tld = end($parts); if (!in_array($tld, $this->supportedTlds)) { - throw new PriceNotFoundException("TLD .{$tld} is not supported", self::RESPONSE_CODE_BAD_REQUEST); + throw new UnsupportedTldException("TLD .{$tld} is not supported", self::RESPONSE_CODE_BAD_REQUEST); } $basePrice = $this->defaultPrice; diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 8f7b13d..6495ea5 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -393,6 +393,9 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = case $e instanceof PriceNotFoundException: throw $e; + case $this->matchError($e) === self::ERROR_UNSUPPORTED_TLD: + throw new UnsupportedTldException($message, $code, $e); + case in_array($this->matchError($e), [self::ERROR_NOT_FOUND, self::ERROR_INVALID_DOMAIN]): throw new PriceNotFoundException($message, $code, $e); diff --git a/tests/Registrar/Base.php b/tests/Registrar/Base.php index dcd478b..fe7d9f1 100644 --- a/tests/Registrar/Base.php +++ b/tests/Registrar/Base.php @@ -9,7 +9,7 @@ use Utopia\Domains\Registrar\Exception\DomainNotTransferableException; use Utopia\Domains\Registrar\Exception\InvalidAuthCodeException; use Utopia\Domains\Registrar\Exception\InvalidContactException; -use Utopia\Domains\Registrar\Exception\PriceNotFoundException; +use Utopia\Domains\Registrar\Exception\UnsupportedTldException; use Utopia\Domains\Registrar\TransferStatusEnum; use Utopia\Domains\Registrar\UpdateDetails; use Utopia\Domains\Registrar\Price; @@ -230,7 +230,7 @@ public function testGetPrice(): void public function testGetPriceWithInvalidDomain(): void { - $this->expectException(PriceNotFoundException::class); + $this->expectException(UnsupportedTldException::class); $this->getRegistrar()->getPrice("invalid.invalidtld", 1, Registrar::REG_TYPE_NEW); } From 51d0d2dbb62980d1e74cc793ddced5f2b53f8186 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 11:32:36 +0530 Subject: [PATCH 07/10] check both --- src/Domains/Registrar/Adapter/NameCom.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 6495ea5..edecef0 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -34,6 +34,7 @@ class NameCom extends Adapter public const ERROR_INVALID_DOMAIN = 'Invalid Domain Name'; public const ERROR_INVALID_DOMAINS = 'None of the submitted domains are valid'; public const ERROR_UNSUPPORTED_TLD = 'unsupported tld'; + public const ERROR_TLD_NOT_SUPPORTED = 'TLD not supported'; public const ERROR_UNSUPPORTED_TRANSFER = 'do not support transfers for'; public const ERROR_UNAUTHORIZED = 'Unauthorized'; @@ -48,6 +49,7 @@ class NameCom extends Adapter self::ERROR_INVALID_DOMAIN => null, self::ERROR_INVALID_DOMAINS => null, self::ERROR_UNSUPPORTED_TLD => 422, + self::ERROR_TLD_NOT_SUPPORTED => null, self::ERROR_UNSUPPORTED_TRANSFER => 400, self::ERROR_UNAUTHORIZED => 401, ]; @@ -393,7 +395,7 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = case $e instanceof PriceNotFoundException: throw $e; - case $this->matchError($e) === self::ERROR_UNSUPPORTED_TLD: + case in_array($this->matchError($e), [self::ERROR_UNSUPPORTED_TLD, self::ERROR_TLD_NOT_SUPPORTED]): throw new UnsupportedTldException($message, $code, $e); case in_array($this->matchError($e), [self::ERROR_NOT_FOUND, self::ERROR_INVALID_DOMAIN]): From a764f81c05e36c77e1a1cb8a5711d0c418cc7dd3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 11:47:19 +0530 Subject: [PATCH 08/10] fix test --- src/Domains/Registrar/Adapter/Mock.php | 3 +-- tests/Registrar/Base.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Domains/Registrar/Adapter/Mock.php b/src/Domains/Registrar/Adapter/Mock.php index 8abf057..47149b4 100644 --- a/src/Domains/Registrar/Adapter/Mock.php +++ b/src/Domains/Registrar/Adapter/Mock.php @@ -8,7 +8,6 @@ use Utopia\Domains\Registrar\Exception\DomainTakenException; use Utopia\Domains\Registrar\Exception\InvalidContactException; use Utopia\Domains\Registrar\Exception\PriceNotFoundException; -use Utopia\Domains\Registrar\Exception\UnsupportedTldException; use Utopia\Domains\Registrar\Domain; use Utopia\Domains\Registrar\Renewal; use Utopia\Domains\Registrar\TransferStatus; @@ -288,7 +287,7 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = $tld = end($parts); if (!in_array($tld, $this->supportedTlds)) { - throw new UnsupportedTldException("TLD .{$tld} is not supported", self::RESPONSE_CODE_BAD_REQUEST); + throw new PriceNotFoundException("TLD .{$tld} is not supported", self::RESPONSE_CODE_BAD_REQUEST); } $basePrice = $this->defaultPrice; diff --git a/tests/Registrar/Base.php b/tests/Registrar/Base.php index fe7d9f1..dcd478b 100644 --- a/tests/Registrar/Base.php +++ b/tests/Registrar/Base.php @@ -9,7 +9,7 @@ use Utopia\Domains\Registrar\Exception\DomainNotTransferableException; use Utopia\Domains\Registrar\Exception\InvalidAuthCodeException; use Utopia\Domains\Registrar\Exception\InvalidContactException; -use Utopia\Domains\Registrar\Exception\UnsupportedTldException; +use Utopia\Domains\Registrar\Exception\PriceNotFoundException; use Utopia\Domains\Registrar\TransferStatusEnum; use Utopia\Domains\Registrar\UpdateDetails; use Utopia\Domains\Registrar\Price; @@ -230,7 +230,7 @@ public function testGetPrice(): void public function testGetPriceWithInvalidDomain(): void { - $this->expectException(UnsupportedTldException::class); + $this->expectException(PriceNotFoundException::class); $this->getRegistrar()->getPrice("invalid.invalidtld", 1, Registrar::REG_TYPE_NEW); } From 64977304d85179cecc31a96365da26efd9553041 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 13:19:17 +0530 Subject: [PATCH 09/10] remove error code --- src/Domains/Registrar/Adapter/NameCom.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index edecef0..9cd74c4 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -44,7 +44,7 @@ class NameCom extends Adapter public const ERROR_MAP = [ self::ERROR_NOT_FOUND => 404, self::ERROR_DOMAIN_TAKEN => null, - self::ERROR_INVALID_AUTH_CODE => 500, + self::ERROR_INVALID_AUTH_CODE => null, self::ERROR_INVALID_CONTACT => null, self::ERROR_INVALID_DOMAIN => null, self::ERROR_INVALID_DOMAINS => null, From e5c18f016afe9272c872113df2e0c7437f5a0119 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 2 Mar 2026 13:29:13 +0530 Subject: [PATCH 10/10] throw proper exception --- src/Domains/Registrar/Adapter/NameCom.php | 25 +++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Domains/Registrar/Adapter/NameCom.php b/src/Domains/Registrar/Adapter/NameCom.php index 9cd74c4..765855b 100644 --- a/src/Domains/Registrar/Adapter/NameCom.php +++ b/src/Domains/Registrar/Adapter/NameCom.php @@ -362,26 +362,39 @@ public function getPrice(string $domain, int $periodYears = 1, string $regType = try { $result = $this->send('GET', '/core/v1/domains/' . $domain . ':getPricing' . '?years=' . $periodYears); - $purchasePrice = (float) ($result['purchasePrice'] ?? 0); - $renewalPrice = (float) ($result['renewalPrice'] ?? 0); - $transferPrice = (float) ($result['transferPrice'] ?? 0); + $purchasePrice = isset($result['purchasePrice']) ? (float) $result['purchasePrice'] : null; + $renewalPrice = isset($result['renewalPrice']) ? (float) $result['renewalPrice'] : null; + $transferPrice = isset($result['transferPrice']) ? (float) $result['transferPrice'] : null; $isPremium = isset($result['premium']) && $result['premium'] === true; + if ($purchasePrice === null && $renewalPrice === null && $transferPrice === null) { + throw new PriceNotFoundException('Price not found for domain: ' . $domain, 400); + } + if ($this->cache) { $cacheKey = $domain . '_' . $periodYears; $this->cache->save($cacheKey, [ - Registrar::REG_TYPE_NEW => ['price' => $purchasePrice, 'premium' => $isPremium], - Registrar::REG_TYPE_RENEWAL => ['price' => $renewalPrice, 'premium' => $isPremium], - Registrar::REG_TYPE_TRANSFER => ['price' => $transferPrice, 'premium' => $isPremium], + Registrar::REG_TYPE_NEW => ['price' => $purchasePrice ?? 0, 'premium' => $isPremium], + Registrar::REG_TYPE_RENEWAL => ['price' => $renewalPrice ?? 0, 'premium' => $isPremium], + Registrar::REG_TYPE_TRANSFER => ['price' => $transferPrice ?? 0, 'premium' => $isPremium], ]); } switch ($regType) { case Registrar::REG_TYPE_NEW: + if ($purchasePrice === null) { + throw new PriceNotFoundException('Purchase price not found for domain: ' . $domain, 400); + } return new Price($purchasePrice, $isPremium); case Registrar::REG_TYPE_RENEWAL: + if ($renewalPrice === null) { + throw new PriceNotFoundException('Renewal price not found for domain: ' . $domain, 400); + } return new Price($renewalPrice, $isPremium); case Registrar::REG_TYPE_TRANSFER: + if ($transferPrice === null) { + throw new PriceNotFoundException('Transfer price not found for domain: ' . $domain, 400); + } return new Price($transferPrice, $isPremium); default: throw new PriceNotFoundException('Price not found for domain: ' . $domain, 400);