Skip to content

Commit b55a5c5

Browse files
authored
Merge pull request #187 from Carlys17/docs/issue-99-api-integration
2 parents 8bbb48b + 38d5146 commit b55a5c5

1 file changed

Lines changed: 302 additions & 0 deletions

File tree

docs/api-integration.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# API Integration Guide
2+
3+
This document describes how the BettaPay frontend talks to the backend API, what endpoints the current UI expects, and how to test API-dependent flows locally or in preview deployments.
4+
5+
## API client configuration
6+
7+
The shared Axios client lives in [`lib/api/axios.ts`](../lib/api/axios.ts):
8+
9+
```ts
10+
export const apiClient = axios.create({
11+
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001',
12+
headers: { 'Content-Type': 'application/json' },
13+
withCredentials: true,
14+
});
15+
```
16+
17+
Key behavior:
18+
19+
- `NEXT_PUBLIC_API_URL` controls the backend base URL.
20+
- If the variable is unset, the frontend defaults to `http://localhost:3001`.
21+
- `withCredentials: true` is enabled so browser requests include backend-set cookies.
22+
- JSON is the default request/response format.
23+
- State-changing requests (`POST`, `PUT`, `PATCH`, `DELETE`) attach the CSRF header from the CSRF cookie when available.
24+
- A separate refresh client posts to `/api/auth/refresh` after a `401` response, then retries the original request.
25+
- A `429` response updates the rate-limit store and shows an accessible toast/announcement.
26+
27+
## Authentication model
28+
29+
The frontend is designed around cookie-based authentication:
30+
31+
- The backend should set auth/session tokens in `HttpOnly` cookies.
32+
- The client-side Zustand auth store may hold an in-memory token for the active tab, but it must not persist the token to `localStorage`.
33+
- The auth store persists only the non-sensitive `role` field.
34+
- Logout calls `DELETE /api/auth/session` so the backend can clear the auth cookie.
35+
36+
See also:
37+
38+
- [`docs/adr/001-state-management.md`](adr/001-state-management.md)
39+
- [`docs/adr/004-mock-auth.md`](adr/004-mock-auth.md)
40+
41+
## CSRF and CORS requirements
42+
43+
For local development and hosted environments, the backend must allow the frontend origin and credentials.
44+
45+
Backend CORS should allow:
46+
47+
- The local frontend origin, usually `http://localhost:3000`.
48+
- Vercel preview/production origins used by the project.
49+
- Credentialed requests (`Access-Control-Allow-Credentials: true`).
50+
- JSON request headers.
51+
- The CSRF header name used by [`lib/utils/csrf.ts`](../lib/utils/csrf.ts).
52+
53+
For state-changing endpoints, the backend should validate the CSRF token using the same double-submit cookie pattern expected by the frontend.
54+
55+
## Expected endpoints
56+
57+
### `POST /api/auth/session`
58+
59+
Sets an authentication cookie for the browser session.
60+
61+
Used by:
62+
63+
- Email login in [`app/auth/login/page.tsx`](../app/auth/login/page.tsx)
64+
- Wallet login in [`app/auth/login/page.tsx`](../app/auth/login/page.tsx)
65+
66+
Request body:
67+
68+
```json
69+
{
70+
"token": "mock_jwt_token_12345",
71+
"role": "merchant"
72+
}
73+
```
74+
75+
Expected response:
76+
77+
```json
78+
{
79+
"ok": true
80+
}
81+
```
82+
83+
Backend responsibilities:
84+
85+
- Validate the token or session payload in real deployments.
86+
- Set an `HttpOnly`, `Secure` where appropriate, `SameSite` auth cookie.
87+
- Return a non-2xx status for invalid session data.
88+
89+
### `GET /api/auth/session`
90+
91+
Restores a session when the in-memory auth store was lost but a persisted role exists.
92+
93+
Used by:
94+
95+
- [`lib/hooks/useSessionCheck.ts`](../lib/hooks/useSessionCheck.ts)
96+
97+
Expected successful response:
98+
99+
```json
100+
{
101+
"user": {
102+
"id": "merchant-id",
103+
"email": "merchant@example.com",
104+
"name": "Merchant User",
105+
"role": "merchant"
106+
},
107+
"token": "session-token"
108+
}
109+
```
110+
111+
If the backend returns `200` without `user` and `token`, the hook treats the cookie-backed session as valid and leaves client state alone.
112+
113+
Expected expired/invalid response:
114+
115+
```json
116+
{
117+
"error": "Session expired"
118+
}
119+
```
120+
121+
Use a `401` or `403` status for expired/invalid sessions so the frontend clears auth state and redirects to login.
122+
123+
### `DELETE /api/auth/session`
124+
125+
Clears the auth cookie during logout.
126+
127+
Used by:
128+
129+
- [`lib/store/authStore.ts`](../lib/store/authStore.ts)
130+
131+
Expected response:
132+
133+
```json
134+
{
135+
"ok": true
136+
}
137+
```
138+
139+
Backend responsibilities:
140+
141+
- Clear the same cookie name used by `POST /api/auth/session`.
142+
- Keep the endpoint idempotent; repeated logout requests should still succeed.
143+
144+
### `POST /api/auth/refresh`
145+
146+
Refreshes a cookie-backed session after an API request receives `401`.
147+
148+
Used by:
149+
150+
- The Axios response interceptor in [`lib/api/axios.ts`](../lib/api/axios.ts)
151+
152+
Expected response:
153+
154+
```json
155+
{
156+
"ok": true
157+
}
158+
```
159+
160+
When this call succeeds, the frontend retries the original request. When it fails, the frontend logs the user out and redirects to `/auth/login`.
161+
162+
### `GET /api/merchants/:id`
163+
164+
Fetches merchant profile data for the login flow.
165+
166+
Used by:
167+
168+
- Email login in [`app/auth/login/page.tsx`](../app/auth/login/page.tsx)
169+
170+
Expected successful response:
171+
172+
```json
173+
{
174+
"id": "GCCHHKNI7GRA5QWC7RCTT3OHO7SKAUMKQA6IBWEQEO2SXI3GF376UHDD",
175+
"name": "Merchant User"
176+
}
177+
```
178+
179+
If the backend is unavailable or returns an error-like response, the login page falls back to mock merchant data for local/preview usability.
180+
181+
### `POST /api/merchants`
182+
183+
Registers a merchant record.
184+
185+
Used by:
186+
187+
- [`app/auth/register/page.tsx`](../app/auth/register/page.tsx)
188+
189+
Request body currently sent by the frontend:
190+
191+
```json
192+
{
193+
"id": "merch_generatedid",
194+
"name": "Acme Corp"
195+
}
196+
```
197+
198+
Expected response:
199+
200+
```json
201+
{
202+
"id": "merch_generatedid",
203+
"name": "Acme Corp"
204+
}
205+
```
206+
207+
If this call fails in preview/local mock mode, the registration page shows a successful mock registration after a short delay.
208+
209+
### `POST /api/payments`
210+
211+
Creates or submits a payment for a payment link.
212+
213+
Used by:
214+
215+
- [`app/pay/[linkId]/page.tsx`](../app/pay/%5BlinkId%5D/page.tsx)
216+
217+
The exact request shape should stay aligned with the payment form fields and backend payment contract. At minimum, the backend should return a payment identifier or transaction metadata that the UI can display after submission.
218+
219+
### `GET /api/payments/:txId`
220+
221+
Fetches payment status by transaction id.
222+
223+
Used by:
224+
225+
- [`app/pay/status/[txId]/page.tsx`](../app/pay/status/%5BtxId%5D/page.tsx)
226+
227+
Expected response should include the payment/transaction status, amount, asset, and any display metadata needed by the status page.
228+
229+
## Local backend development
230+
231+
Run the backend on port `3001` or set the frontend to the backend URL:
232+
233+
```bash
234+
NEXT_PUBLIC_API_URL=http://localhost:3001
235+
```
236+
237+
Then start the frontend:
238+
239+
```bash
240+
npm run dev
241+
```
242+
243+
The frontend runs at `http://localhost:3000` by default. The backend must allow that origin with credentials.
244+
245+
## Vercel preview and mock mode
246+
247+
Preview deployments may not have a live backend. The auth flow deliberately falls back to mock behavior so reviewers can navigate the UI:
248+
249+
- Login accepts any email.
250+
- Emails containing `admin` receive the `admin` role; others receive `merchant`.
251+
- The mock token is kept in memory only.
252+
- The app attempts to set the session cookie, but continues when `/api/auth/session` is unavailable.
253+
- Merchant fetch and registration calls fall back to mock data when the backend is unavailable.
254+
255+
This behavior is for development and previews only. It is not a production authentication system.
256+
257+
## Testing endpoints with curl
258+
259+
Replace `http://localhost:3001` with your backend URL.
260+
261+
Set auth cookie:
262+
263+
```bash
264+
curl -i -X POST http://localhost:3001/api/auth/session \
265+
-H 'Content-Type: application/json' \
266+
--data '{"token":"mock_jwt_token_12345","role":"merchant"}'
267+
```
268+
269+
Check session:
270+
271+
```bash
272+
curl -i http://localhost:3001/api/auth/session \
273+
--cookie 'auth_token=example'
274+
```
275+
276+
Clear session:
277+
278+
```bash
279+
curl -i -X DELETE http://localhost:3001/api/auth/session \
280+
--cookie 'auth_token=example'
281+
```
282+
283+
Fetch merchant:
284+
285+
```bash
286+
curl -i http://localhost:3001/api/merchants/GCCHHKNI7GRA5QWC7RCTT3OHO7SKAUMKQA6IBWEQEO2SXI3GF376UHDD
287+
```
288+
289+
Create merchant:
290+
291+
```bash
292+
curl -i -X POST http://localhost:3001/api/merchants \
293+
-H 'Content-Type: application/json' \
294+
--data '{"id":"merch_demo","name":"Demo Merchant"}'
295+
```
296+
297+
## Troubleshooting
298+
299+
- **Cookies are not sent:** confirm `withCredentials: true`, backend CORS `Access-Control-Allow-Credentials: true`, and a matching allowed origin.
300+
- **CSRF failures:** confirm the CSRF cookie exists and the backend expects the same CSRF header name as the frontend.
301+
- **Login works in preview but not locally:** verify `NEXT_PUBLIC_API_URL` points to a running backend and that CORS allows `http://localhost:3000`.
302+
- **User is redirected after reload:** the persisted role exists, but `GET /api/auth/session` may be returning a non-2xx response; inspect the network tab and backend logs.

0 commit comments

Comments
 (0)