PHPackages                             quellabs/canvas-shipments-sendcloud - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. quellabs/canvas-shipments-sendcloud

ActiveLibrary

quellabs/canvas-shipments-sendcloud
===================================

SendCloud shipping provider integration for the Canvas PHP framework

1.0.0(today)00MITPHPPHP ^8.2

Since Apr 3Pushed todayCompare

[ Source](https://github.com/quellabs/canvas-shipments-sendcloud)[ Packagist](https://packagist.org/packages/quellabs/canvas-shipments-sendcloud)[ RSS](/packages/quellabs-canvas-shipments-sendcloud/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (4)Versions (2)Used By (0)

SendCloud Shipping Provider
===========================

[](#sendcloud-shipping-provider)

A SendCloud shipping provider for the Canvas framework. Part of the Canvas shipments ecosystem.

Installation
------------

[](#installation)

```
composer require quellabs/canvas-shipments-sendcloud
```

Architecture
------------

[](#architecture)

This package sits between the SendCloud API and your application. Your application only ever touches the contracts layer — it never depends on this package directly. `ShipmentRouter` (from `quellabs/canvas-shipments`) discovers this package automatically via composer metadata and routes shipment calls to it.

```
Your Application
      │
      ▼
ShipmentRouter              (quellabs/canvas-shipments — discovery + routing)
      │
      ▼
ShipmentInterface           (quellabs/canvas-shipments-contracts)
      │
      ▼
SendCloud Driver            (this package — implements the interface)
      │
      ▼
SendCloudGateway            (raw SendCloud API calls)

```

Status updates are decoupled from your application via signals. When SendCloud sends a webhook, the package emits a `shipment_exchange` signal carrying a `ShipmentState`. Your application listens for that signal and handles it.

Configuration
-------------

[](#configuration)

Create `config/sendcloud.php` in your Canvas application:

```
return [
    'public_key'     => '',
    'secret_key'     => '',
    'partner_id'     => '',
    'webhook_secret' => '',
    'webhook_url'    => 'https://example.com/webhooks/sendcloud',
    'from_country'   => 'NL',
    'sender_address' => [
        'name'         => 'My Webshop',
        'company_name' => 'My Webshop B.V.',
        'address'      => 'Keizersgracht 123',
        'city'         => 'Amsterdam',
        'postal_code'  => '1015 CJ',
        'country'      => ['iso_2' => 'NL'],
        'email'        => 'logistics@example.com',
        'phone'        => '+31201234567',
    ],
];
```

KeyRequiredDescription`public_key`YesYour SendCloud API public key. Found in your SendCloud panel under Settings → Integrations → API`secret_key`YesYour SendCloud API secret key. Found alongside the public key in your integration settings`partner_id`NoSendCloud Partner ID for partner analytics. Leave empty if you are not a registered SendCloud partner`webhook_secret`YesSigning secret for verifying incoming webhook signatures. Set in your SendCloud panel under Settings → Integrations → Webhooks`webhook_url`YesFull URL SendCloud POSTs parcel status events to. Must be publicly reachable. Configure in your SendCloud panel`from_country`NoISO 3166-1 alpha-2 sender country code used to filter available shipping methods. Defaults to `'NL'``sender_address`NoDefault sender address pre-filled on every parcel. Can be overridden per-request via `ShipmentRequest::$extraData`Usage
-----

[](#usage)

### Creating a shipment

[](#creating-a-shipment)

Inject `ShipmentInterface` via Canvas DI and call `create()`:

```
use Quellabs\Shipments\Contracts\ShipmentInterface;
use Quellabs\Shipments\Contracts\ShipmentAddress;
use Quellabs\Shipments\Contracts\ShipmentRequest;
use Quellabs\Shipments\Contracts\ShipmentCreationException;
use Quellabs\Canvas\Controllers\BaseController;

class OrderController extends BaseController {

    public function __construct(private ShipmentInterface $router) {}

    /**
     * @Route("...")
     */
    public function ship(): void {
        $address = new ShipmentAddress(
            name:        'Jan de Vries',
            street:      'Keizersgracht',
            houseNumber: '123',
            houseNumberSuffix: null,
            postalCode:  '1015 CJ',
            city:        'Amsterdam',
            country:     'NL',
            email:       'jan@example.com',
        );

        $request = new ShipmentRequest(
            shippingModule: 'sendcloud_postnl',
            reference:      'order-12345',
            deliveryAddress: $address,
            weightGrams:    1200,
            methodId:       8,   // SendCloud shipping method ID from getShippingOptions()
        );

        try {
            $result = $this->router->create($request);
            // Persist $result->parcelId and $result->provider — needed for exchange() and cancel()
            // $result->trackingUrl is ready to embed in your confirmation email
        } catch (ShipmentCreationException $e) {
            // handle error
        }
    }
}
```

### Cancelling a shipment

[](#cancelling-a-shipment)

```
use Quellabs\Shipments\Contracts\CancelRequest;
use Quellabs\Shipments\Contracts\ShipmentCancellationException;

$request = new CancelRequest(
    shippingModule: 'sendcloud_postnl',
    parcelId:       $parcelId,   // ShipmentResult::$parcelId persisted at creation time
    reference:      'order-12345',
);

try {
    $result = $this->router->cancel($request);

    if (!$result->accepted) {
        // Carrier already has the parcel — cancellation rejected
        echo $result->message;
    }
} catch (ShipmentCancellationException $e) {
    // handle error
}
```

### Fetching available shipping methods

[](#fetching-available-shipping-methods)

Call `getShippingOptions()` to retrieve the SendCloud shipping methods available for a given module. Store the method ID on your shipping module configuration — it is required when creating a shipment.

```
$methods = $this->router->getShippingOptions('sendcloud_postnl');

foreach ($methods as $method) {
    echo $method['id'] . ' — ' . $method['name'];
}
```

### Listening for shipment state changes

[](#listening-for-shipment-state-changes)

```
use Quellabs\Canvas\Annotations\ListenTo;
use Quellabs\Shipments\Contracts\ShipmentState;
use Quellabs\Shipments\Contracts\ShipmentStatus;

class OrderService {

    /**
     * @ListenTo("shipment_exchange")
     */
    public function onShipmentExchange(ShipmentState $state): void {
        match ($state->state) {
            ShipmentStatus::LabelPrinted    => $this->markLabelPrinted($state->reference),
            ShipmentStatus::InTransit       => $this->markShipped($state->reference, $state->trackingUrl),
            ShipmentStatus::Delivered       => $this->markDelivered($state->reference),
            ShipmentStatus::ReturnedToSender => $this->handleReturn($state->reference),
            default                         => null,
        };
    }
}
```

### Reconciling missed webhooks

[](#reconciling-missed-webhooks)

If a webhook was missed, call `exchange()` with the driver name and parcel ID to re-fetch the current state. This emits the same `shipment_exchange` signal as a real webhook.

```
use Quellabs\Shipments\Contracts\ShipmentExchangeException;

try {
    $state = $this->router->exchange('sendcloud', $parcelId);
} catch (ShipmentExchangeException $e) {
    // handle error
}
```

Supported modules
-----------------

[](#supported-modules)

ModuleCarrier`sendcloud_postnl`PostNL`sendcloud_dhl`DHL`sendcloud_dpd`DPD`sendcloud_ups`UPS`sendcloud_bpost`bpost`sendcloud_mondial`Mondial Relay`sendcloud_multi`All carriersLicense
-------

[](#license)

MIT

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/57e4ab872b3e37536367f2d26b192df3d3bb6a6a1cebec9a104d14a6d2ffe157?d=identicon)[noescom](/maintainers/noescom)

---

Tags

canvasshippingshipmentsendcloudcanvas-shipments

### Embed Badge

![Health badge](/badges/quellabs-canvas-shipments-sendcloud/health.svg)

```
[![Health](https://phpackages.com/badges/quellabs-canvas-shipments-sendcloud/health.svg)](https://phpackages.com/packages/quellabs-canvas-shipments-sendcloud)
```

###  Alternatives

[magepsycho/magento2-custom-shipping

Magento 2 Custom Shipping

697.5k](/packages/magepsycho-magento2-custom-shipping)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
