=== CoinAvio for WooCommerce ===
Contributors: coinavio
Tags: woocommerce, crypto, payments, checkout, coinavio
Requires at least: 5.8
Requires PHP: 7.4
Requires Plugins: woocommerce
Stable tag: 1.1.5
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Accept crypto through CoinAvio hosted checkout. Price orders in USD and confirm payments with signed webhooks.

== Description ==

CoinAvio creates a hosted invoice for each WooCommerce order, using its USD total and WooCommerce order ID. Buyers select a supported coin and network on CoinAvio checkout. A verified invoice.paid or invoice.overpaid webhook, or authenticated server-side reconciliation, completes an eligible order.

* PHP 7.4 or newer and WooCommerce 6.0 or newer.
* USD store currency only; no automatic currency conversion.
* Classic shortcode checkout only. The WooCommerce Checkout block is not supported.
* Uses WooCommerce order CRUD APIs for updates. Bounded recovery selects eligible IDs from the active legacy or High-Performance Order Storage tables.
* Server-side API key and webhook secret settings.
* Raw-body HMAC-SHA256 signature checks, a five-minute timestamp tolerance, and constant-time comparison.
* Checks the stored invoice ID, WooCommerce order reference, persisted store/site identity, USD total, invoice status and test/live mode before updating an order.
* Serializes order updates and stores processed event IDs in order metadata. Duplicate deliveries do not repeat payment completion, and delayed underpayment events do not downgrade paid orders.
* Underpaid invoices place unpaid orders on hold. Expired invoices add a note.
* Underpayment tolerance defaults to 0% (exact amount required). Merchants can choose percentages with up to two decimals in CoinAvio Settings; accepted shortfalls are recorded in order notes even if the store already completed the order manually. Duplicate events do not repeat the note.
* Invoice ID and checkout URL appear in admin order notes. Unpaid orders show a Pay link on the thank-you page.

This plugin connects to your configured CoinAvio API over HTTPS. Invoice requests include the USD total, order ID, shop name, return URLs and WooCommerce order/store/site metadata. API keys are sent only to the configured API origin. CoinAvio hosts checkout and sends signed events to the shop. See https://coinavio.com/docs and https://coinavio.com/policies for service details.

== Installation ==

1. Download coinavio-woocommerce.zip from https://coinavio.com/downloads/coinavio-woocommerce.zip.
2. In WordPress, open Plugins > Add New > Upload Plugin. Upload the zip, install it and activate it.
3. Activate WooCommerce 6.0+ and set the store currency to USD. Use the classic checkout shortcode [woocommerce_checkout].
4. Add and verify your shop domain in CoinAvio Dashboard > Sites. Both the order-received URL and checkout cancellation URL must use a verified domain.
5. Create a Test API key in CoinAvio Dashboard > Developers.
6. Set the webhook endpoint in CoinAvio Developers to https://YOUR-SHOP/?wc-api=coinavio. Copy the signing secret.
7. Open WooCommerce > Settings > Payments > CoinAvio. Enable the gateway, set its title/description, and enter the full API key and webhook secret. The API base URL defaults to https://api.coinavio.com (without /v1).
8. Place a test order, choose a coin on hosted checkout, then press Simulate payment (test mode). Confirm the webhook marks the WooCommerce order paid.
9. Replace the Test key with a Live key when ready for real payments. Keep the webhook secret in sync when rotating it in CoinAvio.

== Frequently Asked Questions ==

= How does test mode work? =

Use a ck_test_ key. The invoice is marked testMode and hosted checkout provides a Simulate payment button after coin selection. It settles through the normal pipeline on the recovery schedule (normally within about 120 seconds on live networks; scheduler/provider delays can take longer) and webhooks still fire. No crypto moves. Test orders can trigger WooCommerce emails, stock changes and fulfilment automations, so use a staging store or clearly identified test orders.

= Why is the payment method missing? =

Enable it, supply both the API key and webhook secret, use an HTTPS API URL, set USD as the store currency, and use the classic shortcode checkout. The Checkout block requires a separate integration.

= Why does invoice creation report an unverified domain? =

Verify the domain of your order-received and checkout URLs in CoinAvio Sites. The API rejects unverified successUrl/cancelUrl domains with DOMAIN_NOT_VERIFIED. Verification instructions also appear in the admin order note.

= Can several stores share one merchant? =

Yes. New keys use wc-{site SHA-256}-{order ID}-{attempt}; the full shop URL includes any site path. Invoice metadata carries wc_store_id and wc_store_url, which must match the saved attempt before adoption or fulfillment. Different API keys alone do not separate the merchant invoice namespace. Preserve saved mappings when moving a store.

= Can a customer return URL mark an order paid? =

