=== LivePay Payments for WooCommerce ===
Requires at least: 6.5
Requires PHP: 7.4
Requires Plugins: woocommerce
Stable tag: 1.0.7
License: GPL-3.0-or-later
License URI: https://www.gnu.org/licenses/gpl-3.0.html

Accept mobile money and hosted USD card payments through LivePay.

== Installation ==
1. Upload livepay-woocommerce.zip through Plugins > Add New > Upload Plugin.
2. Activate it alongside WooCommerce.
3. Open WooCommerce > Settings > Payments > LivePay > Manage.
4. Enter the verified LivePay business account number, API key and webhook secret.
5. Copy the exact Webhook URL displayed there into your LivePay dashboard and use
   the matching webhook secret. Keep the URL unchanged, including any query string.
6. Enable mobile money currencies for your active LivePay wallets and/or cards.
7. Enable the gateway and save. The shop and callback URL must use HTTPS.
8. Click Check saved LivePay credentials on the settings screen. This only reads
   account access and does not collect money. It reports account, API-key,
   approval, API-access and IP-allowlist problems without printing credentials.
9. HTTP 401 means LivePay rejected the active API key. Replace it with the active
   key from the matching LivePay business account. If LPW_API_KEY is defined in
   wp-config.php, it overrides the saved field; the settings page now says so.
   A rejected key prevents both mobile prompts and hosted card checkout.
10. Keep Use IPv4 for LivePay API requests enabled when your IPv4 address is
    allowlisted. This applies through WordPress's cURL transport only to LivePay
    requests. PHP cURL must be enabled. The plugin does not bind a source IP or
    bypass a configured WordPress proxy. If 403 continues, the order note shows
    the provider-observed request IP when the provider supplies a valid address.

== Currency settings ==
Mobile money collects the order currency without conversion. Supported choices:
UGX (MTN/Airtel), KES (Safaricom/Airtel), GHS (AirtelTigo/MTN/Vodafone),
XAF (MTN/Orange). USD mobile money is not offered. This plugin does not switch
your store currency; set it in WooCommerce or your multicurrency extension.
For UGX, LivePay determines the network from the phone number; no network field
is sent. Other currencies still require an explicit network selection.

Cards always collect USD, between USD 1.00 and USD 5,000.00. USD orders use 1:1.
For other supported currencies you may enter a merchant-maintained rate expressed
as LOCAL UNITS PER USD 1. For example, a hypothetical rate of 3700 UGX per USD 1
converts a UGX 37000 order into a USD 10.00 card charge. This example is not a live
exchange rate. Zero disables cards for that currency. Conversion rates are not
fetched automatically. Customers see the charge and conversion before submitting.
The original WooCommerce order total and currency remain unchanged. The rate and
USD charge are saved with the payment attempt for later reconciliation.

== Checkout and confirmation ==
Continue with LivePay opens a processing sheet while checkout submits. After an
accepted mobile request, the order receipt asks the customer to complete payment
on their phone and keeps checking until confirmation or a terminal outcome.
Customers can hide and reopen the sheet without stopping checks while the page
is open. Connection interruptions keep the payment unconfirmed, never failed.
The same status appears in order details, order emails, My Account orders and
the admin order screen. Customers can resume checks by opening their order.

Supports classic checkout, Checkout Blocks, order-pay, and WooCommerce HPOS via
the order CRUD APIs. Customers approve mobile requests on their phones or follow
a redirect to LivePay's hosted card page. Card numbers, CVVs and mobile money PINs
are never requested by the plugin.
Choose Credit / debit card, then Continue with LivePay. Enter the card number,
expiry and security code on the hosted page. For a local-currency shop, save a
valid conversion rate before cards become available. Unavailable cards are shown
as a disabled option when mobile money is available, with an admin setup warning.

Version 1.0.1 also normalizes whitespace and an accidentally pasted Bearer prefix
in API keys. Failed collection attempts record a safe reason and HTTP code in the
order notes and the WooCommerce livepay log. No raw provider responses, customer
data, API keys or hosted card secrets are logged. Connection checks cannot prove
that collection is enabled for a particular amount, wallet or card account.

Orders stay on hold until the authenticated transaction status API confirms the
expected reference, internal reference, amount and currency. Signed webhooks only
queue a check; their unsigned amount/currency fields cannot settle an order. The
browser return URL never marks an order paid. Confirmed payments call WooCommerce's
payment_complete method to trigger stock, fulfilment and email handling.

Repeated checkout submissions reuse an active attempt. Timeouts and ambiguous
responses are reconciled using the saved reference rather than making another
collection request. A new attempt is allowed only after a definite rejection or
verified failure. All attempts are retained (maximum eight per order). A payment
received for a cancelled/changed/already-paid order is flagged for merchant review.

