Skip to content

Commit 9e09ccc

Browse files
committed
feat: adding kotlin sample
1 parent 58e58da commit 9e09ccc

21 files changed

Lines changed: 290 additions & 182 deletions

.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

samples/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Grid API Samples
22

3-
Sample applications demonstrating the Grid API payout flow — creating a customer, linking a bank account, creating and executing a quote, and simulating funding in the sandbox.
3+
Sample applications demonstrating the Grid API payout flow — creating a customer, linking a bank account, creating a quote, and simulating funding in the sandbox.
44

55
## Structure
66

@@ -23,7 +23,6 @@ Each backend must implement the same API contract:
2323
| POST | `/api/customers` | Create a customer |
2424
| POST | `/api/customers/{id}/external-accounts` | Create an external account |
2525
| POST | `/api/quotes` | Create a quote |
26-
| POST | `/api/quotes/{id}/execute` | Execute a quote |
2726
| POST | `/api/sandbox/send-funds` | Simulate sandbox funding |
2827
| POST | `/api/webhooks` | Receive webhook events from Grid |
2928
| GET | `/api/sse` | Stream webhook events to the frontend via SSE |

samples/frontend/src/App.tsx

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import WebhookStream from './components/WebhookStream'
44
import CreateCustomer from './steps/CreateCustomer'
55
import CreateExternalAccount from './steps/CreateExternalAccount'
66
import CreateQuote from './steps/CreateQuote'
7-
import ExecuteQuote from './steps/ExecuteQuote'
87
import SandboxFund from './steps/SandboxFund'
98

