=== Jeet Pickup Handover Code ===
Contributors: jeetsanwal
Tags: woocommerce, local pickup, collection, order management
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 7.4
Requires Plugins: woocommerce
Stable tag: 1.0.1
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

A private, single-use collection code and a clear staff handover receipt for native WooCommerce local pickup.

== Description ==

A pickup counter needs a simple answer: has this order already been handed over? Jeet Pickup Handover Code lets staff explicitly generate a collection code, check the customer's code against the correct order, and record a single handover with the time and staff account.

There are no adverts, signup, premium locks, tracking, remote services or frontend credits. No email is sent. Payment, stock and order status are not changed by collection actions.

= The focused workflow =

* Open an eligible order in the standard WooCommerce HPOS or legacy order editor.
* Save order edits first, check payment and pickup readiness, tick the confirmation, then generate a code.
* A logged-in order owner sees the code only in My account > Orders > View. There is no public order-ID or guest lookup endpoint.
* For a guest order, authorised staff see the code in the order editor and must privately share it after verifying the collector through their normal process. The plugin does not deliver it for them.
* Ask the customer for their current code. Enter it into the same order and choose Record collection.
* A final receipt shows the collection time in the site's timezone and staff account. A collected order cannot be reset or issued another code here.

= Eligible orders =

Only saved Processing or On hold orders containing remaining physical goods are supported. Every shipping line must use WooCommerce's native `local_pickup` or `pickup_location` method. This includes the native Checkout Block pickup method, but does not promise support for third-party pickup methods.

Pending payment, Failed, Cancelled, Refunded and Completed orders are excluded from code generation and redemption. Mixed delivery/pickup orders, orders without shipping, virtual-only orders and orders with missing products are excluded. Recorded item quantity refunds reduce remaining physical goods; money-only refunds do not change quantities. Staff must check the original order before handover.

On hold does not mean paid. The confirmation deliberately requires a staff judgment; the code does not certify payment, identity, stock availability or legal entitlement. Anyone with the current code could present it, so keep it private and follow your normal pickup verification.

= Small and private =

The plugin uses WooCommerce CRUD APIs for order metadata, supports HPOS and classic order editors, and loads its small admin CSS/JavaScript only on WooCommerce order screens. There are no frontend scripts or new checkout fields. The standard account order-details template must render WooCommerce's `woocommerce_order_details_after_order_table` hook; custom account templates that omit it need theme integration.

Codes use ten cryptographically random base-32 characters (50 bits), with ambiguous characters removed. Display hyphens and letter case are ignored during checking. Incorrect codes are compared with a stored SHA-256 digest using a constant-time comparison. Five incorrect attempts on one order block checking for five minutes. The customer code is kept in protected order metadata while active because the owner must be able to read it; it is not encrypted. It is removed from order metadata when collection succeeds.

Staff actions require both WooCommerce management and per-order editing capability, an order-specific nonce and a current collection-record version. Requests are serialized by a short-lived database option lock. A permanent, atomic per-order option receipt prevents double redemption even if a request stops before the WooCommerce metadata copy is saved. In that failure case the authoritative receipt still appears on the order. A stale lock expires after sixty seconds. Five-minute attempt cooldowns and collected receipts also survive ordinary page refreshes.

There is no public REST or unauthenticated AJAX endpoint. Collection metadata is removed from standard WooCommerce REST order responses. WooCommerce REST v2/v3 create, update and batch requests cannot write or rename this plugin's protected order metadata, including by its existing metadata ID. Administrators, database access, backups, exports and third-party code may still read or change protected order metadata; do not treat it as a vault.

= Limits and recovery =

* One code covers the whole order, not individual items, packages, partial pickups, locations or named collectors.
* Staff explicitly generate codes; this does not automatically decide that an order is ready.
* Replacing an uncollected code invalidates the old one. Refresh stale tabs before retrying. Keep customers informed through your normal private process.
* Ineligible statuses or shipping changes prevent code display and redemption. If an uncollected order becomes eligible again, its existing code can be used unless staff replace it. Check current readiness and replace any code that should no longer be valid.
* A collection receipt remains visible even if the order later changes status. Financial refunds and status corrections must be handled separately in WooCommerce.
* No automated email/SMS, QR scanner, guest retrieval page, pickup scheduling, identity verification, cash collection, signature capture or payment changes.
* No automatic updater. Install future self-hosted ZIP releases manually.

= Data and uninstall =

The protected `_jeet_ph_state` order metadata stores the current code/hash, generation time/staff ID, failed-attempt counter/cooldown and the final collection receipt. Atomic receipt options `_jeet_ph_claim_ORDERID` store only the collection timestamp and staff user ID. Temporary `_jeet_ph_lock_ORDERID` options coordinate actions. No IP addresses or extra customer profile fields are recorded.

Deactivation and uninstall retain these records so reinstalling cannot accidentally allow another handover of a collected order. Codes and receipts follow your site's order/database backup and privacy retention practices. When an order is permanently deleted while this plugin is active, its coordination/receipt options are removed too. If you uninstall first, retained options are not automatically removed when another component later deletes orders. Reinstall before permanent order deletion or have an administrator remove only the matching records during an intentional data-retention cleanup. Never remove an active order's atomic collection receipt simply to retry a failed request: refresh and inspect the existing receipt first.

== Installation ==

1. Activate WooCommerce. Meet its own WordPress and PHP requirements as well as this plugin's WordPress 6.5+/PHP 7.4+ minimum.
2. Upload the ZIP via Plugins > Add New > Upload Plugin and activate it.
3. Configure a native WooCommerce Local pickup method and create an eligible pickup order through your normal checkout.
4. Open the saved order, find Pickup handover under the order details, then follow the confirmation/code workflow.
5. Test account visibility and a full pickup on a staging copy before using your store's custom theme or extensions in production.

== Frequently Asked Questions ==

= Is everything free? =
Yes. All included features are free and ad-free, with no paid tier, tracking, signup or external service.

= Is this an OTP sent by email? =
No. It is a single-use collection code displayed to the logged-in order owner in their account. No email or SMS is sent, and the code has no timed expiry. Staff can replace an uncollected code.

= Does it support guest checkout? =
There is deliberately no public guest-code page. Staff can generate a guest order's code, see it privately in the order editor and share it after verifying the collector through their normal process. The plugin cannot automatically deliver guest codes.

= Does collecting an order mark it paid or Completed? =
No. The plugin records collection only. Staff keep payment/status decisions separate and must confirm readiness, particularly on On hold orders.

= Can two staff members collect it at once? =
Only one atomic per-order receipt can be created. Further redemption is rejected, including after a request fails between the receipt claim and its order-metadata copy. Refresh the order to inspect the receipt before retrying.

= Can I reverse a collection or issue a second pickup? =
No. The receipt is final in this plugin. Partial pickups, corrections to physical handover and returns need your normal store workflow; this plugin does not alter financial order records.

= Will it work with every pickup extension? =
No. It supports the native WooCommerce local_pickup and pickup_location shipping IDs only, and excludes mixed delivery/pickup orders.

== Changelog ==

= 1.0.1 =
* Re-read saved collection metadata before confirming generation, replacement, failed-attempt counts or the receipt copy.
* Report silent storage failures while retaining the atomic handover receipt that prevents duplicate collection.

= 1.0.0 =
* First release: staff-generated pickup codes, owner-only account display, guarded single-use redemption and immutable handover receipts.
