Skip to content

WooCommerce Integration

Reference for App\Integrations\WooCommerce\WooCommerceIntegration. Covers the connectivity verification contract, the HTTP endpoint it targets, the three possible outcomes, and the test coverage for each.

Class

Namespace: App\Integrations\WooCommerce\WooCommerceIntegration Extends: App\Services\AbstractIntegration

Connectivity Verification

WooCommerceIntegration implements performVerification(SystemConnection $connection): VerificationResult, which is called by the sealed AbstractIntegration::verify() method. Any uncaught Throwable from performVerification() is caught by verify() and converted into a failed VerificationResult, so callers never handle exceptions from the verification path.

What it does

  1. Reads connection_token from the connection's encrypted credentials array.
  2. Strips any trailing /wp-json/… path from base_url to derive $storeBase.
  3. POSTs to {storeBase}/wp-json/conflux-connector/v1/connectivity-check with connection_token and connection_id in the request body.
  4. Returns a VerificationResult based on the HTTP response status.

Endpoint

POST {storeBase}/wp-json/conflux-connector/v1/connectivity-check

Request body

Field Type Description
connection_token string Token from credentials['connection_token']; empty string if absent
connection_id string UUID of the SystemConnection

Outcomes

HTTP status VerificationResult returned
200 VerificationResult::success($storeBase)successful = true, externalUserId = $storeBase
401 VerificationResult::failure('Invalid connection token.')successful = false, message = 'Invalid connection token.'
Any other status $response->throw() raises an Illuminate\Http\Client\RequestException, caught by AbstractIntegration::verify() and converted to VerificationResult::failure($e->getMessage())

VerificationResult shape

Namespace: App\Data\VerificationResult

Property Type Description
successful bool true on success, false on any failure
externalUserId ?string The store base URL on success; null on failure
message ?string Human-readable failure reason; null on success

Bulk Export

WooCommerceIntegration implements fetchBulkExport(SystemConnection $connection, ExportRequest $request): Collection, called by the sealed AbstractIntegration::requestBulkExport(). Supported capabilities: SystemCapability::Customers, SystemCapability::Orders, and SystemCapability::Products. The endpointAndMapper() method is exhaustive — all three capabilities are handled and there is no fall-through branch.

Capability dispatch

The private endpointAndMapper() helper maps a capability to its WC REST endpoint and item mapper:

Capability Endpoint Mapper
SystemCapability::Customers /wp-json/wc/v3/customers WooCommerceTransformer::mapCustomer()
SystemCapability::Orders /wp-json/wc/v3/orders WooCommerceTransformer::mapOrder()
SystemCapability::Products /wp-json/wc/v3/products WooCommerceTransformer::mapProduct()

Credentials

Bulk export authenticates via HTTP Basic Auth using WooCommerce REST API keys read from the connection's encrypted credentials array:

Credential key Description
consumer_key WooCommerce REST API consumer key
consumer_secret WooCommerce REST API consumer secret

Delta sync (modified_after)

When ExportRequest::$modifiedAfter is set, the integration appends a modified_after query parameter (ISO 8601 string) to every request made by performProbeBulkExport and performFetchBulkExportPageRange. The WooCommerce REST API filters results server-side so that only records with a date_modified value later than the supplied timestamp are returned. When modifiedAfter is null, the parameter is omitted and all records are returned (full sync).

The cursor is supplied by BulkSyncJob — the integration never reads or writes it directly. See Delta Sync for the cursor derivation rules.

Pagination

All three endpoints share the same pagination contract. The integration passes page (integer, starts at 1) and per_page (50) as query parameters, reads the X-WP-TotalPages response header to determine the total number of pages, and iterates until all pages are fetched. If the header is absent or empty it defaults to 1 page.

Error handling

Any non-2xx response causes fetchBulkExportBatches()/performProbeBulkExport()/performFetchBulkExportPageRange() to throw IntegrationRequestException with the HTTP status code. AbstractIntegration's sealed wrapper methods pass this through unchanged; any other Throwable is wrapped in InvalidIntegrationResponseException.

Mapping: WooCommerce order → OrderData

WooCommerceTransformer::mapOrder() is a public static method (callable via first-class callable syntax as the mapper closure).

