=== eFakturuj for WooCommerce ===
Contributors: efakturuj
Tags: woocommerce, invoices, accounting, peppol, slovakia
Requires at least: 6.5
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.0.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Send WooCommerce orders and refunds to eFakturuj automatically and manage the resulting invoices and credit notes inside WordPress.

== Description ==

eFakturuj for WooCommerce connects one WooCommerce store to one eFakturuj company using a short-lived, one-time pairing code. Pairing records and protects the normalized store URL so the same store cannot silently be attached to another company.

Normal synchronization is automatic and does not make checkout or an order edit wait for eFakturuj. The plugin captures an immutable canonical order snapshot in a durable local outbox, encrypts its payload with AES-256-GCM, and sends it in the background. The safe default creates invoices only when the customer requested one; the merchant can add the built-in checkout checkbox, use an existing checkout extension's order-meta key, or deliberately enable every eligible order.

WooCommerce > eFakturuj invoices is the dedicated operational workspace. It provides:

* invoice and credit-note totals, issue counts, search, type filters, and pagination;
* deduplicated documents linked to their WooCommerce orders;
* authenticated eFakturuj invoice and PDF actions returned by the signed status API;
* durable synchronization history with delivery and status-poll attempt counts;
* separate exact-delivery and fresh-data recovery controls; and
* Action Scheduler or WP-Cron mode, pending-action count, last-worker activity, and a protected Run now control.

The WooCommerce order editor also contains an HPOS- and legacy-compatible eFakturuj invoices card. It shows documents and recent synchronization status for that order, provides authenticated Open/PDF actions when available, and allows an authorized administrator to send the current order again.

The plugin captures new orders, persisted order edits, status changes, saved order items, completed payments, created refunds, completed order refunds, and later refund edits. Duplicate hooks are coalesced before one immutable snapshot is stored. Itemized partial refunds include product, shipping, fee, and per-tax-rate allocations so eFakturuj can create linked type 381 credit notes. An amount-only or unreconciled refund remains visible for review instead of inventing an accounting allocation.

Security details:

* Pairing codes are never stored and cannot be reused.
* The connection is bound to the normalized WooCommerce store URL.
* The signing secret and immutable outbox payloads are encrypted at rest with AES-256-GCM using key material derived from the WordPress AUTH_KEY.
* Connection options are not autoloaded.
* Version 1.0.0 requests signature version 2 during pairing and uses the version returned by eFakturuj. Version 2 authenticates the canonical event UUID together with the exact JSON bytes; version 1 remains supported for an existing connection upgraded from an older plugin.
* Every status request is signed separately and scoped to its connection, event, company, and store.
* Remote POST requests do not follow redirects, so signed credentials and payloads are not forwarded to another host.
* Every mutating administrator action requires the manage_woocommerce capability and a purpose-specific WordPress nonce.
* Customer/order payloads and secrets are not written to activity rows or plugin logs.
* The workspace stores only bounded safe summaries and document/correlation identifiers outside the encrypted payload.

== Requirements ==

* WordPress 6.5 or newer
* PHP 7.4 or newer with OpenSSL and AES-256-GCM support
* WooCommerce installed and active
* WordPress loopback/background processing enabled (Action Scheduler is preferred; WP-Cron is the fallback)
* The WordPress server must be able to make outbound HTTP requests to the eFakturuj API

The isolated 1.0.0 acceptance run passed all 27 checks on WordPress 7.0.1 and WooCommerce 10.9.4.

== Installation ==

1. Obtain the official `efakturuj-for-woocommerce-1.0.4.zip` release. The ZIP contains one top-level directory named `efakturuj-for-woocommerce` and the main file `efakturuj-for-woocommerce/efakturuj-for-woocommerce.php`.
2. In the customer's WordPress administration, open Plugins > Add New Plugin > Upload Plugin.
3. Select the ZIP, choose Install Now, and activate the plugin. Activation installs or upgrades the durable outbox table.
4. In eFakturuj, open the WooCommerce application for the correct company and generate a one-time pairing code.
5. In WordPress, open WooCommerce > eFakturuj settings.
6. Enter the eFakturuj API base URL without `/api/v1`, paste the pairing code, and choose Connect to eFakturuj.
7. Confirm the automatic invoice policy and then open WooCommerce > eFakturuj invoices.

For command-line installation:

`wp plugin install /path/to/efakturuj-for-woocommerce-1.0.4.zip --activate`

