Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

- Run all tests: `just test` (builds first)
- Run a specific test: `just test --grep "test name pattern"`
- Run type tests: `just types-test` (builds first, then type-checks `testProjects/types/typescriptTest.ts`)
- Tests use mocha
- Must build TypeScript before testing (handled automatically by `just` commands)
- Most changes that add or modify public API surface should include a corresponding type test in `testProjects/types/typescriptTest.ts`

## Formatting & Linting

Expand All @@ -19,7 +21,7 @@
- Node.js HTTP implementation: `src/net/NodeHttpClient.ts`
- Fetch-based HTTP implementation: `src/net/FetchHttpClient.ts`
- Request orchestration (headers, auth, retries): `src/RequestSender.ts`
- Core client setup: `src/stripe.core.ts`
- Core client setup: `src/stripe.core.ts` (CJS) and `src/stripe.esm.node.ts` (ESM) — changes to one usually need mirroring in the other
- API version: `src/apiVersion.ts`

## Generated Code
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 22.5.0 - 2026-08-10
* [#2805](https://github.com/stripe/stripe-node/pull/2805) Emit Claude Code plugin hint at module load time
- Emits new Claude Code plugin hint when `CLAUDECODE` or `CLAUDE_CODE_CHILD_SESSION` environment variables are detected.
* [#2794](https://github.com/stripe/stripe-node/pull/2794) add/adjust event parsing helpers

- Added methods that return their respective `Event`/`EventNotification` objects without verifying authenticity. Use them when you've previously verified an event (e.g. you verified, put the event in a queue, and are now processing). Supports events from [AWS EventBridge](https://docs.stripe.com/event-destinations/eventbridge) and [Azure Event Grid](https://docs.stripe.com/event-destinations/eventgrid) natively.
- `stripe.webhooks.constructEventWithoutVerification(payload)`
- `stripe.constructEventWithoutVerification(payload)`
- `stripe.parseEventNotificationWithoutVerification(payload)`
* [#2799](https://github.com/stripe/stripe-node/pull/2799) Add `stripe.major_api_version` constant

## 22.5.0-alpha.2 - 2026-08-05
* [#2797](https://github.com/stripe/stripe-node/pull/2797) Update generated code for private-preview
* Add support for new resource `Billing.FeedbackOptions`
Expand Down
2 changes: 1 addition & 1 deletion CODEGEN_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
85839f80afcaccd622aa30bf53a414cdffbd57bc
2f45a32278692f9e1801089ee4ebc0801e938fa2
2 changes: 1 addition & 1 deletion OPENAPI_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2369
v2393
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
"prepack": "just install && just build"
},
"exports": {
"extensibility": {
"import": "./esm/stripe.esm.extensibility.js",
"require": "./cjs/stripe.cjs.extensibility.js"
},
"browser": {
"import": "./esm/stripe.esm.worker.js",
"require": "./cjs/stripe.cjs.worker.js"
Expand Down
7 changes: 7 additions & 0 deletions src/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const generateV2Error = (
return new FinancialAccountNotOpenError(rawStripeError);
case 'fx_quote_expired':
return new FxQuoteExpiredError(rawStripeError);
case 'fx_quote_needs_refresh':
return new FxQuoteNeedsRefreshError(rawStripeError);
case 'insufficient_funds':
return new InsufficientFundsError(rawStripeError);
case 'invalid_payment_method':
Expand Down Expand Up @@ -470,6 +472,11 @@ export class FxQuoteExpiredError extends StripeError {
super(rawStripeError, 'FxQuoteExpiredError');
}
}
export class FxQuoteNeedsRefreshError extends StripeError {
constructor(rawStripeError: StripeRawError = {}) {
super(rawStripeError, 'FxQuoteNeedsRefreshError');
}
}
export class InsufficientFundsError extends StripeError {
constructor(rawStripeError: StripeRawError = {}) {
super(rawStripeError, 'InsufficientFundsError');
Expand Down
72 changes: 41 additions & 31 deletions src/RequestSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
ApiMode,
} from './Types.js';
import {RawRequestOptions, RequestOptions} from './lib.js';
import {HttpClient, HttpClientResponseInterface} from './net/HttpClient.js';
import {
HttpClient,
HttpClientResponseInterface,
HttpClientRuntimeError,
} from './net/HttpClient.js';
import {Stripe} from './stripe.core.js';
import {
jsonStringifyRequestData,
Expand Down Expand Up @@ -57,7 +61,7 @@
return clientContext?.toString() || null; // return null for empty strings
}

_addHeadersDirectlyToObject(obj: any, headers: RequestHeaders): void {

Check warning on line 64 in src/RequestSender.ts

View workflow job for this annotation

GitHub Actions / Static Checks

Argument 'obj' should be typed with a non-any type
// For convenience, make some headers easily accessible on
// lastResponse.

Expand Down Expand Up @@ -679,37 +683,43 @@
)(res);
}
})
.catch((error: HttpClientResponseError) => {
if (
RequestSender._shouldRetry(
null,
requestRetries,
maxRetries,
error
)
) {
return retryRequest(
makeRequest,
apiVersion,
headers,
requestRetries
);
} else {
const isTimeoutError =
error.code && error.code === HttpClient.TIMEOUT_ERROR_CODE;

return callback(
new StripeConnectionError({
message: isTimeoutError
? `Request aborted due to timeout being reached (${timeout}ms)`
: RequestSender._generateConnectionErrorMessage(
requestRetries
),
detail: error,
})
);
.catch(
(error: HttpClientResponseError | HttpClientRuntimeError) => {
if (error instanceof HttpClientRuntimeError) {
return callback(error);
}

if (
RequestSender._shouldRetry(
null,
requestRetries,
maxRetries,
error
)
) {
return retryRequest(
makeRequest,
apiVersion,
headers,
requestRetries
);
} else {
const isTimeoutError =
error.code && error.code === HttpClient.TIMEOUT_ERROR_CODE;

return callback(
new StripeConnectionError({
message: isTimeoutError
? `Request aborted due to timeout being reached (${timeout}ms)`
: RequestSender._generateConnectionErrorMessage(
requestRetries
),
detail: error,
})
);
}
}
});
);
})
.catch((e: any) => {
throw new StripeError({
Expand Down
78 changes: 78 additions & 0 deletions src/StripeEventEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {RequestEvent, ResponseEvent} from './Types.js';

type Listener = (...args: any[]) => any;
type ListenerRegistration = {
listener: Listener;
once: boolean;
};

/**
* @private
* (For internal use in stripe-node.)
* Minimal EventEmitter for runtimes without Event or EventTarget.
*/
export class StripeEventEmitter {
private _listeners: Map<string, Array<ListenerRegistration>>;

constructor() {
this._listeners = new Map();
}

on(eventName: string, listener: Listener): void {
this._getListeners(eventName).push({listener, once: false});
}

once(eventName: string, listener: Listener): void {
this._getListeners(eventName).push({listener, once: true});
}

removeListener(eventName: string, listener: Listener): void {
const listeners = this._listeners.get(eventName);
if (!listeners) {
return;
}

const index = listeners.findIndex((entry) => entry.listener === listener);
if (index === -1) {
return;
}

this._removeAt(eventName, index);
}

emit(eventName: string, data?: RequestEvent | ResponseEvent): boolean {
const listeners = this._listeners.get(eventName);
if (!listeners || listeners.length === 0) {
return false;
}

for (const entry of [...listeners]) {
if (entry.once) {
this._removeAt(eventName, listeners.indexOf(entry));
}
entry.listener(data);
}
return true;
}

private _getListeners(eventName: string): Array<ListenerRegistration> {
let listeners = this._listeners.get(eventName);
if (!listeners) {
listeners = [];
this._listeners.set(eventName, listeners);
}
return listeners;
}

private _removeAt(eventName: string, index: number): void {
const listeners = this._listeners.get(eventName);
if (!listeners || index === -1) {
return;
}

listeners.splice(index, 1);
if (listeners.length === 0) {
this._listeners.delete(eventName);
}
}
}
14 changes: 14 additions & 0 deletions src/StripeEventNotificationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,17 @@ const KNOWN_EVENT_TYPES = new Set([
'v1.application_fee.refund.updated',
'v1.application_fee.refunded',
'v1.balance.available',
'v1.balance_settings.updated',
'v1.billing.alert.triggered',
'v1.billing.credit_balance_transaction.created',
'v1.billing.credit_grant.created',
'v1.billing.credit_grant.updated',
'v1.billing.meter.created',
'v1.billing.meter.deactivated',
'v1.billing.meter.error_report_triggered',
'v1.billing.meter.no_meter_found',
'v1.billing.meter.reactivated',
'v1.billing.meter.updated',
'v1.billing_portal.configuration.created',
'v1.billing_portal.configuration.updated',
'v1.billing_portal.session.created',
Expand Down Expand Up @@ -86,13 +94,18 @@ const KNOWN_EVENT_TYPES = new Set([
'v1.customer_cash_balance_transaction.created',
'v1.entitlements.active_entitlement_summary.updated',
'v1.file.created',
'v1.financial_connections.account.account_numbers_updated',
'v1.financial_connections.account.created',
'v1.financial_connections.account.deactivated',
'v1.financial_connections.account.disconnected',
'v1.financial_connections.account.expected_deactivation_date_updated',
'v1.financial_connections.account.reactivated',
'v1.financial_connections.account.refreshed_balance',
'v1.financial_connections.account.refreshed_ownership',
'v1.financial_connections.account.refreshed_transactions',
'v1.financial_connections.account.supported_payment_method_types_updated',
'v1.financial_connections.account.upcoming_account_number_expiry',
'v1.financial_connections.account.upcoming_deactivation',
'v1.identity.verification_session.canceled',
'v1.identity.verification_session.created',
'v1.identity.verification_session.processing',
Expand All @@ -108,6 +121,7 @@ const KNOWN_EVENT_TYPES = new Set([
'v1.invoice.overpaid',
'v1.invoice.paid',
'v1.invoice.payment_action_required',
'v1.invoice.payment_attempt_required',
'v1.invoice.payment_failed',
'v1.invoice.payment_succeeded',
'v1.invoice.sent',
Expand Down
1 change: 1 addition & 0 deletions src/Types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable camelcase */
// TODO(DEVSDK-3113): Remove EventEmitter from shared types in next major version.

Check warning on line 2 in src/Types.ts

View workflow job for this annotation

GitHub Actions / Static Checks

Unexpected 'todo' comment: 'TODO(DEVSDK-3113): Remove EventEmitter...'
// eslint-disable-next-line wintertc-compat
import {EventEmitter} from 'events';
import {
Expand Down Expand Up @@ -77,6 +77,7 @@
| 'feature_not_enabled'
| 'financial_account_not_open'
| 'fx_quote_expired'
| 'fx_quote_needs_refresh'
| 'insufficient_funds'
| 'invalid_payment_method'
| 'invalid_payout_method'
Expand Down
Loading
Loading