Files
WooNooW/docs/SUBSCRIPTION_GATEWAY_CAPABILITIES.md

167 lines
6.9 KiB
Markdown

# Subscription Gateway Capabilities
> How WooNooW decides which payment gateways can auto-debit subscription
> renewals, and how merchants can override that decision.
## Why this exists
Before this system, a subscription renewal would attempt to call
`$gateway->process_subscription_renewal_payment($order, $subscription)` if
the gateway *happened* to implement that method. That had three problems:
1. **Capability was invisible** — the merchant had no way to see, declare,
or override which gateways supported subscription auto-renew.
2. **The default was unsafe** — a gateway without the method silently fell
through to manual payment. The system "worked," but the merchant
believed auto-debit was happening and customers were surprised when
they had to log in and pay manually.
3. **No override was possible** — a merchant running a custom Stripe
wrapper that *does* support auto-debit could not declare it, and a
merchant using stock Stripe could not opt out.
## What it is now
A **per-gateway capability table** that the merchant (or a WooNooW
defaults policy) controls explicitly. The system consults the table at
renewal time and decides whether to attempt auto-debit or fall through
to manual. PHP method existence alone is no longer authoritative.
### Storage
```
wp_option('woonoow_gateway_subscription_capabilities', [
'<gateway_id>' => [ 'subscription_auto_renew' => bool ],
...
])
```
### Decision flow
For a renewal where the subscription's stored `payment_method` is
`<gateway_id>`:
1. If the site-level `force_manual_renewal` setting is on, fall through
to manual. (Kill switch — see below.)
2. Look up `<gateway_id>` in the merged capability map (defaults <
stored overrides < `woonoow_gateway_subscription_capabilities` filter).
3. If the lookup returns `subscription_auto_renew = true`, attempt
auto-debit via the gateway's `process_subscription_renewal_payment`
method. On success, run `handle_renewal_success`. On failure, fall
through to manual and notify the customer.
4. If the lookup is missing or false, skip auto-debit entirely, create a
manual renewal order, and send the `renewal_payment_due` email.
The decision is made by
`WooNooW\Modules\Subscription\GatewayCapabilities::should_attempt_auto_renew($gateway_id)`.
## Built-in defaults
| Gateway ID | Default auto-renew | Why |
|---------------------|--------------------|-----|
| `paypal` | true | PayPal Reference Transactions supports recurring |
| `stripe` | true | With a WooNooW Stripe adapter implementing the contract |
| `stripe_cc` | true | Alias for stripe credit card |
| `stripe_sepa` | true | SEPA Direct Debit supports recurring |
| `dodo` | true | Dodo Payments supports recurring subscriptions |
| `tripay` | false | VA/QRIS/e-wallet — no recurring |
| `midtrans` | false | VA/QRIS/e-wallet — no recurring |
| `xendit` | false | Indonesian credit card requires customer re-auth (BI/PCI-DSS) |
| `doku` | false | Indonesian manual-only |
| `duitku` | false | Indonesian manual-only |
| `cheque`, `bacs`, `cod` | false | Offline / no auto-debit |
| **any unknown** | false | Safe default |
The default for any unknown gateway is `false`. A merchant who has a
custom adapter for an unknown gateway can flip the toggle in the admin
UI (Settings → Modules → Subscription → Gateway Auto-Renew Capabilities).
## Why per-gateway, not site-level "billing mode"
A site-level "manual vs auto" toggle asks the merchant to understand a
concept that does not exist in their head. The merchant thinks in
**payment gateways**. A checkbox next to each gateway in the admin is
data the merchant already knows.
Additionally:
- Different merchants use different gateways. A site-level toggle forces
a single behavior even when the merchant runs two gateways (one
auto-capable, one not) for different products.
- The capability is a property of the **integration**, not of the
**store**. The merchant did not choose "manual mode" — they chose
Tripay, and Tripay is a manual gateway.
- The capability can change as WooNooW ships new adapters. A
per-gateway table updates as adapters ship.
## Site-level kill switch
There is one site-level override:
- `force_manual_renewal` (default **off**) — when on, all renewals are
manual regardless of the per-gateway capability table. Useful as a
kill switch during an incident or regulatory change.
This lives in the standard module settings form (Settings → Modules →
Subscription) and is not on the gateway capability matrix screen.
## Admin UI
`Settings → Modules → Subscription` now has two sections:
1. **Configuration** — the standard 12-field schema (button text,
pause/cancel permissions, retry policy, kill switch, etc.). Driven
by the existing `SubscriptionSettings` schema.
2. **Gateway Auto-Renew Capabilities** — one row per WooCommerce
payment gateway with a per-gateway toggle. Built dynamically from
`WC()->payment_gateways()`. The merchant can flip a gateway on or
off, and the change is persisted via
`POST /woonoow/v1/subscriptions/gateway-capabilities`.
When the kill switch is on, every row shows a "Forced manual" badge and
the per-gateway toggles are disabled.
## Customer messaging
The order-pay response (`/checkout/order/{id}`) and the subscription
detail response both include `gateway_supports_auto_renew`. The
customer-spa OrderPay page renders a different callout for manual
gateways (amber) versus auto-renew gateways (blue):
- **Auto-renew:** "Your subscription will renew automatically on the
date shown below."
- **Manual:** "Your saved payment method cannot be charged
automatically for this gateway, so please complete the payment to
continue your subscription."
This ensures the customer is never promised auto-debit that the system
will not deliver.
## Extending the table (for gateway adapter authors)
A gateway adapter or third-party plugin can extend the capability
table at boot time via the
`woonoow_gateway_subscription_capabilities` filter:
```php
add_filter('woonoow_gateway_subscription_capabilities', function ($caps) {
$caps['my_custom_stripe'] = ['subscription_auto_renew' => true];
return $caps;
});
```
The adapter is then responsible for implementing
`process_subscription_renewal_payment(WC_Order $order, $subscription)`
on its gateway class. If the method does not exist, the capability
declaration is meaningless — the renewal will fall through to manual.
## Migration / no migration
This is a behavioral improvement, not a schema change. Existing
subscriptions keep their `payment_method` value. The capability table
is consulted at renewal time, not retroactively.
If a merchant upgrades WooNooW and previously relied on PHP method
existence alone, the renewal will continue to work — but the merchant
will now see the capability matrix and can confirm or override each
gateway.