This plugin publishes under a new slug, `efakturuj-for-woocommerce`, replacing the
older `efakturuj-woocommerce` plugin. Deactivate and delete `efakturuj-woocommerce`
**before** installing and activating `efakturuj-for-woocommerce` — WordPress
treats them as two separate plugins, and both declare the same PHP classes and
functions, so running both active at once can fatal. Do not simply "replace the
plugin files" in place; the slug change means that upgrade path does not apply.

Activating `efakturuj-for-woocommerce` installs the outbox schema and migrates
compatible legacy safe activity without exposing or reconstructing old customer
payloads. An already paired version-1 connection remains usable; reconnecting
negotiates the UUID-bound version-2 signature.

== Testing the connection ==

1. Pair the store at WooCommerce > eFakturuj settings and confirm that a Connection ID is shown.
2. Open WooCommerce > eFakturuj invoices and confirm that Background synchronization health identifies Action Scheduler or the WP-Cron fallback.
3. Create a test order with a product, customer billing details, shipping, tax, and a payment method. Mark the invoice-request field when using the default policy.
4. Move the order to a status enabled in the eFakturuj WooCommerce application, for example Processing or Completed.
5. Allow Action Scheduler to run. For an immediate test, use Run now in the invoices workspace or run `wp action-scheduler run` when the Action Scheduler WP-CLI command is available.
6. Confirm that the activity moves from queued/sending through accepted/processing to completed, and that one deduplicated invoice appears.
7. Open the order editor and confirm that the eFakturuj invoices card shows the same document. Test Open and PDF while signed in to an authorized eFakturuj account.
8. Compare customer, line, discount, shipping, tax, currency, payment, and total values with the WooCommerce order.
9. Edit the order or complete its payment and confirm that the plugin captures the persisted change without creating a duplicate document.
10. Create an itemized partial refund and confirm that a linked type 381 credit note appears once with matching product, shipping/fee, and tax allocations.

== Recovery behavior ==

Automatic delivery makes no more than five attempts for one immutable event UUID. Accepted events receive up to 12 signed status checks with bounded backoff.

The invoices workspace deliberately separates two recovery operations:

* Retry delivery decrypts and resends the exact stored JSON bytes under the same event UUID. Use it for a transient transport or queue failure; backend idempotency returns the existing result when the event was already received.
* Send latest data reads the current WooCommerce order, creates a new encrypted snapshot and UUID, and includes the earlier UUID as `retries_event_id`. Use it only after correcting source data or when a review/attention result requires a new event. Both rows remain correlated audit history.

Neither operation lets a submitted request choose another order: the server-side handler resolves the order and event type from the protected outbox row. Both require manage_woocommerce and a row-specific nonce.

== Disconnecting and uninstalling ==

Use WooCommerce > eFakturuj settings > Disconnect to remove the local connection ID, event URL, encrypted signing secret, signature version, and scheduled connector actions. Existing eFakturuj invoices are not deleted. Local outbox history remains available until the plugin is deleted, but it cannot be delivered while disconnected.

Deactivation preserves connection data and outbox rows so normal operation can resume after reactivation. Deleting the plugin from the WordPress Plugins screen removes connection and invoice-policy options, worker-health timestamps, scheduled actions, and the complete encrypted outbox table. Backend accounting documents are never deleted by local cleanup.

Uninstall the WooCommerce application in eFakturuj when access must also be revoked at the backend; that clears the server-side signing secret and rejects later store events.

== External services ==

This plugin connects your store to eFakturuj, an e-invoicing service operated by
eFakturuj (IČO 57546495, Slovakia). The connection is created by you, in the plugin
settings, using a one-time pairing code issued by your eFakturuj account. No data
leaves your site before you pair it.

What is sent, and when:

* On pairing: your normalized store URL, the WordPress site name, the plugin version,
  your WordPress version, your WooCommerce version, and the event signature version
  the plugin requests, so eFakturuj can bind the connection to this store and agree
  on how later events are signed.
* When an eligible order or refund is captured: buyer billing and shipping details,
  company and tax identifiers, email and phone, ordered items, discounts, shipping,
  fees, per-rate taxes, totals, currency, payment state, refund allocations, the
  reason recorded for each refund, and the customer's order note. The order note and
  the refund reason are free text: whatever the shopper or your staff typed there is
  sent exactly as written, so it can contain personal data.
* When synchronization status is checked: only the event identifier, signed with the
  connection secret.

