Repository: stripe/stripe-node
Pull request #764 (2020-01-14 11:05:26)

Added typescript webhook example
Closes #749 

r? @rattrayalex-stripe 

In Paul's absence, I'm looking to finish up the TypeScript webhook sample. 


Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 03123de2..07ef8282 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -9,5 +9,8 @@
   // Extension settings
   "eslint.autoFixOnSave": true,
   "eslint.packageManager": "yarn",
-  "npm.packageManager": "yarn"
+  "npm.packageManager": "yarn",
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  }
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc94880b..04abad41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # CHANGELOG
 
+## 8.3.0 - 2020-01-15
+* [#767](https://github.com/stripe/stripe-node/pull/767) Adding missing events for pending updates on `Subscription`
+
+## 8.2.0 - 2020-01-15
+* [#765](https://github.com/stripe/stripe-node/pull/765) Add support for `pending_update` on `Subscription` to the Typescript definitions
+
+## 8.1.0 - 2020-01-14
+* [#763](https://github.com/stripe/stripe-node/pull/763) Add support for listing line items on a `CreditNote`
+* [#762](https://github.com/stripe/stripe-node/pull/762) Improve docs for core fields such as `metadata` on Typescript definitions
+
 ## 8.0.1 - 2020-01-09
 
 - [#757](https://github.com/stripe/stripe-node/pull/757) [bugfix] Add types dir to npmignore whitelist and stop warning when instantiating stripe with no args
diff --git a/README.md b/README.md
index 98c69719..6fb8c7a3 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,10 @@ const customerEmail: string = (charge.customer as Stripe.Customer).email;
 const btId: string = charge.balance_transaction as string;
 ```
 
+### TypeScript examples
+
+You can find a Node.js TypeScript server example in [stripe-samples](https://github.com/stripe-samples/accept-a-card-payment/tree/master/using-webhooks/server/node-typescript) and a webhook signing example in the [`examples/webhook-signing`](examples/webhook-signing) folder.
+
 ### Using Promises
 
 Every method returns a chainable promise which can be used instead of a regular
diff --git a/VERSION b/VERSION
index cd1d2e94..2bf50aaf 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-8.0.1
+8.3.0
diff --git a/examples/webhook-signing/.env.example b/examples/webhook-signing/.env.example
new file mode 100644
index 00000000..dc8528b2
--- /dev/null
+++ b/examples/webhook-signing/.env.example
@@ -0,0 +1,3 @@
+# Stripe keys
+STRIPE_SECRET_KEY=
+STRIPE_WEBHOOK_SECRET=
diff --git a/examples/webhook-signing/.eslintrc.js b/examples/webhook-signing/.eslintrc.js
deleted file mode 100644
index 3c0bbd0f..00000000
--- a/examples/webhook-signing/.eslintrc.js
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
-    "parserOptions": {
-       "ecmaVersion": 6
-     },
-     "rules": {
-        "new-cap": "off",
-        "no-console": "off"
-    }
-};
diff --git a/examples/webhook-signing/.gitignore b/examples/webhook-signing/.gitignore
new file mode 100644
index 00000000..11448345
--- /dev/null
+++ b/examples/webhook-signing/.gitignore
@@ -0,0 +1,2 @@
+.env
+express-ts.js
diff --git a/examples/webhook-signing/README.md b/examples/webhook-signing/README.md
new file mode 100644
index 00000000..e8fe8535
--- /dev/null
+++ b/examples/webhook-signing/README.md
@@ -0,0 +1,54 @@
+# Checking webhook signatures
+
+Verify the events that Stripe sends to your webhook endpoints. Additional details in the Stripe [docs](https://stripe.com/docs/webhooks/signatures).
+
+You can find a Node.js TypeScript server example in [stripe-samples](https://github.com/stripe-samples/accept-a-card-payment/tree/master/using-webhooks/server/node-typescript).
+
+### Requirements
+
+You’ll need the following:
+
+- [Node.js](http://nodejs.org) >=10.0.0
+- Stripe account to accept payments ([sign up](https://dashboard.stripe.com/register) for free).
+- [Stripe CLI](https://github.com/stripe/stripe-cli) or [ngrok](https://ngrok.com/) to tunnel requests to your local server.
+
+### Setup
+
+In this directory (`cd examples/webhook-signing/`), copy the environment variables file:
+
+    cp .env.example .env
+
+Update `.env` with your own [Stripe API keys](https://dashboard.stripe.com/account/apikeys).
+
+### Install and run
+
+Install dependencies:
+
+    npm install
+
+Next, follow [these installation steps](https://github.com/stripe/stripe-cli#installation) to install the Stripe CLI which we'll use for webhook forwarding.
+
+After the installation has finished, authenticate the CLI with your Stripe account:
+
+    stripe login
+
+To start the webhook forwarding run:
+
+    stripe listen --forward-to localhost:3000/webhook
+
+The Stripe CLI will let you know that webhook forwarding is ready and output your webhook signing secret:
+
+    > Ready! Your webhook signing secret is whsec_xxx
+
+Copy the webhook signing secret (`whsec_xxx`) to your `.env` file.
+
+In a separate terminal window, start the local server:
+
+    npm run vanilla # Runs the vanilla JavaScript example.
+    npm run typescript # Compiles and runs the TypeScript example.
+
+In another separate terminal window, trigger an event, for example:
+
+    stripe trigger payment_intent.succeeded
+
+You should now see some webhook event details being logged to your Node.js console.
diff --git a/examples/webhook-signing/express.js b/examples/webhook-signing/express.js
deleted file mode 100644
index 9317c86f..00000000
--- a/examples/webhook-signing/express.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const stripe = require('stripe')(process.env.STRIPE_API_KEY);
-const express = require('express');
-const bodyParser = require('body-parser');
-
-/**
- * You'll need to make sure this is externally accessible.  ngrok (https://ngrok.com/)
- * makes this really easy.
- *
- * To run this file, just provide your Secret API Key and Webhook Secret, like so:
- * STRIPE_API_KEY=sk_test_XXX WEBHOOK_SECRET=whsec_XXX node express.js
- */
-
-const webhookSecret = process.env.WEBHOOK_SECRET;
-const app = express();
-
-// Stripe requires the raw body to construct the event
-app.post(
-  '/webhooks',
-  bodyParser.raw({type: 'application/json'}),
-  (req, res) => {
-    const sig = req.headers['stripe-signature'];
-
-    let event;
-
-    try {
-      event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
-    } catch (err) {
-      // On error, return the error message
-      return res.status(400).send(`Webhook Error: ${err.message}`);
-    }
-
-    // Do something with event
-    console.log('Success:', event.id);
-
-    // Return a response to acknowledge receipt of the event
-    res.json({received: true});
-  }
-);
-
-app.listen(3000, () => {
-  console.log('Example app listening on port 3000!');
-});
diff --git a/examples/webhook-signing/node-express/.eslintrc.js b/examples/webhook-signing/node-express/.eslintrc.js
new file mode 100644
index 00000000..05807d1f
--- /dev/null
+++ b/examples/webhook-signing/node-express/.eslintrc.js
@@ -0,0 +1,10 @@
+module.exports = {
+  parserOptions: {
+    ecmaVersion: 6,
+    sourceType: 'module',
+  },
+  rules: {
+    'new-cap': 'off',
+    'no-console': 'off',
+  },
+};
diff --git a/examples/webhook-signing/node-express/express.js b/examples/webhook-signing/node-express/express.js
new file mode 100644
index 00000000..ee273975
--- /dev/null
+++ b/examples/webhook-signing/node-express/express.js
@@ -0,0 +1,42 @@
+require('dotenv').config();
+const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
+const express = require('express');
+const bodyParser = require('body-parser');
+
+const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
+
+const app = express();
+
+// Use JSON parser for all non-webhook routes
+app.use((req, res, next) => {
+  if (req.originalUrl === '/webhook') {
+    next();
+  } else {
+    bodyParser.json()(req, res, next);
+  }
+});
+
+// Stripe requires the raw body to construct the event
+app.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
+  const sig = req.headers['stripe-signature'];
+
+  let event;
+
+  try {
+    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
+  } catch (err) {
+    // On error, log and return the error message
+    console.log(`❌ Error message: ${err.message}`);
+    return res.status(400).send(`Webhook Error: ${err.message}`);
+  }
+
+  // Successfully constructed event
+  console.log('✅ Success:', event.id);
+
+  // Return a response to acknowledge receipt of the event
+  res.json({received: true});
+});
+
+app.listen(3000, () => {
+  console.log('Example app listening on port 3000!');
+});
diff --git a/examples/webhook-signing/package-lock.json b/examples/webhook-signing/package-lock.json
new file mode 100644
index 00000000..0e15286e
--- /dev/null
+++ b/examples/webhook-signing/package-lock.json
@@ -0,0 +1,474 @@
+{
+  "name": "webhook-signing-example-express",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@types/body-parser": {
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
+      "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==",
+      "requires": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "@types/connect": {
+      "version": "3.4.33",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
+      "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/express": {
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
+      "integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
+      "requires": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "@types/express-serve-static-core": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.1.tgz",
+      "integrity": "sha512-9e7jj549ZI+RxY21Cl0t8uBnWyb22HzILupyHZjYEVK//5TT/1bZodU+yUbLnPdoYViBBnNWbxp4zYjGV0zUGw==",
+      "requires": {
+        "@types/node": "*",
+        "@types/range-parser": "*"
+      }
+    },
+    "@types/mime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
+      "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
+    },
+    "@types/node": {
+      "version": "13.1.6",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.6.tgz",
+      "integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg=="
+    },
+    "@types/range-parser": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
+      "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
+    },
+    "@types/serve-static": {
+      "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
+      "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
+      "requires": {
+        "@types/express-serve-static-core": "*",
+        "@types/mime": "*"
+      }
+    },
+    "accepts": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+      "requires": {
+        "mime-types": "~2.1.24",
+        "negotiator": "0.6.2"
+      }
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+      "requires": {
+        "bytes": "3.1.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "on-finished": "~2.3.0",
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "qs": {
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+          "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+        }
+      }
+    },
+    "bytes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+    },
+    "content-disposition": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+      "requires": {
+        "safe-buffer": "5.1.2"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        }
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "cookie": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "dotenv": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
+      "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "express": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+      "requires": {
+        "accepts": "~1.3.7",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.19.0",
+        "content-disposition": "0.5.3",
+        "content-type": "~1.0.4",
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.1.2",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.5",
+        "qs": "6.7.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.1.2",
+        "send": "0.17.1",
+        "serve-static": "1.14.1",
+        "setprototypeof": "1.1.1",
+        "statuses": "~1.5.0",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "qs": {
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+          "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        }
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      }
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "http-errors": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.1",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "ipaddr.js": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
+      "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+    },
+    "mime-db": {
+      "version": "1.42.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
+      "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ=="
+    },
+    "mime-types": {
+      "version": "2.1.25",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
+      "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
+      "requires": {
+        "mime-db": "1.42.0"
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "negotiator": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "proxy-addr": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
+      "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
+      "requires": {
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.9.0"
+      }
+    },
+    "qs": {
+      "version": "6.9.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz",
+      "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA=="
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "raw-body": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+      "requires": {
+        "bytes": "3.1.0",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.7.2",
+        "mime": "1.6.0",
+        "ms": "2.1.1",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.1",
+        "statuses": "~1.5.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.17.1"
+      }
+    },
+    "setprototypeof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "stripe": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/stripe/-/stripe-8.0.1.tgz",
+      "integrity": "sha512-0D9r1YGkrNFmX6RRk34P0uslrOw4cuav1yuJVcxlIwwhh8R06XIqTTPU6/PeGvJ89SUTU/+jny8gFZU0MZ0rpg==",
+      "requires": {
+        "@types/node": "^13.1.0",
+        "qs": "^6.6.0"
+      }
+    },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typescript": {
+      "version": "3.7.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
+      "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw=="
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    }
+  }
+}
diff --git a/examples/webhook-signing/package.json b/examples/webhook-signing/package.json
index c8bd811b..b454f77d 100644
--- a/examples/webhook-signing/package.json
+++ b/examples/webhook-signing/package.json
@@ -2,12 +2,19 @@
   "name": "webhook-signing-example-express",
   "version": "1.0.0",
   "description": "",
-  "main": "express.js",
-  "scripts": {},
+  "main": "node-express/express.js",
+  "scripts": {
+    "vanilla": "node node-express/express.js",
+    "typescript": "tsc --build typescript-node-express/tsconfig.json && node typescript-node-express/express-ts.js"
+  },
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "express": "^4.16.4",
-    "stripe": "^6.30.0"
+    "@types/express": "^4.17.2",
+    "@types/node": "^13.1.4",
+    "dotenv": "^8.2.0",
+    "express": "^4.17.1",
+    "stripe": "^8.0.1",
+    "typescript": "^3.7.4"
   }
 }
diff --git a/examples/webhook-signing/typescript-node-express/.eslintrc.js b/examples/webhook-signing/typescript-node-express/.eslintrc.js
new file mode 100644
index 00000000..788ac022
--- /dev/null
+++ b/examples/webhook-signing/typescript-node-express/.eslintrc.js
@@ -0,0 +1,18 @@
+module.exports = {
+  root: true,
+  parser: '@typescript-eslint/parser',
+  plugins: ['@typescript-eslint', 'prettier'],
+  extends: [
+    'eslint:recommended',
+    'plugin:@typescript-eslint/eslint-recommended',
+    'plugin:@typescript-eslint/recommended',
+    'plugin:prettier/recommended',
+  ],
+  rules: {
+    '@typescript-eslint/no-use-before-define': 0,
+    '@typescript-eslint/no-empty-interface': 0,
+    '@typescript-eslint/no-unused-vars': 0,
+    '@typescript-eslint/triple-slash-reference': 0,
+    '@typescript-eslint/ban-ts-ignore': 0,
+  },
+};
diff --git a/examples/webhook-signing/typescript-node-express/express-ts.ts b/examples/webhook-signing/typescript-node-express/express-ts.ts
new file mode 100644
index 00000000..020dff80
--- /dev/null
+++ b/examples/webhook-signing/typescript-node-express/express-ts.ts
@@ -0,0 +1,75 @@
+import Stripe from 'stripe';
+import express from 'express';
+import bodyParser from 'body-parser';
+import env from 'dotenv';
+
+env.config();
+
+const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
+  apiVersion: '2019-12-03',
+  typescript: true,
+});
+
+const webhookSecret: string = process.env.STRIPE_WEBHOOK_SECRET;
+
+const app = express();
+
+// Use JSON parser for all non-webhook routes
+app.use(
+  (
+    req: express.Request,
+    res: express.Response,
+    next: express.NextFunction
+  ): void => {
+    if (req.originalUrl === '/webhook') {
+      next();
+    } else {
+      bodyParser.json()(req, res, next);
+    }
+  }
+);
+
+app.post(
+  '/webhook',
+  // Stripe requires the raw body to construct the event
+  bodyParser.raw({type: 'application/json'}),
+  (req: express.Request, res: express.Response): void => {
+    const sig = req.headers['stripe-signature'];
+
+    let event: Stripe.Event;
+
+    try {
+      event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
+    } catch (err) {
+      // On error, log and return the error message
+      console.log(`❌ Error message: ${err.message}`);
+      res.status(400).send(`Webhook Error: ${err.message}`);
+      return;
+    }
+
+    // Successfully constructed event
+    console.log('✅ Success:', event.id);
+
+    // Cast event data to Stripe object
+    if (event.type === 'payment_intent.succeeded') {
+      const stripeObject: Stripe.PaymentIntent = event.data
+        .object as Stripe.PaymentIntent;
+      console.log(`💰 PaymentIntent status: ${stripeObject.status}`);
+    } else if (event.type === 'charge.succeeded') {
+      const charge = event.data.object as Stripe.Charge;
+      console.log(`💵 Charge id: ${charge.id}`);
+    } else {
+      console.warn(`🤷‍♀️ Unhandled event type: ${event.type}`);
+    }
+
+    // Return a response to acknowledge receipt of the event
+    res.json({received: true});
+  }
+);
+
+app.listen(
+  3000,
+  (): void => {
+    console.log('Example app listening on port 3000!');
+  }
+);
diff --git a/examples/webhook-signing/typescript-node-express/tsconfig.json b/examples/webhook-signing/typescript-node-express/tsconfig.json
new file mode 100644
index 00000000..4f085eba
--- /dev/null
+++ b/examples/webhook-signing/typescript-node-express/tsconfig.json
@@ -0,0 +1,15 @@
+{
+  "compilerOptions": {
+    /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
+    "target": "es5",
+    /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+    "module": "commonjs",
+
+    /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
+    "esModuleInterop": true,
+
+    /* Advanced Options */
+    /* Disallow inconsistently-cased references to the same file. */
+    "forceConsistentCasingInFileNames": true
+  }
+}
diff --git a/lib/resources/CreditNotes.js b/lib/resources/CreditNotes.js
index b11f8069..b19ed12a 100644
--- a/lib/resources/CreditNotes.js
+++ b/lib/resources/CreditNotes.js
@@ -17,4 +17,16 @@ module.exports = StripeResource.extend({
     method: 'POST',
     path: '/{id}/void',
   }),
+
+  listLineItems: stripeMethod({
+    method: 'GET',
+    path: '/{creditNote}/lines',
+    methodType: 'list',
+  }),
+
+  listPreviewLineItems: stripeMethod({
+    method: 'GET',
+    path: '/preview/lines',
+    methodType: 'list',
+  }),
 });
diff --git a/package.json b/package.json
index f79c15c5..b01b9884 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "stripe",
-  "version": "8.0.1",
+  "version": "8.3.0",
   "description": "Stripe API wrapper",
   "keywords": [
     "stripe",
diff --git a/types/2019-12-03/Accounts.d.ts b/types/2019-12-03/Accounts.d.ts
index cfff59e9..17e8d9ee 100644
--- a/types/2019-12-03/Accounts.d.ts
+++ b/types/2019-12-03/Accounts.d.ts
@@ -1794,7 +1794,7 @@ declare module 'stripe' {
        *
        * Accounts created using test-mode keys can be deleted at any time. Accounts created using live-mode keys can only be deleted once all balances are zero.
        *
-       * If you want to delete your own account, use the [data tab in your account settings](https://dashboard.stripe.com/account/data) instead.
+       * If you want to delete your own account, use the [account information tab in your account settings](https://dashboard.stripe.com/account) instead.
        */
       del(
         id: string,
diff --git a/types/2019-12-03/CreditNoteLineItems.d.ts b/types/2019-12-03/CreditNoteLineItems.d.ts
new file mode 100644
index 00000000..cf31c41c
--- /dev/null
+++ b/types/2019-12-03/CreditNoteLineItems.d.ts
@@ -0,0 +1,212 @@
+declare module 'stripe' {
+  namespace Stripe {
+    /**
+     * The CreditNoteLineItem object.
+     */
+    interface CreditNoteLineItem {
+      /**
+       * Unique identifier for the object.
+       */
+      id: string;
+
+      /**
+       * String representing the object's type. Objects of the same type share the same value.
+       */
+      object: 'credit_note_line_item';
+
+      /**
+       * The integer amount in **%s** representing the gross amount being credited for this line item, excluding (exclusive) tax and discounts.
+       */
+      amount: number;
+
+      /**
+       * Description of the item being credited.
+       */
+      description: string | null;
+
+      /**
+       * The integer amount in **%s** representing the discount being credited for this line item.
+       */
+      discount_amount: number;
+
+      /**
+       * ID of the invoice line item being credited
+       */
+      invoice_line_item?: string;
+
+      /**
+       * Has the value `true` if the object exists in live mode or the value `false` if the object exists in test mode.
+       */
+      livemode: boolean;
+
+      /**
+       * The number of units of product being credited.
+       */
+      quantity: number | null;
+
+      /**
+       * The amount of tax calculated per tax rate for this line item
+       */
+      tax_amounts: Array<CreditNoteLineItem.TaxAmount>;
+
+      /**
+       * The tax rates which apply to the line item.
+       */
+      tax_rates: Array<Stripe.TaxRate>;
+
+      /**
+       * The type of the credit note line item, one of `custom_line_item` or `invoice_line_item`. When the type is `invoice_line_item` there is an additional `invoice_line_item` property on the resource the value of which is the id of the credited line item on the invoice.
+       */
+      type: CreditNoteLineItem.Type;
+
+      /**
+       * The cost of each unit of product being credited.
+       */
+      unit_amount: number | null;
+
+      /**
+       * Same as `unit_amount`, but contains a decimal value with at most 12 decimal places.
+       */
+      unit_amount_decimal: string | null;
+    }
+
+    namespace CreditNoteLineItem {
+      interface TaxAmount {
+        /**
+         * The amount, in %s, of the tax.
+         */
+        amount: number;
+
+        /**
+         * Whether this tax amount is inclusive or exclusive.
+         */
+        inclusive: boolean;
+
+        /**
+         * The tax rate that was applied to get this tax amount.
+         */
+        tax_rate: string | Stripe.TaxRate;
+      }
+
+      type Type = 'custom_line_item' | 'invoice_line_item';
+    }
+
+    interface CreditNoteLineItemListParams extends PaginationParams {
+      /**
+       * Specifies which fields in the response should be expanded.
+       */
+      expand?: Array<string>;
+    }
+
+    interface CreditNoteLineItemListPreviewParams extends PaginationParams {
+      /**
+       * ID of the invoice.
+       */
+      invoice: string;
+
+      /**
+       * The integer amount in **%s** representing the total amount of the credit note.
+       */
+      amount?: number;
+
+      /**
+       * The integer amount in **%s** representing the amount to credit the customer's balance, which will be automatically applied to their next invoice.
+       */
+      credit_amount?: number;
+
+      /**
+       * Specifies which fields in the response should be expanded.
+       */
+      expand?: Array<string>;
+
+      /**
+       * Line items that make up the credit note.
+       */
+      lines?: Array<CreditNoteLineItemListPreviewParams.Line>;
+
+      /**
+       * The credit note's memo appears on the credit note PDF.
+       */
+      memo?: string;
+
+      /**
+       * Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to `metadata`.
+       */
+      metadata?: MetadataParam;
+
+      /**
+       * The integer amount in **%s** representing the amount that is credited outside of Stripe.
+       */
+      out_of_band_amount?: number;
+
+      /**
+       * Reason for issuing this credit note, one of `duplicate`, `fraudulent`, `order_change`, or `product_unsatisfactory`
+       */
+      reason?: CreditNoteLineItemListPreviewParams.Reason;
+
+      /**
+       * ID of an existing refund to link this credit note to.
+       */
+      refund?: string;
+
+      /**
+       * The integer amount in **%s** representing the amount to refund. If set, a refund will be created for the charge associated with the invoice.
+       */
+      refund_amount?: number;
+    }
+
+    namespace CreditNoteLineItemListPreviewParams {
+      interface Line {
+        /**
+         * The line item amount to credit. Only valid when `type` is `invoice_line_item` and the referenced invoice line item does not have a quantity, only an amount.
+         */
+        amount?: number;
+
+        /**
+         * The description of the credit note line item. Only valid when the `type` is `custom_line_item`.
+         */
+        description?: string;
+
+        /**
+         * The invoice line item to credit. Only valid when the `type` is `invoice_line_item`.
+         */
+        invoice_line_item?: string;
+
+        /**
+         * The line item quantity to credit.
+         */
+        quantity?: number;
+
+        /**
+         * The tax rates which apply to the credit note line item. Only valid when the `type` is `custom_line_item`.
+         */
+        tax_rates?: Array<string> | '';
+
+        /**
+         * Type of the credit note line item, one of `custom_line_item` or `invoice_line_item`
+         */
+        type: Line.Type;
+
+        /**
+         * The integer unit amount in **%s** of the credit note line item. This `unit_amount` will be multiplied by the quantity to get the full amount to credit for this line item. Only valid when `type` is `custom_line_item`.
+         */
+        unit_amount?: number;
+
+        /**
+         * Same as `unit_amount`, but accepts a decimal value with at most 12 decimal places. Only one of `unit_amount` and `unit_amount_decimal` can be set.
+         */
+        unit_amount_decimal?: string;
+      }
+
+      namespace Line {
+        type Type = 'custom_line_item' | 'invoice_line_item';
+      }
+
+      type Reason =
+        | 'duplicate'
+        | 'fraudulent'
+        | 'order_change'
+        | 'product_unsatisfactory';
+    }
+  }
+}
diff --git a/types/2019-12-03/CreditNotes.d.ts b/types/2019-12-03/CreditNotes.d.ts
index c3fe152b..8efe6b6a 100644
--- a/types/2019-12-03/CreditNotes.d.ts
+++ b/types/2019-12-03/CreditNotes.d.ts
@@ -42,11 +42,21 @@ declare module 'stripe' {
         | Stripe.CustomerBalanceTransaction
         | null;
 
+      /**
+       * The integer amount in **%s** representing the amount of the discount that was credited.
+       */
+      discount_amount?: number;
+
       /**
        * ID of the invoice.
        */
       invoice: string | Stripe.Invoice;
 
+      /**
+       * Line items that make up the credit note
+       */
+      lines?: ApiList<Stripe.CreditNoteLineItem>;
+
       /**
        * Has the value `true` if the object exists in live mode or the value `false` if the object exists in test mode.
        */
@@ -67,6 +77,11 @@ declare module 'stripe' {
        */
       number: string;
 
+      /**
+       * Amount that was credited outside of Stripe.
+       */
+      out_of_band_amount?: number | null;
+
       /**
        * The link to download the PDF of the credit note.
        */
@@ -87,6 +102,21 @@ declare module 'stripe' {
        */
       status: CreditNote.Status;
 
+      /**
+       * The integer amount in **%s** representing the amount of the credit note, excluding tax and discount.
+       */
+      subtotal?: number;
+
+      /**
+       * The aggregate amounts calculated per tax rate for all line items.
+       */
+      tax_amounts?: Array<CreditNote.TaxAmount>;
+
+      /**
+       * The integer amount in **%s** representing the total amount of the credit note, including tax and discount.
+       */
+      total?: number;
+
       /**
        * Type of this credit note, one of `post_payment` or `pre_payment`. A `pre_payment` credit note means it was issued when the invoice was open. A `post_payment` credit note means it was issued when the invoice was paid.
        */
@@ -107,6 +137,23 @@ declare module 'stripe' {
 
       type Status = 'issued' | 'void';
 
+      interface TaxAmount {
+        /**
+         * The amount, in %s, of the tax.
+         */
+        amount: number;
+
+        /**
+         * Whether this tax amount is inclusive or exclusive.
+         */
+        inclusive: boolean;
+
+        /**
+         * The tax rate that was applied to get this tax amount.
+         */
+        tax_rate: string | Stripe.TaxRate;
+      }
+
       type Type = 'post_payment' | 'pre_payment';
     }
 
@@ -131,6 +178,11 @@ declare module 'stripe' {
        */
       expand?: Array<string>;
 
+      /**
+       * Line items that make up the credit note.
+       */
+      lines?: Array<CreditNoteCreateParams.Line>;
+
       /**
        * The credit note's memo appears on the credit note PDF.
        */
@@ -163,6 +215,52 @@ declare module 'stripe' {
     }
 
     namespace CreditNoteCreateParams {
+      interface Line {
+        /**
+         * The line item amount to credit. Only valid when `type` is `invoice_line_item` and the referenced invoice line item does not have a quantity, only an amount.
+         */
+        amount?: number;
+
+        /**
+         * The description of the credit note line item. Only valid when the `type` is `custom_line_item`.
+         */
+        description?: string;
+
+        /**
+         * The invoice line item to credit. Only valid when the `type` is `invoice_line_item`.
+         */
+        invoice_line_item?: string;
+
+        /**
+         * The line item quantity to credit.
+         */
+        quantity?: number;
+
+        /**
+         * The tax rates which apply to the credit note line item. Only valid when the `type` is `custom_line_item`.
+         */
+        tax_rates?: Array<string> | '';
+
+        /**
+         * Type of the credit note line item, one of `custom_line_item` or `invoice_line_item`
+         */
+        type: Line.Type;
+
+        /**
+         * The integer unit amount in **%s** of the credit note line item. This `unit_amount` will be multiplied by the quantity to get the full amount to credit for this line item. Only valid when `type` is `custom_line_item`.
+         */
+        unit_amount?: number;
+
+        /**
+         * Same as `unit_amount`, but accepts a decimal value with at most 12 decimal places. Only one of `unit_amount` and `unit_amount_decimal` can be set.
+         */
+        unit_amount_decimal?: string;
+      }
+
+      namespace Line {
+        type Type = 'custom_line_item' | 'invoice_line_item';
+      }
+
       type Reason =
         | 'duplicate'
         | 'fraudulent'
@@ -232,6 +330,11 @@ declare module 'stripe' {
        */
       expand?: Array<string>;
 
+      /**
+       * Line items that make up the credit note.
+       */
+      lines?: Array<CreditNotePreviewParams.Line>;
+
       /**
        * The credit note's memo appears on the credit note PDF.
        */
@@ -264,6 +367,52 @@ declare module 'stripe' {
     }
 
     namespace CreditNotePreviewParams {
+      interface Line {
+        /**
+         * The line item amount to credit. Only valid when `type` is `invoice_line_item` and the referenced invoice line item does not have a quantity, only an amount.
+         */
+        amount?: number;
+
+        /**
+         * The description of the credit note line item. Only valid when the `type` is `custom_line_item`.
+         */
+        description?: string;
+
+        /**
+         * The invoice line item to credit. Only valid when the `type` is `invoice_line_item`.
+         */
+        invoice_line_item?: string;
+
+        /**
+         * The line item quantity to credit.
+         */
+        quantity?: number;
+
+        /**
+         * The tax rates which apply to the credit note line item. Only valid when the `type` is `custom_line_item`.
+         */
+        tax_rates?: Array<string> | '';
+
+        /**
+         * Type of the credit note line item, one of `custom_line_item` or `invoice_line_item`
+         */
+        type: Line.Type;
+
+        /**
+         * The integer unit amount in **%s** of the credit note line item. This `unit_amount` will be multiplied by the quantity to get the full amount to credit for this line item. Only valid when `type` is `custom_line_item`.
+         */
+        unit_amount?: number;
+
+        /**
+         * Same as `unit_amount`, but accepts a decimal value with at most 12 decimal places. Only one of `unit_amount` and `unit_amount_decimal` can be set.
+         */
+        unit_amount_decimal?: string;
+      }
+
+      namespace Line {
+        type Type = 'custom_line_item' | 'invoice_line_item';
+      }
+
       type Reason =
         | 'duplicate'
         | 'fraudulent'
@@ -351,6 +500,27 @@ declare module 'stripe' {
         id: string,
         options?: RequestOptions
       ): Promise<Stripe.CreditNote>;
+
+      /**
+       * When retrieving a credit note, you'll get a lines property containing the the first handful of those items. There is also a URL where you can retrieve the full (paginated) list of line items.
+       */
+      listLineItems(
+        id: string,
+        params?: CreditNoteLineItemListParams,
+        options?: RequestOptions
+      ): ApiListPromise<Stripe.CreditNoteLineItem>;
+      listLineItems(
+        id: string,
+        options?: RequestOptions
+      ): ApiListPromise<Stripe.CreditNoteLineItem>;
+
+      /**
+       * When retrieving a credit note preview, you'll get a lines property containing the first handful of those items. This URL you can retrieve the full (paginated) list of line items.
+       */
+      listPreviewLineItems(
+        params: CreditNoteLineItemListPreviewParams,
+        options?: RequestOptions
+      ): ApiListPromise<Stripe.CreditNoteLineItem>;
     }
   }
 }
diff --git a/types/2019-12-03/InvoiceItems.d.ts b/types/2019-12-03/InvoiceItems.d.ts
index 5356750f..bb5f4fd6 100644
--- a/types/2019-12-03/InvoiceItems.d.ts
+++ b/types/2019-12-03/InvoiceItems.d.ts
@@ -144,11 +144,6 @@ declare module 'stripe' {
     }
 
     interface InvoiceItemCreateParams {
-      /**
-       * Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase. Must be a [supported currency](https://stripe.com/docs/currencies).
-       */
-      currency: string;
-
       /**
        * The ID of the customer who will be billed when this invoice item is billed.
        */
@@ -159,6 +154,11 @@ declare module 'stripe' {
        */
       amount?: number;
 
+      /**
+       * Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase. Must be a [supported currency](https://stripe.com/docs/currencies).
+       */
+      currency?: string;
+
       /**
        * An arbitrary string which you can attach to the invoice item. The description is displayed in the invoice for easy tracking.
        */
diff --git a/types/2019-12-03/InvoiceLineItems.d.ts b/types/2019-12-03/InvoiceLineItems.d.ts
index 6eeccc62..dafa934c 100644
--- a/types/2019-12-03/InvoiceLineItems.d.ts
+++ b/types/2019-12-03/InvoiceLineItems.d.ts
@@ -204,6 +204,11 @@ declare module 'stripe' {
        */
       subscription_prorate?: boolean;
 
+      /**
+       * Determines how to handle [prorations](https://stripe.com/docs/subscriptions/billing-cycle#prorations) when the billing cycle changes (e.g., when switching plans, resetting `billing_cycle_anchor=now`, or starting a trial), or if an item's `quantity` changes. The value defaults to `create_prorations`, indicating that proration invoice items should be created. Prorations can be disabled by setting the value to `none`. Passing `always_invoice` will cause an invoice to immediately be created for any prorations.
+       */
+      subscription_proration_behavior?: InvoiceLineItemListUpcomingParams.SubscriptionProrationBehavior;
+
       /**
        * If previewing an update to a subscription, and doing proration, `subscription_proration_date` forces the proration to be calculated as though the update was done at the specified time. The time given must be within the current subscription period, and cannot be before the subscription was on its current plan. If set, `subscription`, and one of `subscription_items`, or `subscription_trial_end` are required. Also, `subscription_proration` cannot be set to false.
        */
@@ -351,6 +356,11 @@ declare module 'stripe' {
           usage_gte: number;
         }
       }
+
+      type SubscriptionProrationBehavior =
+        | 'always_invoice'
+        | 'create_prorations'
+        | 'none';
     }
   }
 }
diff --git a/types/2019-12-03/Invoices.d.ts b/types/2019-12-03/Invoices.d.ts
index 05d7ef8f..09e5f417 100644
--- a/types/2019-12-03/Invoices.d.ts
+++ b/types/2019-12-03/Invoices.d.ts
@@ -858,6 +858,11 @@ declare module 'stripe' {
        */
       subscription_prorate?: boolean;
 
+      /**
+       * Determines how to handle [prorations](https://stripe.com/docs/subscriptions/billing-cycle#prorations) when the billing cycle changes (e.g., when switching plans, resetting `billing_cycle_anchor=now`, or starting a trial), or if an item's `quantity` changes. The value defaults to `create_prorations`, indicating that proration invoice items should be created. Prorations can be disabled by setting the value to `none`. Passing `always_invoice` will cause an invoice to immediately be created for any prorations.
+       */
+      subscription_proration_behavior?: InvoiceRetrieveUpcomingParams.SubscriptionProrationBehavior;
+
       /**
        * If previewing an update to a subscription, and doing proration, `subscription_proration_date` forces the proration to be calculated as though the update was done at the specified time. The time given must be within the current subscription period, and cannot be before the subscription was on its current plan. If set, `subscription`, and one of `subscription_items`, or `subscription_trial_end` are required. Also, `subscription_proration` cannot be set to false.
        */
@@ -1005,6 +1010,11 @@ declare module 'stripe' {
           usage_gte: number;
         }
       }
+
+      type SubscriptionProrationBehavior =
+        | 'always_invoice'
+        | 'create_prorations'
+        | 'none';
     }
 
     interface InvoiceSendInvoiceParams {
diff --git a/types/2019-12-03/SubscriptionItems.d.ts b/types/2019-12-03/SubscriptionItems.d.ts
index 17b72327..72523217 100644
--- a/types/2019-12-03/SubscriptionItems.d.ts
+++ b/types/2019-12-03/SubscriptionItems.d.ts
@@ -116,6 +116,11 @@ declare module 'stripe' {
        */
       prorate?: boolean;
 
+      /**
+       * Determines how to handle [prorations](https://stripe.com/docs/subscriptions/billing-cycle#prorations) resulting from the `billing_cycle_anchor`. The value defaults to `create_prorations`, indicating that proration invoice items should be created. Prorations can be disabled by setting the value to `none`. `always_invoice` cannot be passed.
+       */
+      proration_behavior?: SubscriptionItemCreateParams.ProrationBehavior;
+
       /**
        * If set, the proration will be calculated as though the subscription was updated at the given time. This can be used to apply the same proration that was previewed with the [upcoming invoice](https://stripe.com/docs/api#retrieve_customer_invoice) endpoint.
        */
@@ -144,6 +149,8 @@ declare module 'stripe' {
         | 'allow_incomplete'
         | 'error_if_incomplete'
         | 'pending_if_incomplete';
+
+      type ProrationBehavior = 'always_invoice' | 'create_prorations' | 'none';
     }
 
     interface SubscriptionItemRetrieveParams {
@@ -188,6 +195,11 @@ declare module 'stripe' {
        */
       prorate?: boolean;
 
+      /**
+       * Determines how to handle [prorations](https://stripe.com/docs/subscriptions/billing-cycle#prorations) when the billing cycle changes (e.g., when switching plans, resetting `billing_cycle_anchor=now`, or starting a trial), or if an item's `quantity` changes. The value defaults to `create_prorations`, indicating that proration invoice items should be created. Prorations can be disabled by setting the value to `none`. Passing `always_invoice` will cause an invoice to immediately be created for any prorations.
+       */
+      proration_behavior?: SubscriptionItemUpdateParams.ProrationBehavior;
+
       /**
        * If set, the proration will be calculated as though the subscription was updated at the given time. This can be used to apply the same proration that was previewed with the [upcoming invoice](https://stripe.com/docs/api#retrieve_customer_invoice) endpoint.
        */
@@ -216,6 +228,8 @@ declare module 'stripe' {
         | 'allow_incomplete'
         | 'error_if_incomplete'
         | 'pending_if_incomplete';
+
+      type ProrationBehavior = 'always_invoice' | 'create_prorations' | 'none';
     }
 
     interface SubscriptionItemListParams extends PaginationParams {
diff --git a/types/2019-12-03/Subscriptions.d.ts b/types/2019-12-03/Subscriptions.d.ts
index 837cff87..67bdfe11 100644
--- a/types/2019-12-03/Subscriptions.d.ts
+++ b/types/2019-12-03/Subscriptions.d.ts
@@ -134,6 +134,11 @@ declare module 'stripe' {
        */
       pending_setup_intent: string | Stripe.SetupIntent | null;
 
+      /**
+       * If specified, [pending updates](https://stripe.com/docs/billing/subscriptions/pending-updates) that will be applied to the subscription once the `latest_invoice` has been paid.
+       */
+      pending_update: Subscription.PendingUpdate | null;
+
       /**
        * Hash describing the plan the customer is subscribed to. Only set if the subscription contains a single plan.
        */
@@ -219,6 +224,33 @@ declare module 'stripe' {
         type Interval = 'day' | 'month' | 'week' | 'year';
       }
 
+      interface PendingUpdate {
+        /**
+         * If the update is applied, determines the date of the first full invoice, and, for plans with `month` or `year` intervals, the day of the month for subsequent invoices.
+         */
+        billing_cycle_anchor: number | null;
+
+        /**
+         * The point after which the changes reflected by this update will be discarded and no longer applied.
+         */
+        expires_at: number;
+
+        /**
+         * List of subscription items, each with an attached plan, that will be set if the update is applied.
+         */
+        subscription_items: Array<Stripe.SubscriptionItem> | null;
+
+        /**
+         * Unix timestamp representing the end of the trial period the customer will get before being charged for the first time, if the update is applied.
+         */
+        trial_end: number | null;
+
+        /**
+         * Indicates if a plan's `trial_period_days` should be applied to the subscription. Setting `trial_end` per subscription is preferred, and this defaults to `false`. Setting this flag to `true` together with `trial_end` is not allowed.
+         */
+        trial_from_plan: boolean | null;
+      }
+
       type Status =
         | 'active'
         | 'canceled'
@@ -339,6 +371,11 @@ declare module 'stripe' {
        */
       prorate?: boolean;
 
+      /**
+       * Determines how to handle [prorations](https://stripe.com/docs/subscriptions/billing-cycle#prorations) resulting from the `billing_cycle_anchor`. The value defaults to `create_prorations`, indicating that proration invoice items should be created. Prorations can be disabled by setting the value to `none`. `always_invoice` cannot be passed.
+       */
+      proration_behavior?: SubscriptionCreateParams.ProrationBehavior;
+
       /**
        * A non-negative decimal (with at most four decimal places) between 0 and 100. This represents the percentage of the subscription invoice subtotal that will be calculated and added as tax to the final amount in each billing period. For example, a plan which charges $10/month with a `tax_percent` of `20.0` will charge $12 per invoice. To unset a previously-set value, pass an empty string. This field has been deprecated and will be removed in a future API version, for further information view the [migration docs](https://stripe.com/docs/billing/migration/taxes) for `tax_rates`.
        */
@@ -437,6 +474,8 @@ declare module 'stripe' {
         type Interval = 'day' | 'month' | 'week' | 'year';
       }
 
+      type ProrationBehavior = 'always_invoice' | 'create_prorations' | 'none';
+
       interface TransferData {
         /**
          * ID of an existing, connected Stripe account.
@@ -545,6 +584,11 @@ declare module 'stripe' {
        */
       prorate?: boolean;
 
+      /**
+       * Determines how to handle [prorations](https://stripe.com/docs/subscriptions/billing-cycle#prorations) when the billing cycle changes (e.g., when switching plans, resetting `billing_cycle_anchor=now`, or starting a trial), or if an item's `quantity` changes. The value defaults to `create_prorations`, indicating that proration invoice items should be created. Prorations can be disabled by setting the value to `none`. Passing `always_invoice` will cause an invoice to immediately be created for any prorations.
+       */
+      proration_behavior?: SubscriptionUpdateParams.ProrationBehavior;
+
       /**
        * If set, the proration will be calculated as though the subscription was updated at the given time. This can be used to apply exactly the same proration that was previewed with [upcoming invoice](https://stripe.com/docs/api#retrieve_customer_invoice) endpoint. It can also be used to implement custom proration logic, such as prorating by day instead of by second, by providing the time that you wish to use for proration calculations.
        */
@@ -660,6 +704,8 @@ declare module 'stripe' {
         type Interval = 'day' | 'month' | 'week' | 'year';
       }
 
+      type ProrationBehavior = 'always_invoice' | 'create_prorations' | 'none';
+
       interface TransferData {
         /**
          * ID of an existing, connected Stripe account.
diff --git a/types/2019-12-03/WebhookEndpoints.d.ts b/types/2019-12-03/WebhookEndpoints.d.ts
index 7f57b455..25a80591 100644
--- a/types/2019-12-03/WebhookEndpoints.d.ts
+++ b/types/2019-12-03/WebhookEndpoints.d.ts
@@ -245,6 +245,8 @@ declare module 'stripe' {
         | 'customer.source.updated'
         | 'customer.subscription.created'
         | 'customer.subscription.deleted'
+        | 'customer.subscription.pending_update_applied'
+        | 'customer.subscription.pending_update_expired'
         | 'customer.subscription.trial_will_end'
         | 'customer.subscription.updated'
         | 'customer.tax_id.created'
@@ -428,6 +430,8 @@ declare module 'stripe' {
         | 'customer.source.updated'
         | 'customer.subscription.created'
         | 'customer.subscription.deleted'
+        | 'customer.subscription.pending_update_applied'
+        | 'customer.subscription.pending_update_expired'
         | 'customer.subscription.trial_will_end'
         | 'customer.subscription.updated'
         | 'customer.tax_id.created'
diff --git a/types/2019-12-03/index.d.ts b/types/2019-12-03/index.d.ts
index babf93d2..e7c57aba 100644
--- a/types/2019-12-03/index.d.ts
+++ b/types/2019-12-03/index.d.ts
@@ -79,6 +79,7 @@
 ///<reference path='./Persons.d.ts' />
 ///<reference path='./FeeRefunds.d.ts' />
 ///<reference path='./BitcoinTransactions.d.ts' />
+///<reference path='./CreditNoteLineItems.d.ts' />
 ///<reference path='./CustomerBalanceTransactions.d.ts' />
 ///<reference path='./CustomerSources.d.ts' />
 ///<reference path='./TaxIds.d.ts' />


Test patch:
diff --git a/test/resources/CreditNotes.spec.js b/test/resources/CreditNotes.spec.js
index 914c8e31..95f4a8e3 100644
--- a/test/resources/CreditNotes.spec.js
+++ b/test/resources/CreditNotes.spec.js
@@ -48,6 +48,32 @@ describe('CreditNotes Resource', () => {
     });
   });
 
+  describe('listLineItems', () => {
+    it('Sends the correct request', () => {
+      stripe.creditNotes.listLineItems('cn_123');
+      expect(stripe.LAST_REQUEST).to.deep.equal({
+        method: 'GET',
+        url: '/v1/credit_notes/cn_123/lines',
+        headers: {},
+        data: {},
+        settings: {},
+      });
+    });
+  });
+
+  describe('listPreviewLineItems', () => {
+    it('Sends the correct request', () => {
+      stripe.creditNotes.listPreviewLineItems();
+      expect(stripe.LAST_REQUEST).to.deep.equal({
+        method: 'GET',
+        url: '/v1/credit_notes/preview/lines',
+        headers: {},
+        data: {},
+        settings: {},
+      });
+    });
+  });
+
   describe('update', () => {
     it('Sends the correct request', () => {
       stripe.creditNotes.update('cn_123', {application_fee: 200});