Repository: stripe/stripe-node
Pull request #1773 (2023-04-27 21:02:42)

Update generated code for beta
Codegen for openapi v322.

## Changelog
* Add support for `link` on `Checkout.Session.payment_method_options` and `CheckoutSessionCreateParams.payment_method_options`
* Add support for `brand`, `country`, `description`, `exp_month`, `exp_year`, `fingerprint`, `funding`, `iin`, `issuer`, `last4`, `network`, and `wallet` on `SetupAttempt.payment_method_details.card`



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6623c6bd..ed79e758 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 12.4.0 - 2023-05-04
+* [#1774](https://github.com/stripe/stripe-node/pull/1774) Update generated code
+  * Add support for `link` on `Checkout.Session.payment_method_options` and `CheckoutSessionCreateParams.payment_method_options`
+  * Add support for `brand`, `country`, `description`, `exp_month`, `exp_year`, `fingerprint`, `funding`, `iin`, `issuer`, `last4`, `network`, and `wallet` on `SetupAttempt.payment_method_details.card`
+* [#1782](https://github.com/stripe/stripe-node/pull/1782) Let user supply a timestamp when verifying webhooks
+
 ## 12.4.0-beta.1 - 2023-04-27
 * [#1769](https://github.com/stripe/stripe-node/pull/1769) Update generated code for beta
   * Add support for `billing_cycle_anchor` and `proration_behavior` on `CheckoutSessionCreateParams.subscription_data`
diff --git a/OPENAPI_VERSION b/OPENAPI_VERSION
index 9b1782ff..1ec95384 100644
--- a/OPENAPI_VERSION
+++ b/OPENAPI_VERSION
@@ -1,1 +1,1 @@
-v316
\ No newline at end of file
+v322
\ No newline at end of file
diff --git a/src/Webhooks.ts b/src/Webhooks.ts
index 0bafa73d..0f847091 100644
--- a/src/Webhooks.ts
+++ b/src/Webhooks.ts
@@ -30,14 +30,16 @@ type WebhookSignatureObject = {
     encodedHeader: WebhookHeader,
     secret: string,
     tolerance: number,
-    cryptoProvider: CryptoProvider
+    cryptoProvider: CryptoProvider,
+    receivedAt: number
   ) => boolean;
   verifyHeaderAsync: (
     encodedPayload: WebhookPayload,
     encodedHeader: WebhookHeader,
     secret: string,
     tolerance: number,
-    cryptoProvider: CryptoProvider
+    cryptoProvider: CryptoProvider,
+    receivedAt: number
   ) => Promise<boolean>;
 };
 type WebhookObject = {
@@ -48,14 +50,16 @@ type WebhookObject = {
     header: WebhookHeader,
     secret: string,
     tolerance: null,
-    cryptoProvider: CryptoProvider
+    cryptoProvider: CryptoProvider,
+    receivedAt: number
   ) => WebhookEvent;
   constructEventAsync: (
     payload: WebhookPayload,
     header: WebhookHeader,
     secret: string,
     tolerance: number,
-    cryptoProvider: CryptoProvider
+    cryptoProvider: CryptoProvider,
+    receivedAt: number
   ) => Promise<WebhookEvent>;
   generateTestHeaderString: (opts: WebhookTestHeaderOptions) => string;
 };
@@ -72,14 +76,16 @@ export function createWebhooks(
       header: WebhookHeader,
       secret: string,
       tolerance: null,
-      cryptoProvider: CryptoProvider
+      cryptoProvider: CryptoProvider,
+      receivedAt: number
     ): WebhookEvent {
       this.signature.verifyHeader(
         payload,
         header,
         secret,
         tolerance || Webhook.DEFAULT_TOLERANCE,
-        cryptoProvider
+        cryptoProvider,
+        receivedAt
       );
 
       const jsonPayload =
@@ -94,14 +100,16 @@ export function createWebhooks(
       header: WebhookHeader,
       secret: string,
       tolerance: number,
-      cryptoProvider: CryptoProvider
+      cryptoProvider: CryptoProvider,
+      receivedAt: number
     ): Promise<WebhookEvent> {
       await this.signature.verifyHeaderAsync(
         payload,
         header,
         secret,
         tolerance || Webhook.DEFAULT_TOLERANCE,
-        cryptoProvider
+        cryptoProvider,
+        receivedAt
       );
 
       const jsonPayload =
@@ -159,7 +167,8 @@ export function createWebhooks(
       encodedHeader: WebhookHeader,
       secret: string,
       tolerance: number,
-      cryptoProvider: CryptoProvider
+      cryptoProvider: CryptoProvider,
+      receivedAt: number
     ): boolean {
       const {
         decodedHeader: header,
@@ -184,7 +193,8 @@ export function createWebhooks(
         details,
         expectedSignature,
         tolerance,
-        suspectPayloadType
+        suspectPayloadType,
+        receivedAt
       );
 
       return true;
@@ -195,7 +205,8 @@ export function createWebhooks(
       encodedHeader: WebhookHeader,
       secret: string,
       tolerance: number,
-      cryptoProvider: CryptoProvider
+      cryptoProvider: CryptoProvider,
+      receivedAt: number
     ): Promise<boolean> {
       const {
         decodedHeader: header,
@@ -221,7 +232,8 @@ export function createWebhooks(
         details,
         expectedSignature,
         tolerance,
-        suspectPayloadType
+        suspectPayloadType,
+        receivedAt
       );
     },
   };
@@ -319,7 +331,8 @@ export function createWebhooks(
     details: WebhookParsedHeader,
     expectedSignature: string,
     tolerance: number,
-    suspectPayloadType: boolean
+    suspectPayloadType: boolean,
+    receivedAt: number
   ): boolean {
     const signatureFound = !!details.signatures.filter(
       platformFunctions.secureCompare.bind(platformFunctions, expectedSignature)
@@ -345,7 +358,10 @@ export function createWebhooks(
       });
     }
 
-    const timestampAge = Math.floor(Date.now() / 1000) - details.timestamp;
+    const timestampAge =
+      Math.floor(
+        (typeof receivedAt === 'number' ? receivedAt : Date.now()) / 1000
+      ) - details.timestamp;
 
     if (tolerance > 0 && timestampAge > tolerance) {
       // @ts-ignore
diff --git a/types/AccountsResource.d.ts b/types/AccountsResource.d.ts
index 1e5b5f54..06362e26 100644
--- a/types/AccountsResource.d.ts
+++ b/types/AccountsResource.d.ts
@@ -1239,7 +1239,7 @@ declare module 'stripe' {
         namespace Payouts {
           interface Schedule {
             /**
-             * The number of days charge funds are held before being paid out. May also be set to `minimum`, representing the lowest available value for the account country. Default is `minimum`. The `delay_days` parameter does not apply when the `interval` is `manual`.
+             * The number of days charge funds are held before being paid out. May also be set to `minimum`, representing the lowest available value for the account country. Default is `minimum`. The `delay_days` parameter does not apply when the `interval` is `manual`. [Learn more about controlling payout delay days](https://stripe.com/docs/connect/manage-payout-schedule).
              */
             delay_days?: 'minimum' | number;
 
@@ -2475,7 +2475,7 @@ declare module 'stripe' {
         namespace Payouts {
           interface Schedule {
             /**
-             * The number of days charge funds are held before being paid out. May also be set to `minimum`, representing the lowest available value for the account country. Default is `minimum`. The `delay_days` parameter does not apply when the `interval` is `manual`.
+             * The number of days charge funds are held before being paid out. May also be set to `minimum`, representing the lowest available value for the account country. Default is `minimum`. The `delay_days` parameter does not apply when the `interval` is `manual`. [Learn more about controlling payout delay days](https://stripe.com/docs/connect/manage-payout-schedule).
              */
             delay_days?: 'minimum' | number;
 
diff --git a/types/Capital/FinancingOffersResource.d.ts b/types/Capital/FinancingOffersResource.d.ts
index 960eba87..e12e7a41 100644
--- a/types/Capital/FinancingOffersResource.d.ts
+++ b/types/Capital/FinancingOffersResource.d.ts
@@ -77,8 +77,7 @@ declare module 'stripe' {
 
         /**
          * Acknowledges that platform has received and delivered the financing_offer to
-         * the intended merchant recipient. This is required to make the application
-         * accessible.
+         * the intended merchant recipient.
          */
         markDelivered(
           id: string,
diff --git a/types/Checkout/Sessions.d.ts b/types/Checkout/Sessions.d.ts
index cf574b3a..f48e91da 100644
--- a/types/Checkout/Sessions.d.ts
+++ b/types/Checkout/Sessions.d.ts
@@ -757,6 +757,8 @@ declare module 'stripe' {
 
           konbini?: PaymentMethodOptions.Konbini;
 
+          link?: PaymentMethodOptions.Link;
+
           oxxo?: PaymentMethodOptions.Oxxo;
 
           p24?: PaymentMethodOptions.P24;
@@ -1124,6 +1126,21 @@ declare module 'stripe' {
             setup_future_usage?: 'none';
           }
 
+          interface Link {
+            /**
+             * Indicates that you intend to make future payments with this PaymentIntent's payment method.
+             *
+             * Providing this parameter will [attach the payment method](https://stripe.com/docs/payments/save-during-payment) to the PaymentIntent's Customer, if present, after the PaymentIntent is confirmed and any required actions from the user are complete. If no Customer was provided, the payment method can still be [attached](https://stripe.com/docs/api/payment_methods/attach) to a Customer after the transaction completes.
+             *
+             * When processing card payments, Stripe also uses `setup_future_usage` to dynamically optimize your payment flow and comply with regional legislation and network rules, such as [SCA](https://stripe.com/docs/strong-customer-authentication).
+             */
+            setup_future_usage?: Link.SetupFutureUsage;
+          }
+
+          namespace Link {
+            type SetupFutureUsage = 'none' | 'off_session';
+          }
+
           interface Oxxo {
             /**
              * The number of calendar days before an OXXO invoice expires. For example, if you create an OXXO invoice on Monday and you set expires_after_days to 2, the OXXO invoice will expire on Wednesday at 23:59 America/Mexico_City time.
diff --git a/types/Checkout/SessionsResource.d.ts b/types/Checkout/SessionsResource.d.ts
index 01ae1148..525b3912 100644
--- a/types/Checkout/SessionsResource.d.ts
+++ b/types/Checkout/SessionsResource.d.ts
@@ -900,6 +900,11 @@ declare module 'stripe' {
            */
           konbini?: PaymentMethodOptions.Konbini;
 
+          /**
+           * contains details about the Link payment method options.
+           */
+          link?: PaymentMethodOptions.Link;
+
           /**
            * contains details about the OXXO payment method options.
            */
@@ -1309,6 +1314,21 @@ declare module 'stripe' {
             setup_future_usage?: 'none';
           }
 
+          interface Link {
+            /**
+             * Indicates that you intend to make future payments with this PaymentIntent's payment method.
+             *
+             * Providing this parameter will [attach the payment method](https://stripe.com/docs/payments/save-during-payment) to the PaymentIntent's Customer, if present, after the PaymentIntent is confirmed and any required actions from the user are complete. If no Customer was provided, the payment method can still be [attached](https://stripe.com/docs/api/payment_methods/attach) to a Customer after the transaction completes.
+             *
+             * When processing card payments, Stripe also uses `setup_future_usage` to dynamically optimize your payment flow and comply with regional legislation and network rules, such as [SCA](https://stripe.com/docs/strong-customer-authentication).
+             */
+            setup_future_usage?: Link.SetupFutureUsage;
+          }
+
+          namespace Link {
+            type SetupFutureUsage = 'none' | 'off_session';
+          }
+
           interface Oxxo {
             /**
              * The number of calendar days before an OXXO voucher expires. For example, if you create an OXXO voucher on Monday and you set expires_after_days to 2, the OXXO invoice will expire on Wednesday at 23:59 America/Mexico_City time.
diff --git a/types/SetupAttempts.d.ts b/types/SetupAttempts.d.ts
index ca3cbbcc..b7f00204 100644
--- a/types/SetupAttempts.d.ts
+++ b/types/SetupAttempts.d.ts
@@ -189,14 +189,76 @@ declare module 'stripe' {
 
         interface Card {
           /**
-           * Check results by Card networks on Card address and CVC at time of payment.
+           * Card brand. Can be `amex`, `diners`, `discover`, `eftpos_au`, `jcb`, `mastercard`, `unionpay`, `visa`, or `unknown`.
+           */
+          brand: string | null;
+
+          /**
+           * Check results by Card networks on Card address and CVC at the time of authorization
            */
           checks: Card.Checks | null;
 
+          /**
+           * Two-letter ISO code representing the country of the card. You could use this attribute to get a sense of the international breakdown of cards you've collected.
+           */
+          country: string | null;
+
+          /**
+           * A high-level description of the type of cards issued in this range. (For internal use only and not typically available in standard API requests.)
+           */
+          description?: string | null;
+
+          /**
+           * Two-digit number representing the card's expiration month.
+           */
+          exp_month: number | null;
+
+          /**
+           * Four-digit number representing the card's expiration year.
+           */
+          exp_year: number | null;
+
+          /**
+           * Uniquely identifies this particular card number. You can use this attribute to check whether two customers who've signed up with you are using the same card number, for example. For payment methods that tokenize card information (Apple Pay, Google Pay), the tokenized number might be provided instead of the underlying card number.
+           *
+           * *Starting May 1, 2021, card fingerprint in India for Connect will change to allow two fingerprints for the same card --- one for India and one for the rest of the world.*
+           */
+          fingerprint?: string | null;
+
+          /**
+           * Card funding type. Can be `credit`, `debit`, `prepaid`, or `unknown`.
+           */
+          funding: string | null;
+
+          /**
+           * Issuer identification number of the card. (For internal use only and not typically available in standard API requests.)
+           */
+          iin?: string | null;
+
+          /**
+           * The name of the card's issuing bank. (For internal use only and not typically available in standard API requests.)
+           */
+          issuer?: string | null;
+
+          /**
+           * The last four digits of the card.
+           */
+          last4: string | null;
+
+          /**
+           * Identifies which network this charge was processed on. Can be `amex`, `cartes_bancaires`, `diners`, `discover`, `eftpos_au`, `interac`, `jcb`, `mastercard`, `unionpay`, `visa`, or `unknown`.
+           */
+          network: string | null;
+
           /**
            * Populated if this authorization used 3D Secure authentication.
            */
           three_d_secure: Card.ThreeDSecure | null;
+
+          /**
+           * If this Card is part of a card wallet, this contains the details of the card wallet.
+           */
+          wallet: Card.Wallet | null;
         }
 
         namespace Card {
@@ -263,6 +325,25 @@ declare module 'stripe' {
 
             type Version = '1.0.2' | '2.1.0' | '2.2.0';
           }
+
+          interface Wallet {
+            apple_pay?: Wallet.ApplePay;
+
+            google_pay?: Wallet.GooglePay;
+
+            /**
+             * The type of the card wallet, one of `apple_pay`, `google_pay`, or `link`. An additional hash is included on the Wallet subhash with a name matching this value. It contains additional information specific to the card wallet type.
+             */
+            type: Wallet.Type;
+          }
+
+          namespace Wallet {
+            interface ApplePay {}
+
+            interface GooglePay {}
+
+            type Type = 'apple_pay' | 'google_pay' | 'link';
+          }
         }
 
         interface CardPresent {
diff --git a/types/Tax/Transactions.d.ts b/types/Tax/Transactions.d.ts
index 5ea48dea..c3698ac0 100644
--- a/types/Tax/Transactions.d.ts
+++ b/types/Tax/Transactions.d.ts
@@ -201,7 +201,7 @@ declare module 'stripe' {
           amount_tax: number;
 
           /**
-           * The ID of an existing [ShippingRate](https://stripe.com/docs/api/shipping_rates/object)
+           * The ID of an existing [ShippingRate](https://stripe.com/docs/api/shipping_rates/object). (It is not populated for the transaction resource object and will be removed in the next API version.)
            */
           shipping_rate?: string;
 
@@ -211,7 +211,7 @@ declare module 'stripe' {
           tax_behavior: ShippingCost.TaxBehavior;
 
           /**
-           * Detailed account of taxes relevant to shipping cost.
+           * Detailed account of taxes relevant to shipping cost. (It is not populated for the transaction resource object and will be removed in the next API version.)
            */
           tax_breakdown?: Array<ShippingCost.TaxBreakdown>;
 
diff --git a/types/Webhooks.d.ts b/types/Webhooks.d.ts
index 96a99e70..93cfefcd 100644
--- a/types/Webhooks.d.ts
+++ b/types/Webhooks.d.ts
@@ -36,7 +36,12 @@ declare module 'stripe' {
         /**
          * Optional CryptoProvider to use for computing HMAC signatures.
          */
-        cryptoProvider?: CryptoProvider
+        cryptoProvider?: CryptoProvider,
+
+        /**
+         * Optional: timestamp to use when checking signature validity. Defaults to Date.now().
+         */
+        receivedAt?: number
       ): Stripe.Event;
 
       /**
@@ -73,7 +78,12 @@ declare module 'stripe' {
         /**
          * Optional CryptoProvider to use for computing HMAC signatures.
          */
-        cryptoProvider?: CryptoProvider
+        cryptoProvider?: CryptoProvider,
+
+        /**
+         * Optional: timestamp to use when checking signature validity. Defaults to Date.now().
+         */
+        receivedAt?: number
       ): Promise<Stripe.Event>;
 
       /**


Test patch:
diff --git a/test/Integration.spec.ts b/test/Integration.spec.ts
index f4622d4a..3868a890 100644
--- a/test/Integration.spec.ts
+++ b/test/Integration.spec.ts
@@ -1,6 +1,8 @@
 import * as childProcess from 'child_process';
 const testUtils = require('./testUtils.js');
 
+const nodeVersion = parseInt(process.versions.node.split('.')[0], 10);
+
 describe('Integration test', function() {
   this.timeout(50000);
   const testExec = (cmd: string): Promise<void> => {
@@ -32,7 +34,7 @@ describe('Integration test', function() {
 
   it('should work with ESModule imports', async function() {
     // Node supports ES Modules starting at v12
-    if (parseInt(process.versions.node.split('.')[0], 10) <= 12) {
+    if (nodeVersion <= 12) {
       this.skip();
     }
 
@@ -41,7 +43,7 @@ describe('Integration test', function() {
 
   it('should work with Typescript ESModule imports', async function() {
     // Node supports ES Modules starting at v12
-    if (parseInt(process.versions.node.split('.')[0], 10) <= 12) {
+    if (nodeVersion <= 12) {
       this.skip();
     }
 
@@ -70,7 +72,7 @@ describe('Integration test', function() {
   });
 
   const runWebhookTest = (projectName: string): Promise<void> => {
-    if (parseInt(process.versions.node.split('.')[0], 10) < 14) {
+    if (nodeVersion < 14) {
       console.log('Webhook test requires at least node.js v14, skipping test');
       return Promise.resolve();
     }
@@ -87,7 +89,14 @@ describe('Integration test', function() {
 
   it('Webhook sample koa', () => runWebhookTest('koa'));
 
-  it('Webhook sample nextjs', () => runWebhookTest('nextjs'));
+  it('Webhook sample nextjs', function() {
+    // Next.js supports Node.js >=16
+    if (nodeVersion < 16) {
+      this.skip();
+    }
+
+    runWebhookTest('nextjs');
+  });
 
   it('Webhook sample deno', () => runWebhookTest('deno'));
 });
diff --git a/test/Webhook.spec.ts b/test/Webhook.spec.ts
index 182b3d9b..f4b4bbbb 100644
--- a/test/Webhook.spec.ts
+++ b/test/Webhook.spec.ts
@@ -227,7 +227,30 @@ describe('Webhooks', () => {
         );
       });
 
-      it('should raise a SignatureVerificationError when the timestamp is not within the tolerance', async () => {
+      it('should raise a SignatureVerificationError when the timestamp is not within the tolerance of the provided timestamp', async () => {
+        const receivedAt = 20000000;
+        const header = stripe.webhooks.generateTestHeaderString({
+          timestamp: receivedAt / 1000 - 15,
+          payload: EVENT_PAYLOAD_STRING,
+          secret: SECRET,
+        });
+
+        await expect(
+          verifyHeaderFn(
+            EVENT_PAYLOAD_STRING,
+            header,
+            SECRET,
+            10,
+            null,
+            receivedAt
+          )
+        ).to.be.rejectedWith(
+          StripeSignatureVerificationError,
+          /Timestamp outside the tolerance zone/
+        );
+      });
+
+      it('should raise a SignatureVerificationError when the timestamp is not within the tolerance of Date.now()', async () => {
         const header = stripe.webhooks.generateTestHeaderString({
           timestamp: Date.now() / 1000 - 15,
           payload: EVENT_PAYLOAD_STRING,
@@ -244,7 +267,7 @@ describe('Webhooks', () => {
 
       it(
         'should return true when the header contains a valid signature and ' +
-          'the timestamp is within the tolerance',
+          'the timestamp is within the tolerance of Date.now()',
         async () => {
           const header = stripe.webhooks.generateTestHeaderString({
             timestamp: Date.now() / 1000,
@@ -258,6 +281,30 @@ describe('Webhooks', () => {
         }
       );
 
+      it(
+        'should return true when the header contains a valid signature and ' +
+          'the timestamp is within the tolerance of the provided timestamp',
+        async () => {
+          const receivedAt = 20000000;
+          const header = stripe.webhooks.generateTestHeaderString({
+            timestamp: receivedAt / 1000 - 9,
+            payload: EVENT_PAYLOAD_STRING,
+            secret: SECRET,
+          });
+
+          expect(
+            await verifyHeaderFn(
+              EVENT_PAYLOAD_STRING,
+              header,
+              SECRET,
+              10,
+              null,
+              receivedAt
+            )
+          ).to.equal(true);
+        }
+      );
+
       it('should return true when the header contains at least one valid signature', async () => {
         let header = stripe.webhooks.generateTestHeaderString({
           timestamp: Date.now() / 1000,