|
| 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