Skip to main content

External Services Integration

External Services Integration

Project: Drop Version: 0.1.0 Date: 2026-02-23 Author: Platform Architect (AI) Status: In Review Reviewers: Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 2026-02-23 Platform Architect (AI) Initial draft from source code and services analysis

1. Overview

Drop integrates with the following external services. Service mode is controlled by NEXT_PUBLIC_SERVICE_MODE env var (mock | production).

Service Status Purpose
BankID OIDC PRODUCTION Norwegian eID authentication — mandatory for all users
Sumsub PRODUCTION KYC/identity verification — WebSDK + webhook
Open Banking (AISP/PISP) TBD — provider selection pending Read bank balance, initiate payments
Slack PRODUCTION Operational alerting via incoming webhook
BetterStack PRODUCTION External uptime monitoring
Swan Open Banking DEPRECATED Was planned, no longer selected
Stripe Issuing MOCK (future) Card issuance — no SDK, no API keys

Key rule: Sumsub is the ONLY connected external service with real API calls. All other services except BankID and Slack are currently mocked.


2. BankID OIDC — Norwegian eID

2.1 Purpose

BankID is Drop's sole authentication mechanism. All users must authenticate with Norwegian BankID (eID) before accessing any features. This satisfies PSD2 Strong Customer Authentication (SCA) requirements.

2.2 Integration Details

