Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Transip Api Php Laravel Package

transip/transip-api-php

PHP client for the TransIP REST API, letting you manage domains, DNS records, VPS instances, and other services from your PHP application with simple, authenticated API calls.

Deep Wiki
Context7

Getting Started

1. Install and generate a key pair. The client itself has no Laravel service provider — you wire it up yourself.

composer require transip/transip-api-php
openssl genrsa -out storage/app/transip/private.key 4096
openssl rsa -in storage/app/transip/private.key -pubout -out storage/app/transip/public.key

Paste the public key into the TransIP control panel (API settings), enable the API for your account, and whitelist the IP you'll call from. Nothing works until that whitelist entry exists.

2. Configure it. Keep the private key on disk, not in .env — env vars mangle the PEM newlines.

// config/services.php
'transip' => [
    'login' => env('TRANSIP_LOGIN'),
    'private_key' => storage_path('app/transip/private.key'),
    'read_only' => env('TRANSIP_READ_ONLY', true),
],

3. Bind a singleton that refreshes its own token. Tokens are short-lived (~30 min), so cache them rather than re-authenticating on every call.

// app/Providers/TransipServiceProvider.php
$this->app->singleton(TransIPAPI::class, fn () => new TransIPAPI(
    config('services.transip.login'),
    file_get_contents(config('services.transip.private_key')),
    true,                                  // whitelist-only token
    config('services.transip.read_only'),
));

$this->app->singleton(TransipClient::class, fn ($app) => new TransipClient(
    $app->make(TransIPAPI::class)
));

4. First use case: read your DNS. Start read-only. Dump every record for one domain and eyeball the shape of the data before you write anything.

$api->setToken(Cache::remember('transip:token', now()->addMinutes(25), fn () => $api->getToken()));

foreach ($api->dns()->getByDomainName('example.com') as $entry) {
    // $entry->getName(), ->getExpire(), ->getType(), ->getContent()
}

The entity objects (Transip\Api\Library\Entity\DnsEntry, Domain, Vps, SshKey) are plain getter/setter bags — that's the API surface you'll actually be programming against.

Implementation Patterns

Wrap the client, never inject it directly into controllers/jobs. The TransIPAPI object is stateful (setToken() mutates it) and throws raw exceptions. One thin wrapper gives you a fake-able seam for tests plus a single place to handle token refresh and retries.

final class TransipClient
{
    public function __construct(private TransIPAPI $api) {}

    private function api(): TransIPAPI
    {
        $this->api->setToken(
            Cache::remember('transip:token', now()->addMinutes(25), fn () => $this->api->getToken())
        );

        return $this->api;
    }

    /** @return DnsEntry[] */
    public function records(string $domain): array
    {
        return Cache::remember("transip:dns:{$domain}", 60, fn () => $this->api()->dns()->getByDomainName($domain));
    }
}

Reconcile, don't mutate. dns()->update($domain, $entries) replaces the entire record set for the domain. The safe pattern for "make sure this A record exists" is read → modify the array → write the whole thing back, inside a transaction-like job:

$records = $api->dns()->getByDomainName($domain);

$records = array_filter($records, fn ($r) => !($r->getName() === 'www' && $r->getType() === 'A'));

$records[] = (new DnsEntry())
    ->setName('www')
    ->setExpire(300)
    ->setType('A')
    ->setContent('203.0.113.10');

$api->dns()->update($domain, array_values($records));
Cache::forget("transip:dns:{$domain}");

Queue every write. DNS and VPS calls are network-bound and rate-limited; a 5xx or 429 in a request cycle is a bad user experience. Use a dedicated queue with $tries = 3, backoff() returning 10/60/300, and failed() logging the domain + intended state so you can replay it.

Cache reads aggressively. Domain lists, record sets, and SSH keys change on human timescales. Cache for 30–60s and bust on write — this also keeps you well clear of rate limits when a dashboard renders.

Good day-to-day use cases. Dynamic DNS updater (scheduled command: fetch public IP → reconcile @ A record), per-tenant subdomain provisioning on signup, SSH key rotation across a fleet (ssh()->getAll() / create / delete), and an ops dashboard that lists expiring domains via domains()->getAll().

Test the wrapper, not the API. Because the client isn't PSR-18-injectable, bind a fake TransipClient into the container in tests and assert on intent. Keep one integration test tagged @group external for real calls.

Gotchas and Tips

  • IP whitelisting is the number one support ticket. Whitelist-only tokens are bound to the requesting IP, so they break the moment you deploy to a new host, run CI elsewhere, or your NAT egress changes. Keep the whitelist to a stable egress IP.
  • Read-only is a constructor flag, not a per-call option. If read_only is true, writes fail at the API — in local dev that's what you want, but it produces confusing 403s if you flip it only in .env and forget to clear config cache.
  • Private key formatting. file_get_contents() a real .pem file. If you must use an env var, base64-encode it and decode at boot; a key pasted with literal \n will fail signing with an opaque error.
  • Clock skew breaks signing. Requests are signed with a timestamp; if the server drifts more than a few minutes, you get auth failures that look like bad credentials. Run NTP.
  • delete() needs an exact match. Deleting a DNS record means matching name, expire, type, and content. Fetch first, pass the entity back — don't reconstruct it from memory and expect it to match.
  • Root records are @, not the bare domain. Subdomain labels are relative (www), not FQDNs. Getting this wrong silently creates a duplicate record rather than erroring.
  • TXT records with spaces are the classic source of "record created but doesn't validate" — verify the stored content round-trips exactly as you sent it.
  • update() is destructive by design. There is no per-record PUT; a partial array wipes records. Always read-modify-write, and log the before/after array on failure.
  • Token refresh inside Octane. A long-lived singleton can hold an expired token between requests. Refreshing via Cache::remember on every entry point (as above) avoids this; with Octane, prefer scoped binding over singleton.
  • Domain registration isn't self-contained. Registering/transferring requires contact handles that already exist in the control panel — the client doesn't create them. Treat registration as a semi-manual flow and script only the DNS layer until you've proven the contact IDs.
  • Extension points are thin. There's no logging middleware or event system; wrap the client (or decorate TransipClient with a logging decorator) if you want request/response visibility.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons