Skip to content

Developer notes

Technical reference for developers who customize N11 Marketplace Connector.

Item Value
Add-on id kv_n11
Function prefix fn_kv_n11_
Language variables kv_n11.*
CSS scope .kv-n11
PHP namespace Tygh\Addons\KvN11
Preset marketplace (generated by kv new --kind integration), extended with the Marketplace Hub

The add-on has three layers. Only the last one knows N11.

Layer Classes Role
Base Client\BaseClient, Accounts, Secret, Config, Access, Queue, Jobs, Log, Webhook HTTP (cURL, TLS verification, timeouts), encrypted credentials per company, settings, job queue (back-off 5 min / 15 min / 1 h / 3 h / 12 h), log with masking and CSV
Marketplace Hub (Hub\) DriverInterface, Hub, Options, Rules, StatusMap, Cache, Categories, Listings, ProductMap, ProductSync, Orders, Shipments, Returns Channel independent: category and attribute mapping, price rules and stock buffer, listing builder, product and batch sync, order import with buyer and address mapping, status mapping, shipment steps, returns, per item errors. It talks to the channel only through DriverInterface with normalized arrays (documented in the interface file)
Driver (Drivers\N11\, Client\Api) Driver, Mapper, Api Api = HTTP calls (appKey / appSecret headers, REST endpoints, SOAP as plain XML), Mapper = pure translation (unit tested with samples), Driver = the DriverInterface implementation. Hub\Hub::DRIVER names it

One host, https://api.n11.com, for REST and SOAP. REST calls carry the headers appKey and appSecret; the catalogue calls (/cdn/...) need only the key. SOAP calls carry <auth><appKey/><appSecret/></auth> in the body.

Purpose Call
Connection test GET /ms/product-query?page=0&size=1
Categories, attributes GET /cdn/categories, GET /cdn/category/{id}/attribute (attribute values come with the attributes)
Create products POST /ms/product/tasks/product-create (payload.integrator, payload.skus[], up to 1000, returns a task id)
Stock and price POST /ms/product/tasks/price-stock-update (stockCode, quantity, salePrice + listPrice together, currencyType)
Task result POST /ms/product/task-details/page-query (taskId, pageable; per item SUCCESS / FAIL with reasons; task status PROCESSED / IN_QUEUE / REJECT)
Orders GET /rest/delivery/v1/shipmentPackages (startDate / endDate in epoch ms, page from 0, size up to 100, packageIds)
Approve lines PUT /rest/order/v1/update (lines[].lineId = orderLineId, status Picking; the only status the service supports)
Returns SOAP /ws/returnService/ ClaimReturnList, ClaimReturnApprove
Cancel an approved line SOAP /ws/claimCancelService/ ClaimCancelPartial, ClaimCancelPartialReasonType
Cargo codes PUT /rest/delivery/v1/collectionRequest (logistics-company shipments) is documented by N11 but not used: it needs box count and desi per package

Notes that shape the code:

  • The hub’s barcode is sent as the N11 stockCode (the key of price, stock and order lines); no N11 barcode is sent so a product is never matched into the n11 catalogue by accident.
  • Prices must have a dot and exactly two decimals. Mapper::money() writes a marker that Api::send() turns into a bare number just before the request leaves.
  • The brand is an attribute of the category (“Marka”), not a field: Driver::withBrand() adds it from the brand of the category mapping; the brand attribute is not asked for on the attribute page.
  • vatRate accepts 0, 1, 10, 20 only; the mapper snaps to the nearest.
  • A seller cannot report a tracking number; Driver::pushShipment() does nothing and the tracking number is read from the package. “invoiced” has no REST counterpart in the services used and is skipped.
  • Returns and partial cancellations exist only as SOAP; the add-on sends plain SOAP 1.1 XML over cURL (document/literal, empty SOAPAction), so the PHP SOAP extension is not needed.
  • The documentation names no maximum date range for the package list; the add-on never asks for more than 14 days and always looks back at least 3 days.
  • N11 documents a limit of 1000 package-list requests per minute; a 429 answer is retried by the queue.
Table Purpose
?:kv_n11_accounts Credentials per company (secrets sealed), last connection check
?:kv_n11_log, ?:kv_n11_jobs Log, queue
?:kv_n11_product_map Product mapping: stock code, listing state (N / P / Y), dirty flags, last pushed values, error
?:kv_n11_order_map Imported orders (package id), channel status, steps pushed, import error and retry time; the lines column keeps line id, SKU and quantity
?:kv_n11_options Rules per account
?:kv_n11_category_map, ?:kv_n11_attr_map Category mapping with price rule, attribute mapping
?:kv_n11_cache Cached reference data (categories, attributes)
?:kv_n11_batches Task ids waiting for their per item result
?:kv_n11_returns Return requests

PHP hooks: update_product_post (marks the mapped product for the next sync), delete_product_post (removes its mapping) and init_templater_post (license badge). Template hooks: index:styles (admin styles), addons:manage, addons:tabs_content. The pages come from the add-on’s own controller (kv_n11) and the menu schema.

The base URL can be overridden for tests in config.local.php (development only):

$config['kv_n11_base_url'] = 'http://127.0.0.1:11850';

tests/mock-server/server.js follows the documented API (headers, tasks, packages, SOAP envelopes, the two-decimal price rule); tests/unit.php covers the mapping with samples shaped like the documentation; tests/e2e/e2e.mjs drives a whole store against the mock; tests/e2e/e2e-vendor.mjs does the Multi-Vendor vendor panel.