No. Only a signed paid/overpaid event or an authenticated server-side GET matching the stored order, invoice, amount and mode can complete an eligible order. Underpaid orders need manual review. The thank-you Pay link reopens the stored invoice and cannot renew an expired invoice.

= What if the webhook fails? =

WP-Cron starts authenticated reconciliation every 15 minutes for pending/on-hold CoinAvio orders created in the last seven days, including creations with a lost response. Cancelled orders in that window with unresolved creation attempts also enter recovery. Recovery hooks load on cron/admin requests even when no checkout gateway has been constructed. Each batch selects at most 20 eligible orders. Its ten-second traversal budget starts after selection and always permits the first durably checkpointed order attempt, even after slow successful database work. Each API call has a five-second timeout; a started order may finish after the traversal budget. A continuation runs no sooner than one minute later. Saved order and invoice cursors survive restarts and rotate past unavailable records; new orders join the next cycle. Three consecutive runs without a checkpointed order attempt show a warning to WooCommerce administrators. Progress or a confirmed empty selection clears the warning; failed selection does not. A batch already owned by another worker does not count as a skipped run. The warning measures traversal progress, not successful payment recovery.

WP-Cron needs traffic or a real cron trigger; these intervals are scheduling targets, not a delivery guarantee. For an older order, select "Reconcile CoinAvio payment" in its WooCommerce order actions. This action remains available for cancelled orders and lost creation responses without a saved invoice ID. Each run first checks any unresolved creation key, then one saved invoice attempt; repeat to review earlier attempts. An unavailable creation lookup does not prevent checking saved invoices. Exhausted CoinAvio deliveries also appear in Developers with Redeliver and an email notice. Keep /?wc-api=coinavio publicly accessible over HTTPS and exclude it from caching or authentication. Verify the secret matches CoinAvio and synchronize the server clock. A temporary processing or order-lock failure returns an error so delivery can be retried. Duplicate signed events return 200. Database MySQL/MariaDB advisory locks must be available for order updates.

= What happens if I edit or cancel an order? =

WooCommerce's hold-stock timer protects verified open, grace, finality-pending and receipt-bearing invoices. Orders with no submitted creation can cancel normally. Lost creation responses are recovered by GET using the saved attempt key; the validated snapshot and cursor are saved with its binding. Histories over 20 invoices rotate across checks. Exhausting the ten-second budget keeps the order pending and resumes on the next stock check, even after an old uncertainty deadline. Unvisited retained invoices and missing credentials for retained invoices also keep the order pending. The 24-hour uncertainty fallback applies only after retained-invoice checks actually run and every retained invoice has a recorded decision, or to an unresolved creation with no retained invoice IDs. It cancels with a review note and a scheduled recovery retry; open invoices and previously verified receipts remain protected. Each check runs under the checkout lock with at most one pending-key GET plus 20 retained-invoice GETs, all with five-second HTTP timeouts; a busy lock retries on the next WooCommerce stock check. Later payments on cancelled orders get review notes without reopening or automatic refunds. Both manual and automatic cancellation retain pending creation keys until authenticated recovery succeeds.

Changing a total does not let an older amount fulfill the edited order. A receipt-free NEW invoice is cancelled and checked before a new payment attempt is opened. Existing money or an uncertain cancellation requires review first. A timed-out invoice creation retains the same attempt key; do not change its total again until it is resolved. If you already changed it, restore the previous total and retry the same attempt before making another edit.

Store cancellation checks retained invoice attempts with the owning API key, recovering initial or replacement creation keys before deciding whether their invoices can be cancelled. NEW invoices are cancelled; a detected receipt remains available for merchant review. Transport errors, rate limits, server errors, missing credentials, 404 responses and order-lock contention retain a scheduled retry; a 404 cannot prove an earlier creation request will never complete. Each cancellation run checks at most one pending key and one saved invoice, with at most four five-second HTTP requests including cancellation and receipt-race verification. An unavailable invoice cannot block the other attempts. Keep the corresponding Test/Live key available when recovering invoices issued in that mode. Cancelled, refunded, completed and processing orders retain their store state; received funds produce review notes, never automatic reopening or refunds.

= Does the plugin support refunds or subscriptions? =

This version supports single product payments. It does not implement automatic crypto refunds, recurring subscriptions, currency conversion or the Checkout block.

== Changelog ==

= 1.1.5 =
* Keep stock when slow successful creation recovery exhausts the check budget; persist recovered evidence and resume retained invoices without treating skipped checks as uncertainty.
* Start automatic reconciliation's traversal budget after selection, guarantee its first durable order attempt, and retain the 20-order and five-second request bounds.
* Persist consecutive zero-progress runs and warn WooCommerce administrators after three; clear the warning on progress or confirmed idle work.

