Skip to content

Commit b73d0dc

Browse files
authored
Merge pull request #2755 from stripe/xavdid/merge-node-beta
Merge to beta
2 parents 9447931 + 522db65 commit b73d0dc

234 files changed

Lines changed: 1935 additions & 4930 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ trim_trailing_whitespace = true
1010

1111
[justfile]
1212
indent_size = 4
13+
14+
[CHANGELOG.md]
15+
trim_trailing_whitespace = false

.github/pull_request_template.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,15 @@ List out the key changes made in this PR, e.g.
99

1010
### See Also
1111
<!-- Include any links or additional information that help explain this change. -->
12+
13+
## Changelog
14+
<!-- Heads up! This section should include entries for any user-facing changes.
15+
Either fill it out or remove it if there are no entries to report.
16+
17+
List changes that affect end users, e.g.
18+
- Fixes crash when calling `foo.bar()` with null argument
19+
- Adds support for new `baz` parameter on `PaymentIntent` creation
20+
21+
List breaking changes first with a ⚠️ prefix, e.g.
22+
- ⚠️ Removes deprecated `legacy_method` function
23+
-->

CHANGELOG.md

Lines changed: 91 additions & 17 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,25 @@ value:
4242
```js
4343
const stripe = require('stripe')('sk_test_...');
4444

45-
stripe.customers.create({
45+
const customer = await stripeClient.customers.create({
4646
email: 'customer@example.com',
47-
})
48-
.then(customer => console.log(customer.id))
49-
.catch(error => console.error(error));
47+
});
48+
49+
console.log(customer.id);
5050
```
5151

52-
Or using ES modules and `async`/`await`:
52+
Or using CJS:
5353

5454
```js
55-
import Stripe from 'stripe';
56-
const stripe = new Stripe('sk_test_...');
57-
58-
const customer = await stripe.customers.create({
59-
email: 'customer@example.com',
60-
});
55+
const Stripe = require('stripe');
56+
const stripeClient = Stripe('sk_test_...');
6157

62-
console.log(customer.id);
58+
stripeClient.customers
59+
.create({
60+
email: 'customer@example.com',
61+
})
62+
.then((customer) => console.log(customer.id))
63+
.catch((error) => console.error(error));
6364
```
6465

6566
> [!WARNING]
@@ -420,7 +421,11 @@ const header = stripe.webhooks.generateTestHeaderString({
420421
secret,
421422
});
422423

423-
const event = stripe.webhooks.constructEvent(payloadString, header, secret);
424+
const event = stripeClient.webhooks.constructEvent(
425+
payloadString,
426+
header,
427+
secret
428+
);
424429

