# 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

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

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

| Field | Formula | Example |
|-------|---------|---------|
| Gross revenue | Sum of all QR payment amounts | 4 350 NOK |
| Merchant fee | Gross x 1% (fee_rate) | 43.50 NOK |
| Net payout | Gross - fee | 4 306.50 NOK |
| Payout account | `merchant.bankAccount` | IBAN or Norwegian account |

### 5.3 Merchant Dashboard API

**Endpoint:** `GET /api/merchants/dashboard?period={today|week|month}`

**Response:**
```json
{
  "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

| Property | Value |
|----------|-------|
| Format | URI: `drop://pay/{merchantId}` |
| Encoding | Standard QR code (alphanumeric) |
| Generation | Client-side (from returned `qrUri`) |
| Display | "Vis min QR-kode" button on merchant dashboard |
| Printing | Merchant can screenshot or print for in-store display |

**QR endpoint:** `GET /api/merchants/qr`

```json
{
  "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`
- Welcome header with business name
- Period filter tabs (I dag / Uke / Maaned)
- Green gradient revenue card with stats
- QR code button
- Transaction list with customer names

---

## 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'`

---

## 10. Platform Differences

| 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

- **Merchant registration API:** `POST /api/merchants/register` — See [API Reference](../../backend/API-REFERENCE.md)
- **Merchant dashboard API:** `GET /api/merchants/dashboard` — See [API Reference](../../backend/API-REFERENCE.md)
- **Merchant QR API:** `GET /api/merchants/qr` — See [API Reference](../../backend/API-REFERENCE.md)
- **Merchant transactions API:** `GET /api/merchants/transactions` — See [API Reference](../../backend/API-REFERENCE.md)
- **Merchants schema:** `merchants` table — See [Database Schema](../../backend/DATABASE-SCHEMA.md)
- **Transactions schema:** `transactions` table — See [Database Schema](../../backend/DATABASE-SCHEMA.md)
- **Component overview:** See [component-overview.md](../hld/component-overview.md)
- **Figma merchant dashboard:** `mockups/figma-make-export/src/app/screens/MerchantDashboard.tsx`
- **QR payment flow:** See [flow-qr-payment.md](flow-qr-payment.md)
- **Authentication flow:** See [flow-login-authentication.md](flow-login-authentication.md)