# Automatic customer subscriptions

This extension is separate from manual recurring invoices and Coinvay's merchant plan purchases. Platform Free/Pro/Enterprise plan renewal remains manual.

## Customer flow

A merchant with the automatic-subscriptions feature and a verified EVM receiving wallet creates a fixed-token monthly subscription link. Customers need no Coinvay account. The management link states the exact token, network, receiving wallet, monthly amount, maximum number of payments and maximum total.

Setup is performed once for that subscription, but involves separate wallet confirmations: a finite token approval (and an allowance reset when required) followed by activation. Activation immediately transfers the first payment directly to the merchant. No unlimited allowance is requested. The combined finite approval includes remaining budgets of other subscriptions using that payer, token and contract.

Renewals follow the activation timestamp's original UTC calendar day/time. Short months clamp to their last day; subsequent months return to the original day. The amount, recipient, token, start and maximum number of monthly payments cannot be edited. A price or receiving-wallet change requires a new customer authorization. The initial default is 12 monthly payments, configurable from 1 through 120. Authorization expires at its maximum term even if some cycles were skipped.

The relayer pays renewal gas. Customers pay setup/cancellation gas from their wallet. Token price in fiat is not fixed. Insufficient token balance or allowance causes a deferred collection; the runner retries on later ticks. Missed calendar cycles are skipped without arrears or multiple charges in one current cycle. Contract enforcement, not browser time or the database, prevents early/duplicate collections.

## Supported networks and assets

- Ethereum: USDC, USDT, DAI, PYUSD.
- Base: USDC.
- Polygon: USDC, PYUSD.
- BNB Chain: USDT.
- Base Sepolia: testnet USDC, for testing only.

Native ETH, BNB, POL, BTC, SOL, TRX, Gram, TRC-20 and non-EVM assets are not automatic-debit methods in this release. They remain available where already supported for manual invoice payments.

## Customer cancellation

The payer or merchant recipient can cancel from the customer management page using their wallet. The same `cancel(bytes32 id)` function can be called directly on the contract, without Coinvay, the administrator, the relayer or a login. The page displays the contract and on-chain subscription ID. Revoking the contract's token allowance also stops collection, but affects all subscriptions for that token/contract. A lower allowance or empty balance is not an on-chain cancellation. Closing an unused invitation only hides its activation controls; it does not revoke an authorization already submitted on-chain. Payers can reconnect and recover an existing authorization even after a link is closed.

Cancellation takes effect when its transaction is included on-chain. A payment already included before cancellation remains paid; cancellation does not issue refunds. The page waits for the network's required confirmation depth before displaying a final cancellation state. Reopening the original management link permits payer-wallet recovery after local browser state is lost. Pausing Coinvay's runner is not equivalent to cancelling authorizations: contract collection is permissionless, so third parties can pay gas to collect within the customer-approved terms.

## Admin deployment and activation

1. Apply migration `0017_new_blockbuster.sql` in migration order. Sites applies pending migrations through the normal publication workflow.
2. Read and independently review `contracts/CoinvaySubscriptions.sol` before deploying to production with real customer funds. The new custom contract has automated tests, but has not received an independent professional smart-contract audit.
3. Run `npm run contracts:build` to reproduce the artifact at `/contracts/recurring.json`. Compiler and OpenZeppelin dependencies are pinned. The immutable contract has no owner, proxy, upgrade, arbitrary spending, fee or custody functions.
4. In **Admin → Automatic billing**, connect the administrator's deployment wallet, select the network and deploy using the supplied button. This is a gas-spending transaction requiring the human wallet owner's review and signature. The constructor allowlist is the network's exact supported token addresses.
5. Verify/save the deployed contract. Saving requires the administrator's current password and enabled MFA. The server checks RPC chain identity, exact runtime bytecode and expected token allowlist. Customers' links pin the contract and receiving wallet; replacing an address while open links exist is blocked.
6. Create a separate gas-only EVM wallet and configure its key as the secret runtime environment value `RECURRING_RELAYER_PRIVATE_KEY` using the hosting secret manager. Never use customer or merchant wallet keys. Never enter keys in chat, source code, browser fields or database records. Only the derived public relayer address is returned to admin.
7. Fund the relayer with native gas on every enabled network. Keep its balance limited. It sends only zero-native-value calls to verified billing contracts. Gas-price safety caps are Ethereum 50 gwei, Base 5 gwei, Polygon 300 gwei, BNB Chain 10 gwei, Base Sepolia 10 gwei. An estimated call above 600,000 gas is rejected; the submitted gas includes a 20% buffer.
8. Configure the existing external authenticated monitor runner (`operations/monitor-runner.mjs`) to call `POST /api/jobs/tick` every minute with `Authorization: Bearer MONITOR_SECRET`. This Site does not deploy that separate runner. Receiving an authenticated tick writes a heartbeat. The admin page reports its actual last-seen time; enabling requires a tick within the last five minutes, a configured relay key, a nonzero native gas balance, and confirmation that contract review and test subscription/cancellation were completed.
9. Test end-to-end on Base Sepolia before enabling each production network. Opening a page or saving configuration does not install a scheduler or constitute a live money test.

