# Payfast subscriptions

Membership connects Payfast hosted monthly card subscriptions: personal R99.99 and approved business listing R299.99. Customer amounts are selected from server-side prices, not request input. Personal workspace and support access requires an active 30-day trial or a paid live personal membership. The owner retains access. Payments do not automatically approve/publish business listings.

## Owner setup

Sign into Sovereign as the owner, then open Membership → Connect your Payfast account. Enter the Merchant ID, Merchant Key and required security passphrase from Payfast integration settings. Sandbox accounts have separate credentials. Test an initial payment, renewal notification, and cancellation before selecting Live. Live setup requires confirmation that the merchant account is approved and recurring billing enabled. Paid sandbox checkout, external ITN delivery and cancellation were verified previously. The new zero-upfront trial flow still needs an owner sandbox checkout before live launch.

The production PAYMENT_CONFIG_KEY secret is a randomly generated AES-GCM key managed in Sites. Merchant settings are encrypted in D1; the passphrase is never returned to the browser or included in the hosted checkout form. Payfast requires the Merchant ID and Key as checkout form fields. Only the owner's signed-in identity can write merchant settings. Preserve the encryption key across deployments; changing it would make existing encrypted settings unreadable.

Checkout signs fields in Payfast documentation order. Return and cancellation URLs reopen Membership; they cannot activate a subscription. The public /api/payments/notify endpoint checks the signature, merchant, known subscription, amount, mode, token and independent Payfast server validation. Invalid/transient callbacks fail closed. The receipt ledger makes repeated notifications idempotent, and an optimistic transaction guard prevents concurrent updates losing a paid month. Card data remains with Payfast.

The cancellation endpoint verifies subscription ownership and waits for Payfast API confirmation before recording cancellation. Already paid periods remain recorded. Missing renewal payments show overdue after the recorded paid period. Business payments do not approve a listing.

## Verification

Run `node tests/payfast.test.mjs` for encryption, signature encoding, prices, authentication, owner-only setup, same-origin writes, payment validation, duplicate notifications, ownership and cancellation failures/success. Tests use an in-memory SQLite database and mocked Payfast responses. Run TypeScript checking and the normal production build.

Before launch with live merchant credentials, use a separate sandbox account to verify external ITN delivery, recurring token creation and cancellation API access. Account credentials can only be switched after active test subscriptions have been cancelled and pending checkouts have aged out. A live account approval checkbox records the owner's confirmation; it does not independently verify Payfast KYC status.

Official references: https://developers.payfast.co.za/docs and https://developers.payfast.co.za/api .

## Free personal trial

Visitors add their card on Payfast with an explicit unchecked consent box authorising R0 today and R99.99 monthly automatically after the trial unless cancelled. A first checkout atomically reserves the one-time trial and a pending zero-amount subscription. A validated zero-value ITN saves the subscription token and opens trial access; it does not create a paid period. Payfast holds card details; Sovereign stores only the subscription reference. The old card-free start endpoint is disabled. Existing card-free trials keep access but acquire automatic billing only after explicit card authorisation.

The first billing date is the next midnight at or after the exact 30-day minimum, in South African time. Trial access lasts through that scheduled billing boundary, including after cancellation. Checkout retries and re-enrolment cannot reset the original deadline. Expired trials require an immediate first paid month. Recurring receipts must match the server price and cannot be accepted before the scheduled billing date. Zero-value notification replays never extend trial or paid access.

Customers can cancel under Membership during the trial. Payfast must confirm cancellation before the app records it. Cancel before the displayed trial end to avoid the first charge; after that, cancellation stops future monthly charges. The app shows the remaining trial time. The checkout enables Payfast buyer subscription notifications (seven days before trial expiry, subject to Payfast settings). No app email or push notification is sent. Business listings remain separately paid without a trial.