425430
// Do something with mocked signed event
426431
expect(event.id).to.equal(payload.id);
@@ -436,20 +441,11 @@ See [undocumented params and properties](https://docs.stripe.com/sdks/server-sid
436441

437442
If you're writing a plugin that uses the library, we'd appreciate it if you instantiated your stripe client with `appInfo`, eg;
438443

439-
```js
440-
const stripe = require('stripe')('sk_test_...', {
441-
appInfo: {
442-
name: 'MyAwesomePlugin',
443-
version: '1.2.34', // Optional
444-
url: 'https://myawesomeplugin.info', // Optional
445-
},
446-
});
447-
```
448-
449-
Or using ES modules or TypeScript:
444+
With ES modules or TypeScript:
450445

451446
```js
452-
const stripe = new Stripe(apiKey, {
447+
import Stripe from 'stripe';
448+
const stripeClient = new Stripe(apiKey, {
453449
appInfo: {
454450
name: 'MyAwesomePlugin',
455451
version: '1.2.34', // Optional
@@ -574,7 +570,7 @@ const stripe = new Stripe('sk_test_...', {
574570

575571
### Private Preview SDKs
576572

577-
Stripe has features in the [private preview phase](https://docs.stripe.com/release-phases) that can be accessed via versions of this package that have the `-alpha.X` suffix like `18.6.0-alpha.1`. These are invite-only features. Once invited, you can install the private preview SDKs by following the same instructions as for the [public preview SDKs](https://github.com/stripe/stripe-node?tab=readme-ov-file#public-preview-sdks) above and replacing the term `public-preview` with `private-preview`:
573+
Stripe has features in the [private preview phase](https://docs.stripe.com/release-phases) that can be accessed via versions of this package that have the `-alpha.X` suffix like `18.6.0-alpha.1`. You can install the private preview SDKs by following the same instructions as for the [public preview SDKs](#public-preview-sdks) above and replacing the term `public-preview` with `private-preview`. Note that access to specific private preview API features may require separate approval:
578574

579575
```
580576
npm install stripe@private-preview --save-exact
@@ -629,6 +625,9 @@ New features and bug fixes are released on the latest major version of the `stri
629625

630626
## Development
631627

628+
> [!WARNING]
629+
> External contributions to this repo from first-time contributors are currently on hiatus. If you'd like to see a change made to the package, please open an issue.
630+
632631
[Contribution guidelines for this project](CONTRIBUTING.md)
633632

634633
The tests depend on [stripe-mock][stripe-mock], so make sure to fetch and

src/RequestSender.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import {StripeContext} from './StripeContext.js';
1010
import {
1111
RequestHeaders,
12+
ResponseHeaders,
1213
RequestEvent,
1314
ResponseEvent,
1415
RequestCallback,
@@ -96,6 +97,15 @@ export class RequestSender {
9697
return headers['request-id'] as string;
9798
}
9899

100+
private _emitStripeNotice(headers: ResponseHeaders): void {
101+
const notice = headers['stripe-notice'];
102+
if (notice) {
103+
this._stripe._platformFunctions.emitWarning(
104+
typeof notice === 'string' ? notice : notice[0]
105+
);
106+
}
107+
}
108+
99109
/**
100110
* Used by methods with spec.streaming === true. For these methods, we do not
101111
* buffer successful responses into memory or do parse them into stripe
@@ -113,6 +123,7 @@ export class RequestSender {
113123
): (res: HttpClientResponseInterface) => RequestCallbackReturn {
114124
return (res: HttpClientResponseInterface): RequestCallbackReturn => {
115125
const headers = res.getHeaders();
126+
this._emitStripeNotice(headers);
116127

117128
const streamCompleteCallback = (): void => {
118129
const responseEvent = this._makeResponseEvent(
@@ -152,6 +163,7 @@ export class RequestSender {
152163
) {
153164
return (res: HttpClientResponseInterface): void => {
154165
const headers = res.getHeaders();
166+
this._emitStripeNotice(headers);
155167
const requestId = this._getRequestId(headers);
156168
const statusCode = res.getStatusCode();
157169

@@ -294,10 +306,7 @@ export class RequestSender {
294306
return false;
295307
}
296308

297-
_getSleepTimeInMS(
298-
numRetries: number,
299-
retryAfter: number | null = null
300-
): number {
309+
_getSleepTimeInMS(numRetries: number, retryAfter?: number): number {
301310
const initialNetworkRetryDelay = this._stripe.getInitialNetworkRetryDelay();
302311
const maxNetworkRetryDelay = this._stripe.getMaxNetworkRetryDelay();
303312

@@ -578,7 +587,7 @@ export class RequestSender {
578587
apiVersion: string,
579588
headers: RequestHeaders,
580589
requestRetries: number,
581-
retryAfter: number | null
590+
retryAfter?: number
582591
): ReturnType<typeof setTimeout> => {
583592
return setTimeout(
584593
requestFn,
@@ -693,8 +702,7 @@ export class RequestSender {
693702
makeRequest,
694703
apiVersion,
695704
headers,
696-
requestRetries,
697-
null
705+
requestRetries
698706
);
699707
} else {
700708
const isTimeoutError =

src/autoPagination.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class V2ListIterator<T> implements AsyncIterator<T> {
173173
private firstPagePromise: Promise<PageResult<T>> | null;
174174
private currentPageIterator: Iterator<T> | null;
175175
private nextPageUrl: string | null;
176+
private promiseCache: PromiseCache;
176177
private options: RequestOptions | undefined;
177178
private spec: MakeRequestSpec | undefined;
178179
private stripeResource: StripeResourceObject;
@@ -185,6 +186,7 @@ class V2ListIterator<T> implements AsyncIterator<T> {
185186
this.firstPagePromise = firstPagePromise;
186187
this.currentPageIterator = null;
187188
this.nextPageUrl = null;
189+
this.promiseCache = {currentPromise: null};
188190
this.options = options;
189191
this.spec = spec;
190192
this.stripeResource = stripeResource;
@@ -210,19 +212,45 @@ class V2ListIterator<T> implements AsyncIterator<T> {
210212
this.currentPageIterator = page.data[Symbol.iterator]();
211213
return this.currentPageIterator;
212214
}
213-
async next(): Promise<IteratorResult<T>> {
215+
private async _next(): Promise<IteratorResult<T>> {
214216
await this.initFirstPage();
215217
if (this.currentPageIterator) {
216218
const result = this.currentPageIterator.next();
217219
if (!result.done) return {done: false, value: result.value};
218220
}
221+
return this.nextFromNewPage();
222+
}
223+
private async nextFromNewPage(): Promise<IteratorResult<T>> {
219224
const nextPageIterator = await this.turnPage();
220225
if (!nextPageIterator) {
221226
return {done: true, value: undefined};
222227
}
223228
const result = nextPageIterator.next();
224229
if (!result.done) return {done: false, value: result.value};
225-
return {done: true, value: undefined};
230+
// Empty intermediate page — recurse to try the next page.
231+
return this.nextFromNewPage();
232+
}
233+
next(): Promise<IteratorResult<T>> {
234+
/**
235+
* If a user calls `.next()` multiple times in parallel,
236+
* return the same result until something has resolved
237+
* to prevent page-turning race conditions.
238+
*/
239+
if (this.promiseCache.currentPromise) {
240+
return this.promiseCache.currentPromise;
241+
}
242+
243+
const nextPromise = (async (): Promise<IteratorResult<T>> => {
244+
try {
245+
return await this._next();
246+
} finally {
247+
this.promiseCache.currentPromise = null;
248+
}
249+
})();
250+
251+
this.promiseCache.currentPromise = nextPromise;
252+
253+
return nextPromise;
226254
}
227255
}
228256

src/platform/NodePlatformFunctions.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as crypto from 'crypto';
22
import * as http from 'http';
3+
import * as os from 'os';
34
import {CryptoProvider} from '../crypto/CryptoProvider.js';
45
import {EventEmitter} from 'events';
56
import {HttpClient, NodeHttpClientInterface} from '../net/HttpClient.js';
@@ -8,7 +9,6 @@ import {NodeHttpClient} from '../net/NodeHttpClient.js';
89
import {PlatformFunctions} from './PlatformFunctions.js';
910
import {StripeError} from '../Error.js';
1011
import {concat} from '../utils.js';
11-
import {arch, release} from 'os';
1212
import {MultipartRequestData, RequestData, BufferedFile} from '../Types.js';
1313

1414
class StreamProcessingError extends StripeError {}
@@ -28,7 +28,7 @@ export class NodePlatformFunctions extends PlatformFunctions {
2828

2929
/** @override */
3030
getPlatformInfo(): string {
31-
return `${process.platform} ${release()} ${arch()}`;
31+
return `${process.platform} ${os.release()} ${os.arch()}`;
3232
}
3333

3434
/** @override */
@@ -50,6 +50,38 @@ export class NodePlatformFunctions extends PlatformFunctions {
5050
return process.version;
5151
}
5252

53+
private getUname(): string | null {
54+
try {
55+
const parts = [os.type(), os.release(), os.arch()];
56+
// os.version() returns detailed kernel version, available since Node 10.7.0
57+
// It may not exist in older typings, so access carefully
58+
const version = (os as any).version?.();
59+
if (version) parts.push(version);
60+
try {
61+
parts.push(os.hostname());
62+
// eslint-disable-next-line no-empty
63+
} catch (_e) {}
64+
return parts.join(' ');
65+
} catch {
66+
return null;
67+
}
68+
}
69+
70+
/** @override */
71+
getSourceHash(): string | null {
72+
try {
73+
const uname = this.getUname();
74+
return uname
75+
? crypto
76+
.createHash('md5')
77+
.update(uname)
78+
.digest('hex')
79+
: null;
80+
} catch {
81+
return null;
82+
}
83+
}
84+
5385
/**
5486
* @override
5587
* Secure compare, from https://github.com/freewil/scmp

src/platform/PlatformFunctions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export class PlatformFunctions {
3535
return null;
3636
}
3737

38+
getSourceHash(): string | null {
39+
return null;
40+
}
41+
3842
/**
3943
* Emits a warning. Node.js uses process.emitWarning; other runtimes
4044
* fall back to console.warn.

src/resources/AccountNotices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class AccountNoticeResource extends StripeResource {
2626
): Promise<Response<AccountNotice>> {
2727
return this._makeRequest(
2828
'GET',
29-
`/v1/account_notices/${id}`,
29+
`/v1/account_notices/${encodeURIComponent(id)}`,
3030
params,
3131
options
3232
) as any;
@@ -41,7 +41,7 @@ export class AccountNoticeResource extends StripeResource {
4141
): Promise<Response<AccountNotice>> {
4242
return this._makeRequest(
4343
'POST',
44-
`/v1/account_notices/${id}`,
44+
`/v1/account_notices/${encodeURIComponent(id)}`,
4545
params,
4646
options
4747
) as any;

0 commit comments

Comments
 (0)