Clover Commerce Sync
# Clover Commerce Sync
Synchronizes product prices and stock levels from a [Clover POS](https://www.clover.com)
account to Drupal Commerce product variations using the Clover REST API v3.
## Features
- **Price sync** — converts Clover item prices (stored in cents) to your
store currency and updates Commerce product variation prices automatically.
- **Stock sync** — reads Clover item stock levels and updates Commerce stock,
with native support for the [Commerce Stock](https://www.drupal.org/project/commerce_stock)
module (3.x) and a plain integer field fallback.
- **Cron sync** — runs on a configurable schedule (every 15 minutes up to
once per day).
- **Real-time webhooks** — optional Clover webhook endpoint
(`/clover-sync/webhook`) for instant updates when an item changes in Clover.
- **Manual sync** — one-click sync trigger from the admin settings page.
- **Rate-limit handling** — automatic 750 ms paging delay and exponential
backoff retry on Clover 429 responses, safe for large catalogues.
- **SKU-based matching** — items are matched by SKU between Clover and
Drupal Commerce; unmatched items are skipped silently.
## Requirements
- Drupal 10.2 or 11
- [Drupal Commerce](https://www.drupal.org/project/commerce) 3.x
(`commerce_product`, `commerce_price`)
- A Clover merchant account with an API token
(see [Configuration](#configuration) below)
- PHP 8.1 or higher
### Optional
- [Commerce Stock](https://www.drupal.org/project/commerce_stock) 3.x —
when installed, stock is managed via the transaction API. Without it, the
module falls back to a plain integer field on the product variation.
## Installation
Install with Composer (recommended):
```bash
composer require drupal/clover_commerce_sync
drush en clover_commerce_sync
```
Or manually:
1. Download and extract the module into `modules/custom/clover_commerce_sync/`.
2. Enable it at **Admin → Extend** or via Drush:
```bash
drush en clover_commerce_sync
drush cr
```
## Configuration
### 1. Get your Clover API credentials
**Merchant ID**
Log in to your Clover dashboard at [www.clover.com/dashboard](https://www.clover.com/dashboard).
Go to **Settings → Business Information**. Your alphanumeric Merchant ID
(e.g. `FB923TN9SW3X1`) is shown there. Do not use the numeric MID shown in
some sections — the REST API requires the alphanumeric form.
**API Token**
1. In the Clover dashboard, click the **Settings** icon (top right).
2. Type **API** in the settings search bar and select **API Tokens**.
3. Click **Create new token**, give it a name (e.g. "Drupal Sync"), and
enable the **Inventory → Read** permission.
4. Click **Create Token** and copy the token immediately — it is only shown once.
### 2. Configure the module
Go to **Commerce → Clover Sync** (`/admin/config/commerce/clover-sync`) and fill in:
| Field | Value |
|---|---|
| Merchant ID | Your alphanumeric Clover Merchant ID |
| API Access Token | The token you just created |
| Environment | Production (or Sandbox for testing) |
| Currency Code | Your store currency (e.g. `USD`) |
| Cron Sync Interval | How often cron will sync |
| Sync prices | Check to update prices |
| Sync stock levels | Check to update stock |
| Stock Field (fallback) | Machine name of the integer field on your product variation (only used without Commerce Stock module) |
### 3. Match SKUs
Every Clover item you want to sync **must have a SKU set in Clover** that
exactly matches the SKU on the corresponding Drupal Commerce product variation.
Items without a matching SKU are silently skipped.
### 4. Test the sync
Click **Run sync now** on the settings page. Check **Admin → Reports →
Recent log messages** for sync results.
### 5. Real-time webhook (optional)
Register the following URL in your Clover developer app's webhook settings:
```
https://yoursite.com/clover-sync/webhook
```
Clover will POST to this endpoint whenever an item changes, triggering an
immediate targeted sync. You can optionally set a **Webhook Verification
Secret** to validate incoming requests.
## Troubleshooting
**401 Unauthorized**
Your API token is invalid or expired, or you are using the numeric MID
instead of the alphanumeric Merchant ID. Re-check both values.
**429 Too Many Requests**
Clover's rate limit was hit. The module retries automatically with
exponential backoff (up to 3 attempts). For very large catalogues, reduce
the cron sync interval to spread load over time.
**Items are not syncing**
Confirm that the Clover item has a SKU and that the same SKU exists on the
Drupal Commerce product variation. SKU matching is case-sensitive.
**500 error on cache clear (PHP 8.4)**
Ensure you are running the latest version of this module. Earlier versions
had a constructor property promotion conflict with `ControllerBase` that
was fixed in 1.0.1.
**Stock not updating**
If Commerce Stock module is installed, stock is updated via transactions —
check the stock transaction log. If it is not installed, confirm the integer
field name in the **Stock Field** setting matches the field on your product
variation type.
## How it works
1. On cron (or manual trigger), the module calls
`GET /v3/merchants/{mId}/items?expand=itemStock` with pagination.
2. For each item with a SKU, it looks up the matching Commerce product
variation by SKU.
3. If **Sync prices** is enabled, it converts the Clover price from cents to
a decimal and calls `ProductVariation::setPrice()`.
4. If **Sync stock** is enabled and Commerce Stock is present, it calculates
the delta between the current stock level and the Clover quantity and
creates a `STOCK_IN` transaction. Without Commerce Stock, it sets the
integer field directly.
5. Only changed variations are saved, minimizing database writes.
## Maintainers
Contributions, bug reports, and pull requests are welcome.