@molecule/api-kyc-stripe-identity
v1.0.1
Published
Stripe Identity KYC bond for molecule.dev — document + selfie verification, normalized status, signed webhooks.
Maintainers
Readme
@molecule/api-kyc-stripe-identity
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Stripe Identity KYC bond for molecule.dev.
Implements the {@link KycProvider} contract from @molecule/api-kyc using
Stripe Identity's REST API. Verification sessions are document + selfie
(document), id-number (id_number), or hosted address checks
(provider-dependent). Webhooks are verified against
STRIPE_IDENTITY_WEBHOOK_SECRET.
Setup
- Create a Stripe account and enable Stripe Identity.
- Set
STRIPE_SECRET_KEYandSTRIPE_IDENTITY_WEBHOOK_SECRETin the API environment (or passsecretKey/webhookSecretto {@link createProvider}). - Configure a webhook endpoint subscribed to
identity.verification_session.verified,identity.verification_session.requires_input, andidentity.verification_session.canceled. - Bond at startup:
setProvider(provider).
Quick Start
import { setProvider } from '@molecule/api-kyc'
import { provider } from '@molecule/api-kyc-stripe-identity'
setProvider(provider)Type
provider
Installation
npm install @molecule/api-kyc-stripe-identity @molecule/api-bond @molecule/api-kyc @molecule/api-secretsAPI
Interfaces
CreateKycSessionOptions
Caller-supplied parameters when creating a verification session.
interface CreateKycSessionOptions {
/**
* Caller's stable identifier for the end user. Stored as provider metadata
* so webhook events and status lookups can be correlated.
*/
userId: string
/** Type of identity check to request. */
type: KycVerificationType
/**
* URL to send the user to after they complete or abandon the
* provider-hosted flow. Some providers also expose a hosted URL — see
* {@link KycSession.url}.
*/
returnUrl?: string
/**
* Free-form key/value pairs forwarded to the provider as session metadata.
* Useful for correlating with caller-side records (case id, app id, etc.).
* Values are coerced to strings by the provider.
*/
metadata?: Record<string, string>
}KycProvider
KYC provider contract.
Each method is stack-neutral; bonds for different providers (Stripe Identity, Persona, Onfido, Sumsub) expose the same shape. Providers throw on errors with sanitized messages — never leaking API keys, webhook secrets, or verbatim provider error bodies that may echo credentials.
interface KycProvider {
/**
* Creates a new verification session at the provider.
*
* @param options - Session parameters (user, type, return URL, metadata).
* @returns The created session, including a hosted URL where applicable.
*/
createVerificationSession(options: CreateKycSessionOptions): Promise<KycSession>
/**
* Fetches the current status of a verification session.
*
* @param sessionId - Provider-specific session id from
* {@link KycSession.sessionId}.
* @returns The normalized session status.
*/
getVerificationStatus(sessionId: string): Promise<KycSessionStatus>
/**
* Cancels a verification session. Idempotent — canceling an already-canceled
* session SHOULD return the same status without throwing.
*
* @param sessionId - Provider-specific session id from
* {@link KycSession.sessionId}.
* @returns The session status after cancellation.
*/
cancelVerificationSession(sessionId: string): Promise<KycSessionStatus>
/**
* Verifies the signature of an inbound webhook and returns the normalized
* event. Throws if the signature is invalid or the payload cannot be parsed.
*
* @param headers - Inbound request headers (verbatim — bonds extract the
* right signature header).
* @param body - Raw request body bytes (do NOT pass a parsed JSON object —
* most providers sign the exact byte sequence).
* @returns The normalized webhook event.
*/
processWebhook(headers: KycWebhookHeaders, body: string | Buffer): Promise<KycWebhookEvent>
}KycSession
A verification session created with a KYC provider.
Sessions are the unit of state — once created they progress through
statuses ({@link KycStatus}) until terminal (verified or canceled).
interface KycSession {
/** Provider-specific session identifier. Opaque to callers. */
sessionId: string
/**
* Provider-hosted URL to redirect the user to. Some providers (e.g. those
* using a client-side SDK with a single-use token) MAY return `null` —
* callers must then use the SDK directly.
*/
url: string | null
/**
* Optional epoch-millis expiry of the hosted session. After this time the
* session URL stops working and the caller must create a new session.
*/
expiresAt?: number
}KycSessionStatus
Result of {@link KycProvider.getVerificationStatus}.
interface KycSessionStatus {
/** Provider-specific session identifier. */
sessionId: string
/** Normalized status across providers. */
status: KycStatus
/**
* Verification type at create time. Useful for callers that do not store
* the type alongside the session id.
*/
type?: KycVerificationType
/**
* Provider-specific reason code when status is `requires_input` or
* `canceled`. Opaque string — meant for logging / display, not branching.
*/
lastErrorCode?: string
/** Provider-specific human-readable error reason. */
lastErrorReason?: string
}KycWebhookEvent
A normalized webhook event. Returned by {@link KycProvider.processWebhook} after the signature has been verified.
interface KycWebhookEvent {
/** The normalized event type. */
type: KycWebhookEventType
/** Provider-specific session identifier the event applies to. */
sessionId: string
/** Caller-supplied user id stored in session metadata. */
userId?: string
/** Verification type at create time. */
verificationType?: KycVerificationType
/** Caller-supplied metadata stored on the session. */
metadata?: Record<string, string>
/** Provider-specific reason code for failure events. */
lastErrorCode?: string
/** Provider-specific human-readable failure reason. */
lastErrorReason?: string
/** Raw provider event object — kept for round-tripping / debugging. */
raw?: Record<string, unknown>
}StripeIdentityProviderOptions
Configuration options for {@link createProvider}.
Every field defaults to environment variables so handlers can call
createProvider() with no arguments. Tests inject overrides
(especially {@link StripeIdentityProviderOptions.fetch} and
{@link StripeIdentityProviderOptions.apiBaseUrl}).
interface StripeIdentityProviderOptions {
/**
* Stripe secret API key. Defaults to `process.env.STRIPE_SECRET_KEY`.
*/
secretKey?: string
/**
* Stripe Identity webhook signing secret. Defaults to
* `process.env.STRIPE_IDENTITY_WEBHOOK_SECRET`.
*/
webhookSecret?: string
/**
* Override the Stripe API base URL. Defaults to `https://api.stripe.com`.
* Useful for tests pointing at a fake Stripe server.
*/
apiBaseUrl?: string
/** Request timeout in milliseconds. Defaults to `15_000`. */
timeoutMs?: number
/**
* Override the Stripe API version sent on every request. Defaults to
* Stripe's pinned `2024-06-20`.
*/
apiVersion?: string
/**
* Maximum allowed clock-drift between the bond's host and Stripe when
* verifying webhook signatures, in seconds. Defaults to `300` (5 minutes),
* matching Stripe's recommendation.
*/
webhookToleranceSeconds?: number
/**
* Replace the global fetch implementation. Tests inject a stub here;
* production callers should leave it unset.
*/
fetch?: typeof fetch
}Types
KycStatus
Normalized verification status, common across all KYC bonds.
pending— the session has been created but the user has not started.requires_input— provider has rejected or paused; the user must take another action (resubmit document, retry selfie, etc.).processing— provider is currently reviewing submitted material.verified— verification succeeded.canceled— the session was canceled (by caller, user, or provider).
type KycStatus = 'pending' | 'requires_input' | 'processing' | 'verified' | 'canceled'KycVerificationType
Type of identity check requested for a verification session.
document— government-issued photo ID + selfie liveness check.id_number— verifies a government-issued number (e.g. SSN) without a document.address— verifies the user's residential address.
Provider support varies; bonds MAY throw when asked for a type they do not support.
type KycVerificationType = 'document' | 'id_number' | 'address'KycWebhookEventType
Discriminated event type emitted by {@link KycProvider.processWebhook}.
Bonds normalize provider-specific event names into one of these three variants. Provider-specific raw payload is preserved in {@link KycWebhookEvent.raw} so callers needing extra detail can opt in.
type KycWebhookEventType =
'verification.verified' | 'verification.requires_input' | 'verification.canceled'KycWebhookHeaders
Headers required for {@link KycProvider.processWebhook}. Lower-cased keys.
Different providers use different header names for the signature; bonds are responsible for picking the right one(s) from this map. Callers should pass the request's headers verbatim.
type KycWebhookHeaders = Record<string, string | string[] | undefined>Functions
createProvider(options)
Creates a Stripe Identity provider.
function createProvider(options?: StripeIdentityProviderOptions): KycProvideroptions— Optional configuration. Falls back toSTRIPE_SECRET_KEY/STRIPE_IDENTITY_WEBHOOK_SECRETenv vars whensecretKey/webhookSecretare omitted.
Returns: A {@link KycProvider} implementation.
verifyStripeSignature(payload, signatureHeader, secret, toleranceSeconds, now)
Verifies a Stripe webhook signature. Equivalent to
Stripe.webhooks.constructEvent minus the SDK dependency.
function verifyStripeSignature(
payload: string | Buffer<ArrayBufferLike>,
signatureHeader: string | undefined,
secret: string,
toleranceSeconds: number,
now?: number,
): voidpayload— The raw request body.signatureHeader— Thestripe-signatureheader value.secret— The webhook signing secret.toleranceSeconds— Allowed clock-drift between hosts.now— Current epoch millis (injectable for tests).
Constants
kycStripeIdentitySecretDefinitions
Secret definitions required by the Stripe Identity KYC bond.
const kycStripeIdentitySecretDefinitions: SecretDefinition[]provider
The Stripe Identity provider. Lazily initialized on first use so that environment variables are read at call time rather than import time.
const provider: KycProviderCore Interface
Implements @molecule/api-kyc interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-kyc'
import { provider } from '@molecule/api-kyc-stripe-identity'
export function setupKycStripeIdentity(): void {
setProvider(provider)
}Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-kyc^1.0.1@molecule/api-secrets^1.0.1
Environment Variables
STRIPE_SECRET_KEY(required) — Stripe secret key- Setup: Stripe Dashboard → Developers → API keys; use the sk_test_ key in test mode, sk_live_ in production.
- Get it here: https://dashboard.stripe.com/apikeys
- Example:
sk_test_...
STRIPE_IDENTITY_WEBHOOK_SECRET(required) — Stripe Identity webhook signing secret- Setup: Add a webhook endpoint for Identity events in the Stripe Dashboard and copy its signing secret.
- Get it here: https://dashboard.stripe.com/webhooks
- Example:
whsec_...
Runtime Dependencies
@molecule/api-bond@molecule/api-kyc@molecule/api-secrets
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- [ ] Starting a verification from the UI creates a real session:
createVerificationSessionis called, the app persists the returnedKycSession.sessionIdon the user's record with stored statuspending, and the user is handed to the provider-hostedsession.url— NOT a home-grown document-capture screen, and NOT marked verified at creation. - [ ] COUNTERPARTY (the identity check runs out-of-band on the external
vendor and can't be completed for real in-sandbox): verify the decision
against the app's OWN stored KYC state — deliver a
verification.verified(orverification.requires_input/verification.canceled) event to the webhook endpoint, or pollgetVerificationStatus, and confirm the user's stored status flipspending→verified/requires_input/canceledand the UI shows it. Observe the transition, never guess it. - [ ] KYC-gated features are enforced SERVER-SIDE: while the stored status is
not
verified, the restricted action is REJECTED by the server (not merely a hidden button); onceverified, the same user is allowed. Flipping the stored status changes access after a full reload. - [ ]
processWebhookrejects a forged decision — a bad/missing signature THROWS and becomes a 4xx with NO state change (the user stays unverified); only a signature-verified event may flip stored status. - [ ] A user CANNOT self-verify: no endpoint accepts a client-sent "verified"
flag or lets a caller PATCH their own status, and landing back on
returnUrlalone changes nothing — the only path toverifiedis a signature-verified webhook or a server-sidegetVerificationStatuscheck. - [ ] SECURITY / PRIVACY — identity documents and PII stay server-side: the user is redirected to the provider-hosted flow (the app never receives or stores raw ID images), one user can't read another's session/status/PII by guessing its id, and neither the documents nor the webhook secret are logged in the clear.