= 1.1.4 =
* Namespace new attempt keys by the complete shop URL, including site paths, and persist wc_store_id/wc_store_url binding for POST adoption, authenticated GET recovery and webhook fulfillment. Several stores under one merchant cannot fulfill against one invoice.
* Preserve established legacy invoice mappings. An unbound legacy attempt requires exact local return and checkout URLs before adoption; otherwise retain it for operator review.

= 1.1.3 =
* Recover lost initial/replacement creation responses through cancellation, manual/cron reconciliation, checkout retries and unbound webhook delivery, using authenticated GET under the order lock.
* Keep pending-only cancelled orders reachable and retain cancellation retries through API/configuration/lock failures; preserve historical bindings, terminal store state and deduplicated receipt-review notes.

= 1.1.2 =
* Release never-submitted orders, recover pending attempts by GET and rotate histories larger than 20 invoices.
* Bound unknown payment state to a persisted 24-hour stock hold, then cancel with an explicit review note; preserve verified open invoices and receipts.

= 1.1.1 =
* Protect open/grace/finality-pending CoinAvio invoices from WooCommerce hold-stock automatic cancellation. Verify invoice identity, mode and all retained attempts; retain pending orders when state is uncertain or funds exist.
* Serialize the verified terminal-order cancellation with checkout, preventing a new payment attempt racing the stock timer.

= 1.1.0 =
* Reconcile pending/on-hold CoinAvio orders from the last seven days every 15 minutes using authenticated GET and the webhook binding checks; previous paid invoices complete eligible orders once.
* Register recovery and cancellation callbacks at plugin bootstrap; persist bounded oldest-first order/invoice progress across restarts for both order storage modes. Add explicit order-action recovery beyond seven days.
* Safely retire receipt-free invoices after an order-total edit, preserve ambiguous creation attempt keys, and reject funded replacements or mismatched creation responses.
* Preserve cancelled/refunded/completed/processing states and add deduplicated informational receipt notes; only pending/on-hold/failed orders may move. Delayed network-discovery reviews also produce a note when no receipt exists, without authorizing fulfillment.
* Synchronize store cancellation across retained attempts; retry transport, HTTP 429/408/5xx and lock failures, and verify receipt-race review before ending recovery.
* Refunds remain manual: agree the destination network, address and memo with the buyer; never automatically refund to an exchange sender.

= 1.0.4 =
* Record accepted-shortfall notes on orders already paid or manually completed, once per event ID, without repeating payment completion.

= 1.0.3 =
* Show expected, received and missing amounts on underpaid orders and keep them on hold for review.
* Note accepted shortfalls on paid invoices; preserve signature, order/mode/amount checks and event deduplication.

= 1.0.2 =
* Retry expired/cancelled invoices and changed totals with a new payment attempt.
* Preserve invoice mappings and route earlier-attempt events to manual review.

= 1.0.1 =
* Brand name shown as CoinAvio. No functional changes; settings are kept.

= 1.0.0 =
* Initial hosted-checkout gateway, signed webhooks, test-key support and downloadable zip.

== Upgrade Notice ==

= 1.1.5 =
Preserve all settings and order metadata. Slow stock checks now keep orders pending for the next check. Restore missing API credentials before retained invoices can authorize automatic cancellation. Investigate the new recovery warning if three consecutive runs make no order progress; manual reconciliation remains available.

= 1.1.4 =
Preserve all order metadata. New attempts have store-qualified keys and persisted store binding. Established legacy mappings still reconcile after a domain move; unbound old attempts without matching local return URLs require manual review instead of adoption.

= 1.1.3 =
Keep existing order metadata and the matching Test/Live API key. Review cancelled orders with uncertain creations using "Reconcile CoinAvio payment"; recent pending-only cancellations also enter cron recovery. Recovery never reopens a terminal order or refunds funds automatically.

= 1.1.2 =
Keep order metadata. Unknown payment state now holds stock for at most 24 hours after the first uncertain stock check. Review cancellation notes and later receipts before fulfillment or manual refunds.

= 1.1.1 =
Keep existing settings and order metadata. The stock timer now waits for verified invoice closure; investigate unavailable or receipt-bearing invoices through reconciliation instead of treating a local timer as payment expiry.

= 1.1.0 =
Keep existing order metadata and credentials. Configure a reliable WP-Cron trigger and reconcile an unpaid test order before enabling live payments. Review older orders with the new order action or redeliver their CoinAvio webhook. Real WordPress/storefront acceptance is still required for your deployment.

= 1.0.0 =
Use a Test key and verify the full payment/webhook flow before enabling live payments.
