|
@@ -0,0 +1,170 @@
|
|
|
|
|
+# Reverb OpenCart Integration
|
|
|
|
|
+
|
|
|
|
|
+An OpenCart extension that syncs products, stock, prices, orders, and images between an OpenCart store and a Reverb.com marketplace listing.
|
|
|
|
|
+
|
|
|
|
|
+## Project Overview
|
|
|
|
|
+
|
|
|
|
|
+**Type:** OpenCart 3.x Extension (with planned OpenCart 4.x compatibility)
|
|
|
|
|
+
|
|
|
|
|
+**Primary Market:** Australia (reverb.com/au)
|
|
|
|
|
+
|
|
|
|
|
+**Sync Direction:** Bidirectional (OpenCart ↔ Reverb)
|
|
|
|
|
+
|
|
|
|
|
+## Reference Documentation
|
|
|
|
|
+
|
|
|
|
|
+- [Reverb Developer Integrations](https://reverb.com/au/page/integrations)
|
|
|
|
|
+- [Reverb Help: Developer Sections](https://help.reverb.com/hc/en-us/sections/40908931289883)
|
|
|
|
|
+- [OpenCart Extension Development Guide](https://github.com/opencart/opencart/wiki/OpenCart-Extension-Development-Guide)
|
|
|
|
|
+- Reverb API Base URL: `https://api.reverb.com/api/`
|
|
|
|
|
+- Auth: `Authorization: Bearer <token>` + `Accept-Version: 3.0` headers
|
|
|
|
|
+
|
|
|
|
|
+## Requirements
|
|
|
|
|
+
|
|
|
|
|
+### Admin Settings Page
|
|
|
|
|
+
|
|
|
|
|
+The extension must provide an admin panel (`Admin > Extensions > Modules > Reverb`) that allows the store owner to:
|
|
|
|
|
+
|
|
|
|
|
+- Enter and save their personal Reverb.com API token
|
|
|
|
|
+- Select sync direction: One-way (OpenCart → Reverb) or Both-ways (bidirectional)
|
|
|
|
|
+- Select which OpenCart categories are eligible for sync
|
|
|
|
|
+- Manually trigger a full catalogue sync
|
|
|
|
|
+
|
|
|
|
|
+### Product Page Integration
|
|
|
|
|
+
|
|
|
|
|
+Each product in the OpenCart admin must show a toggle/checkbox to:
|
|
|
|
|
+
|
|
|
|
|
+- Enable or disable syncing that individual product to Reverb
|
|
|
|
|
+- Show the Reverb listing ID and a direct link once the product is listed
|
|
|
|
|
+
|
|
|
|
|
+### Sync Scope (Bidirectional)
|
|
|
|
|
+
|
|
|
|
|
+| Field | OC → Reverb | Reverb → OC |
|
|
|
|
|
+|--------------|:-----------:|:-----------:|
|
|
|
|
|
+| Title / Name | ✓ | ✓ |
|
|
|
|
|
+| Description | ✓ | ✓ |
|
|
|
|
|
+| Price | ✓ | ✓ |
|
|
|
|
|
+| Stock / Qty | ✓ | ✓ |
|
|
|
|
|
+| Images | ✓ | — |
|
|
|
|
|
+| Orders | — | ✓ |
|
|
|
|
|
+
|
|
|
|
|
+## Extension Architecture (OpenCart 3.x)
|
|
|
|
|
+
|
|
|
|
|
+### File Structure
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+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.
|
|
|
|
|
+
|
|
|
|
|
+### Database Tables
|
|
|
|
|
+
|
|
|
|
|
+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.
|
|
|
|
|
+
|
|
|
|
|
+### Key Design Decisions
|
|
|
|
|
+
|
|
|
|
|
+1. **API Token storage:** Stored via OpenCart's native settings system (`oc_setting`) with the key prefix `module_reverb_`. Never stored in a raw custom table.
|
|
|
|
|
+2. **Per-product toggle:** Stored in `oc_reverb_product_map.sync_enabled`; defaults to `0` (disabled) for all products.
|
|
|
|
|
+3. **Category filter:** Admin selects OpenCart categories; only products in those categories AND with `sync_enabled = 1` are synced.
|
|
|
|
|
+4. **Webhooks vs polling:** Use Reverb webhooks for real-time order/listing updates; fall back to a scheduled cURL ping (OpenCart cron or system cron calling `catalog/controller/extension/module/reverb/cron`) for stock/price polling.
|
|
|
|
|
+5. **Image sync:** Upload OpenCart product images to Reverb via `POST /listings/{id}/photos`. Only push images OC → Reverb; do not pull images back.
|
|
|
|
|
+
|
|
|
|
|
+## Reverb API Essentials
|
|
|
|
|
+
|
|
|
|
|
+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 Flow
|
|
|
|
|
+
|
|
|
|
|
+### OpenCart → Reverb (triggered on product save event or manual sync)
|
|
|
|
|
+
|
|
|
|
|
+1. Check product has `sync_enabled = 1` AND is in an allowed category.
|
|
|
|
|
+2. Map OC product fields to a Reverb listing payload via `ProductMapper::toReverb()`.
|
|
|
|
|
+3. If `reverb_listing_id` exists in the map → `PUT /listings/{id}`; otherwise → `POST /listings` and store the returned ID.
|
|
|
|
|
+4. Upload any new/changed images via `POST /listings/{id}/photos`.
|
|
|
|
|
+5. Update `last_synced_at` and log result in `oc_reverb_sync_log`.
|
|
|
|
|
+
|
|
|
|
|
+### Reverb → OpenCart (webhook or scheduled poll)
|
|
|
|
|
+
|
|
|
|
|
+1. Receive webhook POST to `catalog/controller/extension/module/reverb/webhook` (or poll `/my/listings` + `/my/orders`).
|
|
|
|
|
+2. For listing updates: look up OC product by `reverb_listing_id`, update price / stock / description via model.
|
|
|
|
|
+3. For new orders: map Reverb order payload to OC order via `OrderMapper::toOpenCart()`, create via `$this->model_checkout_order->addOrder()`.
|
|
|
|
|
+4. Log result.
|
|
|
|
|
+
|
|
|
|
|
+## OpenCart 4.x Compatibility Notes
|
|
|
|
|
+
|
|
|
|
|
+- OC 4.x uses PHP namespaces: `namespace Opencart\Admin\Controller\Extension\Module;`
|
|
|
|
|
+- Controllers must extend `\Opencart\System\Engine\Controller`
|
|
|
|
|
+- Template paths and module structure differ from 3.x
|
|
|
|
|
+- Planned approach: ship separate 4.x controller files; share `system/library/reverb/` classes between both versions (they are framework-agnostic)
|
|
|
|
|
+
|
|
|
|
|
+## Development Guidelines
|
|
|
|
|
+
|
|
|
|
|
+- PHP 7.4+ required; PHP 8.1+ preferred
|
|
|
|
|
+- Use `$this->db->query()` and `$this->db->escape()` for all database access — never raw PDO or unsanitised input
|
|
|
|
|
+- All user-visible strings must be defined in the language file, not hardcoded in controllers or templates
|
|
|
|
|
+- Wrap all Reverb API calls in try/catch; log errors to `oc_reverb_sync_log` rather than surfacing raw exceptions to the UI
|
|
|
|
|
+- The OCMOD XML file is required for injecting the per-product toggle without modifying core OpenCart files
|
|
|
|
|
+- Test against a Reverb sandbox account before connecting a live store
|