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

Tourcms Php Laravel Package

tourcms/tourcms-php

A PHP client for the TourCMS API that simplifies connecting your app to tours, bookings, customers, and related resources. It handles API authentication and requests, making it easier to build tour-booking and management features in Laravel or plain PHP.

View on GitHub
Deep Wiki
Context7

Getting Started

The tourcms/tourcms-php library is the official PHP SDK for the TourCMS booking API. It is framework-agnostic but drops cleanly into a Laravel app, so day-to-day usage is: install it, wrap it once, then call methods.

  1. Install via Composer:

    composer require tourcms/tourcms-php
    
  2. Drop credentials into config/services.php (don't invent a new config file unless you want to publish one):

    // config/services.php
    'tourcms' => [
        'marketplace_id' => env('TOURCMS_MARKETPLACE_ID'),
        'api_key'        => env('TOURCMS_API_KEY'),
        'channel_id'     => env('TOURCMS_CHANNEL_ID', 0),
        'environment'    => env('TOURCMS_ENV', 'test'), // 'test' vs 'live'
    ],
    
  3. Bind a configured singleton in a service provider (there is no first-party Laravel provider — you own this glue):

    // app/Providers/TourCmsServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(\TourCMS\Utils\TourCMS::class, function ($app) {
            $c = $app['config']->get('services.tourcms');
            return new \TourCMS\Utils\TourCMS(
                $c['marketplace_id'],
                $c['api_key'],
                $c['environment'],
                $c['channel_id']
            );
        });
    }
    

    Prefer to bind an interface (TourCmsGateway) rather than the SDK class itself — see below.

  4. First use case: fetch a tour catalogue by channel (the single most common read in travel booking flows). Something like:

    $client = app(\TourCMS\Utils\TourCMS::class);
    $response = $client->listTours($channelId, ['per_page' => 20]);
    

    Verify you get a parseable payload back before building anything else — this is your first proof that auth, credentials, and channel scoping are correct.

  5. Where to look first in the repo: the examples/ folder and the class in TourCMS\Utils\TourCMS. The method names are thin wrappers over TourCMS REST endpoints, so the docs at https://www.tourcms.com/support/api/ are the real API reference for parameters and responses.

Aim to have a successful listTours and a successful getBooking (against a sandbox booking ID) before you write anything else — those two calls validate reads on two shapes, tour catalogue and transaction records.

Implementation Patterns

Always wrap the SDK behind a gateway. The SDK returns XML-derived structures that have changed type in 4.10 (see Gotchas), so callers should never touch them directly. Define a small interface and an SDK-backed implementation, and map SDK responses into your own DTOs/arrays at that boundary:

interface TourCmsGateway {
    public function listTours(int $channelId, array $filters = []): Collection;
    public function getTour(int $tourId): TourData;
    public function createBooking(array $payload): BookingData;
    public function verifyCustomer(array $payload): CustomerVerification;
    public function createCustomer(array $payload): CustomerData;
}

This is the single most important pattern for day-to-day sanity: it means an SDK upgrade is a change in one class, not in every controller and job that touched it.

Inject, don't app(). Bind the gateway interface in your service provider and type-hint it in controllers, jobs, and listeners:

$this->app->bind(TourCmsGateway::class, fn ($app) =>
    new SdkTourCmsGateway($app->make(\TourCMS\Utils\TourCMS::class))
);

Now your controller doesn't import any TourCMS\Utils\* class — which is the goal.

Cache read-heavy reference data. Tours, channels, and location data change rarely and are read constantly. Wrap the listTours/getTour calls in Cache::remember("tourcms:tour:{$id}", 300, fn () => ...). Do not cache availability — that is the thing that must be live during checkout.

Queue booking writes. createBooking, createCustomer, and verifyCustomer are transactional against a real booking system and can be slow. Do not call them synchronously inside a checkout web request if you can avoid it; dispatch a job and either poll or use a webhook-equivalent callback. This also lets you attach retries/timeouts cleanly:

class CreateTourCmsBooking implements ShouldQueue {
    use \Illuminate\Foundation\Bus\Dispatchable;
    public int $tries = 3;
    public int $backoff = 30;
    public function handle(TourCmsGateway $gateway) { ... }
}

