Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Fintech engineering expertise — payment processing (Stripe, Plaid), PCI DSS compliance, financial data modeling (double-entry bookkeeping), fraud detection patterns, bank-grade security (encryption, tokenization), open banking APIs, cryptocurrency/blockchain integration, regulatory compliance (KYC/AML), and idempotent financial transaction design. Use for payment systems, banking apps, trading platforms, and fintech infrastructure.
version
1.1.0
model
sonnet
invoked_by
both
user_invocable
true
tools
["Bash","Read","Write","Edit","WebFetch"]
best_practices
["All financial operations must be idempotent (use idempotency keys)","Use double-entry bookkeeping — never modify existing ledger entries","Never store raw card numbers — use tokenization (Stripe Elements, etc.)","Encrypt PII at rest (AES-256-GCM) and in transit (TLS 1.3)","All monetary values must be stored as integers (cents/smallest unit)","Every financial action requires an audit log entry"]
error_handling
graceful
streaming
not_applicable
verified
false
lastVerifiedAt
"2026-03-15T00:00:00.000Z"
source
builtin
trust_score
100
provenance_sha
67920cb5352819de
Fintech Engineer Skill
Overview
Financial technology engineering covering payment processing, ledger design, compliance, security, and fintech API integration. Core principle: correctness over speed — financial bugs have real monetary consequences.
Critical Rules
IRON LAWS OF FINANCIAL ENGINEERING:
1. Monetary values = integers (cents/pence/satoshis) — NEVER floats
2. All writes are idempotent (idempotency keys on every mutation)
3. Double-entry bookkeeping — debits always equal credits
4. Audit log every financial event — immutable, append-only
5. Fail safe — on error, roll back fully or do nothing
6. Never store card PANs — use tokenization providers
7. Assume network failures — design for exactly-once delivery
Monetary Value Handling
// ALWAYS store as integer (smallest currency unit)// NEVER use floating point for money// BAD — floating point arithmetic errorsconst price = 9.99;
const tax = price * 0.08; // 0.7992000000000001 — WRONG// GOOD — integer arithmetic in centsconst priceInCents = 999; // $9.99const taxInCents = Math.round(priceInCents * 0.08); // 80 cents = $0.80// Currency formatting (display only — never compute with these)functionformatMoney(cents: number, currency = 'USD'): string {
returnnewIntl.NumberFormat('en-US', {
style: 'currency',
currency,
minimumFractionDigits: 2,
}).format(cents / );
}
= {
: ;
: ;
};
(): {
(a. !== b.) ();
{ : a. + b., : a. };
}
100
// Money type for type safety
type
Money
amount
number
// Integer in smallest unit
currency
string
// ISO 4217 (USD, EUR, GBP)
function
addMoney
a: Money, b: Money
Money
if
currency
currency
throw
new
Error
'Currency mismatch'
return
amount
amount
amount
currency
currency
Double-Entry Ledger Design
-- Ledger accounts tableCREATE TABLE accounts (
id UUID PRIMARY KEYDEFAULT gen_random_uuid(),
type TEXT NOT NULLCHECK (type IN ('asset', 'liability', 'equity', 'revenue', 'expense')),
name TEXT NOT NULL,
currency TEXT NOT NULLDEFAULT'USD',
created_at TIMESTAMPTZ NOT NULLDEFAULT NOW()
);
-- Immutable ledger entries (double-entry)CREATE TABLE ledger_entries (
id UUID PRIMARY KEYDEFAULT gen_random_uuid(),
transaction_id UUID NOT NULL, -- Groups debit+credit pairs
account_id UUID NOT NULLREFERENCES accounts(id),
amount BIGINTNOT NULL, -- Positive = debit, Negative = credit
currency TEXT NOT NULL,
description TEXT,
reference_type TEXT, -- 'payment', 'refund', 'fee', etc.
reference_id TEXT, -- External ID (Stripe charge ID, etc.)
created_at TIMESTAMPTZ NOT NULLDEFAULT NOW(),
-- Ledger entries are NEVER updated or deletedCONSTRAINT no_zero_amount CHECK (amount !=0)
);
-- Account balance view (computed from ledger)CREATEVIEW account_balances ASSELECT
account_id,
currency,
SUM(amount) AS balance
FROM ledger_entries
GROUPBY account_id, currency;
// Card data — NEVER store, log, or transmit raw PANs// Use Stripe Elements or similar to keep card data out of your systems// WRONG — PCI violation:// const cardNumber = req.body.cardNumber; // Never touches your server with Stripe Elements// CORRECT — Stripe Elements flow:// 1. Browser: stripe.createToken(cardElement) → returns { token: { id: 'tok_xxx' } }// 2. Browser sends tok_xxx to your server// 3. Server uses tok_xxx with Stripe API — never sees card data// Masking for logsfunctionmaskPAN(pan: string): string {
return`****-****-****-${pan.slice(-4)}`;
}
// PCI-required: no card data in logsfunctionsanitizeForLogging(obj: Record<string, unknown>): Record<string, unknown> {
constREDACT_FIELDS = ['card_number', 'cvv', 'pan', 'ssn', 'account_number'];
returnObject.fromEntries(
Object.entries(obj).map(([k, v]) => (REDACT_FIELDS.includes(k) ? [k, '[REDACTED]'] : [k, v]))
);
}
For European payments, handle SCA challenges properly:
// On frontend: handle requires_action statusconst { paymentIntent, error } = await stripe.confirmCardPayment(clientSecret);
if (paymentIntent?.status === 'requires_action') {
// Stripe.js handles 3DS challenge automatically// Server webhook will fire payment_intent.succeeded when complete
}
// Never mark order as paid until webhook payment_intent.succeeded fires// Frontend confirmation is NOT authoritative