Download the PHP package mahoudeau/universal-shipping-for-sylius without Composer

On this page you can find all versions of the php package mahoudeau/universal-shipping-for-sylius. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package universal-shipping-for-sylius

Universal Shipping for Sylius

CI Packagist

Shipping carriers for Sylius 2, starting with the part every French shop asks for first: letting the customer pick a Mondial Relay point at checkout.

Still 0.x, so a minor version may change things. Built alongside a small French shop that will be its first production user. What changed, and when: CHANGELOG.md.

Independent project. Not affiliated with, endorsed by, or connected to Sylius, Sendcloud or Mondial Relay. "Sylius" is used here only to say what this plugin is for.

Status

What has been checked, and how far. Updated as each item moves.

Checked against the real services, in the shop that runs it (Sylius 2.2, PHP 8.4, MariaDB 11.8):

Installed from scratch on a fresh Sylius-Standard 2.2 (Sylius 2.2.10, MariaDB), following this README step by step, with the fake provider: the whole checkout, the summary, a paid order's label and the admin pages. The screenshots here come from that shop.

Covered by unit tests, not yet run against the real Sendcloud:

Not tried yet: PostgreSQL (#3), a shop with several channels (#4), addresses outside France.

CI runs the unit tests on PHP 8.2, 8.3 and 8.4, with Sylius 2.1.15 and the latest 2.x.

Configuration is in YAML and environment variables, not in the admin: the carrier keys, test labels and delivery options. The admin only links a shipping method to a delivery option. An admin settings page is planned (#5).

What it does today

Colissimo comes next. See the roadmap.

Why another shipping plugin

When this started, in September 2026, there was no maintained Mondial Relay integration for Sylius 2. The existing plugins stop at Sylius 1, and the one Sylius 2 plugin we tried did not work with the 2.2 checkout. French shops also increasingly reach Mondial Relay through Sendcloud rather than the carrier's own API, which none of them covered.

So this plugin is carrier-neutral from the start. Mondial Relay through Sendcloud is the first provider, not the whole design. Adding a carrier means writing one class.

How it works

A few choices worth knowing before you install it:

One more thing, found on the way: Sylius declares its shipping step as a live component but its template never switches it on in the browser. This plugin replaces that one template with a fixed copy.

Everything else goes through Twig hooks, and no Sylius template file is overridden. Besides adding its own blocks, the plugin swaps three of Sylius's hookables for its own, all in config/twig_hooks.yaml:

If your theme replaces the same hookables, yours or the plugin's wins depending on bundle order: check those three first.

Requirements

Installation

1. Require the package

2. Enable the bundle in config/bundles.php. There is no Flex recipe yet, so add the line yourself:

3. Add the fields to your entities. The point and the parcel live on the shipment, the delivery option on the shipping method.

Sylius-Standard already has both entities in src/Entity/Shipping/, mapped with attributes: add the lines below to them.

Leave out ParcelAwareInterface if you only want the picker: the label buttons then stay hidden. The index is for the tracking webhook, which looks a shipment up by its parcel id.

Then generate and run the migration. It adds nullable columns only: universal_shipping_pickup_point, universal_shipping_parcel and universal_shipping_parcel_id on sylius_shipment, and universal_shipping_delivery_option on sylius_shipping_method.

Read the generated migration before running it. diff compares the whole schema, so it also picks up anything else your project has drifted on: on a fresh Sylius-Standard 2.2 that's an index on Symfony Messenger's table. Keep or drop those lines as you would without the plugin.

On a brand new project, set Sylius up first (sylius:install), then add the plugin. The other way round, the demo data is written before the plugin's columns exist, and sylius:install stops on "Unknown column 'universal_shipping_delivery_option'". If that happened, generate and run the migration as above, then bin/console sylius:fixtures:load.

4. Import the routes in config/routes/universal_shipping.yaml. The label buttons go under the admin prefix, so the admin firewall guards them. The webhook goes without a prefix: carriers call it, not people. The shop routes serve the checkout's scripts (only address suggestions for now) and don't depend on the locale.

Leave the shop routes out and address suggestions stay off, whatever the config says. Nothing else breaks.

5. Declare your delivery options in config/packages/universal_shipping.yaml. A delivery option is one way of delivering with one carrier. The Sylius shipping method on top of it adds the price, the zone and the name customers see.

The keys come from the Sendcloud panel: Settings > Connected stores > Sendcloud API. Turn on pickup point delivery there and tick the carriers you want. Keep the keys out of the repository.

6. Link a shipping method in the admin: edit it and choose the delivery option in the Universal Shipping field. That method now asks for a point at checkout.

7. Publish the scripts and styles under public/bundles/, then clear the cache. Sylius-Standard's Composer scripts already run the first command after every composer require and composer update; run it yourself if your project doesn't, and again after each upgrade. The files are plain ES modules and CSS: nothing to add to your Webpack Encore build.

Configuration reference

Sendcloud options:

Option Default Meaning
point_types all servicepoint for relay points, locker for lockers
radius 5000 search radius in metres
limit 10 how many points the customer sees
shipping_option none Sendcloud shipping option code for labels. POST /api/v3/shipping-options lists yours
contract_id none only when you have several contracts with the carrier
insure_above none orders above this total, in euros, get Sendcloud's additional insurance (additional_insured_price, charged per label by Sendcloud)
carrier_cover 0 what the carrier already covers, in euros, taken off the insured amount (Mondial Relay: 25)

To develop or test without credentials, point a delivery option at the fake provider:

The map

Off by default. Turn it on and pick where the map comes from:

style is the URL of any MapLibre style. Three free sources work well:

Source style Notes
OpenFreeMap https://tiles.openfreemap.org/styles/positron (or liberty, bright) OpenStreetMap data, worldwide, no key, no account. The default
IGN Géoplateforme https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json The French state's map, open licence, no key. Best in France, thin elsewhere
Protomaps, self-hosted a style whose source is pmtiles://https://your-server/france.pmtiles One file on your own server or object storage: no third party at all

MapLibre and PMTiles ship with the plugin (both BSD-3), so no script comes from a CDN. MapLibre is only downloaded on a page that shows a map.

Privacy. With OpenFreeMap or IGN, the customer's browser loads map tiles from their servers, so they see the customer's IP address. Say so in your privacy policy, or self-host with Protomaps. The pickup point list itself never talks to a third party from the browser: carrier calls happen on your server.

Theming. Give the map your shop's colours:

Every key is optional: leave one out and the style keeps its own colour. Colours must be #rgb or #rrggbb. The map colours apply to OpenMapTiles styles, which covers OpenFreeMap and most free styles; other styles are only partly recoloured. Street names keep the style's font, since the tile server only has its own. For full control, design a style in Maputnik and point style at it.

Addresses

Off by default. It uses the Base Adresse Nationale (BAN), France's official address base: free, open licence, no account, no key.

That turns on two things, each of which can be turned off alone:

It covers metropolitan France and the five overseas departments (Guadeloupe, Martinique, Guyane, La Réunion, Mayotte). For any other country the street field stays a plain field and the relay search stays as it was.

Privacy. The customer's browser only talks to your shop. Your server asks the BAN, the same way carrier calls work. So the BAN sees your server's address and what customers type in the street field, never their IP address or anything else about them.

Where the BAN lives. Since 2025 it is served by the IGN Géoplateforme at https://data.geopf.fr/geocodage, the default url. The older api-adresse.data.gouv.fr was announced as closing in January 2026; don't point url at it. The Géoplateforme allows 50 requests per second per IP address. Answers are cached for a day, so a shop rarely gets near that, but the suggestion route is public: put a rate limiter in front of /universal-shipping/address/suggest if you expect abuse.

Labels. The house number goes to the carrier on its own, whatever this setting: "12 bis rue de la Paix" becomes number "12 bis" and street "rue de la Paix". A line without a leading number ("Lieu-dit Les Pins") stays whole.

Theming. The suggestion list reads Bootstrap's colours. Override them in your CSS:

Another source. Implement AddressProviderInterface (supports, suggest, geocode), register it as a service, and give its id as provider. Throw ProviderUnavailableException when it can't be reached: the plugin caches answers and turns outages into empty ones.

Labels and tracking

A paid shipment that hasn't left yet gets a Create label button on the admin order page, next to Sylius's Ship. It sends the carrier the customer's address, the chosen point, the order total and the parcel's weight. Then:

Most carriers charge as soon as the label exists. Sendcloud refunds a label cancelled within 42 days if the parcel never shipped. A label is never paid twice for one shipment: if Sendcloud's answer gets lost after it created one, the next Create label finds that shipment and takes it.

Partly refunded orders. An order with a piece refunded still ships the rest. The plugin doesn't depend on a refund module, so by default the label weighs and values the whole shipment. With sylius/refund-plugin, tell it what was refunded, and a piece refunded in full leaves the weight while every refund comes off the declared and insured value:

Labels go through Sendcloud's shipments API v3. The older parcels API is closed to accounts opened since April 2026.

Test labels. With sendcloud.test_labels: true, every label is Sendcloud's free "Unstamped letter", sent to the customer's address instead of the point. Same flow, nothing charged. Keep it on everywhere but production:

Fake labels. No carrier account, or one you'd rather not touch? Give the delivery option label_provider: fake. Points still come from the real provider. Labels get a made-up tracking number and a PDF marked as a test. You play the carrier from the console:

The parcel id is on the admin order page. The command won't touch a real carrier's parcel.

Tracking. In Sendcloud, open Settings > Integrations, configure your API integration, tick Webhook feedback enabled and enter:

Sendcloud signs every call with your secret key. The plugin refuses the ones that don't match, and an update that arrives late after a retry never overwrites a newer one. The order page shows:

Status Means
Label ready announced, the carrier has not scanned it yet
In transit the carrier has it
At the pickup point waiting for the customer
Delivered handed over or collected
Returned refused or sent back
Needs attention failed delivery, invalid address, carrier exception
Cancelling, Cancelled the label was voided

The plugin never marks a shipment shipped on its own. That stays a click on Ship, so the email goes out when you decide.

Adding a carrier

Implement PickupPointProviderInterface and give it a code:

Autoconfiguration registers it. Throw ProviderUnavailableException when the carrier cannot be reached, and the plugin takes care of the rest. The Sendcloud provider is a complete example.

For labels, implement LabelProviderInterface with #[AsLabelProvider('my_carrier')]: create a label, return its PDF, cancel it. Throw LabelException with a message the shop owner can act on, since the admin shows it word for word. A carrier without labels is fine, the buttons just don't appear. For tracking, turn the carrier's webhook into a call to ParcelTracker::update(), like the Sendcloud webhook.

Roadmap

Roughly in this order. Nothing here is promised by a date. Each item is an issue, so you can follow it or add to it.

First, check what is built:

  1. Labels against the real Sendcloud (#1).
  2. Tracking webhooks from the real Sendcloud (#2).
  3. PostgreSQL (#3) and several channels (#4).

Then, build:

  1. Carrier settings in the admin: keys stored encrypted, test labels, paper size, with environment variables still winning when set (#5).
  2. Delivery options editable in the admin (#6).
  3. Colissimo, home delivery and point retrait. Sendcloud already offers both, so this is mostly a delivery option away (#7).
  4. Home delivery and lockers as first-class delivery options (#8).
  5. Addresses outside France, with a self-hosted Photon. French addresses are done (#9).
  6. Labels for several orders at once, and an option to mark a shipment shipped on the carrier's first scan (#10).
  7. A shop API endpoint for headless checkouts (#11).
  8. Functional tests of the whole checkout in a Sylius test application (#12).

Want one of these sooner, or a carrier that is not on the list? Open an issue.

Contributing

Contributions are welcome, from a typo to a carrier. See CONTRIBUTING.md for how to run the checks and what a pull request should look like.

License

MIT. See LICENSE.


All versions of universal-shipping-for-sylius with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
sylius/sylius Version ^2.1
symfony/http-client Version ^6.4 || ^7.4
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package mahoudeau/universal-shipping-for-sylius contains the following files

Loading the files please wait ...