Feature-flag the new customer endpoints. createCustomer and verifyCustomer (added in 4.10.0) are the prerequisite for a self-serve signup funnel, but they are also the newest code path in the SDK. Gate them behind config('features.tourcms_customer_endpoints') so you can shut them off during an incident without a redeploy. Roll out in three stages: staging/sandbox → canary in prod for a slice of signups → full.

Use idempotency on writes. The TourCMS API takes a booking_id (client-side reference) in booking creation; use your own UUID so a retried job doesn't create duplicates. For customer creation, keep a local mapping table (users.tourcms_customer_id) so you never call createCustomer twice for the same user — search for an existing record first.

Test against recorded fixtures, not live sandbox for every run. Capture one real response body per endpoint you call into tests/Fixtures/tourcms/*.xml, then stub the gateway in tests. A small contract test per endpoint that asserts the fields your domain layer reads is what protects you on the next minor bump.

Gotchas and Tips

The v4.10.0 string-vs-XML change is the biggest gotcha. TOURCMS-10566 changed some response paths to return plain strings where they used to return XML objects. Any code doing instanceof SimpleXMLElement, property access like $response->tour, or (string) $node may silently break or throw Call to a member function ... on string. Audit every place you consume the raw SDK return value and treat 4.10.0 as a potential breaking change despite the minor version. Add a test that feeds a non-XML body (HTML error page, empty 200, gateway timeout) through your booking path — this is exactly what the change is meant to catch.

Return shapes are not strongly typed. The SDK reflects the TourCMS API, which is XML-shaped and loosely typed by PHP standards. Your domain layer should validate and coerce whatever comes back — don't assume a field is a string, an int, or even present. Wrap parsing in a try/catch that logs the raw body and returns a typed failure rather than throwing up through your controllers.

Auth failures are easy to misconfigure. The client needs marketplace ID, API key, environment (test/live), and channel ID. A blank channel defaults to 0, which silently narrows results. A wrong environment hits the production API with test credentials and returns confusing 401/403s. Put a boot-time assertion or a php artisan tourcms:ping command in place that issues a cheap read and fails loudly.

New endpoints 403 if not enabled on your TourCMS account. Not plan-related to the SDK — the API itself must grant the capability. Check against your account before assuming a 403 from verifyCustomer is a code bug.

Debug by logging request/response pairs at the gateway. The SDK doesn't give you great error context on non-200s. In your SdkTourCmsGateway, wrap every call in:

try {
    $raw = $this->client->someMethod(...);
    Log::debug('tourcms.response', ['method' => 'someMethod', 'response' => $raw]);
    return $this->map($raw);
} catch (\Throwable $e) {
    Log::error('tourcms.error', ['method' => 'someMethod', 'msg' => $e->getMessage()]);
    throw new TourCmsException($e->getMessage(), 0, $e);
}

Redact anything that could be customer PII before it hits logs — customer records are PII and carry GDPR/retention obligations.

No service provider, no facade, no config publish. This package ships nothing Laravel-specific. If you find yourself use-ing TourCMS\Utils\TourCMS outside a single gateway class, that is the signal you're leaking the SDK into your app.

Check PHP and Guzzle constraints during upgrades. The SDK is dependency-light but still pins guzzlehttp/guzzle and a PHP minor. When you bump Laravel, verify these are compatible before assuming Dependabot can just merge — a Guzzle major collision will block the whole Laravel upgrade, not just this package.

Extension points. There aren't many. The SDK is a thin client: extend it by wrapping, not by subclassing. If you need retries, circuit breaking, or caching, put them in the gateway or in a Laravel HTTP client middleware around the SDK — don't fork the package. If you need stronger typing, map responses into your own DTOs rather than trying to convince the SDK to return them.

No circuit breaker, no retries, no timeouts by default. A slow TourCMS will slow down checkout. Set default_socket_timeout or wrap calls via a Laravel HTTP client with ->timeout(5)->retry(2, 100) if you call through your own transport, or at minimum wrap the SDK call in a job timeout with a fallback path (e.g., show "booking temporarily unavailable" rather than hanging).

Rate limits are upstream, not client-side. Bursts of availability checks (every user opening a calendar) will throttle against the TourCMS API, not against your app. Cache where safe, and consider debouncing or aggregating availability calls at the gateway layer.

Upgrade cadence requires attention. Minor versions in this package have shipped type-affecting changes. Pin in composer.lock, don't auto-merge Dependabot, and schedule a quarterly review — with a green integration suite as the gate before any bump.

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