npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@classytic/payee

v0.3.0

Published

Subject-polymorphic payout-recipient engine — verified payout methods (mobile money / bank transfer) + KYC status + an assertPayable eligibility gate. KYC execution is bring-your-own via a ComplianceBridge. Consumed by @classytic/revenue (settlement) an

Readme

@classytic/payee

Subject-polymorphic payout-recipient engine — verified payout methods, KYC status, and one assertPayable eligibility gate. The piece that sits below the money engines: @classytic/revenue moves money and @classytic/payrun runs payouts; payee answers where to send it and whether the party is allowed to be paid.

Aligned with the universal fintech shape — a PayeeAccount ≈ Stripe connected account / PayPal recipient (holds accountHolderType + KYC verification state + outstanding requirements), and a PayoutMethod ≈ Stripe external account / ISO 20022 creditor account (cross-rail: US ACH, EU IBAN/BIC, UK sort code, mobile money). Any payment processor on MongoDB can adopt it.

KYC execution is bring-your-own via a ComplianceBridge (Sumsub / Persona / Onfido / Stripe Identity / a manual review queue). The package stores status, never the verification logic. It moves no money, so it has no LedgerBridge.

Install

npm i @classytic/payee

Peers: @classytic/mongokit >=3.16, @classytic/primitives >=0.7.1, @classytic/repo-core >=0.6, mongoose >=9.4, zod >=4.

Quick start

import { createPayee } from '@classytic/payee';

const payee = await createPayee({
  connection: mongoose.connection,
  tenant: { fieldType: 'objectId' },        // or `false` for platform-level referrers
  bridges: { compliance: myKycProvider },   // optional — defaults to manual review
  fieldCipher: myKmsCipher,                 // PII encryption at rest — see below
});

const subject = { subjectRef: 'orgprofile_123', subjectModel: 'OrgProfile' };

await payee.repositories.payeeAccount.ensureAccount(subject, ctx);

const method = await payee.repositories.payoutMethod.addMethod(
  { ...subject, kind: 'mobile_money', methodCode: 'bkash', walletNumber: '+8801700000000', currency: 'BDT', countryCode: 'BD' },
  ctx,
);
await payee.repositories.payoutMethod.verify(String(method._id), { verifiedBy: 'mgr' }, ctx);
await payee.repositories.payoutMethod.setDefault(String(method._id), ctx);

await payee.repositories.payeeAccount.submitForReview(subject.subjectRef, subject.subjectModel, { level: 'full' }, ctx);
await payee.repositories.payeeAccount.recordDecision(subject.subjectRef, subject.subjectModel, { approved: true }, ctx);

The gate

// Both revenue.settlement and payrun.markPaid call this before disbursing.
const { account, method } = await payee.assertPayable(subjectRef, subjectModel, ctx);
// throws PayeeNotPayableError unless KYC === 'verified' AND a verified default method exists.

Honesty note (TOCTOU): assertPayable is a read — a method can be disabled between the gate and the disbursement. Call it inside the disbursing transaction (thread ctx.session, as the integration example below does) when that window matters.

PII at rest (fieldCipher)

Payout credentials (accountNumber / iban / walletNumber) are the fields a DB dump turns into fraud. Wire a cipher and they're encrypted before persistence (with last4 derived from the plaintext first); list/get APIs and arc adapters only ever see ciphertext + last4:

const payee = await createPayee({
  connection,
  fieldCipher: {
    encrypt: (v) => kms.encrypt(v),   // KMS envelope / AES-GCM — your choice
    decrypt: (v) => kms.decrypt(v),
  },
});

// The ONLY plaintext path — call at disbursement time, never from list views:
const { method, destination } = await payee.revealDestination(String(m._id), ctx);
rail.send({ account: destination.accountNumber, ... });

No cipher = plaintext passthrough (dev only — revealDestination works identically either way, so host code doesn't change when you turn encryption on). Dedupe uses the host-computed fingerprint, never the ciphertext.

Re-KYC

verified → pending is a legal transition (periodic AML re-verification / expired documents): startKyc / submitForReview on a verified account pauses assertPayable until the new decision lands.

Integration (host orchestrates — payee imports neither engine)

await payrun.withTransaction(ctx, async (tx) => {
  const { method } = await payee.assertPayable(subjectRef, subjectModel, tx);
  const run = await payrun.repositories.payrollRun.markPaid(runId, input, tx);
  await revenue.repositories.settlement.schedule(
    { recipientId: String(method._id), payoutMethod: method.kind, bankTransferDetails: method.details },
    { session: tx.session },
  );
});

Arc

Wire the repositories with createAdapter(engine.models.X, engine.repositories.x); expose status transitions as Arc actions and gate raw CRUD (rules 27–30). Register the event catalog:

import { payeeEventDefinitions } from '@classytic/payee/events';
for (const def of payeeEventDefinitions) registry.register(def);

Bridges (all optional, rule 23)

  • ComplianceBridge — KYC execution (startVerification / getStatus / onDecision).
  • NotificationBridge — "you're verified" / "payout method added".
  • SubjectBridge — resolve the subject (creator/referrer) to a display snapshot. Same shape as payrun's, so a host wires it once.

Cleanup (org delete / off-boarding)

mongokit's deleteMany (tenant-scoped, hook-fired, soft-delete-aware) backs two engine verbs so the host never hand-rolls cleanup:

  • engine.purgeSubject(subjectRef, subjectModel, ctx, { mode? }) — a creator/referrer off-boards. Soft delete (default) frees the unique-per-subject slot so they can re-onboard later; { mode: 'hard' } is GDPR erasure.
  • engine.purgeOrganization(ctx, { mode? }) — the organization is deleted. Tenant-scoped, so only that org's accounts + methods go.

Wire into the host's org-delete flow:

// Better Auth org hook (or arc defineResource({ onTenantDelete }))
afterDeleteOrganization: async (org) => {
  await payee.purgeOrganization(
    { organizationId: org.id, actorId: 'system', actorKind: 'system' },
    { mode: 'hard' },
  );
},

Conventions

Built on @classytic/primitives (PaymentMethodKind, PersonName, ApprovalChain, OperationContext, DomainEvent/EventTransport/OutboxStore) and @classytic/repo-core (tenant, errors, pagination) per PACKAGE_RULES.md. ESM-only, no sourcemaps/declaration maps, subpath exports (no barrels), verbatimModuleSyntax. Tests follow testing-infrastructure.md (unit + integration, tenant probe, event no-drift).