DEV Community

Cover image for JoopJS — I Built an 80+ Service TypeScript SDK for Enterprise Banking & Fintech
kundan singh
kundan singh

Posted on • Originally published at npmjs.com

JoopJS — I Built an 80+ Service TypeScript SDK for Enterprise Banking & Fintech

After months of building the same financial primitives across different projects — wallets, loan calculators, AML checks, encryption utilities — I got tired of rewriting everything from scratch. So I packaged it all into JoopJS: a single TypeScript SDK with 80+ production-ready services for enterprise banking and fintech applications.

Links:


What is JoopJS?

JoopJS is a framework-agnostic TypeScript SDK built for financial applications. It gives you 80+ services across 12 domains — all as plain TypeScript classes, no magic, no dependency injection required.

npm install joopjs
Enter fullscreen mode Exit fullscreen mode

That's it. No config files, no setup ceremonies.


The Philosophy

Three rules drove the design:

  1. Plain classesnew JoopWalletService(), nothing more. No decorators, no DI container.
  2. Numbers, not strings — amounts are number, timestamps are epoch milliseconds, currency is ISO-4217 string.
  3. Throw early, throw clearly — no silent failures. Every service throws a descriptive Error if something goes wrong.

What's Inside

🏦 Banking (26 services)

Digital wallets, loan servicing, FX forwards, ledger, reconciliation, card management, disputes, bill payments, split payments, remittance, insurance, standing orders, mandates, beneficiary management, virtual accounts, open banking, and more.

import { JoopDigitalWalletService } from 'joopjs';

const wallet = new JoopDigitalWalletService();

// Create a wallet for a user
const w = wallet.createWallet('user-001', { currency: 'USD' });

// Fund it
wallet.topUp(w.id, 5000);

// Pay a merchant
wallet.pay(w.id, 49.99, 'merchant-001', 'Netflix subscription');

console.log(wallet.getBalance(w.id)); // 4950.01

// React to balance changes in real-time
const unsub = wallet.balance$().subscribe(({ walletId, balance }) => {
  console.log(`Wallet ${walletId} balance: $${balance}`);
});
Enter fullscreen mode Exit fullscreen mode

📊 Finance (13 services)

Mutual funds with SIP, budget tracker, portfolio tracker, tax calculator, cash flow forecaster, expense tracker, goal savings, net worth tracker, investment calculator, financial reports, currency converter, loan calculator, fixed deposit.

import { JoopMutualFundService } from 'joopjs';

const mf = new JoopMutualFundService();

// Register a fund
mf.registerFund({
  id: 'EQ001',
  name: 'Equity Growth Fund',
  currentNav: 45.50,
  currency: 'USD',
  minInvestment: 100,
});

// Lump sum investment
mf.invest('EQ001', 'investor-001', 10000);

// Set up a monthly SIP
const sip = mf.createSip('EQ001', 'investor-001', 500, 'monthly', Date.now());

// NAV update flows through to all holdings automatically
mf.updateNav('EQ001', 48.00);

const holding = mf.getHolding('EQ001', 'investor-001');
console.log(`Return: ${holding.returnPercent.toFixed(2)}%`); // +5.49%
Enter fullscreen mode Exit fullscreen mode

🔒 Security (12 services)

AML engine, sanctions screening (OFAC/UN/EU lists), risk engine, fraud detection, KYC state machine, behavioral biometrics, threat intelligence, compliance policy engine, pen test reporter, data masking, vulnerability tracker, security audit log.

import { JoopSanctionsScreeningService } from 'joopjs';

const sanctions = new JoopSanctionsScreeningService();

// Load your sanctions lists
sanctions.loadList('ofac', ofacEntities);
sanctions.loadList('un', unEntities);

// Screen a customer before onboarding
const result = sanctions.screen({
  name:    'John Doe',
  country: 'US',
});

if (result.status === 'hit') {
  // { status: 'hit', matches: [{ entity, score, matchType }] }
  throw new Error(`Sanctions match: ${result.matches[0].entity.name}`);
}

if (result.status === 'review') {
  await escalateToCompliance(result);
}
Enter fullscreen mode Exit fullscreen mode

🔐 Auth & Sessions (10 services)

Auth service, OTP/TOTP, JWT with blacklisting, MFA, PKCE/OAuth 2.0, OIDC client, SSO (Azure AD, Okta, Google), biometric auth, RBAC permissions, rate limiter, session management with idle timeout and multi-tab sync.

import { JoopJwtService } from 'joopjs';

const jwt = new JoopJwtService({
  secret:   process.env.JWT_SECRET!,
  expiryMs: 3_600_000, // 1 hour
});

// Sign
const token = jwt.sign({ userId: 'u-001', role: 'admin' });

// Verify (returns null if invalid or expired)
const claims = jwt.verify(token);
if (!claims) throw new Error('Unauthorized');

// Revoke (blacklist)
jwt.revoke(token);
Enter fullscreen mode Exit fullscreen mode

🔑 Encryption (8 services)

AES-GCM symmetric, RSA-OAEP asymmetric, X25519 key exchange, end-to-end encryption, hashing with PBKDF2, key manager, secure random, field-level encryption, certificate utilities.

import { JoopX25519Service, JoopGcmService } from 'joopjs/encryption';

const dh  = new JoopX25519Service();
const gcm = new JoopGcmService();

// Alice and Bob each generate key pairs
const aliceKeys = dh.generateKeyPair();
const bobKeys   = dh.generateKeyPair();

// Derive the same shared secret (ECDH — no key transmitted)
const sharedSecret = dh.deriveSharedSecret(aliceKeys.privateKey, bobKeys.publicKey);
const sessionKey   = await dh.deriveAesKey(sharedSecret);

