An OpenCart extension that syncs products, stock, prices, orders, and images between an OpenCart store and a Reverb.com marketplace listing.
Type: OpenCart 3.x Extension (with planned OpenCart 4.x compatibility)
Primary Market: Australia (reverb.com/au)
Sync Direction: Bidirectional (OpenCart ↔ Reverb)
https://api.reverb.com/api/Authorization: Bearer <token> + Accept-Version: 3.0 headersThe extension must provide an admin panel (Admin > Extensions > Modules > Reverb) that allows the store owner to:
Each product in the OpenCart admin must show a toggle/checkbox to:
| Field | OC → Reverb | Reverb → OC |
|---|---|---|
| Title / Name | ✓ | ✓ |
| Description | ✓ | ✓ |
| Price | ✓ | ✓ |
| Stock / Qty | ✓ | ✓ |
| Images | ✓ | — |
| Orders | — | ✓ |
upload/
├── admin/
│ ├── controller/extension/module/
│ │ └── reverb.php # Admin settings + manual sync trigger
│ ├── language/en-gb/extension/module/
│ │ └── reverb.php # All user-facing strings
│ ├── model/extension/module/
│ │ └── reverb.php # DB interactions (token storage, product map)
│ └── view/template/extension/module/
│ └── reverb.twig # Admin settings form
├── catalog/
│ └── controller/extension/module/
│ └── reverb.php # Webhook endpoint for Reverb callbacks
└── system/
└── library/reverb/
├── ReverbApi.php # HTTP client wrapping the Reverb REST API
├── ProductMapper.php # Maps OC product fields ↔ Reverb listing fields
└── OrderMapper.php # Maps Reverb order payload → OC order format
OCMOD patch file (reverb.ocmod.xml) injects the per-product toggle into the product edit page without modifying core files.
The extension creates and manages the following tables.
oc_reverb_product_map
| Column | Type | Notes |
|---|---|---|
product_id |
INT | FK to oc_product |
reverb_listing_id |
VARCHAR(64) | Reverb's listing ID once synced |
sync_enabled |
TINYINT(1) | Per-product on/off toggle |
last_synced_at |
DATETIME | Timestamp of last successful sync |
oc_reverb_sync_log
| Column | Type | Notes |
|---|---|---|
log_id |
INT AUTO_INCREMENT | |
product_id |
INT | |
direction |
ENUM('push','pull') | |
status |
ENUM('success','error') | |
message |
TEXT | Error detail or summary |
created_at |
DATETIME |
API token, sync mode, and category whitelist are stored in OpenCart's native oc_setting table via $this->config — no custom config table needed.
oc_setting) with the key prefix module_reverb_. Never stored in a raw custom table.oc_reverb_product_map.sync_enabled; defaults to 0 (disabled) for all products.sync_enabled = 1 are synced.catalog/controller/extension/module/reverb/cron) for stock/price polling.POST /listings/{id}/photos. Only push images OC → Reverb; do not pull images back.Base URL: https://api.reverb.com/api/
Required headers on every request:
Authorization: Bearer <token>
Accept-Version: 3.0
Content-Type: application/hal+json
Rate limit: ~100 requests/min on the standard plan — batch operations where possible.
Key endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
/my/listings |
GET | Fetch the seller's existing listings |
/listings |
POST | Create a new listing |
/listings/{id} |
PUT | Update an existing listing |
/listings/{id}/end |
PUT | End/delist a listing |
/listings/{id}/photos |
POST | Upload a photo to a listing |
/my/orders |
GET | Fetch seller orders |
/my/orders/{order_number} |
GET | Fetch a single order |
/webhooks |
POST | Register a webhook endpoint |
/categories/flat |
GET | Fetch the full Reverb category tree |
sync_enabled = 1 AND is in an allowed category.ProductMapper::toReverb().reverb_listing_id exists in the map → PUT /listings/{id}; otherwise → POST /listings and store the returned ID.POST /listings/{id}/photos.last_synced_at and log result in oc_reverb_sync_log.catalog/controller/extension/module/reverb/webhook (or poll /my/listings + /my/orders).reverb_listing_id, update price / stock / description via model.OrderMapper::toOpenCart(), create via $this->model_checkout_order->addOrder().namespace Opencart\Admin\Controller\Extension\Module;\Opencart\System\Engine\Controllersystem/library/reverb/ classes between both versions (they are framework-agnostic)$this->db->query() and $this->db->escape() for all database access — never raw PDO or unsanitised inputoc_reverb_sync_log rather than surfacing raw exceptions to the UI