Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New config object #703

Merged
merged 1 commit into from
Nov 7, 2019
Merged
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
84 changes: 77 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@ const stripe = Stripe('sk_test_...');
On older versions of Node, you can use [promises](#using-promises)
or [callbacks](#using-callbacks) instead of `async`/`await`.

## Initialize with config object

The package can be initialized with several options:

```js
import ProxyAgent from 'https-proxy-agent';

const stripe = Stripe('sk_test_...', {
apiVersion: '2019-08-08',
maxNetworkRetries: 1,
httpAgent: new ProxyAgent(process.env.http_proxy),
timeout: 1000,
host: 'api.example.com',
port: 123,
telemetry: true,
});
```

| Option | Default | Description |
| ------------------- | ----------------------------- | ------------------------------------------------------------------------------------- |
| `apiVersion` | `null` | Stripe API version to be used. If not set the account's default version will be used. |
| `maxNetworkRetries` | 0 | The amount of times a request should be [retried](#network-retries). |
| `httpAgent` | `null` | [Proxy](#configuring-a-proxy) agent to be used by the library. |
| `timeout` | 120000 (Node default timeout) | [Maximum time each request can take in ms.](#configuring-timeout) |
| `host` | `'api.stripe.com'` | Host that requests are made to. |
| `port` | 443 | Port that requests are made to. |
| `telemetry` | `true` | Allow Stripe to send latency [telemetry](#request-latency-telemetry) |

Note: Both `maxNetworkRetries` and `timeout` can be overridden on a per-request basis. `timeout` can be updated at any time with [`stripe.setTimeout`](#configuring-timeout).

### Usage with TypeScript

Stripe does not currently maintain typings for this package, but there are
Expand Down Expand Up @@ -135,6 +165,29 @@ Request timeout is configurable (the default is Node's default of 120 seconds):
stripe.setTimeout(20000); // in ms (this is 20 seconds)
```

Timeout can also be set globally via the config object:

```js
const stripe = Stripe('sk_test_...', {
timeout: 2000,
});
```

And on a per-request basis:

```js
stripe.customers.create(
{
email: 'customer@example.com',
},
{
timeout: 1000,
}
);
```

If `timeout` is set globally via the config object, the value set in a per-request basis will be favored.

### Configuring For Connect

A per-request `Stripe-Account` header for use with [Stripe Connect][connect]
Expand All @@ -144,7 +197,7 @@ can be added to any method:
// Retrieve the balance for a connected account:
stripe.balance
.retrieve({
stripe_account: 'acct_foo',
stripeAccount: 'acct_foo',
})
.then((balance) => {
// The balance object for the connected account
Expand All @@ -159,24 +212,41 @@ stripe.balance
An [https-proxy-agent][https-proxy-agent] can be configured with
`setHttpAgent`.

To use stripe behind a proxy you can pass to sdk:
To use stripe behind a proxy you can pass to sdk on initialization:

```js
if (process.env.http_proxy) {
const ProxyAgent = require('https-proxy-agent');
stripe.setHttpAgent(new ProxyAgent(process.env.http_proxy));

const stripe = Stripe('sk_test_...', {
httpProxy: new ProxyAgent(process.env.http_proxy),
});
}
```

### Network retries

Automatic network retries can be enabled with `setMaxNetworkRetries`.
Automatic network retries can be enabled with the `maxNetworkRetries` config option.
This will retry requests `n` times with exponential backoff if they fail due to an intermittent network problem.
[Idempotency keys](https://stripe.com/docs/api/idempotent_requests) are added where appropriate to prevent duplication.

```js
// Retry a request twice before giving up
stripe.setMaxNetworkRetries(2);
const stripe = Stripe('sk_test_...', {
maxNetworkRetries: 2, // Retry a request twice before giving up
});
```

Network retries can also be set on a per-request basis:

```js
stripe.customers.create(
{
email: 'customer@example.com',
},
{
maxNetworkRetries: 2, // Retry this specific request twice before giving up
}
);
```

### Examining Responses
Expand All @@ -185,7 +255,7 @@ Some information about the response which generated a resource is available
with the `lastResponse` property:

```js
charge.lastResponse.requestId; // see: https://stripe.com/docs/api/node#request_ids
charge.lastResponse.requestId; // see: https://stripe.com/docs/api/request_ids?lang=node
charge.lastResponse.statusCode;
```

Expand Down
46 changes: 35 additions & 11 deletions lib/StripeResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,9 @@ StripeResource.prototype = {
},

// For more on when and how to retry API requests, see https://stripe.com/docs/error-handling#safely-retrying-requests-with-idempotency
_shouldRetry(res, numRetries) {
_shouldRetry(res, numRetries, maxRetries) {
// Do not retry if we are out of retries.
if (numRetries >= this._stripe.getMaxNetworkRetries()) {
if (numRetries >= maxRetries) {
return false;
}

Expand Down Expand Up @@ -283,9 +283,19 @@ StripeResource.prototype = {
return sleepSeconds * 1000;
},

_defaultIdempotencyKey(method) {
// Max retries can be set on a per request basis. Favor those over the global setting
_getMaxNetworkRetries(settings = {}) {
return settings.maxNetworkRetries &&
Number.isInteger(settings.maxNetworkRetries)
? settings.maxNetworkRetries
: this._stripe.getMaxNetworkRetries();
},

_defaultIdempotencyKey(method, settings) {
// If this is a POST and we allow multiple retries, ensure an idempotency key.
if (method === 'POST' && this._stripe.getMaxNetworkRetries() > 0) {
const maxRetries = this._getMaxNetworkRetries(settings);

if (method === 'POST' && maxRetries > 0) {
return `stripe-node-retry-${utils.uuid4()}`;
}
return null;
Expand All @@ -297,7 +307,8 @@ StripeResource.prototype = {
apiVersion,
clientUserAgent,
method,
userSuppliedHeaders
userSuppliedHeaders,
userSuppliedSettings
) {
const defaultHeaders = {
// Use specified auth token or use default from this stripe instance:
Expand All @@ -309,7 +320,10 @@ StripeResource.prototype = {
'X-Stripe-Client-User-Agent': clientUserAgent,
'X-Stripe-Client-Telemetry': this._getTelemetryHeader(),
'Stripe-Version': apiVersion,
'Idempotency-Key': this._defaultIdempotencyKey(method),
'Idempotency-Key': this._defaultIdempotencyKey(
method,
userSuppliedSettings
),
};

return Object.assign(
Expand Down Expand Up @@ -358,7 +372,7 @@ StripeResource.prototype = {
}
},

_request(method, host, path, data, auth, options, callback) {
_request(method, host, path, data, auth, options = {}, callback) {
let requestData;

const retryRequest = (
Expand All @@ -378,7 +392,14 @@ StripeResource.prototype = {
};

const makeRequest = (apiVersion, headers, numRetries) => {
const timeout = this._stripe.getApiField('timeout');
// timeout can be set on a per-request basis. Favor that over the global setting
const timeout =
options.settings &&
Number.isInteger(options.settings.timeout) &&
options.settings.timeout >= 0
? options.settings.timeout
: this._stripe.getApiField('timeout');

const isInsecureConnection =
this._stripe.getApiField('protocol') == 'http';
let agent = this._stripe.getApiField('agent');
Expand Down Expand Up @@ -409,6 +430,8 @@ StripeResource.prototype = {

const requestRetries = numRetries || 0;

const maxRetries = this._getMaxNetworkRetries(options.settings);

req._requestEvent = requestEvent;

req._requestStart = requestStartTime;
Expand All @@ -418,7 +441,7 @@ StripeResource.prototype = {
req.setTimeout(timeout, this._timeoutHandler(timeout, req, callback));

req.once('response', (res) => {
if (this._shouldRetry(res, requestRetries)) {
if (this._shouldRetry(res, requestRetries, maxRetries)) {
return retryRequest(
makeRequest,
apiVersion,
Expand All @@ -432,7 +455,7 @@ StripeResource.prototype = {
});

req.on('error', (error) => {
if (this._shouldRetry(null, requestRetries)) {
if (this._shouldRetry(null, requestRetries, maxRetries)) {
return retryRequest(
makeRequest,
apiVersion,
Expand Down Expand Up @@ -478,7 +501,8 @@ StripeResource.prototype = {
apiVersion,
clientUserAgent,
method,
options.headers
options.headers,
options.settings
);

makeRequest(apiVersion, headers);
Expand Down
5 changes: 4 additions & 1 deletion lib/makeRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ function getRequestOpts(self, requestArgs, spec, overrideData) {
auth: options.auth,
headers,
host,
settings: options.settings,
};
}

Expand Down Expand Up @@ -89,13 +90,15 @@ function makeRequest(self, requestArgs, spec, overrideData) {
utils.stringifyRequestData(opts.queryData),
].join('');

const {headers, settings} = opts;

self._request(
opts.requestMethod,
opts.host,
path,
opts.bodyData,
opts.auth,
{headers: opts.headers},
{headers, settings},
requestCallback
);
});
Expand Down
Loading