// Now encrypt messages with the derived session key
const { ciphertext, iv } = await gcm.encrypt('Hello from Alice', sessionKey);
const message = await gcm.decrypt(ciphertext, sessionKey, iv);
Enter fullscreen mode Exit fullscreen mode

Reactive Observables — Not RxJS

Every service exposes reactive streams via JoopSubject and JoopBehaviorSubject — lightweight primitives that work everywhere without requiring RxJS.

// Subscribe
const unsub = service.change$().subscribe(event => {
  console.log('Changed:', event);
});

// Unsubscribe when done
unsub();
Enter fullscreen mode Exit fullscreen mode

The key rule: use .next(value) to emit, .subscribe(callback) to listen. No .pipe(), no operators — but also no 300KB RxJS bundle.


Framework Integration

React Hook Pattern

import { useEffect, useState } from 'react';
import { JoopDigitalWalletService } from 'joopjs';

// Singleton outside component
const walletSvc = new JoopDigitalWalletService();

function useWalletBalance(walletId: string) {
  const [balance, setBalance] = useState(0);

  useEffect(() => {
    return walletSvc.balance$().subscribe(({ walletId: id, balance }) => {
      if (id === walletId) setBalance(balance);
    });
  }, [walletId]);

  return balance;
}

function BalanceCard({ walletId }: { walletId: string }) {
  const balance = useWalletBalance(walletId);
  return <div>Balance: ${balance.toFixed(2)}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Angular

import { inject, Component, OnInit, OnDestroy } from '@angular/core';
import { JOOP_WALLET } from 'joopjs/angular';

@Component({ selector: 'app-balance', template: `{{ balance | currency }}` })
export class BalanceComponent implements OnInit, OnDestroy {
  private wallet = inject(JOOP_WALLET);
  balance = 0;
  private unsub?: () => void;

  ngOnInit() {
    this.unsub = this.wallet.balance$().subscribe(({ balance }) => {
      this.balance = balance;
    });
  }

  ngOnDestroy() { this.unsub?.(); }
}
Enter fullscreen mode Exit fullscreen mode

Vue Composable

<script setup lang="ts">
import { useJoopWallet } from 'joopjs/vue';

const { balance, topUp, pay } = useJoopWallet('wallet-001');
</script>

<template>
  <div>Balance: ${{ balance }}</div>
  <button @click="topUp(100)">+ $100</button>
</template>
Enter fullscreen mode Exit fullscreen mode

Tree-shaking with Sub-path Imports

JoopJS ships 35 sub-path exports. Import only what you need:

import { JoopGcmService }   from 'joopjs/encryption';  // ~24KB
import { JoopAuthService }  from 'joopjs/auth';         // ~33KB
import { JoopAmlService }   from 'joopjs/security';     // ~45KB
import { validatePan }      from 'joopjs/india';        // ~9KB
Enter fullscreen mode Exit fullscreen mode

Every sub-path has "sideEffects": false — bundlers (Vite, webpack, Rollup, esbuild) will dead-code-eliminate anything you don't import.


TypeScript First

Everything is typed. All public types are prefixed Joop*:

import type {
  JoopWallet,
  JoopWalletStatus,
  JoopLoanAccount,
  JoopInstallment,
  JoopFxForward,
  JoopSanctionsEntity,
  JoopScreeningResult,
  JoopAuthSession,
} from 'joopjs';
Enter fullscreen mode Exit fullscreen mode

Full declaration files (.d.ts and .d.mts) ship with both the ESM and CJS builds.


The Numbers

  • 80+ services across 12 domains
  • 35 sub-path exports for tree-shaking
  • 143 test files, 2168 tests — all passing
  • Dual ESM + CJS output
  • Zero framework dependencies — React, Angular, Vue are all optional peer deps
  • 3 runtime dependencies@noble/ciphers, @noble/curves, base64-arraybuffer

Interactive Playground

Not sure which service does what? There's a live Swagger-style playground where you can call every method directly in the browser, fill in parameters, and see the output in real time.

👉 Try it live: kundan-leo.github.io/JoopJs/demo


Quick Start

npm install joopjs
Enter fullscreen mode Exit fullscreen mode
import { JoopDigitalWalletService, JoopAmlService } from 'joopjs';
import { JoopGcmService } from 'joopjs/encryption';

// Wallet
const wallet = new JoopDigitalWalletService();
const w = wallet.createWallet('u-001', { currency: 'USD' });
wallet.topUp(w.id, 1000);
console.log(wallet.getBalance(w.id)); // 1000

// Encryption
const gcm = new JoopGcmService();
const key = await gcm.generateKey();
const { ciphertext, iv } = await gcm.encrypt('secret data', key);

// AML
const aml = new JoopAmlService();
aml.addRule({ id: 'R001', name: 'Large Transaction', type: 'threshold', threshold: 10000, currency: 'USD', action: 'flag', enabled: true });
const alert = aml.screenTransaction({ id: 'TXN-001', amount: 12000, currency: 'USD', type: 'transfer', userId: 'u-001', timestamp: Date.now() });
console.log(alert?.severity); // 'high'
Enter fullscreen mode Exit fullscreen mode

What's Next

  • More India-specific services (GST, UPI, Aadhaar)
  • React hooks package (joopjs/react composable hooks)
  • More open banking connectors
  • Payment gateway adapters

Links

If you're building a fintech product and find yourself writing the same auth, wallet, or encryption boilerplate — give JoopJS a try. Feedback and contributions welcome!


Built by Kundan Singh

Top comments (0)