Overview
Ship to BOX NOW parcel lockers in Greece, Cyprus, Bulgaria and Croatia from OpenCart 4.1. The extension integrates with the BOX NOW Partner API using your own BOX NOW partner account: customers choose a locker at checkout, you request the delivery and print the label from the order page, and parcel events come back to the order.
What it does
- Locker choice at checkout. A "BOX NOW locker" shipping method with your own price (flat or by weight, free from an order value). Production accounts open the official BOX NOW map; Stage accounts, or a map that cannot load, use a built-in list of BOX NOW lockers near the postal code. The browser only reports a locker id: the shop looks it up in the BOX NOW locker list of your country and keeps that record. An order without a valid locker is not created.
- Delivery requests. From the BOX NOW tab of the order (or automatically at an order status): 1 to 10 parcels, compartment size, your warehouse or any locker as origin, prepaid or cash on delivery (by payment method, amount editable, in euro, above 0 and below 5000).
- Labels. PDF, or ZPL at 200 or 300 dpi, for one parcel or the whole delivery.
- Never twice. The request is saved with its BOX NOW order number before it is sent. After a timeout the shop looks the number up at BOX NOW before anything is sent again, and BOX NOW refuses a number twice. A delivery BOX NOW already holds under the same number is adopted only when its locker and customer match; otherwise you are asked to set an order number prefix.
- Cancel and returns. Cancel every parcel at BOX NOW, change the return option per delivery, create a customer return and download its label.
- Tracking. BOX NOW webhook events update the delivery at once; the cron run reads open deliveries as a safety net. The tracking link goes to the order history; a collected delivery can move the order to a status of your choice.
- Safe by default. Client secrets stored encrypted, automatic calls paused after BOX NOW refuses the credentials (no endless retries), names, phones and e-mails masked in the log. Updates keep your data.
Requirements
- OpenCart 4.1.0.x, PHP 7.4 to 8.3 with cURL and OpenSSL.
- A BOX NOW partner account (client id and client secret for Stage and/or Production). It is not included.
- The euro in System > Localisation > Currencies when your default currency is not the euro (BOX NOW amounts are in euro).
Not in 1.0
Home delivery, several BOX NOW accounts in one shop, labels of many orders in one file. OpenCart 3 is not supported (no OpenCart 3 package is shipped).
BOX NOW is a trademark of its owner. This extension is independent software by Kavio Labs that integrates with the BOX NOW Partner API; it is not affiliated with or endorsed by BOX NOW.
Installation
- Install the extension. Extensions > Installer > Upload
kv_box_now.ocmod.zip, then Install. - Install the shipping method. Extensions > Extensions > Shipping > BOX NOW Lockers > Install, then Edit.
- Connection tab. Choose the environment (Stage first) and your country. Enter the client id and client secret BOX NOW sent you for that environment, and the API URL only if BOX NOW sent another one. Press Save and test: the shop logs in, lists your warehouses and loads the BOX NOW locker list.
- Origin and sender. Choose your warehouse (or "any BOX NOW locker" if you drop the parcels in a locker), enter the sender name, phone in international format and e-mail, then Save connection.
- Webhook. Send the webhook address shown on the Connection tab to BOX NOW integration support. It carries a secret key; keep it private.
- Checkout tab. Switch the method on and enter a flat price or a weight table. Choose the picker: the BOX NOW map (Production) or the built-in list.
- Deliveries tab. Label format, default compartment, cash on delivery payment codes (for example
cod), an order number prefix if the BOX NOW account is shared, and the order statuses. - Cron. Make sure the OpenCart cron runs (
*/10 * * * * php /path/to/cron.php); the extension adds an hourly job. You can also call the secret cron address shown on the Deliveries tab. - Test. Place an order to an address in your BOX NOW country, choose a locker, then request the delivery from the BOX NOW tab of the order and download the label.
- Go live. Enter the Production credentials, switch the environment to Production and press Save and test again.
Updating
Uninstall the shipping method (Extensions > Shipping > Uninstall), uninstall and delete the old files in the Installer, upload the new zip, install, then install the shipping method. Deliveries, lockers, the connection, the log and your settings are kept.
Usage
At checkout
The customer chooses "BOX NOW locker", then Choose a locker: the BOX NOW map (Production) or the list near the delivery postal code. The chosen locker is shown under the method and in the shipping method name of the order, the invoice and the e-mails. Continuing without a locker is refused. If the address or the account changes so the locker no longer fits, the confirm step asks the customer to choose again and no order is created.
On the order page (BOX NOW tab)
- Locker: set or change it by its BOX NOW id (checked against the locker list).
- Request delivery: parcels (1 to 10), compartment, cash on delivery (by payment method, yes or no) and the amount in euro.
- Label: PDF or ZPL; fetched again on download if it was not ready.
- Refresh: reads the parcels from BOX NOW.
- Check with BOX NOW: for a request whose result is unknown (timeout, server error). It looks the order number up and adopts the delivery, or sends the same request again. It never creates a second delivery.
- Cancel: cancels every parcel at BOX NOW. A new request then uses the next order number (
<number>-2). - Returns: allow or stop returns for the delivery, create a customer return and download its label. A return with an unknown result is never sent again automatically: check the BOX NOW partner portal, then press "Nothing was created".
Delivery list and log
The Delivery list tab shows the last 40 deliveries; the Log tab shows connection, request and webhook entries with secrets and personal data masked.
Developer notes
Extension code kv_box_now (OpenCart 4.1), namespace KavioLabs\KvBoxNow (shared core in system/library/), shipping code kv_box_now.locker. Port of the CS-Cart add-on kv_box_now 1.0.0 with its audit fixes (research/quality/AUDIT-2026-11-08-boxnow.md: M1 adoption only for the same locker and customer, M2 "Check with BOX NOW" under the order lock). PHP 7.4 to 8.3.
Sources (public BOX NOW documents)
- Partner API manual v1.69 ("API Integration manual BOX NOW - EN", https://www.boxnow.bg/media/PDF/API%20integration%20manual%20BOX%20NOW%20-%20EN%20v1.69.pdf): auth 4.1 / 7.1, origins 4.2, locker lists 4.3.1, delivery requests 4.4 / 7.3.1, update 4.5 / 7.3.5, parcels 4.6, labels 5.0 / 7.4, cancel 7.5.2, customer returns 7.3.3, entrusted partners 7.6, error codes section 3, map widget 6.1.
- Webhook Based Parcel Tracking Guide v1.4 (https://www.boxnow.bg/media/PDF/Webhook-Based_Parcel_Tracking_Guide_EN_new.pdf): CloudEvents body,
data.event, data.time ordering, retries until 200. - Map widget script
https://widget-cdn.boxnow.<cc>/map-widget/client/v5.js, config _bn_map_widget_config (parentElement, afterSelect -> boxnowLockerId, partnerId, type, autoclose, gps, zip). - Copies: research/api-docs/boxnow/; contract transcribed in tests/fixtures/official/contract.json.
Source map
| File | Role |
|---|
Client/Api.php, Client/BaseClient.php, Client/Locations.php | Partner API (JSON client credentials, sealed token cache, X-PartnerID, {code, message} errors, X403 pause), public locker list |
Accounts.php, Client.php | One account per shop (Stage + Production, AES-256-GCM sealed), connection test |
Lockers.php, Points.php | Locker list per environment and country, server-side locker check, checkout/session and order locker |
Shipments.php | Record-before-call (order number saved first), P410 / unknown lookup + sameRequest(), labels, cancel, allowReturn, customer returns, events (rank, data.time), polling |
Jobs.php, Cron.php | Automatic requests and unknown results (atomic claim, retries in minutes), tracking, locker refresh, log retention |
Rates.php, Parcel.php, Orders.php | Checkout price, field rules (international phone, "0.00" amounts, mb-safe cuts, compartment fit), order reading (decoded text) and history |
Webhook.php | Keyed webhook address, CloudEvents parsing, event ids |
Events
admin/view/sale/order_info/before (order tab), admin/controller/common/header/before (licence tick), catalog/view/checkout/shipping_method/after (picker), catalog/controller/checkout/shipping_method.save/after (locker required), catalog/controller/checkout/confirm/before and confirm.confirm/before (confirm guard), catalog/view/checkout/confirm/after (guard message), catalog/model/checkout/order.addOrder/after and editOrder/after (locker stored), catalog/model/checkout/order.addHistory/after (automatic request). Cron kv_box_now (hourly). No OCMOD.
Endpoints
Storefront: extension/kv_box_now/shipping/kv_box_now.point|lockers (POST, session token), .webhook (POST, account key in key or X-Kv-Key), .cron (OpenCart cron or key). A parameter-free notify.php is not needed: BOX NOW calls the webhook server to server, no session is involved.
Tables
kv_box_now_accounts, _shipments, _parcels, _points, _lockers, _jobs, _events, _log. Uninstall keeps them (OpenCart 4 updates are uninstall + install); settings are restored by Keep.
Tests
php81 tests/unit.php && php83 tests/unit.php # throw-away MariaDB database, scripted BOX NOW stand-in
KV_AGENT=<name> STORE=oc41b node tests/e2e.js --shots # store pool lock; stand-in addons/box_now/tests/mock-server/server.php on 127.0.0.1
node tooling/release-templates/oc-upgrade/upgrade-test.js oc_box_now <store> opencart/extensions/oc_box_now/tests/upgrade-seed.sql
The e2e refuses to run unless the stand-in and the store base URL are on 127.0.0.1.
FAQ
Does it show live BOX NOW prices?
No. BOX NOW has no rate API for shops; the price is yours (flat or by weight).
Does the customer pick the locker on a map?
Yes, on the official BOX NOW map with a Production account. Stage accounts use the built-in list, because the BOX NOW map works with Production only.
Can a customer send a fake locker id?
No. Every id is looked up on the server in the BOX NOW locker list of your country and environment.
Which countries?
Greece, Cyprus, Bulgaria and Croatia, one country per shop (the country of your BOX NOW account).
Cash on delivery?
Yes, by payment method or per order, in euro, above 0 and below 5000.
What if BOX NOW does not answer?
The request stays "result unknown" and is looked up before anything is sent again (by the cron run or with "Check with BOX NOW"). BOX NOW never creates the same order number twice.
Two shops on one BOX NOW account?
Set an order number prefix in each shop.
Does it use OCMOD?
No. It is a shipping extension with events and cron; no core files are changed.
OpenCart 3?
No, OpenCart 4.1 only.
Is it affiliated with BOX NOW?
No. It is independent software that integrates with the BOX NOW Partner API.