06 - Frontend System Design
Why This Matters for Glomopay
JD says: "Build and own checkout SDKs" and "Architect scalable frontend systems." They may ask you to design a checkout SDK or dashboard architecture at a high level.
Design 1: Checkout SDK Architecture
Scenario: Build an embeddable checkout SDK that merchants integrate into their sites.
Requirements
- Merchants embed it with a script tag or npm package
- Supports multiple payment methods (card, UPI, wallet, netbanking)
- Must be secure (PCI compliance — card data never touches merchant server)
- Customizable (colors, logo, payment methods)
- Works in any framework (React, Vue, vanilla JS)
Architecture
┌─────────────────────────────────────────────────────────┐
│ Merchant Website │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ <iframe> (Checkout SDK) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Payment │ │ Payment Form │ │ │
│ │ │ Methods │ │ (card/upi/etc) │ │ │
│ │ │ Selector │ │ │ │ │
│ │ └──────────┘ └──────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────┐│ │
│ │ │ Order Summary + Pay Button ││ │
│ │ └──────────────────────────────────┘│ │
│ └──────────────────────────────────────┘ │
│ │
│ <script src="glomopay-sdk.js"></script> │
│ GlomoPay.open({ orderId, amount, merchantKey }) │
└─────────────────────────────────────────────────────────┘
│
│ postMessage (events)
▼
┌─────────────────────────┐
│ Glomopay Backend │
│ - Payment processing │
│ - Token management │
│ - Webhook to merchant │
└─────────────────────────┘Key Decisions
1. iframe for security
- Card data stays in Glomopay's iframe (PCI compliant)
- Merchant can't access card numbers via JS
- Communication via
postMessage
2. SDK public API
// Merchant-facing API — simple, minimal surface
interface GlomoPayConfig {
merchantKey: string;
orderId: string;
amount: number;
currency: string;
customerEmail?: string;
theme?: { primaryColor: string; logo?: string };
paymentMethods?: PaymentMethod[];
onSuccess: (result: PaymentResult) => void;
onError: (error: PaymentError) => void;
onClose: () => void;
}
// Usage
GlomoPay.open(config);
GlomoPay.close();3. Inside the iframe (React app)
src/
components/
CheckoutShell.tsx # Layout + step management
PaymentMethodList.tsx # Method selection
CardForm.tsx # Card input (tokenized)
UPIForm.tsx # UPI input
OrderSummary.tsx # Review step
context/
CheckoutContext.tsx # State: step, method, amount
services/
PaymentService.ts # API calls to Glomopay backend
PostMessageBridge.ts # Communication with parent window
types/
index.ts # Shared types4. postMessage protocol
// SDK → Merchant
type SDKEvent =
| { type: 'GLOMOPAY_READY' }
| { type: 'GLOMOPAY_SUCCESS'; payload: PaymentResult }
| { type: 'GLOMOPAY_ERROR'; payload: PaymentError }
| { type: 'GLOMOPAY_CLOSE' }
| { type: 'GLOMOPAY_RESIZE'; height: number };
// Merchant → SDK
type MerchantEvent =
| { type: 'GLOMOPAY_INIT'; config: GlomoPayConfig }
| { type: 'GLOMOPAY_CLOSE' };Design 2: Merchant Dashboard
Scenario: Real-time dashboard showing transactions, settlements, refunds.
Architecture
┌────────────────────────────────────────────────┐
│ Dashboard App (Next.js) │
│ │
│ ┌──────────┐ ┌───────────────────────────┐ │
│ │ Sidebar │ │ Main Content │ │
│ │ │ │ │ │
│ │ Overview │ │ ┌───────────────────────┐ │ │
│ │ Txns │ │ │ Filters + Date Range │ │ │
│ │ Settle │ │ └───────────────────────┘ │ │
│ │ Refunds │ │ ┌───────────────────────┐ │ │
│ │ Settings │ │ │ Stats Cards (KPIs) │ │ │
│ │ │ │ └───────────────────────┘ │ │
│ │ │ │ ┌───────────────────────┐ │ │
│ │ │ │ │ Chart (volume/trend) │ │ │
│ │ │ │ └───────────────────────┘ │ │
│ │ │ │ ┌───────────────────────┐ │ │
│ │ │ │ │ Transaction Table │ │ │
│ │ │ │ │ (virtualized, 10k+) │ │ │
│ │ │ │ └───────────────────────┘ │ │
│ └──────────┘ └───────────────────────────┘ │
└────────────────────────────────────────────────┘State Architecture
Server State (TanStack Query):
- transactions (paginated, filterable)
- settlements
- dashboard stats
- user/merchant profile
Client State (Zustand):
- sidebar collapsed/expanded
- active filters
- selected date range
- table sort/column config
Form State (React Hook Form):
- refund form
- settings form
- filter formKey Patterns
1. Optimistic updates for refunds
const refundMutation = useMutation({
mutationFn: api.createRefund,
onMutate: async (refundData) => {
await queryClient.cancelQueries({ queryKey: ['transactions'] });
const previous = queryClient.getQueryData(['transactions']);
// Optimistically update
queryClient.setQueryData(['transactions'], (old) =>
old.map(tx =>
tx.id === refundData.transactionId
? { ...tx, status: 'refund_pending' }
: tx
)
);
return { previous };
},
onError: (err, data, context) => {
queryClient.setQueryData(['transactions'], context.previous); // rollback
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['transactions'] });
},
});2. Real-time updates via WebSocket
function useRealtimeTransactions() {
const queryClient = useQueryClient();
useEffect(() => {
const ws = new WebSocket(WS_URL);
ws.onmessage = (event) => {
const update = JSON.parse(event.data);
if (update.type === 'NEW_TRANSACTION') {
queryClient.setQueryData(['transactions'], (old) => [update.data, ...old]);
}
if (update.type === 'STATUS_UPDATE') {
queryClient.setQueryData(['transactions'], (old) =>
old.map(tx => tx.id === update.data.id ? { ...tx, ...update.data } : tx)
);
}
};
return () => ws.close();
}, [queryClient]);
}3. Virtualized table for performance
- 10k+ transactions → virtualize with @tanstack/react-virtual
- Server-side pagination for initial load
- Client-side sort/filter for current page
Design 3: Design System (Component Library)
If asked about this:
Structure:
packages/
ui/ # Core components
src/
Button/
Button.tsx
Button.test.tsx
Button.stories.tsx # Storybook
index.ts
Input/
Card/
Modal/
...
package.json
tokens/ # Design tokens
colors.ts
spacing.ts
typography.ts
icons/ # Icon components
src/
generated/ # Auto-generated from SVGs
Key decisions:
- Tailwind for styling (utility-first, no runtime cost)
- Compound component pattern for complex components
- Storybook for documentation and visual testing
- Changesets for versioning
- Turbo Repo for monorepo build orchestrationQuestions They Might Ask
Q: How would you version a checkout SDK? A: Semantic versioning. Breaking changes in major. SDK loaded via versioned URL (/v1/checkout.js). Support N-1 version for migration period.
Q: How do you handle SDK errors gracefully? A: Never crash the merchant's page. Wrap everything in error boundaries. Failed payment → show error in SDK UI, emit error event to merchant. Network failure → retry with exponential backoff.
Q: How would you handle multiple currencies? A: Intl.NumberFormat for display. Amount always stored in smallest unit (paise/cents). Currency config from backend per merchant. Never do floating point arithmetic on money.