# User Stories: Drop — Fintech Payment App

# User Stories: Drop — Fintech Payment App

> **Project:** Drop — Remittance + QR Payments
> **Version:** 1.0
> **Date:** 2026-02-23
> **Author:** John (AI Director)
> **Status:** Approved
> **Reviewers:** Alem Bašić (CEO)

## Document History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 0.1 | 2026-02-23 | John | Initial stories from user journeys in business-case-v2.md |

---

## 1. Epic Overview

| Epic ID | Epic Name | Business Goal | Story Count | Status | Target Release |
|---------|----------|--------------|-------------|--------|---------------|
| EP-01 | User Onboarding & Authentication | BO-02 — user acquisition | 4 | Done (Phase 0.5) | Phase 1 |
| EP-02 | Remittance (Send Money) | BO-01, BO-02 — revenue + users | 5 | Done (Phase 1) | Phase 1 |
| EP-03 | QR Merchant Payments | BO-01, BO-03 — revenue + merchants | 5 | Done (Phase 1) | Phase 1 |
| EP-04 | Open Banking (AISP) | BO-02 — user trust | 2 | Mock (Phase 2) | Phase 2 |
| EP-05 | Transaction History & Notifications | BO-02, BO-03 | 3 | Done (Phase 1) | Phase 1 |
| EP-06 | Merchant Analytics | BO-03 — merchant retention | 2 | Done (Phase 1) | Phase 1 |
| EP-07 | KYC & Compliance | BO-05 — regulatory | 2 | Mock (Phase 2) | Phase 2 |

---

## 2. Story Format Guide

**Standard Story Format:**
```
As a [persona/role],
I want [feature/action],
So that [benefit/outcome].
```

**Acceptance Criteria Format (Given/When/Then):**
```
Given [a precondition that must be true],
When [the user performs an action],
Then [the expected outcome occurs].
```

---

## 4. Story Backlog

### Epic EP-01: User Onboarding & Authentication

---

#### US-001: Account Registration (3-step onboarding)

| Attribute | Value |
|-----------|-------|
| Epic | EP-01: Onboarding |
| Priority | Must Have |
| Story Points | 8 |
| Sprint | Sprint 1 |
| Assigned To | Builder agent |
| Status | Done |
| FR Reference | FR-001 |
| BR Reference | BR-001, BR-002 |

**Story:**
As a **Norwegian resident (18+)**,
I want **to register for Drop with my email and date of birth**,
So that **I can access remittance and QR payments at lower fees than existing services**.

**Context:**
3-step flow: (1) personal details + DOB validation, (2) OTP on Norwegian phone (+47), (3) PIN setup. BankID integration in Phase 2 replaces DOB validation with real SCA.

**Acceptance Criteria:**
- [ ] **Given** valid email, password ≥8 chars, Norwegian phone (+47), DOB ≥18 years, **when** user submits registration, **then** 201 created; user proceeds to OTP step
- [ ] **Given** DOB under 18 years, **when** user submits, **then** 422 "Du må være minst 18 år"
- [ ] **Given** duplicate email, **when** submitted, **then** 409 "Email already in use"
- [ ] **Given** OTP sent, **when** user enters correct 6 digits, **then** user proceeds to PIN setup
- [ ] **Given** valid 4-digit PIN entered and confirmed, **when** submitted, **then** account activated; JWT cookie set

**Technical Notes:**
- Age validation: `(today - DOB) >= 18 years`
- OTP: 6-digit code; in MVP any code accepted (mock); real SMS in Phase 2
- Password: bcrypt 12 rounds

**UI/UX Notes:**
- Screen: `mockups/figma-make-export/src/components/Onboarding.js`
- 3-step progress indicator shown

**Dependencies:** Blocked by: None | Blocks: US-002, US-003, US-010

---

#### US-002: User Login

| Attribute | Value |
|-----------|-------|
| Epic | EP-01 |
| Priority | Must Have |
| Story Points | 3 |
| Sprint | Sprint 1 |
| Status | Done |
| FR Reference | FR-002 |

**Story:**
As a **registered Drop user**,
I want **to log in with my email and password**,
So that **I can access my account and make payments**.

**Acceptance Criteria:**
- [ ] **Given** valid email + password, **when** login submitted, **then** JWT cookie set; redirected to dashboard
- [ ] **Given** wrong password, **when** submitted, **then** 401 "Invalid email or password" (no enumeration)
- [ ] **Given** 10 failed attempts from same IP, **when** next attempt, **then** 429 rate limit error

