Skip to main content

Services

Drop External Services

Source: src/drop-app/src/lib/services/

Overview

Drop uses a PSD2 pass-through model — it never holds customer money. AISP reads bank balances via Open Banking, PISP initiates payments from the user's own bank account.

Drop integrates with three external service providers. All are currently mock implementations for development and testing. Production implementations require replacing mock files with real SDK integrations.

Service mode is controlled by NEXT_PUBLIC_SERVICE_MODE env var (default: mock).

Source: services/index.ts:21-30

export const config = {
  mode: (process.env.NEXT_PUBLIC_SERVICE_MODE || "mock") as "mock" | "production",
  endpoints: {
    swan: process.env.SWAN_API_URL || "https://api.swan.io",
    stripe: process.env.STRIPE_API_URL || "https://api.stripe.com",
    sumsub: process.env.SUMSUB_API_URL || "https://api.sumsub.com",
  },
};

Swan — Open Banking / PSD2 Provider (FUTURE)

Note: In the pass-through model, Swan (or similar provider) would serve as the PSD2 AISP/PISP integration — reading bank balances and initiating payments from the user's own bank account. Drop does NOT use Swan for IBAN generation or balance management (no wallet model).

File: services/mock-swan.ts Production docs: https://docs.swan.io/ Purpose: FUTURE — Open Banking AISP (balance read) + PISP (payment initiation). Currently mocked.

Interfaces

Interface Description
SwanAccount Bank account with IBAN, BIC, balance, status
SwanTransaction SEPA credit/debit with status tracking

Functions

Function Signature Description
createAccount (userId) → SwanAccount Create new bank account with IBAN
getAccount (accountId) → SwanAccount | null Retrieve account details
getBalance (accountId) → {available, pending} Get balance breakdown
initiateTransfer ({fromAccountId, toIban, amount, ...}) → SwanTransaction Initiate SEPA credit transfer
simulateIncoming ({toAccountId, amount, fromIban}) → SwanTransaction Simulate incoming transfer
getTransactions (accountId, limit?) → SwanTransaction[] List recent transactions
onWebhook (callback) → void Register webhook listener

Mock Behavior

  • 200-800ms simulated latency per call
  • IBAN generated in BA format (Bosnia mock)
  • Transfers start as Pending, settle to Booked after 2 seconds
  • State persisted to localStorage (browser) or in-memory (server)
  • _testHelpers.reset() clears all mock data

Account Statuses

Opened | Closing | Closed

Transaction Types

SepaCredit | SepaDebit | CardTransaction

Transaction Statuses

Pending | Booked | Rejected


Stripe — Card Issuing (FUTURE — feature-flagged)

Note: Cards are a FUTURE feature, gated behind feature flags (all default to false). Requires a card issuing partner before activation.

File: services/mock-stripe.ts Production docs: https://stripe.com/docs/issuing Purpose: FUTURE — Virtual and physical card management, authorization processing

Interfaces

Interface Description
StripeCard Card with type, brand, status, spending limits
StripeCardDetails Full card number, CVC, expiry (virtual only)
StripeAuthorization Card authorization with merchant info

Functions

Function Signature Description
createVirtualCard ({cardholderName, spendingLimit?}) → StripeCard Issue virtual Visa card
orderPhysicalCard ({cardholderName, shippingAddress}) → StripeCard Order physical card
getCardDetails (cardId) → StripeCardDetails Get full card details (virtual only)
setCardStatus (cardId, active) → StripeCard Freeze/unfreeze card
updateSpendingLimit (cardId, limit) → StripeCard Update spending limit
getCards () → StripeCard[] List all cards
simulateAuthorization ({cardId, amount, merchant}) → StripeAuthorization Simulate card purchase
getAuthorizations (cardId?) → StripeAuthorization[] List authorizations

Mock Behavior

  • Virtual cards created instantly with active status, expire in 3 years
  • Physical cards start as pending, activate after 5 seconds (simulating shipping)
  • Default spending limit: 5,000 (virtual), 10,000 (physical)
  • Authorization declined if: card not active OR spending limit exceeded
  • Brand is always Visa
  • Mock card numbers: 4242 4242 4242 {last4}

Card Statuses

active | inactive | canceled | pending

Authorization Statuses

pending | approved | declined


Sumsub — KYC/Identity Verification

File: services/mock-sumsub.ts Production docs: https://docs.sumsub.com/ Purpose: User identity verification, document submission, liveness checks

Interfaces

Interface Description
SumsubApplicant KYC applicant with review status
SumsubDocument Identity document (passport, ID card, etc.)
SumsubVerificationResult Verification outcome with per-check breakdown

Functions

Function Signature Description
createApplicant ({externalUserId, email?, phone?}) → SumsubApplicant Create KYC applicant
getAccessToken (applicantId) → {token, expiresAt} Get WebSDK token (30min)
submitDocument (applicantId, document) → void Submit ID document
submitSelfie (applicantId, selfieData) → void Submit selfie for liveness
getApplicantStatus (applicantId) → SumsubApplicant Check applicant status
getVerificationResult (applicantId) → SumsubVerificationResult Get verification details
forceApprove (applicantId) → void Force approve (testing only)
onWebhook (callback) → void Register webhook listener

Mock Behavior

  • Verification completes after 3-second delay
  • 90% approval rate in mock mode
  • Risk score: 15 (approved) or 85 (rejected)
  • Rejected with label DOCUMENT_UNREADABLE, type RETRY

Applicant Statuses

init | pending | queued | completed | onHold

Review Answers

GREEN (approved) | RED (rejected) | RETRY

Verification Checks

Check Description
documentAuthenticity Document is genuine
livenessCheck Selfie is a real person
facematch Selfie matches document photo
sanctionsCheck Not on sanctions lists
pepCheck Not a politically exposed person

Document Types

PASSPORT | ID_CARD | DRIVERS | RESIDENCE_PERMIT


Service Initialization

Source: services/index.ts:36-48

// Call on app startup
await initializeServices();

// Reset all mocks (testing)
resetMockServices();

Important Notes

  1. All service files are marked @deprecated — they must be replaced with real SDK implementations before production.
  2. Console warnings are emitted on module load to make mock usage visible.
  3. Mock state uses localStorage in browser, in-memory on server — resets on server restart.
  4. Production API endpoints are configurable via environment variables.
  5. The current backend API routes do NOT call these service modules directly — they use the database layer (db.ts) for all operations. The services are available for future integration when real providers are connected.