Skip to content

Commit 792b3b2

Browse files
authored
feat: adding kotlin sample (#192)
1 parent 63b28d6 commit 792b3b2

50 files changed

Lines changed: 6669 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ local.properties
3636
# CLI build output
3737
cli/dist/
3838

39+
# Frontend build output
40+
samples/frontend/dist/
41+
3942
# Figma design tokens (local reference only)
4043
mintlify/tokens/
4144

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Grid API Sample Application Design
2+
3+
## Overview
4+
5+
A sample application demonstrating the Grid API payout flow using the Grid Kotlin SDK. Consists of a shared Vite/React frontend and a Kotlin (Ktor) backend. The frontend is reusable with future backend implementations in other languages.
6+
7+
## Architecture
8+
9+
**Approach:** Thin backend proxy. The Kotlin backend holds API credentials, translates frontend JSON requests into Grid SDK builder calls, and returns raw JSON responses. The frontend orchestrates the step-by-step wizard flow. Webhooks stream from backend to frontend via SSE.
10+
11+
## Directory Structure
12+
13+
```
14+
samples/
15+
├── frontend/ # Shared Vite + React + Tailwind frontend
16+
│ ├── package.json
17+
│ ├── vite.config.ts # Proxies /api → localhost:8080
18+
│ ├── tsconfig.json
19+
│ ├── index.html
20+
│ └── src/
21+
│ ├── main.tsx
22+
│ ├── App.tsx # Wizard flow + webhook panel
23+
│ ├── components/
24+
│ │ ├── StepWizard.tsx # Step container with progress indicator
25+
│ │ ├── JsonEditor.tsx # Editable JSON textarea
26+
│ │ ├── ResponsePanel.tsx # Shows API response JSON
27+
│ │ └── WebhookStream.tsx # SSE-connected live webhook feed
28+
│ ├── steps/
29+
│ │ ├── CreateCustomer.tsx
30+
│ │ ├── CreateExternalAccount.tsx
31+
│ │ ├── CreateQuote.tsx
32+
│ │ ├── ExecuteQuote.tsx
33+
│ │ └── SandboxFund.tsx
34+
│ └── lib/
35+
│ └── api.ts # fetch wrappers for /api/* endpoints
36+
37+
├── kotlin/ # Kotlin backend sample
38+
│ ├── README.md
39+
│ ├── .env.example
40+
│ ├── build.gradle.kts
41+
│ ├── settings.gradle.kts
42+
│ ├── gradle.properties
43+
│ ├── gradlew / gradlew.bat
44+
│ └── src/main/kotlin/com/grid/sample/
45+
│ ├── Application.kt
46+
│ ├── Config.kt
47+
│ ├── GridClientBuilder.kt
48+
│ ├── Routing.kt
49+
│ ├── WebhookStream.kt
50+
│ ├── JsonUtils.kt
51+
│ └── routes/
52+
│ ├── Customers.kt
53+
│ ├── ExternalAccounts.kt
54+
│ ├── Quotes.kt
55+
│ ├── Sandbox.kt
56+
│ ├── Webhooks.kt
57+
│ └── Sse.kt
58+
59+
└── README.md
60+
```
61+
62+
## API Contract
63+
64+
| Method | Path | Description |
65+
|--------|------|-------------|
66+
| `POST` | `/api/customers` | Create an individual customer |
67+
| `POST` | `/api/customers/{customerId}/external-accounts` | Create a USD external bank account |
68+
| `POST` | `/api/quotes` | Create a quote (USDC internal → USD external) |
69+
| `POST` | `/api/quotes/{quoteId}/execute` | Execute the quote to initiate payment |
70+
| `POST` | `/api/sandbox/send-funds` | Simulate funding for the quote |
71+
| `POST` | `/api/webhooks` | Receives webhooks from Grid (not called by frontend) |
72+
| `GET` | `/api/sse` | SSE stream of webhook events to frontend |
73+
74+
## Step-by-Step Flow
75+
76+
### Step 1 — Create Customer
77+
78+
```json
79+
// POST /api/customers
80+
{
81+
"customerType": "INDIVIDUAL",
82+
"platformCustomerId": "sample-customer-001"
83+
}
84+
// Response includes `id` → used in Step 2
85+
```
86+
87+
### Step 2 — Create External Account
88+
89+
```json
90+
// POST /api/customers/{customerId}/external-accounts
91+
{
92+
"currency": "USD",
93+
"accountInfo": {
94+
"accountType": "CHECKING",
95+
"routingNumber": "021000021",
96+
"accountNumber": "123456789"
97+
}
98+
}
99+
// Response includes `id` → used in Step 3
100+
```
101+
102+
### Step 3 — Create Quote
103+
104+
```json
105+
// POST /api/quotes
106+
{
107+
"source": { "internalAccountId": "<platform USDC internal account>" },
108+
"destination": { "externalAccountId": "<from step 2>" },
109+
"lockedCurrencyAmount": 1000,
110+
"lockedCurrencySide": "SENDING"
111+
}
112+
// Response includes `quoteId` → used in Steps 4 and 5
113+
```
114+
115+
### Step 4 — Execute Quote
116+
117+
```json
118+
// POST /api/quotes/{quoteId}/execute
119+
// No body needed
120+
// Response: updated quote with status change
121+
```
122+
123+
### Step 5 — Sandbox Fund
124+
125+
```json
126+
// POST /api/sandbox/send-funds
127+
{
128+
"quoteId": "<from step 3>"
129+
}
130+
// Response: sandbox funding confirmation
131+
```
132+
133+
## Frontend Design
134+
135+
### Layout
136+
137+
Two-panel layout:
138+
139+
- **Left (60%):** Step wizard with vertical stepper. Active step shows editable JSON textarea and submit button. Response panel below. Completed steps collapse to summary. Future steps grayed out.
140+
- **Right (40%):** Webhook stream panel. SSE connection on page load with auto-reconnect. Newest events at top. Each shows timestamp, event type badge, expandable raw JSON.
141+
142+
### Tech Stack
143+
144+
React 18, TypeScript, Vite 5, Tailwind CSS 4. No component libraries.
145+
146+
### Data Flow Between Steps
147+
148+
The frontend auto-populates IDs from previous responses into the next step's JSON template. Users can edit any value before submitting.
149+
150+
## Backend Design (Kotlin)
151+
152+
### Server
153+
154+
Ktor 3.x with Netty engine. CORS enabled for all origins. SSE plugin installed.
155+
156+
### Request Handling Pattern
157+
158+
Each route handler:
159+
1. Receives raw JSON string from request body
160+
2. Parses with Jackson into `JsonNode`
161+
3. Builds Grid SDK params using builder pattern
162+
4. Calls Grid SDK
163+
5. Returns SDK response as JSON
164+
165+
### Key Components
166+
167+
- **`Config.kt`** — Loads `GRID_API_TOKEN_ID`, `GRID_API_CLIENT_SECRET`, `GRID_WEBHOOK_PUBLIC_KEY` from `.env` or system env vars via dotenv-kotlin
168+
- **`GridClientBuilder.kt`** — Lazy singleton `GridOkHttpClient`
169+
- **`WebhookStream.kt`**`MutableSharedFlow<String>(replay = 10)` for broadcasting webhook events
170+
- **`Webhooks.kt`** — Verifies P-256 ECDSA signature via `X-Grid-Signature` header, broadcasts to `WebhookStream`
171+
- **`Sse.kt`** — Collects from `WebhookStream.eventFlow`, sends as `ServerSentEvent`. Heartbeat endpoint for keep-alive.
172+
173+
### Dependencies
174+
175+
- Grid Kotlin SDK (published Maven artifact from `com.grid:grid-kotlin`)
176+
- Ktor 3.x (server-core, server-netty, server-cors, server-sse, server-content-negotiation)
177+
- Jackson (kotlin module)
178+
- dotenv-kotlin
179+
- Logback
180+
181+
### Error Handling
182+
183+
Minimal. SDK exceptions caught and returned as JSON with appropriate HTTP status.
184+
185+
## README Structure
186+
187+
### `samples/README.md`
188+
189+
Overview of the sample apps, directory structure, links to sub-READMEs.
190+
191+
### `samples/kotlin/README.md`
192+
193+
1. Overview of what the sample demonstrates
194+
2. Prerequisites: Java 21+, Node.js 18+, Grid API sandbox credentials
195+
3. Setup: copy `.env.example`, fill in credentials
196+
4. Running: two terminals (backend `./gradlew run` on :8080, frontend `npm run dev` on :5173)
197+
5. Webhook setup: ngrok for local dev, configure webhook URL in Grid dashboard
198+
6. Walkthrough of each wizard step
199+
200+
### `samples/frontend/README.md`
201+
202+
How to run, how to configure proxy target for different backends.

0 commit comments

Comments
 (0)