== Scheduled checks and troubleshooting ==
WP-Cron must run reliably. On a low-traffic shop configure a real scheduled task
to invoke WordPress cron through your host. Webhook processing is asynchronous.
The receipt and account order pages poll the protected local status endpoint.
While open, that endpoint can verify with LivePay at most once per reference per
30 seconds, subject to the same shared budget and locks as background checks.
Polling only reads the original transaction; it never creates another collection.

Checks use backoff and a per-site budget of 45 authenticated status requests in a
rolling 15-minute window, below the documented endpoint limit of 50. Multiple
sites sharing one LivePay account share provider limits, not this local counter.
At high volume ask LivePay about increased limits before deployment.

Automatic checks stop after 24 hours while unresolved orders remain on hold.
Signed webhooks and the order action "Check LivePay payment" can trigger another
check. Review order notes and the LivePay transaction dashboard. A payment request
that timed out may already exist: do not send another charge or manually mark the
order paid without reconciliation. If a card creation response loses its checkout
URL, reconcile the attempt with LivePay before taking a replacement payment.

Failed signatures: match the exact dashboard URL/secret and synchronize the server
clock. Timestamp tolerance is five minutes. API failures: check account approval,
API access, active currency wallets, IP allowlisting and credentials in LivePay.
Unknown statuses remain unconfirmed. The documented Success value completes an
order; Failed/Failure/Cancelled/Canceled/Expired are treated as unsuccessful only
after matching an authenticated status response. Review provider status semantics
during account-specific acceptance testing.

GET_LOCK/RELEASE_LOCK support in MySQL/MariaDB is required to serialize attempts.
If unavailable, initiation fails closed instead of risking duplicate requests.

== Privacy and external service ==
The plugin sends amount, currency, reference and an order description to
https://livepay.me/api. Mobile collections also send the entered phone/network.
Card sessions also send billing name, billing email and the return URL. LivePay
processes these under your merchant agreement. See https://docs.livepay.me/.

Credentials are saved in WordPress gateway settings (not returned to browsers or
written to logs). You can instead set LPW_API_KEY and LPW_WEBHOOK_SECRET constants
in wp-config.php. Order metadata retains charge amounts, rates and references;
pending card checkout URLs contain a session secret and are cleared on completion.
Restrict administrator/database access and protect backups as usual.

== Scope and testing ==
The public API documentation describes live endpoints, including its "sandbox".
There is no non-charging test-mode switch in this plugin. This package was checked
with offline API-response fixtures; no live payment or full WordPress checkout was
performed during development. Validate in your staging store and arrange controlled
account-specific acceptance testing before enabling it for customers.

Automatic refunds, payouts, saved payment methods, subscription renewals and
currency switching are not included. Arrange refunds through LivePay and use
WooCommerce's manual refund recording only after the provider refund is confirmed.
Deactivating the plugin stops its checks; reconcile pending payments first. Order
payment records and settings are retained on removal for accounting purposes.

== Changelog ==
= 1.0.7 =
Reconcile authenticated UGX mobile status amounts that include the reported charge.
Require exact requested principal plus charge, an already known matching internal ID,
and all existing reference, currency, success and unchanged-order checks.
Other currencies and card payments retain exact amount matching. Preserve sanitized
amount/status diagnostics and record the reconciliation basis on the order.

= 1.0.6 =
Add private sanitized amount, charge and status diagnostics for reconciliation.

= 1.0.5 =
Keep checking the original transaction after an incomplete or mismatched status
response instead of showing an immediate terminal review screen. Resume checks
for existing attempts with an earlier mismatch diagnostic. Clear that diagnostic
when a complete matching status arrives. Record safe field-specific reasons in
order notes without storing response values. Settlement checks remain strict;
changed/closed/duplicate orders and the 24-hour reconciliation limit are retained.

= 1.0.4 =
Add an accessible processing/payment progress sheet for checkout and receipts.
Show phone approval instructions and update automatically after verified payment.
Keep pending checks running through temporary browser connection failures.
Verify active customer status requests without depending entirely on WP-Cron.
Display payment status in customer orders, order emails and the admin order view.
Keep scoped IPv4 transport and the UGX payload from 1.0.3.

= 1.0.3 =
Use IPv4 by default for LivePay cURL requests to match IP-allowlisted hosting.
Scope the setting to LivePay requests; retain TLS verification and the WordPress
HTTP API. Match the working test's 15-second connection and 60-second response
timeouts. Allow LivePay to detect the UGX mobile network, while requiring it for
other currencies. Show the validated request IP when LivePay rejects its allowlist.

= 1.0.2 =
Preserve payment selection and phone/network across classic checkout updates.
Normalize pasted Authorization prefixes. Show rejected-key notices tied to the
active configuration and make wp-config.php credential overrides visible.

= 1.0.1 =
Add a read-only saved-credentials check, actionable provider rejection messages,
and clearer hosted card checkout and currency-conversion guidance.

= 1.0.0 =
Initial release.
