Repository navigation
Webhooks
Selldone calls your bridge's webhook URLs to inform you about updates and to fetch information about connected accounts. This page explains how the sample verifies each call, which fields each webhook receives and what the sample returns. Handlers live in app/Http/Controllers/WebhooksController.php; parsing and validation live in selldone/provider/webhook.
Selldone sends three headers with every webhook call:
| Header | Meaning |
|---|---|
Selldone-Sign-Id |
A unique ID for each request: the UTC timestamp. You can check it to reject duplicated old requests. |
Selldone-Sign-Hash |
A Bcrypt hash of {sign key}:{Selldone-Sign-Id}. |
Authorization |
The access token your service issued during authentication, e.g. Bearer <token>. It tells you which account on your service the call is for. |
Selldone\provider\webhook\Webhooks verifies every call before any handler runs:
$sign_id = $request->header('Selldone-Sign-Id');
$sign_hash = $request->header('Selldone-Sign-Hash');
if (!$sign_id || !$sign_hash || !Hash::check($this->webhook_sign_key . ":$sign_id", $sign_hash)) {
throw new Exception("Invalid signature!", 403);
}Hash::check is Laravel's Bcrypt check. The sign key comes from SD_PROVIDER_WEBHOOK_SIGN_KEY.
Every handler in the sample follows the same three steps:
// 1. Initialize and verify the webhook
$providerClient = self::newProviderClient();
try {
$webhook = $providerClient->webhook($request)->onSyncProducts();
} catch (Exception $e) {
return ErrorResponse::Exception($e); // status = exception code (403/400) or 500
}
// 2. Check the access token
if ($webhook->getToken() != 'Bearer ' . Controller::SAMPLE_ACCESS_TOKEN) {
return response("Invalid access token!", 403);
}
// 3. Build the response
return $webhook->response()->addProduct($product)->setTotal(1)->toArray();| Failure | Status | Body |
|---|---|---|
| Missing or invalid signature | 403 |
Invalid signature! |
Missing Authorization header |
403 |
Authentication token does not exist on the header! |
| Payload validation fails | 400 |
Validation messages |
| Token does not match | 403 |
Invalid access token! |
| Webhook | Route in sample | Parser (on…()) |
|---|---|---|
| Notify shop | POST /api/shop |
onNotifyShop() → WebhookNotifyShop
|
| Fetch categories | GET /api/categories |
onSyncCategories() → WebhookSyncCategories
|
| Fetch products | GET /api/products |
onSyncProducts() → WebhookSyncProducts
|
| Create order | POST /api/create-order |
onCreateOrder() → WebhookCreateOrder
|
| Confirm order manually | POST /api/confirm-order |
onConfirmOrder() → WebhookConfirmOrder
|
| Get order status | GET /api/get-order |
onGetOrder() → WebhookGetOrder
|
| Cancel order | POST /api/cancel-order |
onCancelOrder() → WebhookCancelOrder
|
Called when a shop connects your provider or removes it from its Connect OS.
| Field | Rule |
|---|---|
action |
required, one of Add, Remove, Test
|
Sample responses:
-
Test— a list of name/value rows shown to the merchant:[ { "name": "Categories count", "value": 12 }, { "name": "Products count", "value": 85 }, { "name": "Status", "value": "Active" }, { "name": "Message", "value": "Some note..." } ] -
Add/Remove—{ "success": true }
Used to fetch categories from your service and update linked categories on Selldone, each time a new shop connects or a sync is triggered.
| Field | Rule |
|---|---|
offset |
required integer, min 0 |
limit |
required integer, min 1 |
shop_id |
required integer, min 0 |
Build the response with SyncCategoriesResponse:
return $webhook->response()
->addCategory(new WebhookCategory('sample-sync', 'Sample Sync', 'Some note...', null, null))
->setTotal(1)
->toArray(); // { "categories": [...], "total": 1 }WebhookCategory($name, $title, $description, $icon, $category) — category is the parent category name.
Used to fetch products from your service and update linked products on Selldone.
| Field | Rule |
|---|---|
offset |
required integer, min 0 |
limit |
required integer, min 1 |
shop_id |
required integer, min 0 |
Build the response with SyncProductsResponse, which returns { "override": false, "override_price": false, "products": [...], "total": n }. override forces Selldone to overwrite non-pricing values; override_price forces it to overwrite prices.
$product = new WebhookProduct("1001", ProductType::PHYSICAL, 59, 'USD',
"iPhone 14 Pro and iPhone 14 Pro Max", "...", 10,
"https://selldone.com/images/weekly-reports/1.jpg");
$product->setDiscount(5, Carbon::now()->subDay(), Carbon::now()->addMonth())
->addImages("https://.../40.jpg", "https://.../41.jpg")
->setStyle(false)
->addVariants(
new WebhookVariant("1001-1", '#0097A7', null, "Small", null, null, null, 10, "https://.../42.jpg"),
new WebhookVariant("1001-2", '#AFB42B', null, "Small", null, null, null, 10, "https://.../43.jpg"),
);
return $webhook->response()->addProduct($product)->setTotal(1)->toArray();-
WebhookProduct($mpn, $type, $price, $currency, $title, $title_en, $quantity, $icon)—mpnis your unique product ID. Setters includesetCategory,setSku,setGtin,setGpc,setBrand,setWarranty,setSpec,setPros,setCons,setMessage,setInputs,setOutputs,setQuantity,setLead,setExtra,setStyle,setReturnWarranty,setOriginal,setCondition,setVideo,setPriceInput,setCommission,setPriceLabel,setStatus. -
WebhookVariant($mpn, $color, $style, $volume, $weight, $pack, $type, $quantity, $image)—mpnis your unique variant ID; Selldone stores it in the variant's MPN and sends it back in order items. - Enums in
selldone/provider/enums:ProductType(PHYSICAL,VIRTUAL,SERVICE,FILE),ProductStatus(Open,Close),ProductCondition(new,refurbished,used,used_fair,used_good,used_like_new),PriceInputType(default,area,volume,custom),ProductPricing(FIX,ESTIMATION,AGREEMENT,BID).
Called when a new order is created, or when the merchant manually asks to sync an order with your service.
| Field | Rule / content |
|---|---|
shop_id |
required integer — reference shop ID |
id |
required integer — fulfillment ID on Selldone (a basket can be split into several fulfillments, one per provider) |
basket_id |
required integer — basket reference ID (do not use it as a unique order identifier) |
confirm |
optional boolean — auto-confirm flag, based on the provider's configuration |
recipient |
required object — name, company, address1, address2, city, state_code, state_name, country_code, zip, phone, email, tax_number, lng, lat, message
|
items |
required array — each item has index, mpn, quantity, id, basket_id, product_id, variant_id, retail_price, name, sku. Use mpn and quantity to identify what was bought. |
retail_costs |
required object — currency (3–4 chars), subtotal, discount, shipping, tax, total (required) |
gift |
optional object — subject, message
|
packing_slip |
optional object — email, phone, logo_url, store_name, custom_order_id
|
fulfillment |
required object — the reference fulfillment on Selldone |
Respond with an UpdateOrderResponse:
$response = $webhook->response(165, 'USD') // your price and currency
->setRetailCosts('USD', 200, 10, 10, 0, 0, 0, 20, 220)
->setServiceCosts('USD', 120, 20, 10, 0, 30, 15, 0, 165);
if ($webhook->confirm) {
$response->setConfirmAt(Carbon::now()); // auto confirm
}
return $response;setRetailCosts / setServiceCosts take (currency, subtotal, discount, shipping, additional_fee, fulfillment_fee, tax, vat, total).
Called when the merchant confirms an order manually.
| Field | Rule |
|---|---|
shop_id, id, basket_id
|
required integers |
action |
required, must be Confirm
|
fulfillment |
required object |
The sample returns the costs and setConfirmAt(Carbon::now()).
Returns the last status of the order.
| Field | Rule |
|---|---|
shop_id, id, basket_id
|
required integers |
The sample returns costs, a carrier and tracking URL (setCarrier('Fedex', 'https://fedex.com/track/...')) and a confirmation date.
Called when the order is canceled. It is only called if the order was confirmed before.
| Field | Rule |
|---|---|
shop_id, id, basket_id
|
required integers |
fulfillment |
required object |
The sample returns costs, carrier, setConfirmAt(...) and setCancelAt(Carbon::now()).
UpdateOrderResponse is serialized as JSON with: id, price, currency, and — when set — confirm_at, cancel_at, fulfill_at, service_costs, retail_costs, carrier, tracking_url, error.