WooCommerce field OrderData property Notes
id sourceId Cast to string
(constant) sourceSystem Always SystemDriver::WooCommerce
number sourceOrderNumber Human-readable order number; cast to string; null if absent
status status Raw string — not normalised (e.g. completed, processing)
currency currency ISO 4217 code
total total Integer minor currency units (round(float × 100)); defaults to 0
total_tax totalTax Integer minor currency units; null if absent or non-numeric
shipping_total totalShipping Integer minor currency units; null if absent or non-numeric
date_created createdAt ISO 8601 string
date_modified updatedAt ISO 8601 string
date_paid datePaid ISO 8601 string; null if absent or empty string
customer_id customerSourceId Cast to string; null if customer_id is 0 or absent (guest orders)
billing.* addressBilling null if all billing fields are empty
shipping.* addressShipping null if all shipping fields are empty
line_items lineItems Collection<int, LineItemData> — see line item mapping below

Unmapped scalar top-level fields (e.g. discount_total, payment_method) are captured as customAttributes. Fields in ORDER_MAPPED_KEYS and non-scalar values are excluded.

Mapping: WooCommerce line item → LineItemData

Each entry in the line_items array is mapped by the private mapLineItem() method and collected into OrderData::lineItems.

WooCommerce field LineItemData property Notes
id sourceId Cast to string
product_id productSourceId Cast to string — used by AbstractTransformer::storeOrder() to look up the previously-imported Product row by (system_connection_id, source_id)
name name Product name at time of order
quantity quantity Integer; defaults to 0 if absent
price unitPrice Integer minor currency units; defaults to 0 if absent
total total Integer minor currency units; defaults to 0 if absent
sku sku null if absent or empty string

Product linking

productSourceId carries the WooCommerce product_id. When AbstractTransformer::storeOrder() persists a line item it queries Product on (system_connection_id, product_source_id) and writes the resolved UUID to line_items.product_id. If the product has not been synced yet, product_id remains null until the order is re-imported after the Products sync (CFX-83) runs.

Mapping: WooCommerce customer → CustomerData

Field mapping lives on WooCommerceTransformer::mapCustomer() (public static), called directly by WooCommerceIntegration from each of the three fetch hooks above — it is not invoked through the TransformerContract::transform() interface.

WooCommerce field CustomerData property Notes
id sourceId Cast to string
(constant) sourceSystem Always SystemDriver::WooCommerce
email email
date_created createdAt ISO 8601 string
date_modified updatedAt ISO 8601 string
first_name firstName null if empty
last_name lastName null if empty
billing.phone phone null if empty
billing.* addressBilling null if all billing fields are empty
shipping.* addressShipping null if all shipping fields are empty
orders_count orderCount
total_spent totalSpent Converted to integer minor currency units (round(float × 100))
(remaining scalar top-level fields, plus meta_data entries) customAttributes Anything not in the mapped-keys list; see WooCommerceTransformer::MAPPED_KEYS

Unmapped scalar top-level fields and meta_data entries with scalar values are captured as customAttributes. meta_data entries whose values are arrays or objects are silently skipped.

Mapping: WooCommerce product → ProductData

WooCommerce field ProductData property Notes
id sourceId Cast to string
(constant) sourceSystem Always SystemDriver::WooCommerce
name name
status status See status mapping below
date_created createdAt ISO 8601 string
date_modified updatedAt ISO 8601 string
sku sku null if absent or empty
price price Integer minor currency units (round(float × 100)); null if absent or non-numeric
regular_price originalPrice Integer minor currency units (round(float × 100)); null if absent or non-numeric
categories[*].name categories Flat array of category name strings; empty objects and blank names skipped
tags[*].name tags Flat array of tag name strings; empty objects and blank names skipped
images[0].src imageUrl First image only; null if images is empty or src is blank
permalink url null if absent or empty
description description HTML stripped via strip_tags(); null if absent or empty

Status mapping

WooCommerce status ProductStatus
publish Active
trash Deleted
(any other: draft, pending, private, …) Inactive

Unmapped scalar top-level fields (e.g. weight, stock_quantity, featured) are captured as customAttributes. Fields in PRODUCT_MAPPED_KEYS and non-scalar values are excluded.

PHPStan note

base_url is declared as string|null on SystemConnection. Before passing it to preg_replace(), the integration casts it explicitly: (string) $connection->base_url. This satisfies PHPStan level 10 (CFX-80).

Test coverage

Tests live in tests/Unit/Integrations/WooCommerce/WooCommerceIntegrationTest.php and tests/Unit/Integrations/WooCommerce/WooCommerceTransformerTest.php. All tests use Http::fake() to stub the HTTP layer — no database or network access.

performVerification tests

Test Stubbed response Assertion
returns success on 200 response HTTP 200 result->successful is true; result->externalUserId equals the store base URL
returns failure with invalid token message on 401 HTTP 401 result->successful is false; result->message is 'Invalid connection token.'
returns failure on network error HTTP 500 result->successful is false

