Developer notes
Technical reference for developers who customize N11 Marketplace Connector.
Add-on identity
Section titled “Add-on identity”| 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 |
Structure
Section titled “Structure”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 |
N11 API calls used
Section titled “N11 API calls used”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 N11barcodeis 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 thatApi::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. vatRateaccepts 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.
Database tables
Section titled “Database tables”| 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 |
Hooks used
Section titled “Hooks used”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.
Test the HTTP layer without N11
Section titled “Test the HTTP layer without N11”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.