Merchant Onboarding Flow
Flow: Merchant Onboarding
Document: LLD-006
Version: 1.0
Date: 2026-02-21
Author: Frontend Architect (AI Agent)
Status: Draft
Scope: Merchant registration, business verification, QR code generation, merchant dashboard, transaction monitoring, and settlement
1. Overview
Drop supports merchant onboarding for in-store QR payments. Any authenticated user can register as a merchant by providing business details and a valid Norwegian organization number. Upon registration, the user's role is upgraded from user to merchant, granting access to the merchant dashboard with transaction monitoring, daily summaries, and settlement views.
Key facts:
- Merchant registration requires authenticated BankID login
- Organization number verified against Brønnøysundregistrene (production; format validation in demo)
- QR code format:
drop://pay/{merchantId}
- Merchant fee: 1% per QR transaction (lower than card terminal 1.75-2.75%)
- Settlement: T+1 (planned) — funds from transactions deposited to merchant's bank account
2. Merchant Registration Flow
2.1 Sequence Diagram — Registration to Active Merchant
sequenceDiagram
actor User
participant App as Drop App
participant API as Drop API<br/>(/api/merchants)
participant Brreg as Brønnøysund<br/>Register (prod)
participant DB as Database
User->>App: Navigate to merchant registration
App->>App: useAuth() — verify authenticated
User->>App: Fill registration form<br/>(businessName, orgNumber, address, bankAccount)
App->>App: Client-side validation<br/>(name: validateName, orgNumber: 9 digits)
App->>API: POST /api/merchants/register<br/>{ businessName, orgNumber, address, bankAccount }
API->>API: JWT verification
API->>API: Validate input fields
alt Production
API->>Brreg: GET /enhetsregisteret/api/enheter/{orgNumber}
Brreg-->>API: { organisasjonsnummer, navn, organisasjonsform, ... }
API->>API: Verify business exists and is active
else Demo
API->>API: Format validation only (9 digits, unique)
end
API->>DB: Check org_number uniqueness
API->>DB: INSERT merchant (user_id, business_name, org_number, bank_account, fee_rate=0.01)
API->>DB: UPDATE users SET role = 'merchant' WHERE id = ?
API->>API: Generate QR URI: drop://pay/{merchantId}
API-->>App: 201 { merchant: { id, businessName, orgNumber, qrUri } }
App->>App: Show success screen<br/>(QR code display, "Vis min QR-kode" button)
App->>App: Navigate to merchant dashboard
3. Merchant Dashboard Components
3.1 Component Diagram
graph TD
subgraph "Merchant Dashboard"
Header["Header<br/>'VELKOMMEN' + business name<br/>+ Settings button"]
PeriodFilter["PeriodFilter<br/>(I dag / Uke / Maaned)"]
RevenueCard["RevenueCard<br/>(green gradient)"]
QRButton["QRButton<br/>'Vis min QR-kode'"]
TransactionList["TransactionList<br/>'Dagens transaksjoner'"]
BottomNav["BottomNav"]
end
subgraph "Revenue Card"
TotalRevenue["Total omsetning<br/>(4xl Fraunces font)"]
StatsGrid["Stats Grid (2 cols)"]
TxCount["Transaksjoner<br/>(count)"]
FeesInfo["Gebyrer betalt<br/>(NOK amount)"]
end
subgraph "Transaction Item"
CustomerIcon["CheckCircle2<br/>(green)"]
CustomerName["Customer Name<br/>(partially anonymized)"]
TxTime["Timestamp"]
TxAmount["Amount<br/>(+NOK, green)"]
end
RevenueCard --> TotalRevenue
RevenueCard --> StatsGrid
StatsGrid --> TxCount
StatsGrid --> FeesInfo
TransactionList --> CustomerIcon
TransactionList --> CustomerName
TransactionList --> TxTime
TransactionList --> TxAmount
4. Business Verification Checklist
4.1 Registration Requirements
| Requirement |
Field |
Validation |
Demo |
Production |
| Business name |
businessName |
validateName() — 1-100 chars, at least one letter, no HTML/script |
Format check only |
Format check |
| Organization number |
orgNumber |
Exactly 9 digits, unique in DB |
Format + uniqueness |
Brønnøysund API lookup |
| Business address |
address |
Optional, sanitized to 300 chars |
Optional |
Required for settlement |
| Payout bank account |
bankAccount |
Required, non-empty |
Format check |
IBAN/account validation |
| User authentication |
JWT |
Valid BankID session |
Required |
Required |
| KYC status |
user.kycStatus |
Must be approved |
Auto-approved via BankID |
BankID verification |
4.2 Brønnøysundregistrene Verification (Production)
| Check |
API |
Response Field |
Pass Criteria |
| Business exists |
GET /enhetsregisteret/api/enheter/{orgNr} |
organisasjonsnummer |
Matches input |
| Business is active |
Same |
registreringsdatoEnhetsregisteret |
Not null |
| Business type |
Same |
organisasjonsform.kode |
AS, ENK, NUF, DA, ANS |
| Business name match |
Same |
navn |
Approximate match to submitted name |
5. Settlement Schedule
5.1 Settlement Schedule Table
| Period |
Settlement Day |
Payout Time |
Details |
| Daily transactions |
T+1 |
08:00 CET |
Next business day after transaction |
| Weekend transactions |
Monday |
08:00 CET |
Batched for Monday payout |
| Holiday transactions |
Next business day |
08:00 CET |
Following Norwegian business day |
5.2 Settlement Calculation
5.3 Merchant Dashboard API
Endpoint: GET /api/merchants/dashboard?period={today|week|month}
Response:
{
"data": {
"revenue": 4350,
"transactionCount": 12,
"fees": 43.5,
"netRevenue": 4306.5,
"nextPayout": "2026-02-22T07:00:00.000Z",
"payoutTime": "Neste virkedag kl. 08:00"
}
}
6. QR Code Generation
QR endpoint: GET /api/merchants/qr
{
"data": {
"merchantId": "mer_a1b2c3d4e5f6g7h8",
"businessName": "Ahmetov Kebab",
"qrValue": "drop://pay/mer_a1b2c3d4e5f6g7h8",
"address": "Gronlandsleiret 44, 0190 Oslo"
}
}
7. Merchant Transaction Monitoring
7.1 Transaction List
Endpoint: GET /api/merchants/transactions?page=1&limit=20
| Field |
Value |
Privacy |
| Customer name |
First name + last initial |
Partially anonymized (e.g., "Ola N.") |
| Amount |
Positive NOK value |
Full amount shown |
| Status |
"Vellykket" (green) |
Color-coded |
| Timestamp |
HH:MM format |
Time only for today's transactions |
7.2 Period Filtering
| Period |
API Value |
Dashboard Label |
Aggregation |
| Today |
today |
I dag |
Sum of today's transactions |
| This week |
week |
Uke |
Mon-Sun aggregation |
| This month |
month |
Maaned |
Calendar month aggregation |
8. UI Components (Web)
8.1 Merchant Dashboard Layout
| Section |
Component |
Description |
| Header |
Business name (Fraunces) + Settings icon |
Welcome greeting + gear icon |
| Period tabs |
Button group (I dag, Uke, Maaned) |
Green active, gray inactive |
| Revenue card |
Green gradient card (#0B6E35 to #095a2b) |
Total omsetning (4xl), stats grid |
| QR button |
Full-width green button with QrCode icon |
"Vis min QR-kode" |
| Transaction list |
Card list with CheckCircle2 icons |
Customer name, time, +amount (green) |
| Navigation |
BottomNav (5 tabs) |
Standard bottom navigation |
8.2 Figma Reference
Source of truth: mockups/figma-make-export/src/app/screens/MerchantDashboard.tsx
9. Role Upgrade Flow
| Step |
Before |
After |
| 1 |
User has role = 'user' |
Same |
| 2 |
User submits merchant registration |
Same |
| 3 |
API validates and creates merchant record |
role = 'merchant' |
| 4 |
User's JWT still has old role |
Valid until refresh |
| 5 |
On next token refresh / re-login |
New JWT has role = 'merchant' |
Authorization gates:
GET /api/merchants/dashboard — requires role = 'merchant'
GET /api/merchants/qr — requires role = 'merchant'
GET /api/merchants/transactions — requires role = 'merchant'
| Feature |
Web |
Mobile |
| Merchant registration |
Full form via web UI |
Not implemented |
| Merchant dashboard |
Dedicated screen with stats |
Not implemented |
| QR code display |
Button to show QR |
Not implemented |
| Transaction monitoring |
List with period filter |
Not implemented |
| Settlement view |
Inline in dashboard stats |
Not implemented |
11. Accessibility Considerations (WCAG 2.1 AA)
| Requirement |
Implementation |
| Form validation |
Registration form shows inline error messages |
| Revenue card |
Uses both visual (bold text) and semantic (heading) for amounts |
| Period tabs |
Active tab indicated by color AND aria-selected |
| Transaction list |
Each item has descriptive text (customer, amount, status) |
| QR code |
Alt text: "QR-kode for {businessName}" |
| Color contrast |
White text on green gradient meets 4.5:1 |
| Settings icon |
Has aria-label "Innstillinger" |
12. Cross-References
No comments to display
No comments to display