# Developer Guide: Registrar Adapters & Architecture

This document provides complete technical specifications for extending the domain management platform by creating custom Registrar Adapters.

---

## 1. Overview & Source of Truth Architecture

The system uses a **decoupled Registrar Adapter pattern**. All registrar interactions are mediated by `App\Services\RegistrarManager`.

### Source of Truth Rule:
- **Database**: Stores billing history, client ownership, local service IDs, registrar adapter selection, activity logs, and cached fallback data.
- **Live Registrar API**: Whenever an API-connected registrar (such as `Spaceship` or `ResellerClub`) is active for a domain, **all live information** (nameservers, lock status, EPP auth code, contacts, expiry dates) MUST be fetched directly from the Live API when rendering pages or running synchronization. Local database records serve ONLY as fallback/cache.
- **Manual/Email Registrar**: For manual registrars (`EmailRegistrar`), live API synchronization is bypassed. Local/manual database values are displayed, and client actions create manual fulfillment requests in `domain_manual_requests`.

---

## 2. Standard Registrar Interface & Capability System

All registrar adapters must implement `App\Modules\Registrars\RegistrarInterface` or extend `App\Modules\Registrars\AbstractRegistrarAdapter`.

### Capability Map Format

Each adapter defines its capability map via `getCapabilities()`:

```php
public function getCapabilities(): array {
    return [
        'nameservers'    => true,  // Supports nameserver modification
        'renew'          => true,  // Supports live domain renewal
        'epp'            => true,  // Supports EPP/Auth code retrieval
        'contacts'       => true,  // Supports contact details editing
        'dns'            => false, // Supports direct registrar DNS records
        'registrar_lock' => true,  // Supports domain transfer lock toggle
        'auto_renew'     => false, // Supports automated renewal toggle
        'is_live_api'    => true,  // True for API adapters, false for EmailRegistrar
    ];
}
```

The user interface dynamically enables or hides buttons and tabs based on these declared capabilities.

---

## 3. Standardized Response Format

All adapter methods must return standardized PHP associative arrays:

### Success Response Format:
```php
return [
    'success' => true,
    'status'  => 'active',
    'message' => 'Domain information fetched successfully.',
    'data'    => [
        'domain'            => 'example.com',
        'nameservers'       => ['ns1.example.com', 'ns2.example.com'],
        'expiry_date'       => '2027-08-10',
        'registration_date' => '2025-08-10',
        'registrar_lock'    => true,
        'epp_code'          => 'AUTH-KEY-12345',
        'is_live_api'       => true
    ]
];
```

### Error Response Format:
```php
return [
    'success' => false,
    'status'  => 'error',
    'message' => 'Unable to synchronize with registrar API.',
    'code'    => 'REGISTRAR_API_TIMEOUT'
];
```

---

## 4. Complete Code Example: `ExampleRegistrarAdapter`

Below is a complete implementation template for a new API Registrar Adapter:

```php
<?php

namespace App\Modules\Registrars;

use App\Core\Database;
use App\Core\Security;
use App\Core\Logger;

class ExampleRegistrarAdapter extends AbstractRegistrarAdapter {

    public function getKey(): string {
        return 'exampleregistrar';
    }

    public function getName(): string {
        return 'Example Registrar API';
    }

    public function isLiveApi(): bool {
        return true;
    }

    public function getCapabilities(): array {
        return [
            'nameservers'    => true,
            'renew'          => true,
            'epp'            => true,
            'contacts'       => true,
            'dns'            => false,
            'registrar_lock' => true,
            'auto_renew'     => false,
            'is_live_api'    => true,
        ];
    }

    private function getCredentials(): array {
        $keyRow = Database::fetchOne("SELECT setting_value FROM settings WHERE setting_key = 'registrar_exampleregistrar_key'");
        $secRow = Database::fetchOne("SELECT setting_value FROM settings WHERE setting_key = 'registrar_exampleregistrar_secret_encrypted'");

        return [
            'api_key'    => trim($keyRow['setting_value'] ?? ''),
            'api_secret' => !empty($secRow['setting_value']) ? Security::decryptSecret($secRow['setting_value']) : ''
        ];
    }

    public function getDomainInfo(string $domain): array {
        $ns = $this->getNameservers($domain);
        return [
            'success' => true,
            'status' => 'active',
            'data' => [
                'domain' => $domain,
                'nameservers' => $ns,
                'registrar_lock' => $this->getRegistrarLock($domain),
                'epp_code' => $this->getEppCode($domain),
                'is_live_api' => true
            ]
        ];
    }

    public function checkAvailability(string $domain, string $tld): array {
        return ['success' => true, 'available' => true, 'domain' => $domain . $tld, 'price' => 899.00];
    }

    public function getRegistrationPrice(string $tld): float {
        return 899.00;
    }

    public function registerDomain(array $params): array {
        Logger::info("ExampleRegistrar: Registering " . $params['domain_name']);
        return ['success' => true, 'domain_id' => 'EX-' . time(), 'expiry_date' => date('Y-m-d', strtotime('+1 year'))];
    }

    public function transferDomain(array $params): array {
        return ['success' => true, 'message' => 'Transfer initiated.'];
    }

    public function renewDomain(array $params): array {
        return ['success' => true, 'message' => 'Domain renewed.'];
    }

    public function syncDomain(string $domain): array {
        return ['success' => true, 'status' => 'active', 'nameservers' => $this->getNameservers($domain)];
    }

    public function getNameservers(string $domain): array {
        // Implement live cURL API call to registrar endpoint
        return ['ns1.example.com', 'ns2.example.com'];
    }

    public function updateNameservers(string $domain, array $nameservers): array {
        Logger::info("ExampleRegistrar: Updating nameservers for {$domain}");
        return ['success' => true, 'message' => 'Nameservers updated at registrar.'];
    }

    public function getRegistrarLock(string $domain): bool {
        return true;
    }

    public function setRegistrarLock(string $domain, bool $lock): array {
        return ['success' => true, 'locked' => $lock];
    }

    public function getEppCode(string $domain): string {
        return 'AUTH-EX-' . strtoupper(substr(md5($domain), 0, 8));
    }

    public function getContactDetails(string $domain): array {
        return [];
    }

    public function updateContactDetails(string $domain, array $contacts): array {
        return ['success' => true, 'message' => 'Contacts updated.'];
    }
}
```

---

## 5. Registering a New Adapter

To register a new adapter in the system:
1. Save your adapter class in `app/Modules/Registrars/MyCustomRegistrar.php`.
2. Open `app/Services/RegistrarManager.php` and add it to `init()`:
   ```php
   self::register(new MyCustomRegistrar());
   ```
3. Done! The system will automatically present your adapter in the admin registrar selection dropdowns.
