* feat(woocommerce): store order/refund feed extension
Connect a WooCommerce store via the wc-auth key handshake (manual key
fallback) with per-store consumer key/secret AES-256-GCM encrypted at rest,
and import paid orders and refunds into the transactions inbox as a
bank-style feed on the 1680 cash account. Feed-only: nothing auto-books,
gateway fees/payouts are out of scope (core wc/v3 does not expose them).
Sync is cursor-paginated on modified_after (offset pages only inside
same-second date_modified ties), terminates on an empty page, holds the
cursor below failed refund fetches / ingest errors / deadline-skipped work,
checks the time budget between refund fetches, and drops rows dated on or
before bookkeeping_locked_through on every run. Nightly cron gated on the
extension registry + new paid capability woocommerce_sync (backfilled to
existing bank_sync grant holders).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(migrations): move woocommerce migrations past main's 20260806090000
origin/main gained 20260806090000_recurring_schedule_interval_months while
this branch was in flight; identical version timestamps abort the Supabase
apply, so the two new migrations move to 20260806170000/20260806170100.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(woocommerce): resolve CodeRabbit review findings
- callback 503s early when WOOCOMMERCE_CREDENTIALS_ENCRYPTION_KEY is
unset: encryptCredential would otherwise throw after the probe and
strand the pending row without error_message
- disconnect and upstream-revoke clear the encrypted consumer key/secret:
nothing reads them after revoke and keeping decryptable dead
credentials is unnecessary retention
- manual sync gets a 240s time budget and the panel reports a truncated
run as 'partial, sync again' instead of a normal completion
- listOrderRefunds terminates on an empty batch (hosts may cap per_page),
dedupes by id against hosts that ignore page, and caps total pages
- unparseable money strings count as errors and log instead of being
silently identical to a zero total
- pg test uses per-run unique store URLs so committed rows cannot hit
the store_url partial unique index across pg-real runs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(woocommerce): resolve CodeRabbit cycle-2 findings
- listOrderRefunds throws when the page cap is exhausted with data still
flowing, instead of returning a silently partial list the sync cursor
would advance past; the error routes into the existing held-cursor
refund-retry path
- partial sync results keep the row-error count, and the partial toast
string surfaces it (ICU plural, hidden at zero) in both locales
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: retrigger CI after dropped push event
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>