Parameter Value
Protocol OIDC (OpenID Connect)
Flow Authorization Code (PKCE)
Identity claim pid — Norwegian national ID (11 digits)
Age verification DOB encoded in pid — must be >= 18
User storage national_id_hash = SHA-256(pid) — never store raw pid
Web callback BANKID_CALLBACK_URL (e.g., https://getdrop.no/api/auth/bankid/callback)
Mobile callback BANKID_CALLBACK_URL_MOBILE = drop://auth/callback (deep link)
JWKS verification Verified against BankID JWKS endpoint

2.3 Environment Variables

Variable Required Description
BANKID_CLIENT_ID Yes (prod) BankID OIDC client ID
BANKID_CLIENT_SECRET Yes (prod) BankID OIDC client secret — stored in Secrets Manager
BANKID_CALLBACK_URL Yes (prod) Web redirect URI after auth
BANKID_CALLBACK_URL_MOBILE Yes (mobile) Mobile deep link redirect
BANKID_AUTHORIZE_URL No Default: BankID production authorize endpoint
BANKID_TOKEN_URL No Default: BankID production token endpoint
BANKID_JWKS_URL No Default: BankID production JWKS
BANKID_ISSUER No Default: BankID production issuer
BANKID_MOCK No true skips real OIDC (dev/staging only)

2.4 Mock Mode (BANKID_MOCK=true)

In development and staging, BANKID_MOCK=true enables a simulated BankID flow:

  • Skips the full OIDC redirect
  • Auto-generates a mock pid and authenticates a test user
  • No network calls to BankID

2.5 Error Handling

Scenario Handling
State mismatch (CSRF) 400 — reject callback
Code exchange failure 500 — log error, redirect to error page
Invalid ID token 401 — reject
User age < 18 403 — reject with age error
BankID provider down 503 — user sees error message

3. Sumsub — KYC / Identity Verification

3.1 Purpose

Sumsub provides KYC (Know Your Customer) identity verification. This is the only production-ready external service with real API calls.

Verification checks:

  • Document authenticity (passport, ID card, driver's license, residence permit)
  • Liveness check (selfie is a real person)
  • Face match (selfie matches document photo)
  • Sanctions check (not on international sanctions lists)
  • PEP check (not a politically exposed person)

3.2 Integration Architecture

User app (web/mobile)
  │
  ├── 1. GET /api/kyc/initiate → server calls Sumsub createApplicant + getAccessToken
  │                               returns { token } for WebSDK
  │
  ├── 2. Frontend loads Sumsub WebSDK with token
  │       User submits documents + selfie via Sumsub UI
  │
  └── 3. Sumsub webhook → POST /api/kyc/webhook
          Server updates user.kyc_status = 'approved' | 'rejected'

3.3 Key Functions

Function Description
createApplicant({ externalUserId, email?, phone? }) Create KYC applicant in Sumsub
getAccessToken(applicantId) Get WebSDK access token (30min TTL)
getApplicantStatus(applicantId) Check current verification status
getVerificationResult(applicantId) Get per-check breakdown
onWebhook(callback) Register webhook listener for status updates

3.4 Applicant Status Flow

init → pending → queued → completed → (reviewAnswer: GREEN=approved / RED=rejected / RETRY)
Review Answer Drop kyc_status Meaning
GREEN approved User verified, can transact
RED rejected User rejected, cannot transact
RETRY pending Document unreadable, user must resubmit

3.5 Environment Variables

Variable Required Description
SUMSUB_API_URL No Default: https://api.sumsub.com
SUMSUB_APP_TOKEN Yes (prod) Sumsub API token — stored in Secrets Manager
SUMSUB_SECRET_KEY Yes (prod) Webhook signature verification key

3.6 Mock Mode

When NEXT_PUBLIC_SERVICE_MODE=mock:

  • 90% approval rate, 10% rejection
  • 3-second simulated delay
  • Risk score: 15 (approved) or 85 (rejected)
  • Rejected label: DOCUMENT_UNREADABLE, type: RETRY

3.7 Error Handling

Scenario Handling
Sumsub API down Log error, user sees "verification temporarily unavailable"
Webhook signature invalid Reject webhook with 401
Applicant already exists Reuse existing applicant
Document rejected Set kyc_status=rejected, user can retry with different document

4. Open Banking — AISP + PISP (Provider TBD)

4.1 Purpose

  • AISP (Account Information): Read user's bank balance from their Norwegian bank account
  • PISP (Payment Initiation): Initiate transfers directly from user's bank account

This is Drop's core PSD2 architecture — users' funds never move into Drop's control.

4.2 Current State

Component Status
AISP balance read Mocked (Swan deprecated)
PISP payment initiation Mocked
Real provider TBD — provider selection pending
Swan mock Deprecated — will be removed

4.3 Planned Integration Architecture

User initiates payment
  │
  ├── 1. AISP: Refresh balance from bank → update bank_accounts.balance
  ├── 2. PISP: Initiate payment at provider → returns payment_id
  ├── 3. User bank sends consent redirect if needed
  ├── 4. Provider webhook: payment settled → update transaction.status
  └── 5. Notify user

4.4 Required Environment Variables (Future)

Variable Description
OPENBANKING_CLIENT_ID Provider OAuth client ID
OPENBANKING_CLIENT_SECRET Provider OAuth client secret
OPENBANKING_CALLBACK_URL OAuth redirect URI
OPENBANKING_WEBHOOK_SECRET Webhook signature key

4.5 Supported Banks (Target)

Norwegian banks supporting PSD2 (via Open Banking provider):

  • DNB
  • Nordea
  • Handelsbanken
  • SpareBank 1
  • Sbanken / DNB (merged)

5. Slack — Operational Alerting

5.1 Purpose

Slack receives operational alerts from Drop's internal alerting system (src/lib/alerts.ts).

Channel: #drop-ops on alai-talk.slack.com

5.2 Alert Types

Alert Severity Trigger
App startup Info (ℹ️) Application boots successfully
App shutdown Info (ℹ️) SIGTERM/SIGINT received
Error spike Critical (🚨) > 5 errors in 60 seconds
Unhandled exception Critical (🚨) process.on('uncaughtException')
Custom alert Variable sendAlert() called manually

5.3 Integration Details

Parameter Value
Method HTTP POST to Slack Incoming Webhook URL
Auth Webhook URL contains auth token
Cooldown 10 minutes per alert title (in-memory)
Fallback When SLACK_WEBHOOK_URL not set → console.log only

5.4 Environment Variables

Variable Required Description
SLACK_WEBHOOK_URL Yes (prod) Slack incoming webhook URL — stored in Secrets Manager

5.5 Alert Format

🚨 Drop Production Alert — Critical

Title: Error spike detected
Message: 8 errors in the last 60 seconds

Time: 2026-02-23 14:30 UTC

6. BetterStack — External Uptime Monitoring

6.1 Purpose

BetterStack provides external uptime monitoring independent of Drop's infrastructure — detects total infrastructure failures that internal health checks miss.

6.2 Monitors Configured

Monitor URL Check
Health Endpoint https://drop.alai.no/api/health HTTP 200 + "status":"ok"
Landing Page https://drop.alai.no HTTP 200 + Send penger
US East Health https://drop.alai.no/api/health HTTP 200 from US East

6.3 Alert Escalation

Minute 0:   DOWN → Slack #drop-ops
Minute 5:   Still down → Email [email protected]
Minute 15:  Still down → SMS +47 40 47 42 51 (requires paid plan)

6.4 Status Page

Public status page: https://drop-status.betteruptime.com

Setup guide: docs/infrastructure/BETTERSTACK-SETUP.md


7. Deprecated Services

Swan Open Banking (Deprecated)

Status: DEPRECATED — no longer the planned Open Banking provider. Mock code exists at services/mock-swan.ts but will be removed.

Why deprecated: Commercial and technical reasons — specific provider TBD via procurement process.

Stripe Issuing (Mock/Dev — Future)

Status: MOCK ONLY — no Stripe SDK installed. File: services/mock-stripe.ts.

Purpose: Future card issuance feature (virtual + physical cards). All card feature flags default to false. Requires card issuing partner before activation.

Not to be confused with: Stripe Payments — Drop does NOT use Stripe for payments.


8. Service Initialization

Source: src/lib/services/index.ts

// Called once on app startup
await initializeServices();

// In tests — reset all mock state
resetMockServices();

Mock state uses in-memory storage (server-side) — resets on process restart.



Approval

Role Name Date Signature
Author Platform Architect (AI) 2026-02-23
Reviewer
Approver Alem Bašić