109
export default function App() {
@@ -48,6 +47,7 @@ export default function App() {
4847
summary: quoteId ? `ID: ${quoteId}` : null,
4948
content: (
5049
<CreateQuote
50+
customerId={customerId}
5151
externalAccountId={externalAccountId}
5252
disabled={activeStep !== 2}
5353
onComplete={(data) => {
@@ -58,23 +58,12 @@ export default function App() {
5858
),
5959
},
6060
{
61-
title: '4. Execute Quote',
62-
summary: activeStep > 3 ? 'Executed' : null,
63-
content: (
64-
<ExecuteQuote
65-
quoteId={quoteId}
66-
disabled={activeStep !== 3}
67-
onComplete={() => advance()}
68-
/>
69-
),
70-
},
71-
{
72-
title: '5. Sandbox Fund',
73-
summary: activeStep > 4 ? 'Funded' : null,
61+
title: '4. Simulate Funding (Sandbox Only)',
62+
summary: activeStep > 3 ? 'Funded' : null,
7463
content: (
7564
<SandboxFund
7665
quoteId={quoteId}
77-
disabled={activeStep !== 4}
66+
disabled={activeStep !== 3}
7867
onComplete={() => advance()}
7968
/>
8069
),
@@ -85,7 +74,7 @@ export default function App() {
8574
<div className="min-h-screen bg-gray-950 text-gray-100">
8675
<header className="border-b border-gray-800 px-6 py-4">
8776
<h1 className="text-xl font-bold">Grid API Sample</h1>
88-
<p className="text-sm text-gray-400">Payout flow: USDC &rarr; USD via external bank account</p>
77+
<p className="text-sm text-gray-400">Send a real time payment to a US bank account funded with USDC</p>
8978
</header>
9079
<div className="flex">
9180
<main className="w-3/5 p-6 border-r border-gray-800 min-h-[calc(100vh-73px)]">

samples/frontend/src/components/JsonEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default function JsonEditor({ value, onChange, disabled }: JsonEditorProp
2525
onChange={(e) => onChange(e.target.value)}
2626
disabled={disabled}
2727
spellCheck={false}
28-
className="w-full h-48 bg-gray-900 text-green-400 font-mono text-sm p-3 rounded-lg border border-gray-700 focus:border-blue-500 focus:outline-none resize-y disabled:opacity-50"
28+
className="w-full h-72 bg-gray-900 text-green-400 font-mono text-sm p-3 rounded-lg border border-gray-700 focus:border-blue-500 focus:outline-none resize-y disabled:opacity-50"
2929
/>
3030
{error && (
3131
<p className="text-red-400 text-xs mt-1">Invalid JSON: {error}</p>

samples/frontend/src/components/ResponsePanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function ResponsePanel({ response, error }: ResponsePanelProps) {
2020
</button>
2121
{expanded && (
2222
<pre
23-
className={`text-sm font-mono p-3 rounded-lg overflow-auto max-h-64 ${
23+
className={`text-sm font-mono p-3 rounded-lg overflow-auto max-h-96 ${
2424
error
2525
? 'bg-red-950 text-red-300 border border-red-800'
2626
: 'bg-gray-900 text-gray-300 border border-gray-700'

samples/frontend/src/components/StepWizard.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode } from 'react'
1+
import { ReactNode, useState } from 'react'
22

33
interface Step {
44
title: string
@@ -12,20 +12,35 @@ interface StepWizardProps {
1212
}
1313

1414
export default function StepWizard({ steps, activeStep }: StepWizardProps) {
15+
const [expandedSteps, setExpandedSteps] = useState<Set<number>>(new Set())
16+
17+
const toggleStep = (index: number) => {
18+
setExpandedSteps(prev => {
19+
const next = new Set(prev)
20+
if (next.has(index)) next.delete(index)
21+
else next.add(index)
22+
return next
23+
})
24+
}
25+
1526
return (
1627
<div className="space-y-4">
1728
{steps.map((step, i) => {
1829
const isCompleted = i < activeStep
1930
const isActive = i === activeStep
2031
const isFuture = i > activeStep
32+
const isExpanded = expandedSteps.has(i)
2133

2234
return (
2335
<div key={i} className={`rounded-lg border ${
2436
isActive ? 'border-blue-600 bg-gray-900' :
2537
isCompleted ? 'border-green-800 bg-gray-900/50' :
2638
'border-gray-800 bg-gray-900/30 opacity-50'
2739
}`}>
28-
<div className="flex items-center gap-3 p-4">
40+
<div
41+
className={`flex items-center gap-3 p-4 ${isCompleted ? 'cursor-pointer select-none' : ''}`}
42+
onClick={() => isCompleted && toggleStep(i)}
43+
>
2944
<div className={`flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold ${
3045
isCompleted ? 'bg-green-700 text-green-200' :
3146
isActive ? 'bg-blue-600 text-white' :
@@ -41,9 +56,14 @@ export default function StepWizard({ steps, activeStep }: StepWizardProps) {
4156
{step.summary}
4257
</span>
4358
)}
59+
{isCompleted && (
60+
<span className={`text-xs text-gray-500 ${step.summary ? 'ml-2' : 'ml-auto'}`}>
61+
{isExpanded ? '\u25BC' : '\u25B6'}
62+
</span>
63+
)}
4464
</div>
45-
{isActive && (
46-
<div className="px-4 pb-4">
65+
{(isActive || isCompleted) && (
66+
<div className={`px-4 pb-4 ${isCompleted && !isExpanded ? 'hidden' : ''}`}>
4767
{step.content}
4868
</div>
4969
)}

samples/frontend/src/steps/CreateCustomer.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ import ResponsePanel from '../components/ResponsePanel'
44
import { apiPost } from '../lib/api'
55

66
const DEFAULT_BODY = JSON.stringify({
7+
platformCustomerId: `cust_${Math.random().toString(36).slice(2, 10)}`,
78
customerType: "INDIVIDUAL",
8-
platformCustomerId: `sample-customer-${Date.now()}`
9+
fullName: "Jack Doe",
10+
birthDate: "1992-03-25",
11+
address: {
12+
line1: "123 Pine Street",
13+
line2: "Unit 501",
14+
city: "Seattle",
15+
state: "WA",
16+
postalCode: "98101",
17+
country: "US"
18+
}
919
}, null, 2)
1020

1121
interface Props {

samples/frontend/src/steps/CreateExternalAccount.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,29 @@ export default function CreateExternalAccount({ customerId, onComplete, disabled
1717

1818
useEffect(() => {
1919
setBody(JSON.stringify({
20+
customerId: customerId ?? "<customer-id>",
2021
currency: "USD",
22+
platformAccountId: `acct_${Math.random().toString(36).slice(2, 10)}`,
2123
accountInfo: {
22-
accountType: "CHECKING",
23-
routingNumber: "021000021",
24-
accountNumber: "123456789"
24+
accountType: "US_ACCOUNT",
25+
accountNumber: "12345678901",
26+
routingNumber: "123456789",
27+
accountCategory: "CHECKING",
28+
bankName: "Chase Bank"
29+
},
30+
beneficiary: {
31+
beneficiaryType: "INDIVIDUAL",
32+
fullName: "Jack Doe",
33+
birthDate: "1992-03-25",
34+
nationality: "US",
35+
address: {
36+
line1: "123 Pine Street",
37+
line2: "Unit 501",
38+
city: "Seattle",
39+
state: "WA",
40+
postalCode: "98101",
41+
country: "US"
42+
}
2543
}
2644
}, null, 2))
2745
}, [customerId])

samples/frontend/src/steps/CreateQuote.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import ResponsePanel from '../components/ResponsePanel'
44
import { apiPost } from '../lib/api'
55

66
interface Props {
7+
customerId: string | null
78
externalAccountId: string | null
89
onComplete: (response: Record<string, unknown>) => void
910
disabled: boolean
1011
}
1112

12-
export default function CreateQuote({ externalAccountId, onComplete, disabled }: Props) {
13+
export default function CreateQuote({ customerId, externalAccountId, onComplete, disabled }: Props) {
1314
const [body, setBody] = useState('')
1415
const [response, setResponse] = useState<string | null>(null)
1516
const [error, setError] = useState<string | null>(null)
@@ -19,7 +20,8 @@ export default function CreateQuote({ externalAccountId, onComplete, disabled }:
1920
setBody(JSON.stringify({
2021
source: {
2122
sourceType: "REALTIME_FUNDING",
22-
currency: "USDC"
23+
currency: "USDC",
24+
customerId: customerId ?? "<customer-id>"
2325
},
2426
destination: {
2527
destinationType: "ACCOUNT",
@@ -28,7 +30,7 @@ export default function CreateQuote({ externalAccountId, onComplete, disabled }:
2830
lockedCurrencyAmount: 1000,
2931
lockedCurrencySide: "SENDING"
3032
}, null, 2))
31-
}, [externalAccountId])
33+
}, [customerId, externalAccountId])
3234

3335
const submit = async () => {
3436
setLoading(true)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/jsoneditor.tsx","./src/components/responsepanel.tsx","./src/components/stepwizard.tsx","./src/components/webhookstream.tsx","./src/lib/api.ts","./src/steps/createcustomer.tsx","./src/steps/createexternalaccount.tsx","./src/steps/createquote.tsx","./src/steps/executequote.tsx","./src/steps/sandboxfund.tsx"],"version":"5.9.3"}

0 commit comments

Comments
 (0)