No mainnet contracts, relay key, gas funding or new scheduler are provisioned automatically by publishing the app. New configurations default to disabled. Do not market unattended collections as live until the operator completes these steps.

## Runner and receipts

Each network has a database lease to serialize relayer nonce preparation. A signed transaction and its deterministic hash are persisted before broadcast; a retry rebroadcasts the same transaction. The contract checks the expected cycle to prevent a delayed transaction from silently paying another month's invoice. A transaction, agreement and cycle are unique in the receipt table. Up to three due agreements are examined and at most one new transfer is submitted per network per tick, so large volumes require appropriately frequent ticks and a capacity review.

Payments become Confirmed only after a successful billing-contract receipt includes a matching `Collected` event and reaches the configured confirmation depth. These token-denominated receipts live in the automatic-billing history; they do not silently inflate USD invoice revenue or satisfy invoice IDs. Signed webhook event `subscription.payment.completed` carries subscription, cycle (zero-based), network, asset, atomic quantity and transaction hash. Consumers must handle idempotently. The current runner reconciles its submitted transactions and customer-submitted activation hashes. If an independent caller collects directly, submit that hash through the management sync endpoint for receipt import; a general historical event indexer is not included.

## API

- `GET /api/recurring`: authenticated merchant's agreements, receipt history and setup status.
- `POST /api/recurring`: create link with `{title, network, asset, amount, maxPayments}`. `amount` is a decimal token amount, not USD. Requires the automatic-subscriptions plan feature, enabled network and verified receiving wallet. Merchant API keys also require API access.
- `POST /api/recurring/:id/close`: close an unused, owner-scoped link. Active authorizations require an on-chain cancellation.
- `GET /api/recurring-checkout/:token`: capability-based customer terms and readiness; no customer email, secret or private key.
- `POST /api/recurring-checkout/:token/prepare`: `{payer}` creates finite approval/activation transaction data; `{payer, action:"cancel"}` creates payer/recipient-scoped cancellation data. Does not sign or submit transactions.
- `POST /api/recurring-checkout/:token/sync`: `{payer, tx?}` reads authoritative chain state and optionally imports a finalized matching collection receipt. Never trusts a client "paid" flag.
- `GET /api/admin/recurring`: interactive administrator setup and operating status.
- `PUT /api/admin/recurring`: `{network,contract,enabled,reviewed,password,code?}` validates deployment and activation readiness. Never accepts wallet private keys.

## Verification and remaining operations

Automated local-chain tests exercise actual compiled Solidity for monthly timing, leap-year/month-end dates, duplicate and early rejection, skipped cycles, combined finite budgets, third-party collection, scoped cancellation and immutable terms. Gateway integration tests exercise deployment validation, activation proof, monthly relayer submission, final receipts and cancellation against the same local chain.

A separately operated relayer secret, production gas funding, contract deployment transactions, explorer verification, external security review and a production/testnet wallet trial remain operator tasks. Automated tests are not a substitute for these. The operator must monitor runner heartbeat, relayer balance, RPC failures, pending transactions and customer payment failures.
