Integrates OpenCart 4 with the CDON marketplace in Sweden, Denmark, Finland and Norway with your own CDON merchant API credentials: markets with their own currency and exchange rate, VAT added to net OpenCart prices, category mapping with a markup, articles created in bulk with the result per market, price and stock sync, CDON orders imported once into real OpenCart orders, fulfilment with the tracking number, cancellation of new orders, a job queue with retries and a full log. OpenCart 4, no OCMOD.
Tested on 4.1.0.4Version 1.0.0Compatible with OpenCart 4.1.0.4 No core file edits
Built forOpenCart
Your catalogue live on CDON1 of 6
What it does
Integrates OpenCart 4 with the CDON marketplace in Sweden, Denmark, Finland and Norway with your own CDON merchant API credentials: markets with their own currency and exchange rate, VAT added to net OpenCart prices, category mapping with a markup, articles created in bulk with the result per market, price and stock sync, CDON orders imported once into real OpenCart orders, fulfilment with the tracking number, cancellation of new orders, a job queue with retries and a full log. OpenCart 4, no OCMOD.
Know the limits. You need a CDON seller account with API access (merchant ID and API token from CDON merchant support; sandbox credentials on request). The CDON merchant API has no returns endpoints: returns stay in the CDON portal. Each OpenCart product is one CDON article (no variant grouping). CDON is polled by cron, so new orders arrive at the next sync (every 15 minutes by default). Imported orders do not send e-mails or fire the events of other extensions. Tested against a stand-in of the CDON merchant API built from the official documentation, not against a live CDON account. CDON is a trademark of its owner. This extension integrates with the CDON merchant API; it is not made, endorsed or certified by CDON.
Features
Connection
Merchant ID and API token from CDON, sandbox and production; the token is stored encrypted and never logged
One-click connection test; a refused login pauses the account instead of retrying
Four markets, four currencies
Sweden (SEK), Denmark (DKK), Finland (EUR) and Norway (NOK): tick the markets of your agreement
Your own exchange rate per currency or the OpenCart currency table (the currency may stay hidden in your shop)
OpenCart prices are stored without tax: the VAT of each market (editable) is added to products with a tax class, because CDON prices include VAT, so nothing is listed too cheap
Markup in percent and a fixed amount per product, per category or for the account, price endings x.99 / x.90 / whole, applied in each market currency; delivery time per market
Articles
Search the CDON category tree and map your categories, with a markup and a brand per category
The result of each article is read per market and shown on the product: live, partly listed, or the reason CDON gave
A repeated create after a lost answer is recognised: one article per SKU, never two
Product edits and Rules changes are sent as article updates; stock and price go out with every sync
Orders and fulfilment
Every new CDON order becomes an OpenCart order once, even when cron runs overlap: buyer address with Nordic letters intact, market, CDON order number, fulfilment deadline, amount converted to your currency (the order shows SEK, DKK, EUR or NOK when your shop has it), stock taken like a normal order
VAT-inclusive and VAT-exclusive CDON order amounts are told apart per order
Set the order to Shipped and write the tracking number in the order history ("Tracking: 00370712345678901234 Carrier: PostNord"): the CDON order is fulfilled with it; orders still waiting for a tracking number never hold up the others
CDON states only move forward: a late answer never reopens a cancelled order, and an order you moved yourself keeps your status
Cancel a new order at CDON from the Orders page; optional order acceptance; failed imports listed with the reason and a Retry button
Reliable
Job queue with retries; CDON rate limits are respected by waiting for the reset time CDON sends
Filtered log with CSV export, a Jobs page with a run button, data kept on update and uninstall
Events and cron only, no OCMOD, no core edits; OpenCart 4.1, tested on 4.1.0.4
What you get
Download & installOne installer package (.ocmod.zip) for OpenCart 4.1.0.0 – 4.1.x. There is no OpenCart 3 edition. Download it from My licenses right after payment.
12 months of updates & supportNew versions and help for 12 months (24 or 36 months at checkout). After that the extension keeps working; renew anytime at 50%.
License for your storeOne production domain with all its subdomains; staging and local development hosts are free. How licensing works
30-day refundFull refund within 30 days of your first purchase, no reason needed. Refund policy
Tested buildsOpenCart 4.1.0.4. Tested on OpenCart 4.1.0.4.
Documentation & supportFull guide (Installation and setup, Configuration, Usage, CDON Marketplace Connector: developer notes) on this page. Tickets from your account, first reply within 1 business day.
Compatibility
CDON Marketplace Connector works with OpenCart 4.1.0.4.
We list the builds we install and test on. “Tested” means the test plan ran on that build; “Supported” means the build is inside the supported range.
Compatibility
Version
Range
Runs on
Status
OpenCart 4.1.0.x
4.1.0.0 – 4.1.0.4
PHP 8.1 – 8.3
Tested on 4.1.0.4
1.0.0
Latest
First release for OpenCart 4.1: integrates with the CDON merchant API (Sweden, Denmark, Finland, Norway) with your own merchant ID and API token (sandbox and production).
Markets with their own currency (SEK, DKK, EUR, NOK) and exchange rate; the market VAT is added to net OpenCart prices (products with a tax class), so VAT-exclusive prices are never listed too cheap.
Category mapping with a markup and a free-text brand; articles created in bulk with titles and descriptions in every CDON language the store has, GTIN, properties, condition, delivery type, shipped-from and the GPSR manufacturer block; batch results per product; a second create of a SKU is adopted, never duplicated.
Price and stock sync; content changes of live articles are sent as article updates.
CDON orders imported once into real OpenCart orders (recorded before the order is placed, one lock per CDON order), amounts converted to the default currency, VAT-inclusive or VAT-exclusive order amounts detected per order.
CDON states only move forward; an order you moved yourself keeps your status (a cancellation at CDON always follows).
Fulfilment when the order reaches Shipped, with the tracking number from the order history; with "Wait for a tracking number" orders without one wait at the end of the line and never hold up the others; refused fulfilments can be sent again.
Cancel a new order at CDON from the Orders page; optional order acceptance.
Job queue with retries (rate limits wait for the CDON reset window), a full log with CSV export, and data kept on uninstall so updates never lose the order and article links.
How do I install the extension?
Upload. Extensions > Installer: upload the .ocmod.zip. It is for OpenCart 4 (4.1.0.0 to 4.1.x).
Install and enable. Extensions > Extensions > Modules: install, open the settings and switch it on. Events register themselves; nothing is patched.
Enter the license key. Paste the key from My licenses. Subdomains, staging and local hosts are included; you have 14 days to do it.
OpenCart 4.1.0.4. Tested on OpenCart 4.1.0.4. The Compatibility tab lists every version range.
Do the extensions change core files?
No. They work through OpenCart events and standard extension types (modules, order totals, themes). No core file edits, no OCMOD patches, no theme template edits, so OpenCart updates and other extensions keep working.
What does the license cover?
One production store domain with every subdomain (www, shop., staging.), plus local development hosts such as localhost, *.local and *.test. A store on another domain needs its own license; a second-store license is 40% off. You have 14 days after installing to enter the key. 12 months of updates and support are included; after that the extension keeps working and renewals are 50% off. How licensing works
Can I get a refund?
Yes. Ask for a full refund within 30 days of your first purchase, no reason needed: open a ticket or write to [email protected] with your order number. The money goes back to the card or bank account you paid with within 5 business days of approval. Refund policy
How do I get help?
Open a ticket from your account, so your license details are attached. We answer Monday to Friday: bug reports within 1 business day, how-to questions within 2. Support policy. Not a customer yet? Ask a pre-sales question.
Integrates OpenCart 4.1 with the CDON marketplace in Sweden, Denmark, Finland and Norway. Your products become CDON articles with a price per market in SEK, DKK, EUR or NOK, stock and prices stay in sync, and CDON orders arrive as real OpenCart orders that you fulfil with a tracking number from OpenCart. It uses the new CDON merchant API (the platform that replaced CDON Connect in 2026) with your own CDON seller account. OpenCart 4, events only, no OCMOD, no core file changed.
What it does
Articles per market. Lists mapped products on CDON with titles and descriptions in every CDON language your store has (CDON translates the rest), images, brand, GTIN, properties, condition, delivery type and the GPSR manufacturer block.
Four markets, four currencies. Tick SE, DK, FI and NO; prices are converted from your default currency with your own rate or the OpenCart currency table, then your markup and price endings run in each market currency. Delivery time per market.
Net prices handled. OpenCart stores prices without tax. Products with a tax class get the market VAT (25 / 25 / 25.5 / 25 %, editable) added before they are sent, because CDON prices include VAT: VAT-exclusive prices are never listed too cheap.
Stock and price sync. A sale, a cancellation or an edit in OpenCart reaches CDON with the next sync (every 15 minutes by default), with a stock buffer and minimum.
Orders. New CDON orders become OpenCart orders once (never twice, also with overlapping cron runs), with the buyer address, the market, the CDON order number, the amount converted to your default currency (the order shows the CDON currency when your store has it) and the fulfilment deadline. VAT-inclusive and VAT-exclusive order amounts are told apart per order. Optional acceptance at CDON.
Fulfilment with tracking. When the order reaches Shipped (or Complete), the CDON order is fulfilled with the tracking number from the order history ("Tracking: ABC123 Carrier: PostNord"). With "Wait for a tracking number", orders without one wait at the end of the line and never hold up the others.
States only move forward. A late or stale answer from CDON never reopens a cancelled order or moves a completed one back; an order you moved yourself keeps your status (a cancellation at CDON always follows).
Per item results. CDON processes articles in batches; the extension reads the result per article and market and shows refusals (for example "DK: permission_denied") on the Listings page.
Safe operation. Token stored encrypted, sandbox and production, rate limits respected (the job waits for the CDON reset window), a refused login pauses the account, job queue with retries, log with CSV export, data kept on uninstall.
What it does not do
Returns: the CDON merchant API has no return endpoints; returns stay in the CDON merchant portal.
Product variations are listed as separate articles (no CDON product grouping).
OpenCart 3.0 is not supported (OpenCart 4.1.0.0 to 4.1.x).
Compatibility
OpenCart 4.1.0.0 to 4.1.x (tested on 4.1.0.4), PHP 8.1 to 8.3 as OpenCart 4.1 requires.
A CDON seller account with API access (merchant ID and API token from CDON).
The OpenCart cron, or a secret web address called every 5 to 15 minutes.
Next steps
CDON is a trademark of its owner. This extension integrates with the CDON merchant API; it is not made, endorsed or certified by CDON.
Installation and setup
Requirements
OpenCart 4.1.0.0 or later 4.1.x, administrator access.
A CDON seller account for the markets you sell in (Sweden, Denmark, Finland, Norway) and API access to the CDON merchant API.
Your merchant ID and API token. CDON issues them; ask CDON merchant support for API credentials and, for testing, for sandbox credentials (CDON gives them on request). Sandbox and production credentials are separate; CDON and Fyndiq credentials are not interchangeable.
The OpenCart cron or a cron job of your host (step 9).
Install the extension
Download kv_cdon.ocmod.zip from your Kavio Labs or opencart.com account.
Extensions > Installer, upload the file, then click Install (green +) in the list of installed packages.
Extensions > Extensions, choose Modules, find CDON Marketplace Connector and click Install, then Edit.
Enter your licence key and click Check now. Without a key the extension works for 14 days; the admin pages always stay open.
Connect CDON
Open CDON in the admin menu (or the gear on the settings page) > Connection. Choose Sandbox, enter the sandbox merchant ID and API token and click Save and test. "Connected" confirms that CDON accepted them.
Open Rules. Tick your markets, check the exchange rate of each market currency (enter your own rate, or add the currency under System > Localisation > Currencies; it may stay disabled for the storefront), check the VAT per market, set the delivery time per market, your markup and price endings, the default brand and the manufacturer (GPSR) details. Save.
Open Categories, search the CDON category tree and map your OpenCart categories (optionally with a markup and a brand). Optionally map properties (colour, size, material, weight ...) on Attributes.
On Listings, add products (one by one or a whole category) and click Sync now. After a few minutes the Listings page shows each article as live, or the reason CDON gave per market.
Make sure the queue runs: the extension adds the OpenCart cron task "CDON: sync queue", due every minute (System > Maintenance > Cron Jobs; OpenCart's cron needs php <store>/cron.php in your host's crontab, every minute). Without it, call the secret web address shown on the Jobs page every 5 to 15 minutes.
Test an order with the CDON sandbox ("Create a test order" call, sandbox only); the next sync imports it. Set the OpenCart order to Shipped with a history comment such as Tracking: 00370712345678901234 Carrier: PostNord and check that the CDON order is fulfilled.
Go live: switch the Connection page to Production, enter the production merchant ID and token and click Save and test.
Updating and uninstalling
OpenCart 4 updates an extension by uninstalling and installing it again, so uninstall keeps your data: the links between CDON orders and OpenCart orders and between products and CDON articles, the connection, the rules, the settings and the licence state. A reinstall or an update continues where it stopped and never imports an order or creates an article twice. The imported OpenCart orders always stay. To remove everything for good, drop the kv_cdon_* tables after uninstalling.
Off: nothing is sent to CDON, queued work waits (admin pages stay open).
Licence key
Your Kavio Labs licence; 14 days without a key. When the licence is not valid nothing is sent to CDON (also not by Sync now or Run now); the admin is never blocked.
Debug log
Also stores every call to CDON (credentials masked). Switch off when done.
Log retention (days)
Older log lines are removed.
Sync interval (minutes)
How often orders, stock and prices are synchronised (default 15).
Status of imported orders
OpenCart status of a new CDON order (Processing takes the stock). The only setting for new orders.
Connection page
Field
Notes
Mode
Sandbox (merchants-api.sandbox.cdon.com) or Production (merchants-api.cdon.com).
Merchant ID, API token
From CDON, one set per mode. Sent as HTTP Basic authentication over TLS; the token is stored encrypted (AES-256-GCM) and never shown again.
If CDON refuses the credentials (HTTP 401) the account is paused: nothing is sent to CDON until you save the credentials again or a connection test passes. This avoids repeated failed logins.
Rules page
Section
Options
CDON markets
Markets of your merchant agreement (SE, DK, FI, NO); own exchange rate per currency (0 = OpenCart currency table, also a disabled currency); VAT per market added to net prices (defaults 25 / 25 / 25.5 / 25 %); delivery time (min-max days) per market. A ticked market without a rate is skipped and named on the page.
Prices
Markup %, fixed amount (in your default currency, converted per market), price endings (none, whole, .99, .90) applied in each market currency. Category mappings and single products can override the markup.
Stock
Buffer (units kept back), minimum (at or below it CDON sees 0), maximum.
New listings
Create products automatically, default brand, brand source (manufacturer, model, SKU or an attribute), GTIN source, condition (new / refurbished), shipped from (EU / outside the EU), delivery type (mailbox, service point, home delivery).
Manufacturer (GPSR)
Manufacturer name, address, e-mail, website and the EU responsible person (required when the manufacturer is outside the EU).
Orders
Guest orders or match customers by e-mail (CDON sends no e-mail, so guest is usual), accept orders at CDON, wait for a tracking number, OpenCart status for the CDON states accepted, fulfilled and cancelled (new orders take the settings page status).
Net and gross prices
OpenCart stores every product price without tax; the tax class adds it at checkout. CDON prices always include VAT. The extension therefore adds the VAT of the market to every product that has a tax class with a percentage rate (for example "Taxable Goods"). A product without a tax class is sent at its price as it is. Example: 100.00 EUR net, rate 11.5, Sweden 25 % = 1437.50 SEK on CDON.
Tracking numbers
OpenCart has no shipment records. When an imported order reaches the status mapped to Fulfilled (default: Shipped), also when you move it on to Complete before the next sync or set it straight to Complete, the extension reads the newest history comments of the order for Tracking: <number> (also "Tracking number", "Tracking no.") and optionally Carrier: <name>, and sends them to CDON. The number must contain a digit and follow "Tracking:" (or "Tracking number" / "Tracking no."), so a comment such as "Tracking number will follow" never fulfils an order. With Wait for a tracking number on, the order waits until such a comment exists; waiting orders go to the end of the line, so they never hold up the others.
Categories and Attributes
Categories: search the CDON category tree (by name) and map it to an OpenCart category, optionally with a markup and a brand. "Refresh reference data" reloads the tree.
Attributes: CDON properties are the same for every category: free text (colour, size, material ...; up to 36 characters), pre-defined lists (size S/M/L, colour, connection type ...) and numbers (weight, volume, sizes). Map an OpenCart attribute, the manufacturer, model or SKU, or a fixed value.
Cron
The extension registers the OpenCart cron task "CDON: sync queue", due every minute (runs with php <store>/cron.php in your crontab, every minute); the queue itself starts the order import, the sync and the fulfilment every "Sync interval" minutes (default 15). Alternatively call the secret web address shown on the Jobs page every 5 to 15 minutes. Overlapping runs are prevented.
Usage
Listings
CDON > Listings shows every mapped product: its SKU and CDON article id, the state (not listed yet, waiting for CDON, live on CDON), what is synchronised (stock, price), the last values pushed and CDON's last answer.
Add mapping: a product id and optionally its SKU on CDON (default: the product SKU or model). Tick "Already on CDON" for articles you created on CDON before: they are linked, not created again.
Add all products of this category: maps every product of an OpenCart category.
Sync now: queues an order import, a product sync and the fulfilment push at once.
A product that cannot be listed (no image, title shorter than 5 characters, no rate for any market ...) shows the reason and is not sent.
CDON checks each article per market. "Partly listed: DK: permission_denied" means the article is live in the other markets and your account may not sell in Denmark.
Product edits in OpenCart (name, description, images, status) are sent as an article update, and so are changes of the markets, delivery times and article data on the Rules page; stock and prices go out with every sync.
Orders
CDON > Orders lists the imported CDON orders with the CDON order number and id, the CDON state (new, accepted, fulfilled, cancelled), the OpenCart order and what was sent back.
Each CDON order (one article with a quantity) becomes one OpenCart order with a guest customer (CDON sends no e-mail address; a placeholder channel-<number>@channel.invalid is used and no e-mail is sent). The comment holds the CDON order number, the market, the total in the market currency with VAT and the fulfilment deadline. The order page has a CDON panel.
Fulfilment: set the order to Shipped (or Complete) with a history comment Tracking: <number> Carrier: <name>. The next sync fulfils the CDON order with that tracking. With "Wait for a tracking number" off, Shipped alone fulfils the order; with it on, the order shows "Waiting for a tracking number" until the comment is there. A refused fulfilment (for example past the deadline) shows Send fulfilment again.
Cancel at CDON: for a new order you cannot ship (for example out of stock). CDON cancels it and refunds the customer; the OpenCart order gets the status mapped to "cancelled" and the stock comes back. An accepted order can no longer be cancelled through the API.
Retry: a CDON order that could not be imported (for example an SKU that is not mapped) shows the reason; fix it and press Retry.
An order cancelled at CDON is followed in OpenCart within about 30 minutes. Orders you moved on yourself keep your status.
Log and Jobs
Log lists every call and event (filters, CSV export); secrets are masked. Jobs shows the queue (waiting, running, waiting to retry, done, gave up) with Retry and Remove, the cron command and the secret web address.
CDON Marketplace Connector: developer notes
CDON is a trademark of its owner. This extension integrates with the CDON merchant API; it is not made, endorsed or certified by CDON.
OpenCart 4.1 port of the CS-Cart add-on kv_cdon 1.0.0 (same API client, mapper, driver and hub logic, with every fix of its release audit). Extension code kv_cdon (package kv_cdon.ocmod.zip, licence id kv_oc_cdon), PHP namespace KavioLabs\KvCdon in extension/kv_cdon/system/library/ (autoloaded by bootstrap.php). Built on the Kavio Marketplace Hub (Hub\*, channel independent) with one channel driver: Client\Api (HTTP), Drivers\CDON\Mapper (pure translation, unit tested), Drivers\CDON\Driver (Hub\DriverInterface), plus Markets (markets, currencies, exchange rates). OpenCart glue: Store (products, orders, statuses, tracking), compat.php (small db_* helpers over the OpenCart DB object), Keep (data kept across uninstall + install).
Sources (read 2026-11-08)
Only the documentation of the NEW CDON merchant API is used. The legacy CDON Connect API (and the old ma-docs pages on Read the Docs) closed in Q1 2026 and is not called anywhere.
Source
URL
Documentation index (docs.cdon.com redirects here)
https://cdon-api-documentation.readme.io/llms.txt
Introduction: authentication, base URLs, sandbox, request format, order lifecycle, markets / currencies / languages
Every page is also available as Markdown with its OpenAPI 3.1 block (<page>.md); the request and response examples of those blocks are the unit test samples in tests/samples/ (copied unchanged).
Hosts, authentication, format
Rule
Source
Implementation
Production https://merchants-api.cdon.com/api, sandbox https://merchants-api.sandbox.cdon.com/api; the version is part of the path (/v2/articles/bulk, /v1/orders)
Introduction, "Base URLs"
Api::LIVE, Api::SANDBOX; Connection page mode Sandbox / Production
Every endpoint: Authorization: Basic base64(merchantID:token)
Introduction, "Authentication"
Api::authHeaders(); merchant ID and token are the "Merchant ID" / "API token" of the Connection page, sealed with AES-256-GCM
CDON and Fyndiq credentials are not interchangeable
Introduction
The extension talks to CDON only
JSON bodies with Content-Type: application/json, TLS 1.2
Introduction, "Request format"
BaseClient::request() (cURL with peer verification)
Sandbox credentials are given on request; moving to production = new base URL + production credentials
Introduction, "Sandbox"
Separate sandbox and production credential sets
429 with X-RateLimit-Burst-Reset / X-RateLimit-Quota-Reset (Unix seconds) and X-RateLimit-Policy-Violated (burst / quota); rejected calls count against the limits; burst 9 000 / min, quota 270 000 / h
Rate Limiting
Api::rateLimitWait(): the job waits until the reset of the violated window (5 s to 1 h) instead of the normal back-off
Test override: the setting module_kv_cdon_base_url (e.g. http://127.0.0.1:11893/api) replaces the base URL (mock server, tests/mock-server/server.js). tests/e2e.js refuses to run unless it points at 127.0.0.1 / localhost.
PUT /v2/articles/bulk action update_article_price{price}
only price allowed
Stock
PUT /v2/articles/bulk action update_article_quantity{quantity}
only quantity allowed
Results
POST /v1/statuses/batch{batch_ids}
batches[].statuses[] {sku, action, markets[] {market, status ok/failed/pending, error_code}}
New orders
GET /v1/orders?state=CREATED&limit=100&page=n
plain array; up to 1 000 per page; no "changed since" filter
One order
GET /v1/orders/{order_id}
retry of a failed import, read-back of open orders
Acknowledge
PUT /v1/orders/{order_id}/accept
optional, "not available by default"
Ship
PUT /v1/orders/{order_id}/fulfill{tracking_information: [{carrier_name, tracking_number}]}
tracking number required per item, carrier optional, body optional
Cancel
PUT /v1/orders/{order_id}/cancel
only while CREATED; CDON refunds the customer
GET /v1/articles/sku/{sku} is implemented (Api::articleBySku) but not needed by the sync: a second create of an existing SKU is answered with sku_already_exists (status error_code), which the extension adopts (below).
Not used: v1 single-article write endpoints (deprecated, Fyndiq only), shipping configurations, payment reports, settlement notes, receipts, delete_article (an unmapped product stays on CDON; set it to 0 stock or delete it in the CDON portal).
product map SKU (product SKU, else model, by default)
statusfor sale / paused
product status on = for sale, else paused
quantity 0-500 000
stock after buffer / minimum / maximum rules
main_image + images (max 10) URLs
main image + additional images
markets SE DK FI NO
ISO 3166-1 alpha-2
ticked markets that have an exchange rate
price[] {market, value {amount_including_vat, currency}}
VAT included, market currency (SEK, DKK, EUR, NOK); vat_rate defaults to the market rate
OpenCart price, special price included (+ market VAT when the product has a tax class with a percentage rate: OpenCart prices are always net) x rate, then the price rules in the market currency; vat_rate is not sent
languages sv-SE en-US da-DK fi-FI nb-NO; CDON translates missing ones; description "No HTML" (schema DescriptionText)
main admin language (en-gb -> en-US, sv-se -> sv-SE, nb-no -> nb-NO ...; a language CDON does not take is not sent) plus every enabled store language CDON takes; HTML turned into plain text; cut with mb_substr
category (string, max 10)
category mapping
brand 1-50
free text, optional
category brand, brand source (manufacturer, model, SKU or an attribute) or default brand
gtin 8-14
GTIN source (digits only)
properties[] {name, value, language?}
free text 1-36 + language; pre-defined values from a fixed list; numeric 1-50
Attributes page: OpenCart attribute (or manufacturer / model / SKU) or fixed value per property
manufacturer {name, address{street_address, city, postal_code, country}, website, email, responsible_person}
GPSR; responsible person required outside the EU
Rules page (sent when the address is complete)
Variations (parent_sku, variational_properties) are not sent: each OpenCart product is its own article (options are not split).
Never twice
Articles. One bulk create per run. When the answer is lost (timeout, 5xx) the row stays "not listed" and the next run sends the create again; CDON refuses the duplicate SKU (sku_already_exists, at once in failed or later in the batch status), and the extension treats that as "already there": the row is linked and its content goes out once as update_article. Unit and e2e tested (504 after save -> one article).
Orders. The order map row is written BEFORE the order is created (INSERT IGNORE, unique key company + CDON order id), the import of one CDON order runs under GET_LOCK (Idempotency::lock), a placing_at marker and a comment marker [kv_cdon:<account>:<order id>] (written in the same INSERT that creates the order) let a later run adopt an order that an interrupted run created. Store::createOrder removes a half-written order before it rethrows. Parallel cron runs import once (e2e: three cron calls at once).
Fulfilment / acceptance. Rows are claimed with UPDATE ... WHERE state = old (N -> R); a repeat answered 403 "Order row already has fulfilled as state" counts as done. Refusals of this request (400/403/404/409/422, e.g. past the deadline or cancelled) are final ("E", with a "Send fulfilment again" button); network errors, 429 and 5xx retry.
Orders
One CDON order = one SKU with a quantity (Introduction, "The order lifecycle"); the extension creates one OpenCart order per CDON order directly in the order tables (Store::createOrder: no cart, no checkout events, no e-mail), payment and shipping method "CDON". customer_order_id (shown as the CDON order number) is kept in the comment.
price / total_price = the price of the whole quantity; the line price is amount / quantity. Whether amount includes VAT is decided per order (Mapper::gross, see Unverified 1). Amounts are converted from the market currency to the default currency with the same rate as the article prices (Markets::rate: own rate on the Rules page, else the OpenCart currency value, also of a disabled currency). The order carries the CDON currency (when the store has it) with that rate as currency_value, so OpenCart shows the amounts as sold; otherwise the default currency.
Delivery country = the market code (shipping_address.country is a name such as "Sweden"). B2B orders have no first / last name: the full_name is used as company and first name. The API has no customer e-mail: a placeholder channel-<number>@channel.invalid is used.
New orders are read with state=CREATED and state=ACCEPTED; the order map skips known ones. CDON states only move forward (CREATED < ACCEPTED < FULFILLED / NOT_FULFILLED, terminal): a stale read right after an asynchronous accept / cancel (202) is ignored. An OpenCart order follows a CDON state only while its status still equals the status mapped for the previous state (the merchant has not moved it); a cancellation at CDON always follows. Store::setStatus writes the history and moves stock like OpenCart (subtract when entering a processing / complete status, restore when leaving).
Fulfilment: an imported order at the status mapped to "fulfilled" (default Shipped) is sent; the tracking number and carrier come from the newest history comments (Tracking: <number>, Carrier: <name>, Store::parseTracking). With "Wait for a tracking number" the query only takes orders with a "track" comment and an order still without a parsable number is moved to the end (push_waiting_at), so waiting orders never block the queue (unit: 50 waiting rows rotated, none claimed). Rows are claimed N -> R (push_claimed_at); a claim left by a process that died during the call is released after 15 minutes (a repeat is answered "already fulfilled" = done).
Order date = created_at (UTC) shown in the shop time zone; the comment shows date and "fulfil by" (fulfillment_deadline).
States: CREATED -> new, ACCEPTED -> accepted, FULFILLED -> fulfilled, NOT_FULFILLED -> cancelled (OpenCart statuses on the Rules page; defaults Processing, Processing, Shipped, Canceled). Imported orders still CREATED / ACCEPTED are read back every 30 minutes.
Returns: the CDON merchant API has no return or claim endpoints (documentation index, 2026-11-08); returns stay in the CDON portal.
OpenCart integration
Events (no OCMOD): admin/view/common/column_left/before (menu), admin/view/sale/order_info/after (CDON panel on the order page), admin/model/catalog/product.editProduct/after (content and stock -> next sync), admin/model/catalog/product.deleteProduct/after (mapping removed), catalog/model/checkout/order.addHistory/after (stock of ordered products -> next sync), admin/controller/common/header/before (licence check). Nothing is printed on storefront pages.
Cron: OpenCart cron task extension/kv_cdon/channel/cdon.cron (OpenCart passes 5 arguments) or the secret web address (func_num_args() < 4 -> the key must match the stored cron key, compared with hash_equals).
Licence: kv_oc_cdon, 14-day trial; when the licence is not valid no new sync work starts; the admin pages are never blocked.
Data: tables kv_cdon_accounts, _log, _jobs, _product_map, _order_map, _options, _category_map, _attr_map, _cache, _batches. Uninstall keeps all of them (OpenCart 4 updates are uninstall + install; the maps are the guard against duplicate orders and articles); settings and licence state are restored on install (Keep).
Bulk calls refused as a whole (400 / 422) are split in halves down to single articles / SKUs, so one bad article cannot stop the others; the product sync runs its phases (batch results, stock and prices, creates, content updates) independently. Batch statuses are read one batch per call (the endpoint takes a list; fewer calls are a possible optimisation).
Tests
php81 tests/unit.php / php83 tests/unit.php: CDON samples from the documentation, mapper, driver, HTTP (Basic auth, 403 texts, 429 waits, 401 pause), hub rules, OpenCart glue (language codes, currency value, net prices, fulfilment queue rotation, forward-only states, order creation with the CDON rate, marker adoption) and tests/defects.php (recurring defect checklist).
node tests/e2e.js: full flow on an OpenCart 4.1 pool store against the mock (59 checks).
Unverified (the documentation does not say, or contradicts itself)
Whether order price.amount includes VAT. The two documented examples disagree (299 with VAT 59.80 at 25 % = VAT included; 23.20 with VAT 5.80 at 25 % = VAT excluded). The extension decides per order (Mapper::gross): vat_amount = amount x rate -> net, VAT is added; otherwise the amount is taken as VAT included. Check one real sandbox order.
Order vat_rate is a fraction (0.25) in the order examples and a percent (25) in article prices; the extension accepts both.
How long batch statuses are kept: not documented; the extension gives up after 24 hours and sends the create again (adopted if it exists).
The 401 error body shape: not documented; any 401 pauses the account.
Whether the immediate failed text for a duplicate SKU contains "already exists": only the status error_codesku_already_exists is documented; both are handled.
Accept availability: documented as "not available by default"; a 403 "not available" marks the order as not accepted and fulfilment works as usual.
Rate-limit figures (9 000 / min, 270 000 / h) "may be adjusted": the extension follows the response headers.
Troubleshooting
Symptom
Cause and fix
Connection test: "CDON refused the merchant ID or token (HTTP 401)"
Wrong credentials, or sandbox credentials in Production mode (or the other way round). The account is paused until you save the credentials again or a test passes.
Nothing happens after "Sync now"
The queue is not running: check the OpenCart cron (php <store>/cron.php in your crontab) or call the web address on the Jobs page. "Run now" on the Jobs page runs it once.
A ticked market is "skipped"
No exchange rate for its currency: enter your own rate on the Rules page or add the currency under System > Localisation > Currencies (it may stay disabled).
Prices on CDON look 20-25 % too low
The product has no tax class, so its price was taken as VAT-inclusive. Give it a tax class (net price + market VAT) or enter the gross price.
"Title is shorter than 5 characters" / "Description is shorter than 10 characters"
CDON limits; the title is used as description when there is none. Extend the product name.
"No title in a CDON language"
The store language is not Swedish, English, Danish, Finnish or Norwegian and no translation in one of them exists. Add one.
Article "Partly listed: DK: permission_denied"
Your CDON agreement does not cover that market. Untick it or ask CDON.
Order "No product mapping for SKU ..."
The CDON article SKU is not on the Listings page. Map the product with that SKU, then press Retry.
Order "The order currency SEK is not set up in OpenCart"
No rate for the currency: enter a rate on the Rules page, then Retry.
Order shows "Waiting for a tracking number"
Add a history comment Tracking: <number> (or switch "Wait for a tracking number" off).
Fulfilment refused: "Order has passed fulfillment due date"
CDON closed the order. Handle it in the CDON portal.
Jobs wait "to retry" after a 429
CDON's rate limit: the job waits until the reset time CDON sends (seconds to an hour), then continues.
Debug log (settings page) records every call with masked credentials. PHP errors go to system/storage/logs/error.log.
FAQ
Do I need a CDON account?
Yes, a CDON seller account with API access. The extension uses your own merchant ID and API token.
Does it use the old CDON Connect API?
No. CDON Connect closed in 2026; the extension uses the new CDON merchant API only.
Which markets and currencies?
Sweden (SEK), Denmark (DKK), Finland (EUR) and Norway (NOK). Your OpenCart default currency can be any currency; you give a rate per market currency or use the OpenCart currency table.
My OpenCart prices are without tax. Will CDON show the right price?
Yes. Products with a tax class get the market VAT added, because CDON prices include VAT.
Can an order be imported twice?
No. Each CDON order is recorded before the OpenCart order is created, under a lock, and an interrupted import is adopted instead of repeated.
Does it handle returns?
The CDON merchant API has no return endpoints; handle returns in the CDON merchant portal.
Is OCMOD used or is core changed?
No. Events only; no OCMOD, no core file changed.
What happens on update or uninstall?
Your data stays (links, rules, connection, licence state), so updates continue where they stopped.
Does it send e-mails to buyers?
No. CDON has the buyer contact; imported orders use a placeholder address and notify nobody.
OpenCart 3?
Not supported; OpenCart 4.1.x only.
CDON is a trademark of its owner. This extension integrates with the CDON merchant API; it is not made, endorsed or certified by CDON.
Server-side tracking for OpenCart 4.x and 3.0.x: purchases, refunds, add to cart, checkout, product views and leads go from your server to the free official GA4 Measurement Protocol, Meta Conversions API and TikTok Events API, deduplicated with the browser pixels through shared event ids. Consent-aware, SHA-256 hashed customer data, order status mapping, queue with retries, test mode and an event log. No paid relay, events only, no OCMOD.
The department store theme for OpenCart 4.x and 3.0.x: colour-coded departments, a department list beside the hero slider with two side promos, a big search with a department selector, tabbed product blocks, a 2 + 1 + 2 offer grid, deals of the day with real countdowns, a department showcase, a promotions band and a three-column product page with a sticky buy box. Four styles (Meridian, Harbor, Citrus, Night) and your own colours on top. Events only, no core changes.
Stop AI crawlers, scrapers, vulnerability scanners and unwanted countries before OpenCart 4.x or 3.0.x builds a page. Googlebot and Bingbot are verified by DNS and always allowed; 50+ bots get one switch each; country rules use free GeoIP databases and can close only the checkout; IP and network lists, rate limits, honeypots and the cart flood fix; a one-second challenge instead of a captcha; learning mode, dashboard, log, IP tester and Cloudflare export. No core changes, no OCMOD.