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:
- 📦 npm → npmjs.com/package/joopjs
- 🔗 Live Playground → kundan-leo.github.io/JoopJs/demo
- 💻 GitHub → github.com/kundan-leo/JoopJs
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
That's it. No config files, no setup ceremonies.
The Philosophy
Three rules drove the design:
-
Plain classes —
new JoopWalletService(), nothing more. No decorators, no DI container. -
Numbers, not strings — amounts are
number, timestamps are epoch milliseconds, currency is ISO-4217 string. -
Throw early, throw clearly — no silent failures. Every service throws a descriptive
Errorif 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}`);
});
📊 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%
🔒 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);
}
🔐 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);
🔑 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);
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();
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>;
}
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?.(); }
}
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>
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
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';
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
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'
What's Next
- More India-specific services (GST, UPI, Aadhaar)
- React hooks package (
joopjs/reactcomposable hooks) - More open banking connectors
- Payment gateway adapters
Links
| 📦 Install | npm install joopjs |
| 🔗 Playground | https://kundan-leo.github.io/JoopJs/demo/ |
| 💻 GitHub | https://github.com/kundan-leo/JoopJs |
| 📖 npm | https://www.npmjs.com/package/joopjs |
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)