Nothing is sent for orders that are not eligible under your invoice policy.

Service terms: https://efakturuj.sk/p/terms
Privacy policy: https://efakturuj.sk/p/privacy
Service documentation: https://docs.efakturuj.sk

eFakturuj is a paid service. You can evaluate the connector without a paid account
using the free sandbox at https://sandbox.efakturuj.sk/get-started

== Frequently Asked Questions ==

= Does this plugin support HPOS? =

The plugin declares HPOS compatibility and accesses orders through WooCommerce CRUD objects. Its order-side eFakturuj invoices card selects the HPOS order screen when custom order tables are active and the legacy shop_order screen otherwise. Both paths were covered by the 27-check WordPress 7.0.1/WooCommerce 10.9.4 release run.

= Are orders sent during checkout or an order edit? =

No remote request blocks those requests. Platform hooks coalesce a short-lived capture action; the worker then serializes the persisted order into an encrypted immutable outbox snapshot. Delivery and signed status polling happen separately through Action Scheduler, with WP-Cron as a fallback.

= Which order changes are captured? =

The plugin listens for new orders, general persisted updates, status changes, saved items, payment completion, refund creation/completion, and refund edits. Multiple hooks raised by one WooCommerce operation are coalesced, while a later material change produces a new event. The backend's order/refund idempotency prevents a replay from creating a second legal document.

= Which orders create invoices automatically? =

By default, only orders with an affirmative invoice request and complete basic billing details are eligible. The plugin can add an invoice-request checkbox to classic and block checkout, or read the flag written by another extension through a configured HPOS-safe order-meta key. The merchant may explicitly switch to every order in an enabled status. Skipped and incomplete requests remain visible without sending incomplete invoice data.

= What is signature version 2? =

Version 1 signs the timestamp and exact JSON body. Version 2 additionally signs `event-id:<canonical-lowercase-UUID>\n` immediately before those exact body bytes, so an attacker cannot substitute the X-EFakturuj-Event-Id header while reusing a valid body signature. Pairing negotiates the version, and upgraded version-1 connections continue to use version 1 until re-paired.

= Can I use an HTTP API URL for local testing? =

Yes, only when WP_DEBUG is enabled. HTTPS is required otherwise because pairing returns the store's signing secret and events contain invoice data.

= Are all partial refunds converted into credit notes automatically? =

No. Itemized product, shipping, fee, and tax allocations must reconcile to the declared refund amount. WooCommerce also permits an amount-only refund; eFakturuj retains that as needs review because WooCommerce supplied no reliable accounting allocation.

== Privacy ==

When connected, this plugin sends eligible invoice data to the configured eFakturuj API, including customer billing/shipping details, company and tax identifiers, email/phone, purchased items, discounts, totals, taxes, payment state, and refund information. Two of those values are free text rather than structured fields: the customer's order note, written by the shopper at checkout, and the reason recorded for each refund. Both are transmitted exactly as entered, so treat them as potentially containing personal data and tell shoppers not to put anything sensitive in the order note.

Canonical payloads waiting for delivery are retained locally only in AES-256-GCM encrypted form. The invoices workspace and logs expose bounded non-content summaries: event/document identifiers, WooCommerce order identifiers/numbers, status, attempts, timestamps, correlation, document links, and sanitized public errors. They do not store or display the canonical customer/order JSON. Deleting the plugin drops the encrypted outbox and local options; normal database backups may retain deleted data according to the site's backup policy.

