@txmo-dev/midas-fintech
v1.0.0
Published
Open-source TypeScript fintech primitives for African payments — Money value object, currency handling, PCI-compliant card masking, transaction types, and amount utilities. Powers Sikani SmartPOS in partnership with Sikani Technologies Pty Ltd.
Maintainers
Readme
@txmo-dev/midas-fintech
Open-source TypeScript fintech primitives for African payments. Built by Midas Path Software Solutions in partnership with Sikani Technologies Pty Ltd.
Why this exists
Processing payments across Ghana and West Africa involves a set of well-defined but tedious primitives: pesewa-accurate money arithmetic, PCI-compliant card masking, mobile-money network types, gateway amount formatting, and transaction state machines.
@txmo-dev/midas-fintech packages these primitives into a single, zero-dependency,
tree-shakeable library so every team building on GHS / West-African rails gets
them right from day one.
Powers
| Product | Role | |---------|------| | Sikani SmartPOS (Sikani Technologies Pty Ltd) | Core money & card utilities across buyer-app, seller-app, and admin-web | | Midas HMS (Midas Path Software Solutions) | Billing module — patient invoices, insurance claim amounts |
Installation
npm install @txmo-dev/midas-fintech
# or
yarn add @txmo-dev/midas-fintechWorks in Node.js ≥ 18, React Native (via Metro), and modern browsers.
API Reference
Money — Immutable money value object
import { Money } from "@txmo-dev/midas-fintech";
const price = Money.of(49.99, "GHS");
const fee = Money.of(1.50, "GHS");
const total = price.add(fee); // Money { amount: 51.49, currency: "GHS" }
const receipt = total.format(); // "₵ 51.49"
const pesewas = total.minorUnits; // 5149
// From gateway response (minor units)
const gateway = Money.fromMinorUnits(5149, "GHS");Methods: add, subtract, multiply, divide, format, toJSON
Predicates: isZero, isPositive, isNegative, equals, greaterThan, lessThan
Card utilities (PCI DSS compliant)
import { maskPan, maskPanShort, isValidLuhn, detectCardBrand, isValidExpiry } from "@txmo-dev/midas-fintech";
maskPan("4111111111111111") // "411111******1111" (first-6 + last-4)
maskPanShort("4111111111111111") // "****1111" (receipt display)
isValidLuhn("4111111111111111") // true
detectCardBrand("4111111111111111") // "visa"
isValidExpiry(12, 28) // trueNever pass raw PANs to analytics, logs, or API responses. Use these helpers at the point of capture and only pass masked representations downstream.
Transaction utilities
import {
generateTransactionRef,
isTerminalStatus,
canReverse,
maskPhoneNumber,
MOMO_NETWORK_LABELS,
} from "@txmo-dev/midas-fintech";
generateTransactionRef() // "TXN-M4BCDEF-XY7KP"
generateTransactionRef("PAY") // "PAY-M4BCDEF-XY7KP"
isTerminalStatus("completed") // true
isTerminalStatus("pending") // false
canReverse("completed") // true
maskPhoneNumber("0244567890") // "024****890"
MOMO_NETWORK_LABELS["MTN"] // "MTN MoMo"Amount utilities (gateway formatting)
import { toMinorUnits, fromMinorUnits, formatForGateway, percentageFee } from "@txmo-dev/midas-fintech";
toMinorUnits(12.50) // 1250 (pesewas)
fromMinorUnits(1250) // 12.5
formatForGateway(1250) // "000000001250" (12-digit Theteller/PaySwitch format)
percentageFee(100, 1.5) // 1.5 (GHS 1.50 on a GHS 100 transaction)Currency metadata
import { getCurrencyInfo, isCurrencyCode } from "@txmo-dev/midas-fintech";
getCurrencyInfo("GHS") // { code: "GHS", name: "Ghanaian Cedi", symbol: "₵", minorUnits: 2 }
isCurrencyCode("XYZ") // falseSupported: GHS, USD, EUR, GBP, NGN, KES, ZAR
Real-world example
import {
Money,
maskPan, maskPanShort, detectCardBrand, isValidLuhn,
generateTransactionRef, isTerminalStatus, maskPhoneNumber,
toMinorUnits, formatForGateway, percentageFee,
} from "@txmo-dev/midas-fintech";
// ── 1. Validate and mask card ──────────────────────────────────────────────
const rawPan = "4111111111111111";
if (!isValidLuhn(rawPan)) throw new Error("Invalid card number");
const brand = detectCardBrand(rawPan); // "visa"
const masked = maskPan(rawPan); // "411111******1111"
const receipt = maskPanShort(rawPan); // "****1111"
// ── 2. Build the amount ────────────────────────────────────────────────────
const subtotal = Money.of(49.99, "GHS");
const vat = subtotal.multiply(0.125); // 12.5% VAT
const total = subtotal.add(vat); // ₵ 56.24
console.log(total.format()); // "₵ 56.24"
console.log(total.minorUnits); // 5624
// ── 3. Format for gateway ──────────────────────────────────────────────────
const gatewayAmount = formatForGateway(total.minorUnits);
// → "000000005624" (Theteller / PaySwitch 12-digit format)
// ── 4. Generate transaction reference ─────────────────────────────────────
const txRef = generateTransactionRef("TXN");
// → "TXN-M4BCDEF-XY7KP"
// ── 5. Post to gateway ─────────────────────────────────────────────────────
const payload = {
amount: gatewayAmount, // "000000005624"
reference: txRef,
card: masked, // never send raw PAN
phone: maskPhoneNumber("0244567890"), // "024****890"
};
// ── 6. Handle status ───────────────────────────────────────────────────────
const status = "completed";
console.log(isTerminalStatus(status)); // trueReact component example
import { maskPanShort, detectCardBrand } from "@txmo-dev/midas-fintech";
function CardPreview({ pan }: { pan: string }) {
const brand = detectCardBrand(pan); // "visa" | "mastercard" | ...
const display = maskPanShort(pan); // "****1111"
return (
<div className="card-preview">
<span className="brand">{brand.toUpperCase()}</span>
<span className="number">{display}</span>
</div>
);
}Contributing
This package is open-source and welcomes contributions. Please open an issue or pull request at TXMO-dev/midas-packages.
License
MIT © Midas Path Software Solutions
