Picqer en Shopify praten rechtstreeks met elkaar via REST APIs. Geen iPaaS, geen middleware, geen maandelijkse SaaS-kosten voor een tussenlaag. Wat je bouwt, hoe je het test en wat de valkuilen zijn.
Shopify + Picqer: warehouse koppeling zonder middleware
Picqer en Shopify hebben allebei een goed gedocumenteerde REST API. Ze praten rechtstreeks met elkaar. Geen iPaaS, geen extra SaaS-laag die €500 per maand kost, geen visuele configuratietool die de complexiteit verbergt maar nooit wegneemt.
Dit artikel legt uit hoe je een directe koppeling bouwt tussen Shopify en Picqer: order sync, voorraadterugkoppeling, en de pick & pack flow. Met de concrete API-calls, de aandachtspunten bij schaal en de valkuilen die wij tegenkomen in de praktijk.
Wat je leert in dit artikel
- Hoe de Shopify-Picqer architectuur eruitziet
- Order sync van Shopify naar Picqer: API-flow en datamodel
- Voorraad terugkoppelen van Picqer naar Shopify
- Trackingnummers automatisch doorzetten naar Shopify
- Valkuilen bij schaal en hoe je ze voorkomt
De architectuur: twee APIs, één datastroom
Een directe koppeling tussen Shopify en Picqer bestaat uit twee delen:
Shopify → Picqer: nieuwe orders worden via webhooks doorgezet naar Picqer. Picqer neemt de order over voor picking, packing en verzending. Picqer → Shopify: zodra een order verstuurd is, stuurt Picqer het trackingnummer terug naar Shopify. Voorraadmutaties in Picqer worden teruggestuurd naar Shopify om de webshopvoorraad actueel te houden.De middleware die deze datastroom beheert, schrijven wij doorgaans in Laravel. Een kleine applicatie met twee verantwoordelijkheden: webhooks ontvangen en verwerken, en periodieke sync-taken uitvoeren voor voorraad.
Shopify Webhook Laravel Middleware Picqer API
│ │ │
│── orders/create ────────►│ │
│ │── POST /orders ─────────►│
│ │◄── order_id ─────────────│
│ │ │
│ │◄── shipment created ─────│
│◄── fulfillment update ──│ │
│ │ │
│ │◄── stock_changed ────────│
│◄── inventory update ────│ │
Order sync van Shopify naar Picqer
Een order aanmaken in Picqer via de API gaat via een POST-request naar /api/v1/orders.
class ShopifyOrderToPicqer
{
public function handle(array $shopifyOrder): string
{
// Shopify-order omzetten naar Picqer-formaat
$picqerOrder = [
'reference' => (string) $shopifyOrder['id'],
'firstname' => $shopifyOrder['shipping_address']['first_name'],
'lastname' => $shopifyOrder['shipping_address']['last_name'],
'emailaddress' => $shopifyOrder['email'],
'telephone' => $shopifyOrder['shipping_address']['phone'] ?? '',
'deliveryname' => $shopifyOrder['shipping_address']['name'],
'deliveryaddress' => $shopifyOrder['shipping_address']['address1'],
'deliveryzipcode' => $shopifyOrder['shipping_address']['zip'],
'deliverycity' => $shopifyOrder['shipping_address']['city'],
'deliverycountry' => $shopifyOrder['shipping_address']['country_code'],
'products' => $this->mapLineItems($shopifyOrder['line_items']),
];
$response = $this->picqerClient->post('/api/v1/orders', $picqerOrder);
return $response['idorder'];
}
private function mapLineItems(array $lineItems): array
{
return array_map(fn($item) => [
'idproduct' => $this->lookupPicqerProductId($item['sku']),
'amount' => $item['quantity'],
'price' => (float) $item['price'],
], $lineItems);
}
}
De lookupPicqerProductId functie is het kritieke punt. Picqer werkt met interne product-IDs. Je moet een mapping bijhouden tussen Shopify-SKUs en Picqer-product-IDs. Die mapping bouw je op door bij de eerste sync alle Picqer-producten op te halen en te indexeren op SKU.
Shopify-webhook ontvangen en valideren
Shopify ondertekent webhooks met een HMAC-hash. Valideer altijd de handtekening voordat je de payload verwerkt.
class ShopifyWebhookController extends Controller
{
public function handleOrderCreate(Request $request): JsonResponse
{
// Handtekening valideren
$hmac = $request->header('X-Shopify-Hmac-Sha256');
$data = $request->getContent();
$secret = config('services.shopify.webhook_secret');
$calculatedHmac = base64_encode(
hash_hmac('sha256', $data, $secret, true)
);
if (! hash_equals($calculatedHmac, $hmac)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
// Order asynchroon verwerken via queue
ProcessShopifyOrder::dispatch($request->json()->all());
// Shopify verwacht snel een 200-respons
return response()->json(['status' => 'queued'], 200);
}
}
Twee aandachtspunten: geef altijd snel een 200-respons terug. Shopify markeert een webhook als mislukt als de respons langer dan 5 seconden duurt. Verwerk de order asynchroon in een queue job. En valideer altijd de HMAC — anders verwerk je potentieel nep-orders.
Voorraad terugkoppelen naar Shopify
Picqer heeft webhooks die je kunt registreren voor voorraadmutaties. Zodra voorraad verandert — door het inboeken van een inkooporder, het verwerken van een retour of een handmatige correctie — stuurt Picqer een webhook naar jouw middleware.
class PicqerStockWebhookController extends Controller
{
public function handleStockChanged(Request $request): JsonResponse
{
$payload = $request->json()->all();
// Picqer product-ID omzetten naar Shopify inventory item
$shopifyInventoryItemId = $this->lookupShopifyInventoryItem(
$payload['idproduct']
);
if (! $shopifyInventoryItemId) {
Log::warning('Geen Shopify inventory item gevonden', [
'picqer_product_id' => $payload['idproduct'],
]);
return response()->json(['status' => 'skipped'], 200);
}
// Voorraad bijwerken in Shopify
UpdateShopifyInventory::dispatch(
$shopifyInventoryItemId,
$payload['stock'],
$payload['idwarehouse']
);
return response()->json(['status' => 'queued'], 200);
}
}
De Shopify inventory API heeft twee relevante endpoints:
POST /admin/api/2024-01/inventory_levels/set.json— zet de voorraad op een exact aantalPOST /admin/api/2024-01/inventory_levels/adjust.json— pas de voorraad aan met een delta
Gebruik set als Picqer de absolute voorraad teruggeeft. Gebruik adjust als je een delta wilt doorgeven. Bij twijfel: gebruik altijd set. Een delta die twee keer verwerkt wordt, geeft een fout getal. Een absolute waarde die twee keer verwerkt wordt, geeft hetzelfde correcte getal.
Trackingnummers doorzetten
Picqer stuurt een webhook zodra een order verstuurd is. Die webhook bevat het trackingnummer en de vervoerder. Je zet die informatie door naar Shopify als een fulfillment.
class ProcessPicqerShipment implements ShouldQueue
{
public function handle(): void
{
$shopifyOrderId = $this->lookupShopifyOrderId(
$this->picqerOrderReference
);
// Fulfillment aanmaken in Shopify
$fulfillmentData = [
'fulfillment' => [
'location_id' => config('services.shopify.location_id'),
'tracking_number' => $this->trackingNumber,
'tracking_company' => $this->carrier,
'tracking_url' => $this->trackingUrl,
'notify_customer' => true,
'line_items_by_fulfillment_order' => [
[
'fulfillment_order_id' => $this->getFulfillmentOrderId(
$shopifyOrderId
),
],
],
],
];
$this->shopifyClient->post(
"/admin/api/2024-01/fulfillments.json",
$fulfillmentData
);
}
}
Let op: Shopify gebruikt een nieuw fulfillment API-model waarbij je werkt met fulfillment_orders in plaats van directe fulfillments. Het oudere model werkt nog maar is deprecated. Bouw nieuwe koppelingen altijd op het nieuwe model.
De pick & pack flow in Picqer
Picqer organiseert het magazijnproces in drie stappen: picking, packing en verzending. De koppeling met Shopify is primair voor het invoer van orders en het terugkoppelen van resultaten. De interne Picqer-workflow — welke picker welk product pakt, in welke volgorde, via welk lopende band — beheer je in Picqer zelf.
Wat je wel kunt doen via de API: prioriteiten instellen op orders. Een order van een premium klant of een spoedorder kan een hogere pickprioriteit krijgen.
$this->picqerClient->put("/api/v1/orders/{$picqerOrderId}", [
'priority' => 10, // Hogere waarde = hogere prioriteit
]);
Valkuilen bij schaal
Dubbele orderverwerking. Shopify-webhooks worden soms meerdere keren verstuurd. Gebruik idempotency: sla per Shopify order-ID op of de order al verwerkt is, en skip duplicaten.if (PicqerOrder::where('shopify_order_id', $orderId)->exists()) {
Log::info("Order {$orderId} al verwerkt, skipping");
return;
}
Conclusie
Een directe Shopify-Picqer koppeling is technisch niet complex. De API's zijn goed gedocumenteerd, het datamodel is overzichtelijk en de webhooks werken betrouwbaar.
De uitdaging zit in de details: idempotency, rate limit handling, SKU-consistentie en de overgang naar het nieuwe Shopify fulfillment API-model. Bouw die foutafhandeling van het begin af aan — niet als naschrift.
De middleware die deze koppelingen aandrijft, bouwen wij in Laravel — lees meer over Laravel als e-commerce middleware en hoe het Adapter Pattern je integraties systeemonafhankelijk maakt. Zie ook de Picqer API-documentatie en de Shopify Fulfillment API.
Bouw je een Shopify-Picqer koppeling of wil je je bestaande integratie verbeteren? Bekijk onze Shopify-diensten of neem contact op. Gerelateerde artikelen:- Shopify integraties: ERP, WMS en PIM koppelen
- Shopify API rate limits — rate limit handling bij schaal
- Laravel Queues voor e-commerce — asynchroon verwerken

Geschreven door Ruthger Idema
15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.
Meer over ons team →