Review the eFakturuj privacy policy (https://efakturuj.sk/p/privacy), document this processor/data transfer in the store privacy notice, limit administrator access to trusted users, and set an appropriate WordPress backup-retention policy.

== Screenshots ==

1. Pairing a WooCommerce store to an eFakturuj company with a one-time code.
2. The eFakturuj invoices workspace: documents, search, filters, and totals.
3. Background synchronization health with the Action Scheduler status and Run now.
4. The eFakturuj card on a WooCommerce order, with authenticated Open and PDF links.
5. The invoice-request checkbox added to checkout.
6. A partial refund linked to its credit note.

== Changelog ==

= 1.0.4 =

* The outbox's database queries were restructured so that every query string is a
  constant and no query needs a static-analysis suppression. Fixed-width IN() lists and
  bound filter flags replace the strings that were previously assembled before being
  prepared, and the plain row update now uses the wpdb::update() API. The rows matched,
  the ordering, and the row locking are unchanged.
* Checkout input is hardened against non-scalar values: an array or object submitted under
  the invoice-request field is now flattened to an empty string and read as "not
  requested" instead of producing a string-conversion warning.
* Order totals render correctly on the eFakturuj invoices screen. The currency symbol and
  its separator arrive from WooCommerce as HTML entities, which were previously shown as
  literal text (for example "100,00&nbsp;&euro;" instead of "100,00 €").
* The last inline JavaScript is gone. The disconnect confirmation now travels as a data
  attribute and is handled by the enqueued admin script, so the plugin prints no inline
  script and no escaped JavaScript string anywhere.
* Uninstall now cancels the Action Scheduler group the plugin actually schedules into, so
  deleting the plugin leaves no pending background jobs behind.
* Note on translations: the 1.0.0 entry below records that Slovak and Czech translations
  were added, which is historical. Since 1.0.2 no translation catalogs are bundled in the
  plugin; translations are delivered through translate.wordpress.org and only the .pot
  template ships.

= 1.0.3 =

* The outbox's database queries now bind the plugin's table and column identifiers with
  the wpdb::prepare() %i placeholder (WordPress 6.2+) instead of concatenating them into
  the query string, so the queries pass static analysis without inline suppressions.

= 1.0.2 =

* Admin CSS and JavaScript are now enqueued with wp_enqueue_style()/wp_enqueue_script()
  instead of printed inline, scoped to the plugin's own settings/invoices screens and the
  WooCommerce order editor only.
* Bundled .po/.mo translation files are no longer shipped inside the plugin package;
  translations are delivered through translate.wordpress.org. The .pot template is still
  included.
* Removed the unnecessary load_plugin_textdomain() call. WordPress has loaded translations
  automatically since 4.6, well below this plugin's 6.5 minimum.

= 1.0.1 =

* Removed the duplicate Plugin URI header. It was set to the same address as
  the Author URI, which wordpress.org submission validation rejects because
  the two headers must point at different pages. Author URI (https://efakturuj.sk/)
  is kept; no plugin-specific page exists yet.

= 1.0.0 =

* First wordpress.org release, published as efakturuj-for-woocommerce.
* Added Slovak and Czech translations.
* Text domain now matches the plugin slug so translations load from wordpress.org.
* Translations load on init, avoiding the WordPress 6.7 just-in-time notice.
* Documented the eFakturuj service, its terms, and its privacy policy in the readme.
* Raised the minimum WordPress version to 6.5, which is where the Requires Plugins
  header takes effect.

= 0.4.0 =

* Added a dedicated WooCommerce > eFakturuj invoices workspace with document summary, search/filter/pagination, safe activity, scheduler health, and Run now.
* Added an HPOS- and legacy-compatible order-side card with synchronized invoice/credit-note status, authenticated Open/PDF links, and protected manual synchronization.
* Added a durable AES-256-GCM encrypted outbox containing immutable exact JSON snapshots and bounded safe activity summaries.
* Split recovery into exact same-UUID delivery and fresh correlated snapshots using `retries_event_id`.
* Added persisted-order, item, payment, and refund lifecycle capture with duplicate-hook coalescing.
* Added negotiated UUID-bound event signature version 2 while preserving upgraded version-1 connections.
* Hardened store-URL binding, redirects, administrator capabilities/nonces, document links, privacy cleanup, and uninstall lifecycle.
* Expanded the isolated acceptance suite from 20 to 25 live checks and verified the release on WordPress 7.0.1 and WooCommerce 10.9.4.

= 0.3.1 =

* Added visible delivery and signed-status attempt counters to the local activity log.
* Added nonce- and capability-protected fresh Retry now actions for failed and corrected needs-attention events.
* Added direct access to WooCommerce diagnostic logs and explicit automatic retry limits.

= 0.3.0 =

* Added automatic synchronization, signed status polling, invoice-request policy controls, and capped safe activity.

= 0.2.0 =

* Added itemized product, shipping, fee, and per-tax-rate refund allocations for automatic partial credit notes.

= 0.1.0 =

* Initial WooCommerce connector with secure pairing, signed background delivery, HPOS support, manual synchronization, and bounded retries.

== Upgrade Notice ==

= 1.0.0 =
New plugin slug. If the older "efakturuj-woocommerce" plugin is installed, deactivate and delete it BEFORE activating this one — running both at once can fatally crash the site.
