@molecule/api-kyc-stripe-identity
Provider bond · kyc · API (Node) · v1.0.2 · Apache-2.0
Stripe Identity KYC bond for molecule.dev — document + selfie verification, normalized status, signed webhooks.
npm install @molecule/api-kyc-stripe-identitynpm · Source on GitHub · Implements @molecule/api-kyc
How it works
@molecule/api-kyc-stripe-identity is a provider bond on the API (Node) side: it implements the kyc core interface (@molecule/api-kyc) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-kyc'
import { provider } from '@molecule/api-kyc-stripe-identity'
setProvider(provider)Works with: @molecule/api-bond, @molecule/api-kyc, @molecule/api-secrets
Secrets: STRIPE_SECRET_KEY, STRIPE_IDENTITY_WEBHOOK_SECRET
Reference
Stripe Identity KYC bond for molecule.dev.
Implements the 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/webhookSecrettocreateProvider). - Configure a webhook endpoint subscribed to
identity.verification_session.verified,identity.verification_session.requires_input, andidentity.verification_session.canceled. - Bond at startup:
setProvider(provider).
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.
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-secrets
API
Interfaces
StripeIdentityProviderOptions
Configuration options for createProvider.
Every field defaults to environment variables so handlers can call
createProvider() with no arguments. Tests inject overrides
(especially StripeIdentityProviderOptions.fetch and
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
}
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
* `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>
}
KycSession
A verification session created with a KYC provider.
Sessions are the unit of state — once created they progress through
statuses (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 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 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>
}
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
* `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
* `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>
}
Types
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'
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'
KycWebhookHeaders
Headers required for 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>
KycWebhookEventType
Discriminated event type emitted by KycProvider.processWebhook.
Bonds normalize provider-specific event names into one of these three
variants. Provider-specific raw payload is preserved in KycWebhookEvent.raw
so callers needing extra detail can opt in.
type KycWebhookEventType =
'verification.verified' | 'verification.requires_input' | 'verification.canceled'
Functions
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,
): void
payload— 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).
createProvider(options)
Creates a Stripe Identity provider.
function createProvider(options?: StripeIdentityProviderOptions): KycProvider
options— Optional configuration. Falls back toSTRIPE_SECRET_KEY/STRIPE_IDENTITY_WEBHOOK_SECRETenv vars whensecretKey/webhookSecretare omitted.
Returns: A KycProvider implementation.
Constants
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: KycProvider
kycStripeIdentitySecretDefinitions
Secret definitions required by the Stripe Identity KYC bond.
const kycStripeIdentitySecretDefinitions: SecretDefinition[]
Core 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.