Flora API
WordPress WooCommerce plugin for XML-based data exchange with ERP/inventory systems — products, orders, customers, custom checkout fields.
by Alexander Sokolov (AlSokolov2) · github.com/alsokolov2/floraapi · website
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/alsokolov2/floraapi/releases/download/v2.0.0/floraapi-2.0.0.zipReadme
Flora API
WordPress WooCommerce plugin for XML-based data exchange with inventory management systems.
Overview
Flora API bridges a WooCommerce store with an external inventory/ERP system using XML over HTTP. It provides bidirectional data exchange:
- Import products, customers (discounts, bonuses)
- Export products, orders with configurable custom fields
- Update order statuses from the ERP
All endpoints protected by API key (configurable in admin).
Architecture
floraapi/
├── floraapi.php # Entry point + bootstrap
├── composer.json # PSR-4 autoloading
├── phpunit.xml # PHPUnit config
├── LICENSE # Dual-license (GPL + commercial)
├── README.md
├── includes/
│ ├── Core.php # Singleton, hooks, logging
│ ├── Router.php # URI → handler dispatch
│ ├── Auth.php # API key validation (3 sources)
│ ├── Admin.php # Settings page + field CRUD
│ ├── Checkout.php # Checkout integration
│ ├── XML.php # Shared XML utilities
│ └── API/
│ ├── Customers.php # Customer import
│ ├── Orders.php # Order export + status update
│ └── Products.php # Product import/export
├── tests/ # PHPUnit test suite
└── languages/ # Translation files
Key design decisions:
| Decision | Rationale |
|---|---|
Singleton Core with lazy-loaded modules |
Single entry point, instantiation on demand |
FloraAPI namespace, PSR-4 |
Modern PHP (consistent with WooCommerce Subscriptions, Bookings) |
| WordPress Coding Standards | snake_case, tabs, Yoda conditions, proper escaping |
| API key from 3 sources | ?key=, X-Api-Key, Authorization: Bearer — flexible for any ERP |
| Customer data as WP user meta | No custom tables, discount auto-applies at checkout |
wp_options for custom fields |
Simple, sufficient for typical field count |
API Reference
Authentication
Every endpoint requires an API key (unless none is configured in settings). Pass it via:
- Query:
?key=YOUR_KEY - Header:
X-Api-Key: YOUR_KEY - Authorization:
Authorization: Bearer YOUR_KEY
GET /api/products/get — Export Products
| Param | Type | Description |
|---|---|---|
changed |
string | Optional. Y-m-d — filter by modification date |
key |
string | API key |
Response XML:
<yml_catalog date="2026-07-26 12:00:00">
<shop>
<name>Shop Name</name>
<company>Shop Name</company>
<url>https://...</url>
<currency id="rub" rate="1"/>
<offers>
<offer id="123" type="vendor.model">
<url/>
<name>Product Name</name>
<price>150</price>
<categoryId>5</categoryId>
<currencyId>RUR</currencyId>
<description>Description</description>
<model>SKU001</model>
<deleted>0</deleted>
</offer>
</offers>
</shop>
</yml_catalog>
POST /api/products/import — Import Products
Creates new products or updates existing ones (matched by SKU = offer/@id).
Request body:
<yml_catalog>
<shop>
<categories>
<category id="1">Flowers</category>
</categories>
<offers>
<offer id="SKU001">
<name>Rose</name>
<price>150</price>
<categoryId>1</categoryId>
<description>Beautiful red rose</description>
<deleted>0</deleted>
<pictures>
<picture>image1.jpg</picture>
<picture>image2.jpg</picture>
</pictures>
</offer>
</offers>
</shop>
</yml_catalog>
| Field | Description |
|---|---|
offer/@id |
SKU (unique identifier) |
name |
Product name |
price |
Regular price |
categoryId |
References <category @id> |
description |
Product description (falls back to sostav) |
deleted |
0 = publish + instock, 1 = draft + outofstock, -1 = onbackorder |
GET /api/orders/get — Export Orders
| Param | Type | Description |
|---|---|---|
changed |
string | Optional. Date filter |
key |
string | API key |
Response XML includes:
- Customer info: phone, email, name, discount%, bonus points
- Order fields: date, total cost, delivery date/time, address, status, comment
- Custom checkout field values (dynamically named children)
<products>block with line items (name, SKU, price, quantity)
POST /api/orders/update-status — Update Statuses
Request body:
<orders>
<order>
<id>123</id>
<status>completed</status>
</order>
</orders>
Valid statuses: pending, processing, on-hold, completed, cancelled, refunded, failed.
POST /api/customers/import — Import Customers
Updates discount/bonus data for WordPress users. Matched by billing phone.
<customers>
<customer>
<phone>+7 916 123-45-67</phone>
<discount>10</discount>
<bonus>500</bonus>
</customer>
</customers>
- Phone is normalized (stripped of formatting) and matched against
billing_phoneuser meta - If no user found — a new WordPress user is created (role:
customer) - Discount % auto-applied at checkout via
woocommerce_cart_calculate_fees - Bonus value stored in order meta and included in order XML
Custom Checkout Fields
Add arbitrary fields to the WooCommerce checkout via Settings → Flora API → Add New Field.
- Set machine name (Latin, no spaces), label, type (
text/textarea/date/select) - Save
- Field appears on checkout, values saved with order, displayed in admin, exported in XML
Installation
wp-content/plugins/floraapi/
composer install --no-dev
Then activate in WordPress admin. Configure at Settings → Flora API.
Requirements: PHP ≥ 7.4, WordPress ≥ 5.2, WooCommerce ≥ 4.0, ext-simplexml, ext-libxml.
Development
composer install # Dependencies
composer test # Run tests
composer lint # WordPress Coding Standards check
composer lint-fix # Auto-fix CS issues
vendor/bin/phpunit --coverage-html coverage # Coverage report
License
Dual-license model:
- Free for non-commercial use under GPL v2 or later
- Commercial use (client sites, SaaS, revenue-generating stores, agency deployments) requires a commercial license — contact the author
- Copyright © 2024–2026 Alexander Sokolov (AlSokolov2)
See LICENSE for details.
Read the full README on GitHub →
Releases
| Tag | Published | Asset | Downloads |
|---|---|---|---|
| v2.0.0 | Jul 26, 2026 | floraapi-2.0.0.zip | 0 |