**Dependencies:** Blocked by: US-001

---

#### US-003: Session Logout

| Attribute | Value |
|-----------|-------|
| Epic | EP-01 |
| Priority | Must Have |
| Story Points | 1 |
| Sprint | Sprint 1 |
| Status | Done |
| FR Reference | FR-003 |

**Story:**
As a **logged-in Drop user**,
I want **to log out of my account**,
So that **my session is securely terminated on shared devices**.

**Acceptance Criteria:**
- [ ] **Given** authenticated user, **when** they POST /api/auth/logout, **then** JWT cookie cleared; session revoked in DB
- [ ] **Given** logged-out user, **when** they access protected routes, **then** 401 redirect to login

**Dependencies:** Blocked by: US-002

---

#### US-004: BankID Verification (Phase 2)

| Attribute | Value |
|-----------|-------|
| Epic | EP-01 |
| Priority | Must Have |
| Story Points | 8 |
| Sprint | Sprint 4 (Phase 2) |
| Status | Backlog |
| FR Reference | FR-001 |

**Story:**
As a **Norwegian resident**,
I want **to verify my identity via Norwegian BankID**,
So that **my account is secured with Strong Customer Authentication (SCA) as required by PSD2**.

**Acceptance Criteria:**
- [ ] **Given** user at onboarding step 1, **when** they complete BankID verification, **then** DOB, name, and national ID extracted from BankID response
- [ ] **Given** BankID shows DOB < 18 years, **when** verification complete, **then** registration rejected with age error
- [ ] **Given** successful BankID, **when** verification complete, **then** kyc_status set to approved

**Dependencies:** Blocked by: BaaS provider confirmed (DEP-01)

---

### Epic EP-02: Remittance (Send Money)

---

#### US-010: Send Money to Recipient

| Attribute | Value |
|-----------|-------|
| Epic | EP-02: Remittance |
| Priority | Must Have |
| Story Points | 8 |
| Sprint | Sprint 1 |
| Status | Done |
| FR Reference | FR-020 |
| BR Reference | BR-003, BR-005 |

**Story:**
As a **Drop user who wants to support family abroad**,
I want **to send money to a recipient in Serbia/Pakistan/Bosnia/Poland/Turkey/EUR zone at 0.5% fee**,
So that **my family receives money faster and 10x cheaper than Western Union**.

**Context:**
"Amir wants to send 2,000 NOK to his mother Jasmina in Sarajevo. He opens Drop, taps 'Pošalji novac', selects Bosnia, enters Jasmina's IBAN, enters 2,000 NOK. Drop shows: mama receives 4,660 BAM, fee 10 NOK. He confirms. Mama gets SMS notification."

**Acceptance Criteria:**
- [ ] **Given** authenticated + KYC-approved user, **when** POST /api/transactions/remittance with amount 100-50,000 NOK and valid recipientId, **then** 201; transaction created; fee = amount × 0.005
- [ ] **Given** user with insufficient balance, **when** submitting, **then** 402 "Insufficient balance"
- [ ] **Given** amount < 100 NOK or > 50,000 NOK, **when** submitted, **then** 400 validation error
- [ ] **Given** unauthenticated user, **when** submitting, **then** 401 Unauthorized
- [ ] **Given** KYC-pending user, **when** submitting, **then** 403 "KYC verification required"

**Technical Notes:**
- 6 corridors in MVP: NOK→RSD, NOK→BAM, NOK→PKR, NOK→TRY, NOK→PLN, NOK→EUR
- In Phase 2: real PISP via BaaS
- Exchange rates from `exchange_rates` table, updated daily

**UI/UX Notes:** Screen: `mockups/figma-make-export/src/components/SendMoney.js`

**Dependencies:** Blocked by: US-001, US-012, US-013

---

#### US-011: View Exchange Rates

| Attribute | Value |
|-----------|-------|
| Epic | EP-02 |
| Priority | Must Have |
| Story Points | 2 |
| Sprint | Sprint 1 |
| Status | Done |
| FR Reference | FR-021 |

**Story:**
As a **Drop user planning to send money**,
I want **to see current exchange rates before confirming a transfer**,
So that **I know exactly how much my recipient will receive**.

**Acceptance Criteria:**
- [ ] **Given** any user, **when** GET /api/rates, **then** all 6 NOK exchange rates returned (RSD, BAM, PKR, TRY, PLN, EUR)
- [ ] **Given** GET /api/rates/RSD, **when** called, **then** specific NOK→RSD rate returned
- [ ] **Given** GET /api/rates/XXX invalid, **when** called, **then** 404 Not Found

