# Cross-Border Agent Payment API

Laravel 11 API implementation of the supplied 18-page backend specification.

## Included

- Sanctum bearer-token authentication and revocation
- Spatie roles/permissions plus ownership policies
- Countries, currencies, corridors, append-only exchange rates
- Signed 10-minute quotes with immutable rate/fee/payout snapshots
- Transfer issuance, one-time plaintext code response, SHA-256-only storage, opaque QR payloads
- Verify/payout split, pessimistic locking, wrong-country checks, expiry checks and anti-brute-force controls
- Generic idempotency records for transfer creation, payout and code rotation
- Immutable transfer events, immutable merchant ledgers, settlement batches
- Admin merchant/user/rate/corridor/transfer/settlement/report/audit endpoints
- Scheduled expiry, balance-cache refresh, daily settlement drafts, expiring-code notifications
- Redis/Horizon-ready queues, pluggable SMS contract, queued CSV export
- Docker production-like local stack and deployment checklist

## Setup

```bash
cp .env.example .env
docker compose run --rm app composer install
docker compose run --rm app php artisan key:generate
docker compose run --rm app php artisan migrate --seed
docker compose up -d
```

API: `http://localhost:8080/api/v1`. Seed credentials all use `ChangeMe!12345`:

- `admin@example.com`
- `send@example.com`
- `receive@example.com`

Change these passwords immediately outside local development.

## Financial invariants

Amounts are BIGINT minor units with currency codes. Float and double database columns are absent. Exchange-rate rows, ledger entries, transfer events and audit logs are append-only. Transfer status writes go through `TransferStateMachine`. Financial writes use database transactions and locking.

## Production deployment

1. Use MySQL 8/InnoDB and Redis with authentication/TLS where available.
2. Set `APP_ENV=production`, `APP_DEBUG=false`, a generated `APP_KEY`, HTTPS URL, `SESSION_SECURE_COOKIE=true` and trusted proxy/host settings.
3. Run `composer install --no-dev --classmap-authoritative`, `php artisan config:cache`, `route:cache`, `event:cache`, and migrations under a deployment lock.
4. Run Horizon under Supervisor/systemd and one scheduler (`php artisan schedule:work`) per environment. Restrict the Horizon dashboard.
5. Replace `LogSmsGateway` with the selected provider implementation. Keep codes out of logs and URLs.
6. Ship application and financial logs to append-protected centralized storage; alert on failed jobs and payout exceptions.
7. Back up MySQL with point-in-time recovery and regularly test restore procedures.
8. Add KMS/secret-manager integration, WAF/API gateway throttles, observability, penetration testing and reconciliation with real settlement rails before handling live funds.

## Important implementation note

The specification says the plaintext code is never stored but also defines a resend endpoint. Since an unstored code cannot be recovered, this implementation atomically rotates to a new code and sends it to the original recipient. The API never returns the new plaintext code.

## Swagger / OpenAPI

Swagger UI is integrated without a runtime PHP package. Enable it with `SWAGGER_ENABLED=true` and open:

- `GET /api/documentation` — interactive Swagger UI
- `GET /api/documentation/openapi.yaml` — OpenAPI 3.0 specification

For production, keep `SWAGGER_ENABLED=false` unless the documentation route is restricted behind VPN, reverse-proxy authentication, or an internal network. The UI loads Swagger UI assets from `unpkg.com`; self-host those assets where outbound browser access is not allowed.
