# PayFast Setup

## Environment

Use sandbox while testing:

```env
PAYFAST_ENABLED=true
PAYFAST_SANDBOX=true
PAYFAST_MERCHANT_ID=
PAYFAST_MERCHANT_KEY=
PAYFAST_PASSPHRASE=
PAYFAST_RETURN_URL="${APP_URL}/checkout/payment/return"
PAYFAST_CANCEL_URL="${APP_URL}/checkout/payment/cancel"
PAYFAST_NOTIFY_URL="${APP_URL}/payments/payfast/itn"
```

Switch `PAYFAST_SANDBOX=false` only after a successful end-to-end test with the live merchant account.

## Payment Flow

1. Checkout creates an order with `Pending Payment`.
2. Order lines store permanent product, SKU, variant, quantity, price and VAT snapshots.
3. Stock is reserved for the configured reservation window.
4. Customer is redirected to PayFast.
5. Customer return URL only shows a pending confirmation message.
6. PayFast ITN hits `/payments/payfast/itn`.
7. Pantry Hyper validates the signature, merchant ID, payment amount and transaction ID.
8. Duplicate ITN calls are ignored through `payment_events.event_id`.
9. Paid orders convert stock reservations into completed sales.

## Security Notes

- Never hardcode merchant credentials.
- Never expose the PayFast passphrase in HTML.
- Never mark payment as paid from the customer return URL.
- Keep payment status separate from fulfilment status.
