# Managed Payments — setup and review guide

This guide describes the Managed Payments integration prepared for the free plugin's 1.3.0 release. It is not evidence of publication or live account eligibility. Review the scope and complete store-specific sandbox testing before enabling production payments.

The implementation adds a separate Stripe Managed Payments path to the existing Hosted Checkout, Embedded payment page and Embedded popup experiences. It is disabled by default. Standard Stripe Checkout remains a separate payment model with its existing features.

## Supported first-release scope

- One-time purchases of eligible digital products, sold as WooCommerce simple products or variations. Each purchased product or variation must be virtual and explicitly configured for Managed Payments.
- Fixed quantities after Stripe Checkout opens. Customers change quantities in the WooCommerce cart before opening checkout.
- Saved WooCommerce coupon discounts allocated to product prices before the request is sent to Stripe.
- One payment model for the whole cart. The plugin does not split a cart into Managed Payments and standard payments.

Subscriptions, physical products, shipping and additional fee lines are outside this Managed Payments scope. Marking a product virtual does not turn an otherwise ineligible service into an eligible digital product. Stripe determines account and transaction eligibility; merchant configuration and a supported tax category are not approval. Review [Stripe's eligibility requirements](https://docs.stripe.com/payments/managed-payments/eligibility).

The ordinary checkout settings for adjustable quantities, shipping and payment-method selection do not override Managed Payments. This initial path also does not forward the plugin's standard Checkout branding, language, optional phone-number or terms-checkbox settings; Stripe's applicable defaults control those aspects. Stripe controls its available payment methods, final tax calculation and currency conversion. Standard Checkout's feature list must not be read as a promise that every customization also applies to Managed Payments.

## Configure a sandbox for review

1. In Stripe, select the intended sandbox/test environment and activate Managed Payments there. Review any onboarding or terms yourself. Sandbox access does not establish live eligibility.
2. In the plugin's **Stripe Settings**, save the matching sandbox secret/restricted key and publishable key. Embedded checkout requires the publishable key from the same account and environment. Never send keys in a support message or screenshot.
3. Configure the plugin's webhook endpoint and its signing secret. Use the endpoint supplied by the plugin, not the WordPress admin/settings page URL. Subscribe to `checkout.session.completed`, `checkout.session.async_payment_succeeded` and `checkout.session.async_payment_failed`. For refund-event synchronization, also subscribe to `refund.created`, `refund.updated` and `refund.failed`.
4. Choose the checkout experience. For embedded modes, use the plugin's automatic payment page or a published page containing `[sahcfwc_checkout]`. Exclude the payment page and its private query-string URLs from caching. Production embedded checkout requires HTTPS.
5. Open **Checkout Settings → Managed Payments**. Review the eligibility information, choose **Enabled**, and make the merchant eligibility acknowledgement only after checking the requirements.
6. Review **Mixed or ineligible carts** and save the settings. The default is to block checkout and ask the customer to adjust the cart. The optional standard fallback requires a separate explicit acknowledgement of merchant and tax responsibilities.
7. Configure the intended products as described below, then review every relevant payment flow in the sandbox before any live use.

A saved webhook secret alone does not prove delivery works. Use **Setup & health**, complete a sandbox payment and inspect its Stripe delivery result and WooCommerce order. Receipt of a webhook is not itself proof of successful payment. A locally simulated signed event is a code-path test, not evidence that Stripe delivered an event to the site.

## Configure products and variations

For a simple product, open **Products → Edit → Product data**:

1. Confirm that the actual product qualifies as an eligible digital product and mark it **Virtual** where appropriate.
2. Open **Managed Payments** and select **Use Managed Payments**.
3. Choose the **Digital product tax category** that accurately describes the product.
4. Read and make the product eligibility confirmation, then save the product.
5. Check the saved configuration status. “Configured” describes the local fields, not Stripe approval.

For a variable product, the parent's Managed Payments fields form a classification template. Each purchased variation must still be virtual. In its variation settings, choose one of these modes:

- **Inherit product settings:** use the parent's complete saved opt-in, category and eligibility confirmation. The parent must opt in.
- **Use this variation's own settings:** supply a complete category and eligibility confirmation for that variation; missing fields are not borrowed from the parent.
- **Do not use Managed Payments:** exclude that variation from the Managed Payments route.

Saving a product does not submit it for Stripe approval or create a permanent WooCommerce-to-Stripe catalog synchronization. The checkout request supplies the saved product identity and eligible category when constructing Stripe line items. Do not assume that accepting an API request certifies the product's continuing eligibility.

## Whole-cart routing and recovery

When Managed Payments is enabled, every item and charge in a new cart must fit the supported scope. An unconfigured product, ineligible variation, physical item, fee or shipping charge makes the cart unsuitable for this path.

The default policy blocks checkout. If the merchant instead selects **Use standard Stripe Checkout for the entire cart** and makes the separate acknowledgement, the entire transaction uses standard Checkout. None of its items uses Managed Payments, and the merchant's normal tax responsibilities and WooCommerce tax/shipping configuration apply. There is no automatic split payment or fallback caused by a Stripe rejection.

An existing payable or uncertain attempt keeps its original identity and payment model while being recovered. Changing settings does not replace a possibly payable Session. A new attempt after verified expiry rechecks the applicable policy. If an outcome cannot be verified, inspect the existing payment before trying again; do not create a second payment manually to bypass the warning.

## Discounts, tax and currency

Apply coupons in the WooCommerce cart before opening Stripe Checkout. The plugin uses each saved order line's discounted amount; it does not offer a new coupon-entry or live recalculation step inside Managed Payments. Where a discounted line cannot divide evenly by quantity, its Stripe representation can contain more than one price line while preserving the agreed quantity and total.

Usage-limited coupons need valid reservations. A coupon limited per customer also needs a known customer identity before checkout is created. A guest with no saved billing email cannot safely reserve that per-customer allowance and may need to sign in before using the coupon. Do not describe this implementation as supporting every coupon extension or unidentified guest coupon scenario.

Saved product totals, coupon allocations and native WooCommerce tax totals must reconcile exactly at the currency's minor-unit precision. Inconsistent totals or rounding remainders stop checkout instead of silently shifting a cent into another line. Review the store's tax rounding or extension-generated order values if this occurs.

For tax-exclusive pricing, Stripe calculates final customer tax on the discounted product amounts. For tax-inclusive pricing, the inclusive prices are preserved while Stripe determines their final net/tax split. Verified Stripe amounts are recorded as native WooCommerce product and tax amounts, not as an invented fee. Consequently, the final tax and net amounts can differ from WooCommerce's preliminary location-based estimate.

WooCommerce's pre-discount subtotal is an order comparison amount, not a Stripe tax invoice. The verified ledger records customer net, tax and gross in the order's integration currency. If Stripe supplies a different customer presentment currency/amount, that information is recorded separately; it must not replace the order currency, be added to its total, or be treated as settlement proceeds.

## Order accounting and refunds

The order's **Managed Payments accounting** panel shows verified customer amounts when its saved payment ledger and native order values agree. Normal administrator item editing/recalculation is disabled for bound Managed Payments orders. Arbitrary third-party/programmatic modifications are not prevented by that UI control; mismatches require review before further automatic processing.

Use the existing **Stripe refunds** panel for full or partial amount-only refunds on eligible verified orders. Refresh Stripe status first, then review the amount and original payment currency. Pending, unknown or conflicting outcomes require reconciliation rather than another money-moving request. Stripe/Link may also issue a refund independently; refresh status to reconcile it with WooCommerce.

Amount-only refunds do not restock products, revoke downloads or allocate reversals to product, fee, shipping or tax lines. The original product/tax accounting remains unchanged, including after a full refund. Reconcile the refund documents and tax treatment separately; do not invent proportional tax or withholding reversals.

Customer tax is not evidence of tax withheld/remitted by Stripe or of the merchant's tax liability. Managed Payments has tax-coverage exceptions, and the merchant remains responsible for transactions outside that coverage. Consult [Stripe's tax coverage guidance](https://docs.stripe.com/payments/managed-payments/tax-compliance) and an appropriate tax adviser for the store's circumstances.

Stripe/Link provides transaction receipts, tax invoices where applicable and refund documents. WooCommerce confirmations remain order and fulfilment records. Review third-party invoicing/accounting integrations to avoid duplicate tax invoices. Reconcile Stripe's `withheld_tax`, `fee_net_of_withheld_tax` and settlement reports independently; the plugin does not claim that its customer tax ledger is a net-settlement or remittance report. See [how Managed Payments works](https://docs.stripe.com/payments/managed-payments/how-it-works).

## Review checklist and validation status

Release validation is recorded separately in the workspace QA evidence. Do not infer a pass from this document, the presence of implementation code, an offline test, or an earlier proof of concept. Each store should check:

- Default-off behavior and ordinary Checkout with Managed Payments disabled.
- Global settings, product opt-in, category/attestation validation and variation inheritance/override/disable.
- Eligible, mixed and unsupported carts under both block and explicitly acknowledged whole-cart fallback policies.
- Hosted, Embedded page and Embedded popup payments without adding a second WooCommerce checkout form.
- Coupons, uneven price division, inclusive/exclusive tax and final WooCommerce/Stripe amount agreement.
- Correct integration/presentment currency handling when Stripe offers localized pricing.
- Return handling, signed webhook processing, duplicates, cancellation, expiry and uncertain-response recovery without another payable Session.
- Stock/coupon reservations, delayed-payment review paths and appropriate fulfilment status.
- Partial/full/external refunds, pending outcomes, duplicate protection and unchanged item/tax/stock allocations.
- Customer/admin disclosures and compatibility with the store's invoicing/reporting extensions.

Automated tests live under `tests/free-managed-payments-*-regression.php` and the matching UI `.cjs` files. WordPress transactional tests require the deny-all Stripe/HTTP/mail bootstrap `tests/managed-payments-offline-bootstrap.php` via WP-CLI `--require`. Their fixture/settings changes are rolled back. `SAHCFWC_TEST_STORAGE=cpt` or `hpos` selects the suite's process-local storage mode without changing saved storage options. These tests do not certify real Stripe delivery or live account eligibility.

The guarded real-sandbox harness and redacted run evidence are separate workspace QA artifacts, not distributed plugin code. Provider testing must use the reviewed local sandbox scope and bounded fixtures; private Session URLs, client secrets, credentials and full provider responses must stay out of Git, reports and screenshots.

Release publication requires the owner's authorization and the final release checks. This document does not itself authorize publication, production payments or premium testing.