**Dependencies:** None

---

#### US-012: Add Recipient

| Attribute | Value |
|-----------|-------|
| Epic | EP-02 |
| Priority | Must Have |
| Story Points | 3 |
| Sprint | Sprint 1 |
| Status | Done |
| FR Reference | FR-022 |

**Story:**
As a **Drop user sending money regularly to the same person**,
I want **to save recipient details (name, IBAN, country)**,
So that **I don't have to re-enter them every time I send money**.

**Acceptance Criteria:**
- [ ] **Given** authenticated user, **when** POST /api/recipients with valid name, IBAN, country, **then** recipient saved
- [ ] **Given** authenticated user, **when** GET /api/recipients, **then** all user's recipients returned
- [ ] **Given** invalid IBAN format, **when** submitted, **then** 422 validation error

**Dependencies:** Blocked by: US-002

---

### Epic EP-03: QR Merchant Payments

---

#### US-020: Pay Merchant via QR Scan

| Attribute | Value |
|-----------|-------|
| Epic | EP-03: QR Payments |
| Priority | Must Have |
| Story Points | 8 |
| Sprint | Sprint 2 |
| Status | Done |
| FR Reference | FR-030 |
| BR Reference | BR-004, BR-005 |

**Story:**
As a **Drop user at a local merchant**,
I want **to scan the merchant's QR code and pay directly from my bank account**,
So that **I pay 1% merchant fee instead of Vipps' 1.75-2.75%, without needing cash or card terminal**.

**Context:**
"Amir walks into Ahmet's kebab shop. On the counter is a Drop QR sticker. Amir opens Drop, taps 'Skeniraj', points camera at QR → 'Ahmetov Kebab' appears. He enters 129 NOK, taps 'Betal'. Ahmet's phone buzzes: 129 NOK received."

**Acceptance Criteria:**
- [ ] **Given** authenticated + KYC-approved user, **when** POST /api/transactions/qr-payment with valid merchantId and amount ≥1 NOK, **then** 201; merchant_fee = amount × 0.01
- [ ] **Given** invalid merchantId, **when** submitted, **then** 404 "Merchant not found"
- [ ] **Given** amount < 1 NOK, **when** submitted, **then** 400 validation error
- [ ] **Given** missing merchantId, **when** submitted, **then** 400 validation error

**UI/UX Notes:** Screen: `mockups/figma-make-export/src/components/ScanQR.js`

**Dependencies:** Blocked by: US-001, US-021

---

#### US-021: Merchant Business Registration

| Attribute | Value |
|-----------|-------|
| Epic | EP-03 |
| Priority | Must Have |
| Story Points | 5 |
| Sprint | Sprint 2 |
| Status | Done |
| FR Reference | FR-031 |

**Story:**
As a **local business owner**,
I want **to register my business in Drop and receive a QR code**,
So that **I can start accepting payments at 1% fee within 5 minutes**.

**Acceptance Criteria:**
- [ ] **Given** authenticated user, **when** POST /api/merchants with business_name, bank_account, address, **then** merchant created with unique QR code
- [ ] **Given** merchant, **when** GET /api/merchants/me, **then** merchant details + QR code returned
- [ ] **Given** QR code scanned by Drop consumer, **when** payment submitted, **then** merchant correctly identified

**Dependencies:** Blocked by: US-002

---

#### US-022: Merchant Dashboard

| Attribute | Value |
|-----------|-------|
| Epic | EP-03 |
| Priority | Should Have |
| Story Points | 5 |
| Sprint | Sprint 2 |
| Status | Done |
| FR Reference | FR-032 |

**Story:**
As a **merchant using Drop**,
I want **to see my daily/weekly/monthly transaction volume and fees**,
So that **I can understand my Drop revenue and reconcile with my bank statement**.

**Acceptance Criteria:**
- [ ] **Given** authenticated merchant, **when** GET /api/merchants/dashboard?period=week, **then** total_transactions, gross_volume, total_fees returned for that period
- [ ] **Given** GET with period=today, period=month, **when** called, **then** correct period data returned

**Dependencies:** Blocked by: US-021

---

### Epic EP-04: Open Banking (AISP)

---

#### US-030: View Bank Account Balance

| Attribute | Value |
|-----------|-------|
| Epic | EP-04: Open Banking |
| Priority | Should Have |
| Story Points | 5 |
| Sprint | Sprint 4 (Phase 2) |
| Status | Mock (Phase 2) |
| FR Reference | FR-040 |

