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
BAformat (Bosnia mock) - Transfers start as
Pending, settle toBookedafter 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
activestatus, 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, typeRETRY
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
- All service files are marked
@deprecated— they must be replaced with real SDK implementations before production. - Console warnings are emitted on module load to make mock usage visible.
- Mock state uses
localStoragein browser, in-memory on server — resets on server restart. - Production API endpoints are configurable via environment variables.
- 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.