# BW 2Checkout Pricing — Specification

**Version:** 0.1.0 | **Last Updated:** 2026-07-13

## Purpose
A reusable WordPress plugin that renders a **dynamic pricing widget** whose prices are pulled
live from a merchant's **2Checkout / Verifone** account, and that hands off to the correct
ConvertPlus cart at a matching price. First configured client: **Copernic**.

## Why server-side (non-negotiable)
The 2Checkout API authenticates every call with an **HMAC hash of the Merchant Code + Secret
Key**. The secret can never touch the browser, so all API calls run in PHP. The browser talks
only to this plugin's own REST endpoint, which proxies to 2Checkout server-side.

## Core mechanics (from live discovery, 2026-07-08)
- Prices live in **pricing options**, not a base price (all eStore configs are "Without base price").
- **USD is entered by hand; other currencies auto-convert (FX)**. Widget reads whatever the
  eStore **default** pricing config holds — so it auto-adopts per-country prices when the merchant
  finishes them (e.g. Copernic's inactive "Copernic 2026" config).
- All prices are **NET**; tax is added at checkout. Widget displays the net/list price.
- The consolidated products carry everything as options:
  - **CDS** = product `3WL7JRHV69`, options `TERM` × `EDITION_2` (Basic/Advanced/Elite) × `users`.
  - **CSS** = product `5O61WPEHDR`, options `Number of users` × `Term`.
  - `users` is a **Scale** option: the widget passes the actual user count; 2CO maps it to the interval.
- Cart hand-off = ConvertPlus link, e.g.
  `https://shop.copernic.com/checkout/buy?merchant=COPERNIC&tpl=default&prod=3WL7JRHV69&qty=1&opt=TERM:2Y,EDITION_2:advanced,users:5`

## Configuration model (what makes it reusable)
Stored in a single option `bw_2checkout_pricing_settings`. Copernic-specific data is config, not code:
- **Credentials:** Merchant Code + Secret Key. Secret prefers a `wp-config` constant
  (`BW_2CHECKOUT_PRICING_SECRET_KEY`), falls back to the (password) setting. Never rendered back.
- **Endpoints:** API base (`https://api.2checkout.com/rest/6.0/`), checkout base
  (`https://secure.2checkout.com/checkout/buy` or a branded domain like `shop.copernic.com`), test mode.
- **Currencies:** which subset of the merchant's currencies to expose in the selector; default currency.
- **Products config:** a list of "widget products," each mapping UI selectors → a 2CO product code
  + option-group codes + value codes. (e.g. CDS → `3WL7JRHV69` with groups TERM/EDITION_2/users.)
- **Cache TTL** for pricing-config fetches (FX drifts, so bounded).

## Requirements

### Functional
- R1: Authenticate to the 2Checkout REST API 6.0 server-side (HMAC). **[client — this pass]**
- R2: Fetch a product's pricing configurations and cache them. **[client — this pass]**
- R3: Admin settings screen for the configuration model above. **[next]**
- R4: Public REST route returning the price for a selection (product + options + currency). **[next]**
- R5: Front-end widget (shortcode) — selectors → live price → ConvertPlus buy button. **[next, rough]**
- R6: Currency resolution from geolocation (Cloudflare `CF-IPCountry`) + manual override. **[Step 3]**
- R7: Polished, configurable widget + Gutenberg block. **[Step 3]**

### Non-functional
- WordPress 6.0+, PHP 7.4+. Translatable. `wp_remote_*` only, TLS on. Secret never in source/logs/REST.

## Open items to confirm in sandbox (Step 1 residue)
1. **Per-seat vs flat** tier price, and how the user count reaches the cart (native multi-unit is off).
2. Exact REST JSON shape of `pricingconfigurations` (to finalize price extraction) and the getPrice endpoint.
3. Full option value-code set (TERM/EDITION_2 values; CSS group + value codes) — via `getPricingConfigurations`.
4. Whether the ConvertPlus link needs an approved/return URL param.

## Acceptance Criteria
| Requirement | Acceptance | Status |
|---|---|---|
| R1 | With sandbox creds, an authenticated GET returns 200 (not 401). | ☐ |
| R2 | `get_pricing_configurations('3WL7JRHV69')` returns the config; second call hits cache. | ☐ |
| R3 | Merchant code, secret, endpoints, currencies, and a product mapping can be saved. | ☐ |
| R4 | `/wp-json/bw-2checkout-pricing/v1/price` returns a correct price for a selection. | ☐ |
| R5 | Changing edition/term/users updates the shown price and the buy link. | ☐ |