**Story:**
As a **Drop user**,
I want **to see my Norwegian bank account balance in the Drop app**,
So that **I know if I have enough funds before sending money — without needing to open my banking app**.

**Acceptance Criteria:**
- [ ] **Given** authenticated user with linked bank account, **when** GET /api/bank-accounts, **then** masked account number + balance returned
- [ ] **Given** no linked account, **when** viewing accounts screen, **then** prompt to link via BankID

**Technical Notes:** Real AISP requires BaaS partner (DEP-01) — currently mock data in demo

**Dependencies:** Blocked by: DEP-01 (BaaS partner)

---

### Epic EP-05: Transaction History & Notifications

---

#### US-040: Transaction History

| Attribute | Value |
|-----------|-------|
| Epic | EP-05 |
| Priority | Should Have |
| Story Points | 3 |
| Sprint | Sprint 1 |
| Status | Done |
| FR Reference | FR-050 |

**Story:**
As a **Drop user**,
I want **to see a history of all my transactions**,
So that **I can track my spending and verify payments went through**.

**Acceptance Criteria:**
- [ ] **Given** authenticated user, **when** GET /api/transactions, **then** all user's transactions returned (most recent first)
- [ ] **Given** ?type=remittance query, **when** called, **then** only remittance transactions returned

**Dependencies:** Blocked by: US-010 or US-020

---

#### US-041: Transaction Notifications

| Attribute | Value |
|-----------|-------|
| Epic | EP-05 |
| Priority | Should Have |
| Story Points | 3 |
| Sprint | Sprint 2 |
| Status | Done |
| FR Reference | FR-060 |

**Story:**
As a **Drop user or merchant**,
I want **to receive notifications when transactions occur**,
So that **I know immediately when money is sent or received**.

**Acceptance Criteria:**
- [ ] **Given** completed transaction, **when** GET /api/notifications, **then** new notification appears in list
- [ ] **Given** user marks notification as read, **when** PATCH /api/notifications/[id], **then** status updated to read

**Dependencies:** Blocked by: US-010 or US-020

---

## 5. Story Estimation Guide

| Points | Complexity | Examples |
|--------|-----------|---------|
| 1 | Trivial | Fix a label, add config option |
| 2 | Simple | Read-only data display, static endpoint |
| 3 | Moderate | CRUD for one entity, simple filter |
| 5 | Complex | Multi-step form, API integration |
| 8 | Very Complex | New module with CRUD + logic + UI + tests |
| 13+ | Too Large | Break into smaller stories |

---

## 6. Definition of Ready Checklist

Before a story enters a sprint:

- [ ] Story is in As a / I want / So that format
- [ ] Story has at least 2 acceptance criteria (Given/When/Then)
- [ ] Story has been estimated in story points
- [ ] Dependencies are identified and not blocking
- [ ] UI/UX design exists (Figma Make export for core screens)
- [ ] Technical approach is understood
- [ ] Priority assigned (MoSCoW)
- [ ] Story size ≤ 8 points
- [ ] FR reference documented

---

## 7. Story Map

```
USER JOURNEY:  [Register] → [Verify KYC] → [Send Money] → [Pay QR] → [View History]

Phase 1        US-001       US-007(mock)  US-010        US-020     US-040
(Demo)         US-002                     US-011        US-021     US-041
               US-003                     US-012        US-022

Phase 2        US-004       US-007(real)  [Real PISP]   [Real PISP] US-030
(Banking)      (BankID)     (Sumsub)
```

---

## 8. Backlog Summary

| Epic | Total Stories | Estimated Points | Done | Remaining |
|------|-------------|-----------------|------|-----------|
| EP-01: Onboarding | 4 | 20 | 3 | 1 (US-004, Phase 2) |
| EP-02: Remittance | 3 | 13 | 3 | 0 |
| EP-03: QR Payments | 3 | 18 | 3 | 0 |
| EP-04: Open Banking | 1 | 5 | 0 | 1 (Phase 2) |
| EP-05: History/Notifications | 2 | 6 | 2 | 0 |
| **Total** | **13** | **62** | **11** | **2 (Phase 2)** |

---

## Approval

| Role | Name | Date | Signature |
|------|------|------|-----------|
| Author | John (AI Director) | 2026-02-23 | Approved (AI) |
| Product Owner | John | 2026-02-23 | Approved |
| AI Director (John) | John | 2026-02-23 | Approved |