Email Sorter Beta

Ich habe soweit automatisiert the Emails sortieren aber ich muss noch schauen was es fur bugs es gibt wenn die app online  ist deswegen wurde ich mit diesen Commit die website veroffentlichen obwohjl es sein konnte  das es noch nicht fertig ist und verkaufs bereit
This commit is contained in:
2026-01-22 19:32:12 +01:00
parent 95349af50b
commit abf761db07
596 changed files with 56405 additions and 51231 deletions

View File

@@ -1,5 +1,6 @@
/* eslint-disable camelcase */
export const generate = (rawStripeError) => {
/* eslint-disable no-warning-comments */
export const generateV1Error = (rawStripeError) => {
switch (rawStripeError.type) {
case 'card_error':
return new StripeCardError(rawStripeError);
@@ -19,6 +20,22 @@ export const generate = (rawStripeError) => {
return new StripeUnknownError(rawStripeError);
}
};
// eslint-disable-next-line complexity
export const generateV2Error = (rawStripeError) => {
switch (rawStripeError.type) {
// switchCases: The beginning of the section generated from our OpenAPI spec
case 'temporary_session_expired':
return new TemporarySessionExpiredError(rawStripeError);
// switchCases: The end of the section generated from our OpenAPI spec
}
// Special handling for requests with missing required fields in V2 APIs.
// invalid_field response in V2 APIs returns the field 'code' instead of 'type'.
switch (rawStripeError.code) {
case 'invalid_fields':
return new StripeInvalidRequestError(rawStripeError);
}
return generateV1Error(rawStripeError);
};
/**
* StripeError is the base error from which all other more specific Stripe errors derive.
* Specifically for errors returned from Stripe's REST API.
@@ -38,6 +55,7 @@ export class StripeError extends Error {
this.statusCode = raw.statusCode;
// @ts-ignore
this.message = raw.message;
this.userMessage = raw.user_message;
this.charge = raw.charge;
this.decline_code = raw.decline_code;
this.payment_intent = raw.payment_intent;
@@ -50,7 +68,7 @@ export class StripeError extends Error {
/**
* Helper factory which takes raw stripe errors and outputs wrapping instances
*/
StripeError.generate = generate;
StripeError.generate = generateV1Error;
// Specific Stripe Error types:
/**
* CardError is raised when a user enters a card that can't be charged for
@@ -158,3 +176,10 @@ export class StripeUnknownError extends StripeError {
super(raw, 'StripeUnknownError');
}
}
// classDefinitions: The beginning of the section generated from our OpenAPI spec
export class TemporarySessionExpiredError extends StripeError {
constructor(rawStripeError = {}) {
super(rawStripeError, 'TemporarySessionExpiredError');
}
}
// classDefinitions: The end of the section generated from our OpenAPI spec

View File

@@ -1,6 +1,6 @@
import { StripeAPIError, StripeAuthenticationError, StripeConnectionError, StripeError, StripePermissionError, StripeRateLimitError, } from './Error.js';
import { emitWarning, normalizeHeaders, removeNullish, stringifyRequestData, } from './utils.js';
import { StripeAPIError, StripeAuthenticationError, StripeConnectionError, StripeError, StripePermissionError, StripeRateLimitError, generateV1Error, generateV2Error, } from './Error.js';
import { HttpClient } from './net/HttpClient.js';
import { emitWarning, jsonStringifyRequestData, normalizeHeaders, queryStringifyRequestData, removeNullish, getAPIMode, getOptionsFromArgs, getDataFromArgs, } from './utils.js';
const MAX_RETRY_AFTER_WAIT = 60;
export class RequestSender {
constructor(stripe, maxBufferedRequestMetric) {
@@ -66,7 +66,7 @@ export class RequestSender {
* parses the JSON and returns it (i.e. passes it to the callback) if there
* is no "error" field. Otherwise constructs/passes an appropriate Error.
*/
_jsonResponseHandler(requestEvent, usage, callback) {
_jsonResponseHandler(requestEvent, apiMode, usage, callback) {
return (res) => {
const headers = res.getHeaders();
const requestId = this._getRequestId(headers);
@@ -98,8 +98,11 @@ export class RequestSender {
else if (statusCode === 429) {
err = new StripeRateLimitError(jsonResponse.error);
}
else if (apiMode === 'v2') {
err = generateV2Error(jsonResponse.error);
}
else {
err = StripeError.generate(jsonResponse.error);
err = generateV1Error(jsonResponse.error);
}
throw err;
}
@@ -171,7 +174,7 @@ export class RequestSender {
// Apply exponential backoff with initialNetworkRetryDelay on the
// number of numRetries so far as inputs. Do not allow the number to exceed
// maxNetworkRetryDelay.
let sleepSeconds = Math.min(initialNetworkRetryDelay * Math.pow(numRetries - 1, 2), maxNetworkRetryDelay);
let sleepSeconds = Math.min(initialNetworkRetryDelay * Math.pow(2, numRetries - 1), maxNetworkRetryDelay);
// Apply some jitter by randomizing the value in the range of
// (sleepSeconds / 2) to (sleepSeconds).
sleepSeconds *= 0.5 * (1 + Math.random());
@@ -190,26 +193,34 @@ export class RequestSender {
? settings.maxNetworkRetries
: this._stripe.getMaxNetworkRetries();
}
_defaultIdempotencyKey(method, settings) {
_defaultIdempotencyKey(method, settings, apiMode) {
// If this is a POST and we allow multiple retries, ensure an idempotency key.
const maxRetries = this._getMaxNetworkRetries(settings);
if (method === 'POST' && maxRetries > 0) {
return `stripe-node-retry-${this._stripe._platformFunctions.uuid4()}`;
const genKey = () => `stripe-node-retry-${this._stripe._platformFunctions.uuid4()}`;
// more verbose than it needs to be, but gives clear separation between V1 and V2 behavior
if (apiMode === 'v2') {
if (method === 'POST' || method === 'DELETE') {
return genKey();
}
}
else if (apiMode === 'v1') {
if (method === 'POST' && maxRetries > 0) {
return genKey();
}
}
return null;
}
_makeHeaders(auth, contentLength, apiVersion, clientUserAgent, method, userSuppliedHeaders, userSuppliedSettings) {
_makeHeaders({ contentType, contentLength, apiVersion, clientUserAgent, method, userSuppliedHeaders, userSuppliedSettings, stripeAccount, stripeContext, apiMode, }) {
const defaultHeaders = {
// Use specified auth token or use default from this stripe instance:
Authorization: auth ? `Bearer ${auth}` : this._stripe.getApiField('auth'),
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': this._getUserAgentString(),
'Content-Type': contentType,
'User-Agent': this._getUserAgentString(apiMode),
'X-Stripe-Client-User-Agent': clientUserAgent,
'X-Stripe-Client-Telemetry': this._getTelemetryHeader(),
'Stripe-Version': apiVersion,
'Stripe-Account': this._stripe.getApiField('stripeAccount'),
'Idempotency-Key': this._defaultIdempotencyKey(method, userSuppliedSettings),
'Stripe-Account': stripeAccount,
'Stripe-Context': stripeContext,
'Idempotency-Key': this._defaultIdempotencyKey(method, userSuppliedSettings, apiMode),
};
// As per https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2:
// A user agent SHOULD send a Content-Length in a request message when
@@ -238,12 +249,12 @@ export class RequestSender {
// If the user supplied, say 'idempotency-key', override instead of appending by ensuring caps are the same.
normalizeHeaders(userSuppliedHeaders));
}
_getUserAgentString() {
_getUserAgentString(apiMode) {
const packageVersion = this._stripe.getConstant('PACKAGE_VERSION');
const appInfo = this._stripe._appInfo
? this._stripe.getAppInfoAsString()
: '';
return `Stripe/v1 NodeBindings/${packageVersion} ${appInfo}`.trim();
return `Stripe/${apiMode} NodeBindings/${packageVersion} ${appInfo}`.trim();
}
_getTelemetryHeader() {
if (this._stripe.getTelemetryEnabled() &&
@@ -271,8 +282,61 @@ export class RequestSender {
}
}
}
_request(method, host, path, data, auth, options = {}, usage = [], callback, requestDataProcessor = null) {
_rawRequest(method, path, params, options) {
const requestPromise = new Promise((resolve, reject) => {
let opts;
try {
const requestMethod = method.toUpperCase();
if (requestMethod !== 'POST' &&
params &&
Object.keys(params).length !== 0) {
throw new Error('rawRequest only supports params on POST requests. Please pass null and add your parameters to path.');
}
const args = [].slice.call([params, options]);
// Pull request data and options (headers, auth) from args.
const dataFromArgs = getDataFromArgs(args);
const data = Object.assign({}, dataFromArgs);
const calculatedOptions = getOptionsFromArgs(args);
const headers = calculatedOptions.headers;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const authenticator = calculatedOptions.authenticator;
opts = {
requestMethod,
requestPath: path,
bodyData: data,
queryData: {},
authenticator,
headers,
host: null,
streaming: false,
settings: {},
usage: ['raw_request'],
};
}
catch (err) {
reject(err);
return;
}
function requestCallback(err, response) {
if (err) {
reject(err);
}
else {
resolve(response);
}
}
const { headers, settings } = opts;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const authenticator = opts.authenticator;
this._request(opts.requestMethod, opts.host, path, opts.bodyData, authenticator, { headers, settings, streaming: opts.streaming }, opts.usage, requestCallback);
});
return requestPromise;
}
_request(method, host, path, data, authenticator, options, usage = [], callback, requestDataProcessor = null) {
var _a;
let requestData;
authenticator = (_a = authenticator !== null && authenticator !== void 0 ? authenticator : this._stripe._authenticator) !== null && _a !== void 0 ? _a : null;
const apiMode = getAPIMode(path);
const retryRequest = (requestFn, apiVersion, headers, requestRetries, retryAfter) => {
return setTimeout(requestFn, this._getSleepTimeInMS(requestRetries, retryAfter), apiVersion, headers, requestRetries + 1);
};
@@ -284,50 +348,68 @@ export class RequestSender {
options.settings.timeout >= 0
? options.settings.timeout
: this._stripe.getApiField('timeout');
const req = this._stripe
.getApiField('httpClient')
.makeRequest(host || this._stripe.getApiField('host'), this._stripe.getApiField('port'), path, method, headers, requestData, this._stripe.getApiField('protocol'), timeout);
const requestStartTime = Date.now();
// @ts-ignore
const requestEvent = removeNullish({
api_version: apiVersion,
account: headers['Stripe-Account'],
idempotency_key: headers['Idempotency-Key'],
method,
path,
request_start_time: requestStartTime,
});
const requestRetries = numRetries || 0;
const maxRetries = this._getMaxNetworkRetries(options.settings || {});
this._stripe._emitter.emit('request', requestEvent);
req
.then((res) => {
if (RequestSender._shouldRetry(res, requestRetries, maxRetries)) {
return retryRequest(makeRequest, apiVersion, headers, requestRetries,
// @ts-ignore
res.getHeaders()['retry-after']);
}
else if (options.streaming && res.getStatusCode() < 400) {
return this._streamingResponseHandler(requestEvent, usage, callback)(res);
}
else {
return this._jsonResponseHandler(requestEvent, usage, callback)(res);
}
})
.catch((error) => {
if (RequestSender._shouldRetry(null, requestRetries, maxRetries, error)) {
return retryRequest(makeRequest, apiVersion, headers, requestRetries, null);
}
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),
const request = {
host: host || this._stripe.getApiField('host'),
port: this._stripe.getApiField('port'),
path: path,
method: method,
headers: Object.assign({}, headers),
body: requestData,
protocol: this._stripe.getApiField('protocol'),
};
authenticator(request)
.then(() => {
const req = this._stripe
.getApiField('httpClient')
.makeRequest(request.host, request.port, request.path, request.method, request.headers, request.body, request.protocol, timeout);
const requestStartTime = Date.now();
// @ts-ignore
const requestEvent = removeNullish({
api_version: apiVersion,
account: headers['Stripe-Account'],
idempotency_key: headers['Idempotency-Key'],
method,
path,
request_start_time: requestStartTime,
});
const requestRetries = numRetries || 0;
const maxRetries = this._getMaxNetworkRetries(options.settings || {});
this._stripe._emitter.emit('request', requestEvent);
req
.then((res) => {
if (RequestSender._shouldRetry(res, requestRetries, maxRetries)) {
return retryRequest(makeRequest, apiVersion, headers, requestRetries,
// @ts-ignore
detail: error,
}));
}
res.getHeaders()['retry-after']);
}
else if (options.streaming && res.getStatusCode() < 400) {
return this._streamingResponseHandler(requestEvent, usage, callback)(res);
}
else {
return this._jsonResponseHandler(requestEvent, apiMode, usage, callback)(res);
}
})
.catch((error) => {
if (RequestSender._shouldRetry(null, requestRetries, maxRetries, error)) {
return retryRequest(makeRequest, apiVersion, headers, requestRetries, null);
}
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),
// @ts-ignore
detail: error,
}));
}
});
})
.catch((e) => {
throw new StripeError({
message: 'Unable to authenticate the request',
exception: e,
});
});
};
const prepareAndMakeRequest = (error, data) => {
@@ -336,9 +418,21 @@ export class RequestSender {
}
requestData = data;
this._stripe.getClientUserAgent((clientUserAgent) => {
var _a, _b;
const apiVersion = this._stripe.getApiField('version');
const headers = this._makeHeaders(auth, requestData.length, apiVersion, clientUserAgent, method, (_a = options.headers) !== null && _a !== void 0 ? _a : null, (_b = options.settings) !== null && _b !== void 0 ? _b : {});
const headers = this._makeHeaders({
contentType: apiMode == 'v2'
? 'application/json'
: 'application/x-www-form-urlencoded',
contentLength: requestData.length,
apiVersion: apiVersion,
clientUserAgent,
method,
userSuppliedHeaders: options.headers,
userSuppliedSettings: options.settings,
stripeAccount: apiMode == 'v2' ? null : this._stripe.getApiField('stripeAccount'),
stripeContext: apiMode == 'v2' ? this._stripe.getApiField('stripeContext') : null,
apiMode: apiMode,
});
makeRequest(apiVersion, headers, 0);
});
};
@@ -346,7 +440,14 @@ export class RequestSender {
requestDataProcessor(method, data, options.headers, prepareAndMakeRequest);
}
else {
prepareAndMakeRequest(null, stringifyRequestData(data || {}));
let stringifiedData;
if (apiMode == 'v2') {
stringifiedData = data ? jsonStringifyRequestData(data) : '';
}
else {
stringifiedData = queryStringifyRequestData(data || {}, apiMode);
}
prepareAndMakeRequest(null, stringifiedData);
}
}
}

View File

@@ -2,6 +2,9 @@
// It also works recursively, so you could do i.e. `stripe.billing.invoicing.pay`.
function ResourceNamespace(stripe, resources) {
for (const name in resources) {
if (!Object.prototype.hasOwnProperty.call(resources, name)) {
continue;
}
const camelCaseName = name[0].toLowerCase() + name.substring(1);
const resource = new resources[name](stripe);
this[camelCaseName] = resource;

View File

@@ -1,4 +1,4 @@
import { getDataFromArgs, getOptionsFromArgs, makeURLInterpolator, protoExtend, stringifyRequestData, } from './utils.js';
import { getDataFromArgs, getOptionsFromArgs, makeURLInterpolator, protoExtend, queryStringifyRequestData, getAPIMode, } from './utils.js';
import { stripeMethod } from './StripeMethod.js';
// Provide extension mechanism for Stripe Resource Sub-Classes
StripeResource.extend = protoExtend;
@@ -75,6 +75,7 @@ StripeResource.prototype = {
return parts.join('/').replace(/\/{2,}/g, '/');
},
_getRequestOpts(requestArgs, spec, overrideData) {
var _a;
// Extract spec values with defaults.
const requestMethod = (spec.method || 'GET').toUpperCase();
const usage = spec.usage || [];
@@ -118,14 +119,14 @@ StripeResource.prototype = {
spec.validator(data, { headers });
}
const dataInQuery = spec.method === 'GET' || spec.method === 'DELETE';
const bodyData = dataInQuery ? {} : data;
const bodyData = dataInQuery ? null : data;
const queryData = dataInQuery ? data : {};
return {
requestMethod,
requestPath,
bodyData,
queryData,
auth: options.auth,
authenticator: (_a = options.authenticator) !== null && _a !== void 0 ? _a : null,
headers,
host: host !== null && host !== void 0 ? host : null,
streaming,
@@ -158,10 +159,14 @@ StripeResource.prototype = {
const path = [
opts.requestPath,
emptyQuery ? '' : '?',
stringifyRequestData(opts.queryData),
queryStringifyRequestData(opts.queryData, getAPIMode(opts.requestPath)),
].join('');
const { headers, settings } = opts;
this._stripe._requestSender._request(opts.requestMethod, opts.host, path, opts.bodyData, opts.auth, { headers, settings, streaming: opts.streaming }, opts.usage, requestCallback, (_a = this.requestDataProcessor) === null || _a === void 0 ? void 0 : _a.bind(this));
this._stripe._requestSender._request(opts.requestMethod, opts.host, path, opts.bodyData, opts.authenticator, {
headers,
settings,
streaming: opts.streaming,
}, opts.usage, requestCallback, (_a = this.requestDataProcessor) === null || _a === void 0 ? void 0 : _a.bind(this));
});
},
};

View File

@@ -40,23 +40,16 @@ export function createWebhooks(platformFunctions) {
* @property {CryptoProvider} cryptoProvider - Crypto provider to use for computing the signature if none was provided. Defaults to NodeCryptoProvider.
*/
generateTestHeaderString: function (opts) {
if (!opts) {
throw new StripeError({
message: 'Options are required',
});
}
opts.timestamp =
Math.floor(opts.timestamp) || Math.floor(Date.now() / 1000);
opts.scheme = opts.scheme || signature.EXPECTED_SCHEME;
opts.cryptoProvider = opts.cryptoProvider || getCryptoProvider();
opts.signature =
opts.signature ||
opts.cryptoProvider.computeHMACSignature(opts.timestamp + '.' + opts.payload, opts.secret);
const generatedHeader = [
't=' + opts.timestamp,
opts.scheme + '=' + opts.signature,
].join(',');
return generatedHeader;
const preparedOpts = prepareOptions(opts);
const signature = preparedOpts.signature ||
preparedOpts.cryptoProvider.computeHMACSignature(preparedOpts.payloadString, preparedOpts.secret);
return preparedOpts.generateHeaderString(signature);
},
generateTestHeaderStringAsync: async function (opts) {
const preparedOpts = prepareOptions(opts);
const signature = preparedOpts.signature ||
(await preparedOpts.cryptoProvider.computeHMACSignatureAsync(preparedOpts.payloadString, preparedOpts.secret));
return preparedOpts.generateHeaderString(signature);
},
};
const signature = {
@@ -128,7 +121,7 @@ export function createWebhooks(platformFunctions) {
function validateComputedSignature(payload, header, details, expectedSignature, tolerance, suspectPayloadType, secretContainsWhitespace, receivedAt) {
const signatureFound = !!details.signatures.filter(platformFunctions.secureCompare.bind(platformFunctions, expectedSignature)).length;
const docsLocation = '\nLearn more about webhook signing and explore webhook integration examples for various frameworks at ' +
'https://github.com/stripe/stripe-node#webhook-signing';
'https://docs.stripe.com/webhooks/signature';
const whitespaceMessage = secretContainsWhitespace
? '\n\nNote: The provided signing secret contains whitespace. This often indicates an extra newline or space is in the value'
: '';
@@ -191,6 +184,25 @@ export function createWebhooks(platformFunctions) {
}
return webhooksCryptoProviderInstance;
}
function prepareOptions(opts) {
if (!opts) {
throw new StripeError({
message: 'Options are required',
});
}
const timestamp = Math.floor(opts.timestamp) || Math.floor(Date.now() / 1000);
const scheme = opts.scheme || signature.EXPECTED_SCHEME;
const cryptoProvider = opts.cryptoProvider || getCryptoProvider();
const payloadString = `${timestamp}.${opts.payload}`;
const generateHeaderString = (signature) => {
return `t=${timestamp},${scheme}=${signature}`;
};
return Object.assign(Object.assign({}, opts), { timestamp,
scheme,
cryptoProvider,
payloadString,
generateHeaderString });
}
Webhook.signature = signature;
return Webhook;
}

View File

@@ -1,2 +1,2 @@
// File generated from our OpenAPI spec
export const ApiVersion = '2023-10-16';
export const ApiVersion = '2025-02-24.acacia';

View File

@@ -1,5 +1,5 @@
import { callbackifyPromiseWithTimeout, getDataFromArgs } from './utils.js';
class StripeIterator {
import { callbackifyPromiseWithTimeout, getDataFromArgs, getAPIMode, } from './utils.js';
class V1Iterator {
constructor(firstPagePromise, requestArgs, spec, stripeResource) {
this.index = 0;
this.pagePromise = firstPagePromise;
@@ -57,7 +57,7 @@ class StripeIterator {
return nextPromise;
}
}
class ListIterator extends StripeIterator {
class V1ListIterator extends V1Iterator {
getNextPage(pageResult) {
const reverseIteration = isReverseIteration(this.requestArgs);
const lastId = getLastId(pageResult, reverseIteration);
@@ -66,7 +66,7 @@ class ListIterator extends StripeIterator {
});
}
}
class SearchIterator extends StripeIterator {
class V1SearchIterator extends V1Iterator {
getNextPage(pageResult) {
if (!pageResult.next_page) {
throw Error('Unexpected: Stripe API response does not have a well-formed `next_page` field, but `has_more` was true.');
@@ -76,12 +76,56 @@ class SearchIterator extends StripeIterator {
});
}
}
export const makeAutoPaginationMethods = (stripeResource, requestArgs, spec, firstPagePromise) => {
if (spec.methodType === 'search') {
return makeAutoPaginationMethodsFromIterator(new SearchIterator(firstPagePromise, requestArgs, spec, stripeResource));
class V2ListIterator {
constructor(firstPagePromise, requestArgs, spec, stripeResource) {
this.currentPageIterator = (async () => {
const page = await firstPagePromise;
return page.data[Symbol.iterator]();
})();
this.nextPageUrl = (async () => {
const page = await firstPagePromise;
return page.next_page_url || null;
})();
this.requestArgs = requestArgs;
this.spec = spec;
this.stripeResource = stripeResource;
}
if (spec.methodType === 'list') {
return makeAutoPaginationMethodsFromIterator(new ListIterator(firstPagePromise, requestArgs, spec, stripeResource));
async turnPage() {
const nextPageUrl = await this.nextPageUrl;
if (!nextPageUrl)
return null;
this.spec.fullPath = nextPageUrl;
const page = await this.stripeResource._makeRequest([], this.spec, {});
this.nextPageUrl = Promise.resolve(page.next_page_url);
this.currentPageIterator = Promise.resolve(page.data[Symbol.iterator]());
return this.currentPageIterator;
}
async next() {
{
const result = (await this.currentPageIterator).next();
if (!result.done)
return { done: false, value: result.value };
}
const nextPageIterator = await this.turnPage();
if (!nextPageIterator) {
return { done: true, value: undefined };
}
const result = nextPageIterator.next();
if (!result.done)
return { done: false, value: result.value };
return { done: true, value: undefined };
}
}
export const makeAutoPaginationMethods = (stripeResource, requestArgs, spec, firstPagePromise) => {
const apiMode = getAPIMode(spec.fullPath || spec.path);
if (apiMode !== 'v2' && spec.methodType === 'search') {
return makeAutoPaginationMethodsFromIterator(new V1SearchIterator(firstPagePromise, requestArgs, spec, stripeResource));
}
if (apiMode !== 'v2' && spec.methodType === 'list') {
return makeAutoPaginationMethodsFromIterator(new V1ListIterator(firstPagePromise, requestArgs, spec, stripeResource));
}
if (apiMode === 'v2' && spec.methodType === 'list') {
return makeAutoPaginationMethodsFromIterator(new V2ListIterator(firstPagePromise, requestArgs, spec, stripeResource));
}
return null;
};

View File

@@ -28,6 +28,12 @@ export class CryptoProvider {
computeHMACSignatureAsync(payload, secret) {
throw new Error('computeHMACSignatureAsync not implemented.');
}
/**
* Computes a SHA-256 hash of the data.
*/
computeSHA256Async(data) {
throw new Error('computeSHA256 not implemented.');
}
}
/**
* If the crypto provider only supports asynchronous operations,

View File

@@ -16,4 +16,11 @@ export class NodeCryptoProvider extends CryptoProvider {
const signature = await this.computeHMACSignature(payload, secret);
return signature;
}
/** @override */
async computeSHA256Async(data) {
return new Uint8Array(await crypto
.createHash('sha256')
.update(data)
.digest());
}
}

View File

@@ -34,6 +34,10 @@ export class SubtleCryptoProvider extends CryptoProvider {
}
return signatureHexCodes.join('');
}
/** @override */
async computeSHA256Async(data) {
return new Uint8Array(await this.subtleCrypto.digest('SHA-256', data));
}
}
// Cached mapping of byte to hex representation. We do this once to avoid re-
// computing every time we need to convert the result of a signature to hex.

View File

@@ -1,4 +1,4 @@
import { flattenAndStringify, stringifyRequestData } from './utils.js';
import { flattenAndStringify, queryStringifyRequestData } from './utils.js';
// Method for formatting HTTP body for the multipart/form-data specification
// Mostly taken from Fermata.js
// https://github.com/natevw/fermata/blob/5d9732a33d776ce925013a265935facd1626cc88/fermata.js#L315-L343
@@ -21,6 +21,9 @@ const multipartDataGenerator = (method, data, headers) => {
}
const flattenedData = flattenAndStringify(data);
for (const k in flattenedData) {
if (!Object.prototype.hasOwnProperty.call(flattenedData, k)) {
continue;
}
const v = flattenedData[k];
push(`--${segno}`);
if (Object.prototype.hasOwnProperty.call(v, 'data')) {
@@ -42,7 +45,7 @@ const multipartDataGenerator = (method, data, headers) => {
export function multipartRequestDataProcessor(method, data, headers, callback) {
data = data || {};
if (method !== 'POST') {
return callback(null, stringifyRequestData(data));
return callback(null, queryStringifyRequestData(data));
}
this._stripe._platformFunctions
.tryBufferData(data)

View File

@@ -2,6 +2,7 @@
import { resourceNamespace } from './ResourceNamespace.js';
import { Accounts as FinancialConnectionsAccounts } from './resources/FinancialConnections/Accounts.js';
import { ActiveEntitlements as EntitlementsActiveEntitlements } from './resources/Entitlements/ActiveEntitlements.js';
import { Alerts as BillingAlerts } from './resources/Billing/Alerts.js';
import { Authorizations as TestHelpersIssuingAuthorizations } from './resources/TestHelpers/Issuing/Authorizations.js';
import { Authorizations as IssuingAuthorizations } from './resources/Issuing/Authorizations.js';
import { Calculations as TaxCalculations } from './resources/Tax/Calculations.js';
@@ -12,18 +13,27 @@ import { Configurations as BillingPortalConfigurations } from './resources/Billi
import { Configurations as TerminalConfigurations } from './resources/Terminal/Configurations.js';
import { ConfirmationTokens as TestHelpersConfirmationTokens } from './resources/TestHelpers/ConfirmationTokens.js';
import { ConnectionTokens as TerminalConnectionTokens } from './resources/Terminal/ConnectionTokens.js';
import { CreditBalanceSummary as BillingCreditBalanceSummary } from './resources/Billing/CreditBalanceSummary.js';
import { CreditBalanceTransactions as BillingCreditBalanceTransactions } from './resources/Billing/CreditBalanceTransactions.js';
import { CreditGrants as BillingCreditGrants } from './resources/Billing/CreditGrants.js';
import { CreditReversals as TreasuryCreditReversals } from './resources/Treasury/CreditReversals.js';
import { Customers as TestHelpersCustomers } from './resources/TestHelpers/Customers.js';
import { DebitReversals as TreasuryDebitReversals } from './resources/Treasury/DebitReversals.js';
import { Disputes as IssuingDisputes } from './resources/Issuing/Disputes.js';
import { EarlyFraudWarnings as RadarEarlyFraudWarnings } from './resources/Radar/EarlyFraudWarnings.js';
import { EventDestinations as V2CoreEventDestinations } from './resources/V2/Core/EventDestinations.js';
import { Events as V2CoreEvents } from './resources/V2/Core/Events.js';
import { Features as EntitlementsFeatures } from './resources/Entitlements/Features.js';
import { FinancialAccounts as TreasuryFinancialAccounts } from './resources/Treasury/FinancialAccounts.js';
import { InboundTransfers as TestHelpersTreasuryInboundTransfers } from './resources/TestHelpers/Treasury/InboundTransfers.js';
import { InboundTransfers as TreasuryInboundTransfers } from './resources/Treasury/InboundTransfers.js';
import { Locations as TerminalLocations } from './resources/Terminal/Locations.js';
import { MeterEventAdjustments as BillingMeterEventAdjustments } from './resources/Billing/MeterEventAdjustments.js';
import { MeterEventAdjustments as V2BillingMeterEventAdjustments } from './resources/V2/Billing/MeterEventAdjustments.js';
import { MeterEventSession as V2BillingMeterEventSession } from './resources/V2/Billing/MeterEventSession.js';
import { MeterEventStream as V2BillingMeterEventStream } from './resources/V2/Billing/MeterEventStream.js';
import { MeterEvents as BillingMeterEvents } from './resources/Billing/MeterEvents.js';
import { MeterEvents as V2BillingMeterEvents } from './resources/V2/Billing/MeterEvents.js';
import { Meters as BillingMeters } from './resources/Billing/Meters.js';
import { Orders as ClimateOrders } from './resources/Climate/Orders.js';
import { OutboundPayments as TestHelpersTreasuryOutboundPayments } from './resources/TestHelpers/Treasury/OutboundPayments.js';
@@ -86,6 +96,7 @@ export { ExchangeRates } from './resources/ExchangeRates.js';
export { FileLinks } from './resources/FileLinks.js';
export { Files } from './resources/Files.js';
export { InvoiceItems } from './resources/InvoiceItems.js';
export { InvoiceRenderingTemplates } from './resources/InvoiceRenderingTemplates.js';
export { Invoices } from './resources/Invoices.js';
export { Mandates } from './resources/Mandates.js';
export { OAuth } from './resources/OAuth.js';
@@ -118,6 +129,10 @@ export { Transfers } from './resources/Transfers.js';
export { WebhookEndpoints } from './resources/WebhookEndpoints.js';
export const Apps = resourceNamespace('apps', { Secrets: AppsSecrets });
export const Billing = resourceNamespace('billing', {
Alerts: BillingAlerts,
CreditBalanceSummary: BillingCreditBalanceSummary,
CreditBalanceTransactions: BillingCreditBalanceTransactions,
CreditGrants: BillingCreditGrants,
MeterEventAdjustments: BillingMeterEventAdjustments,
MeterEvents: BillingMeterEvents,
Meters: BillingMeters,
@@ -218,3 +233,15 @@ export const Treasury = resourceNamespace('treasury', {
TransactionEntries: TreasuryTransactionEntries,
Transactions: TreasuryTransactions,
});
export const V2 = resourceNamespace('v2', {
Billing: resourceNamespace('billing', {
MeterEventAdjustments: V2BillingMeterEventAdjustments,
MeterEventSession: V2BillingMeterEventSession,
MeterEventStream: V2BillingMeterEventStream,
MeterEvents: V2BillingMeterEvents,
}),
Core: resourceNamespace('core', {
EventDestinations: V2CoreEventDestinations,
Events: V2CoreEvents,
}),
});

View File

@@ -7,6 +7,10 @@ export const Sessions = StripeResource.extend({
method: 'GET',
fullPath: '/v1/checkout/sessions/{session}',
}),
update: stripeMethod({
method: 'POST',
fullPath: '/v1/checkout/sessions/{session}',
}),
list: stripeMethod({
method: 'GET',
fullPath: '/v1/checkout/sessions',

View File

@@ -11,6 +11,14 @@ export const Invoices = StripeResource.extend({
methodType: 'list',
}),
del: stripeMethod({ method: 'DELETE', fullPath: '/v1/invoices/{invoice}' }),
addLines: stripeMethod({
method: 'POST',
fullPath: '/v1/invoices/{invoice}/add_lines',
}),
createPreview: stripeMethod({
method: 'POST',
fullPath: '/v1/invoices/create_preview',
}),
finalizeInvoice: stripeMethod({
method: 'POST',
fullPath: '/v1/invoices/{invoice}/finalize',
@@ -30,6 +38,10 @@ export const Invoices = StripeResource.extend({
fullPath: '/v1/invoices/{invoice}/mark_uncollectible',
}),
pay: stripeMethod({ method: 'POST', fullPath: '/v1/invoices/{invoice}/pay' }),
removeLines: stripeMethod({
method: 'POST',
fullPath: '/v1/invoices/{invoice}/remove_lines',
}),
retrieveUpcoming: stripeMethod({
method: 'GET',
fullPath: '/v1/invoices/upcoming',
@@ -43,6 +55,10 @@ export const Invoices = StripeResource.extend({
method: 'POST',
fullPath: '/v1/invoices/{invoice}/send',
}),
updateLines: stripeMethod({
method: 'POST',
fullPath: '/v1/invoices/{invoice}/update_lines',
}),
updateLineItem: stripeMethod({
method: 'POST',
fullPath: '/v1/invoices/{invoice}/lines/{line_item_id}',

View File

@@ -1,6 +1,6 @@
'use strict';
import { StripeResource } from '../StripeResource.js';
import { stringifyRequestData } from '../utils.js';
import { queryStringifyRequestData } from '../utils.js';
const stripeMethod = StripeResource.method;
const oAuthHost = 'connect.stripe.com';
export const OAuth = StripeResource.extend({
@@ -22,7 +22,7 @@ export const OAuth = StripeResource.extend({
if (!params.scope) {
params.scope = 'read_write';
}
return `https://${oAuthHost}/${path}?${stringifyRequestData(params)}`;
return `https://${oAuthHost}/${path}?${queryStringifyRequestData(params)}`;
},
token: stripeMethod({
method: 'POST',

View File

@@ -3,6 +3,10 @@ import { StripeResource } from '../../StripeResource.js';
const stripeMethod = StripeResource.method;
export const Calculations = StripeResource.extend({
create: stripeMethod({ method: 'POST', fullPath: '/v1/tax/calculations' }),
retrieve: stripeMethod({
method: 'GET',
fullPath: '/v1/tax/calculations/{calculation}',
}),
listLineItems: stripeMethod({
method: 'GET',
fullPath: '/v1/tax/calculations/{calculation}/line_items',

View File

@@ -14,10 +14,18 @@ export const Authorizations = StripeResource.extend({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/authorizations/{authorization}/expire',
}),
finalizeAmount: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/authorizations/{authorization}/finalize_amount',
}),
increment: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/authorizations/{authorization}/increment',
}),
respond: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/authorizations/{authorization}/fraud_challenges/respond',
}),
reverse: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/authorizations/{authorization}/reverse',

View File

@@ -18,4 +18,8 @@ export const Cards = StripeResource.extend({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/cards/{card}/shipping/ship',
}),
submitCard: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/issuing/cards/{card}/shipping/submit',
}),
});

View File

@@ -2,6 +2,10 @@
import { StripeResource } from '../../../StripeResource.js';
const stripeMethod = StripeResource.method;
export const OutboundPayments = StripeResource.extend({
update: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/treasury/outbound_payments/{id}',
}),
fail: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/treasury/outbound_payments/{id}/fail',

View File

@@ -2,6 +2,10 @@
import { StripeResource } from '../../../StripeResource.js';
const stripeMethod = StripeResource.method;
export const OutboundTransfers = StripeResource.extend({
update: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}',
}),
fail: stripeMethod({
method: 'POST',
fullPath: '/v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/fail',

View File

@@ -19,6 +19,10 @@ export const FinancialAccounts = StripeResource.extend({
fullPath: '/v1/treasury/financial_accounts',
methodType: 'list',
}),
close: stripeMethod({
method: 'POST',
fullPath: '/v1/treasury/financial_accounts/{financial_account}/close',
}),
retrieveFeatures: stripeMethod({
method: 'GET',
fullPath: '/v1/treasury/financial_accounts/{financial_account}/features',

View File

@@ -1,21 +1,22 @@
import * as _Error from './Error.js';
import * as apiVersion from './apiVersion.js';
import * as resources from './resources.js';
import { HttpClient, HttpClientResponse } from './net/HttpClient.js';
import { determineProcessUserAgentProperties, pascalToCamelCase, validateInteger, } from './utils.js';
import { CryptoProvider } from './crypto/CryptoProvider.js';
import { RequestSender } from './RequestSender.js';
import { StripeResource } from './StripeResource.js';
import { createWebhooks } from './Webhooks.js';
import { ApiVersion } from './apiVersion.js';
import { CryptoProvider } from './crypto/CryptoProvider.js';
import { HttpClient, HttpClientResponse } from './net/HttpClient.js';
import * as resources from './resources.js';
import { createApiKeyAuthenticator, determineProcessUserAgentProperties, pascalToCamelCase, validateInteger, } from './utils.js';
const DEFAULT_HOST = 'api.stripe.com';
const DEFAULT_PORT = '443';
const DEFAULT_BASE_PATH = '/v1/';
const DEFAULT_API_VERSION = apiVersion.ApiVersion;
const DEFAULT_API_VERSION = ApiVersion;
const DEFAULT_TIMEOUT = 80000;
const MAX_NETWORK_RETRY_DELAY_SEC = 2;
const MAX_NETWORK_RETRY_DELAY_SEC = 5;
const INITIAL_NETWORK_RETRY_DELAY_SEC = 0.5;
const APP_INFO_PROPERTIES = ['name', 'version', 'url', 'partner_id'];
const ALLOWED_CONFIG_PROPERTIES = [
'authenticator',
'apiVersion',
'typescript',
'maxNetworkRetries',
@@ -28,10 +29,11 @@ const ALLOWED_CONFIG_PROPERTIES = [
'telemetry',
'appInfo',
'stripeAccount',
'stripeContext',
];
const defaultRequestSenderFactory = (stripe) => new RequestSender(stripe, StripeResource.MAX_BUFFERED_REQUEST_METRICS);
export function createStripe(platformFunctions, requestSender = defaultRequestSenderFactory) {
Stripe.PACKAGE_VERSION = '14.25.0';
Stripe.PACKAGE_VERSION = '17.7.0';
Stripe.USER_AGENT = Object.assign({ bindings_version: Stripe.PACKAGE_VERSION, lang: 'node', publisher: 'stripe', uname: null, typescript: false }, determineProcessUserAgentProperties());
Stripe.StripeResource = StripeResource;
Stripe.resources = resources;
@@ -66,14 +68,13 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
this.off = this._emitter.removeListener.bind(this._emitter);
const agent = props.httpAgent || null;
this._api = {
auth: null,
host: props.host || DEFAULT_HOST,
port: props.port || DEFAULT_PORT,
protocol: props.protocol || 'https',
basePath: DEFAULT_BASE_PATH,
version: props.apiVersion || DEFAULT_API_VERSION,
timeout: validateInteger('timeout', props.timeout, DEFAULT_TIMEOUT),
maxNetworkRetries: validateInteger('maxNetworkRetries', props.maxNetworkRetries, 1),
maxNetworkRetries: validateInteger('maxNetworkRetries', props.maxNetworkRetries, 2),
agent: agent,
httpClient: props.httpClient ||
(agent
@@ -81,6 +82,7 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
: this._platformFunctions.createDefaultHttpClient()),
dev: false,
stripeAccount: props.stripeAccount || null,
stripeContext: props.stripeContext || null,
};
const typescript = props.typescript || false;
if (typescript !== Stripe.USER_AGENT.typescript) {
@@ -94,7 +96,7 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
this._setAppInfo(props.appInfo);
}
this._prepResources();
this._setApiKey(key);
this._setAuthenticator(key, props.authenticator);
this.errors = _Error;
// Once Stripe.webhooks looses the factory function signature in a future release
// then this should become this.webhooks = Stripe.webhooks
@@ -147,13 +149,22 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
_enableTelemetry: null,
_requestSender: null,
_platformFunctions: null,
rawRequest(method, path, params, options) {
return this._requestSender._rawRequest(method, path, params, options);
},
/**
* @private
*/
_setApiKey(key) {
if (key) {
this._setApiField('auth', `Bearer ${key}`);
_setAuthenticator(key, authenticator) {
if (key && authenticator) {
throw new Error("Can't specify both apiKey and authenticator");
}
if (!key && !authenticator) {
throw new Error('Neither apiKey nor config.authenticator provided');
}
this._authenticator = key
? createApiKeyAuthenticator(key)
: authenticator;
},
/**
* @private
@@ -271,6 +282,9 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
var _a;
const userAgent = {};
for (const field in seed) {
if (!Object.prototype.hasOwnProperty.call(seed, field)) {
continue;
}
userAgent[field] = encodeURIComponent((_a = seed[field]) !== null && _a !== void 0 ? _a : 'null');
}
// URI-encode in case there are unusual characters in the system's uname.
@@ -314,6 +328,9 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
*/
_prepResources() {
for (const name in resources) {
if (!Object.prototype.hasOwnProperty.call(resources, name)) {
continue;
}
// @ts-ignore
this[pascalToCamelCase(name)] = new resources[name](this);
}
@@ -346,6 +363,10 @@ export function createStripe(platformFunctions, requestSender = defaultRequestSe
}
return config;
},
parseThinEvent(payload, header, secret, tolerance, cryptoProvider, receivedAt) {
// parses and validates the event payload all in one go
return this.webhooks.constructEvent(payload, header, secret, tolerance, cryptoProvider, receivedAt);
},
};
return Stripe;
}

View File

@@ -7,6 +7,9 @@ const OPTIONS_KEYS = [
'maxNetworkRetries',
'timeout',
'host',
'authenticator',
'stripeContext',
'additionalHeaders',
];
export function isOptionsHash(o) {
return (o &&
@@ -17,10 +20,11 @@ export function isOptionsHash(o) {
* Stringifies an Object, accommodating nested objects
* (forming the conventional key 'parent[child]=value')
*/
export function stringifyRequestData(data) {
export function queryStringifyRequestData(data, apiMode) {
return (qs
.stringify(data, {
serializeDate: (d) => Math.floor(d.getTime() / 1000).toString(),
arrayFormat: apiMode == 'v2' ? 'repeat' : 'indices',
})
// Don't use strict form encoding by changing the square bracket control
// characters back to their literals. This is fine by the server, and
@@ -87,7 +91,6 @@ export function getDataFromArgs(args) {
*/
export function getOptionsFromArgs(args) {
const opts = {
auth: null,
host: null,
headers: {},
settings: {},
@@ -95,7 +98,7 @@ export function getOptionsFromArgs(args) {
if (args.length > 0) {
const arg = args[args.length - 1];
if (typeof arg === 'string') {
opts.auth = args.pop();
opts.authenticator = createApiKeyAuthenticator(args.pop());
}
else if (isOptionsHash(arg)) {
const params = Object.assign({}, args.pop());
@@ -104,7 +107,7 @@ export function getOptionsFromArgs(args) {
emitWarning(`Invalid options found (${extraKeys.join(', ')}); ignoring.`);
}
if (params.apiKey) {
opts.auth = params.apiKey;
opts.authenticator = createApiKeyAuthenticator(params.apiKey);
}
if (params.idempotencyKey) {
opts.headers['Idempotency-Key'] = params.idempotencyKey;
@@ -112,6 +115,12 @@ export function getOptionsFromArgs(args) {
if (params.stripeAccount) {
opts.headers['Stripe-Account'] = params.stripeAccount;
}
if (params.stripeContext) {
if (opts.headers['Stripe-Account']) {
throw new Error("Can't specify both stripeAccount and stripeContext.");
}
opts.headers['Stripe-Context'] = params.stripeContext;
}
if (params.apiVersion) {
opts.headers['Stripe-Version'] = params.apiVersion;
}
@@ -124,6 +133,19 @@ export function getOptionsFromArgs(args) {
if (params.host) {
opts.host = params.host;
}
if (params.authenticator) {
if (params.apiKey) {
throw new Error("Can't specify both apiKey and authenticator.");
}
if (typeof params.authenticator !== 'function') {
throw new Error('The authenticator must be a function ' +
'receiving a request as the first parameter.');
}
opts.authenticator = params.authenticator;
}
if (params.additionalHeaders) {
opts.headers = params.additionalHeaders;
}
}
}
return opts;
@@ -229,9 +251,7 @@ export function isObject(obj) {
export function flattenAndStringify(data) {
const result = {};
const step = (obj, prevKey) => {
Object.keys(obj).forEach((key) => {
// @ts-ignore
const value = obj[key];
Object.entries(obj).forEach(([key, value]) => {
const newKey = prevKey ? `${prevKey}[${key}]` : key;
if (isObject(value)) {
if (!(value instanceof Uint8Array) &&
@@ -272,6 +292,15 @@ export function determineProcessUserAgentProperties() {
platform: process.platform,
};
}
export function createApiKeyAuthenticator(apiKey) {
const authenticator = (request) => {
request.headers.Authorization = 'Bearer ' + apiKey;
return Promise.resolve();
};
// For testing
authenticator._apiKey = apiKey;
return authenticator;
}
/**
* Joins an array of Uint8Arrays into a single Uint8Array
*/
@@ -285,3 +314,27 @@ export function concat(arrays) {
});
return merged;
}
/**
* Replaces Date objects with Unix timestamps
*/
function dateTimeReplacer(key, value) {
if (this[key] instanceof Date) {
return Math.floor(this[key].getTime() / 1000).toString();
}
return value;
}
/**
* JSON stringifies an Object, replacing Date objects with Unix timestamps
*/
export function jsonStringifyRequestData(data) {
return JSON.stringify(data, dateTimeReplacer);
}
/**
* Inspects the given path to determine if the endpoint is for v1 or v2 API
*/
export function getAPIMode(path) {
if (!path) {
return 'v1';
}
return path.startsWith('/v2') ? 'v2' : 'v1';
}