167 lines
6.9 KiB
Markdown
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.
|