probeBulkExport tests

Test What it covers
returns total pages from API header X-WP-TotalPages is read from a page-1 probe request
throws IntegrationRequestException on HTTP error A 5xx response raises IntegrationRequestException

fetchBulkExport tests (exercised via requestBulkExport(), which drives fetchBulkExportBatches())

Test What it covers
maps WooCommerce customer fields to CustomerData Full field mapping including billing address, phone, orderCount, and totalSpent conversion
fetches all pages when paginating X-WP-TotalPages drives multi-page iteration
sets billing address to null when all billing fields are empty Empty address arrays map to null on both billing and shipping
throws IntegrationRequestException on HTTP error A 5xx response raises IntegrationRequestException

probeBulkExport — Products tests

Test What it covers
returns total pages from API header X-WP-TotalPages header on GET /wp-json/wc/v3/products is read and returned as BulkExportProbe.totalPages
throws IntegrationRequestException on HTTP error A 5xx response raises IntegrationRequestException

fetchBulkExport — Products tests

Test What it covers
maps WooCommerce product fields to ProductData Full field mapping: sourceId, status, sku, price/originalPrice in cents, imageUrl, categories, url
fetches all pages when paginating X-WP-TotalPages drives multi-page iteration for the products endpoint
throws IntegrationRequestException on HTTP error A 5xx response raises IntegrationRequestException

probeBulkExport — Orders tests

Test What it covers
returns total pages from API header for orders X-WP-TotalPages header on GET /wp-json/wc/v3/orders is read and returned as BulkExportProbe.totalPages

fetchBulkExport — Orders tests

Test What it covers
maps WooCommerce order fields to OrderData Full field mapping: sourceId, sourceOrderNumber, status, currency, total in cents, customerSourceId, and lineItems collection with productSourceId
fetches all pages when paginating X-WP-TotalPages drives multi-page iteration for the orders endpoint
throws IntegrationRequestException on HTTP error A 5xx response raises IntegrationRequestException

WooCommerceTransformer::mapOrder tests

Test What it covers
maps core fields correctly All primary order fields round-trip correctly, including monetary conversion to cents
maps line items with productSourceId from WC product_id product_idproductSourceId; priceunitPrice in cents; sku, name, quantity
prices are converted to cents total, total_tax, shipping_total converted via round(float × 100)
customerSourceId is null when customer_id is 0 Guest orders (customer_id: 0) produce null
datePaid is null when absent Empty date_paid maps to null
optional monetary fields default to null when absent Absent total_tax, shipping_total, date_paid, customerSourceId, addresses, and line_items produce correct nulls / empty collection
line item sku is null when empty string Empty sku on a line item maps to null
captures unmapped scalar fields as customAttributes discount_total, payment_method, etc. appear in customAttributes
does not capture mapped keys as customAttributes id, status, total, line_items, billing are excluded

WooCommerceTransformer::mapProduct tests

Test What it covers
maps core fields correctly All primary fields round-trip correctly
status mapping (5 sub-tests) publishActive; trashDeleted; draft, pending, privateInactive
price is converted to cents "9.99"999, "14.99"1499
price is null when empty string Empty price / regular_price map to null
categories and tags are extracted as name arrays Objects with name keys flatten to string arrays
imageUrl comes from first image src Only images[0].src is used
imageUrl is null when images array is empty Missing images produce null
description strips HTML tags strip_tags() is applied
optional fields default to null when absent Absent sku, price, originalPrice, imageUrl, url, description are null; categories and tags are empty arrays
captures unmapped scalar fields as customAttributes weight, stock_quantity, featured appear in customAttributes
does not capture mapped keys as customAttributes id, name, status, sku, price, categories, images are excluded

Source files

File Purpose
app/Integrations/WooCommerce/WooCommerceIntegration.php Integration implementation — performVerification, fetchBulkExportBatches, performProbeBulkExport, performFetchBulkExportPageRange, endpointAndMapper
app/Integrations/WooCommerce/WooCommerceTransformer.php Field mapping — mapCustomer(), mapOrder(), and mapProduct() public static methods; mapLineItem() and mapOrderCustomAttributes() private static helpers
tests/Unit/Integrations/WooCommerce/WooCommerceIntegrationTest.php Unit tests for verification and all three bulk-export capabilities
tests/Unit/Integrations/WooCommerce/WooCommerceTransformerTest.php Unit tests for mapOrder(), mapProduct(), and mapCustomer()

See also