My integration to Stripe is written in TypeScript and Angular using AWS Node.js Lambda functions through the AWS API Gateway. Having modest needs for payment services I chose to not code a Stripe payment page for my application and instead chose to use Stripe Checkout. My application user interface code invokes Stripe Checkout by preparing an options object and passing it to stripe.redirectToCheckout(options?).
Limitations of Stripe Checkout
At present Stripe Checkout does not support metadata within the options parameter of the redirectToCheckout(options) function and as far as I can tell there is no way to provide workaround this limitation. The interface for the options parameter is defined within checkout.d.ts from the @stripe\stripe.js library only provides the following fields:
interface RedirectToCheckoutClientOptions { /** * The URL to which Stripe should send customers when payment is complete. * If you’d like access to the Checkout Session for the successful payment, read more about it in our guide on [fulfilling your payments with webhooks](https://stripe.com/docs/payments/checkout/fulfillment#webhooks). */ successUrl: string; /** * The URL to which Stripe should send customers when payment is canceled. */ cancelUrl: string; /** * An array of objects representing the items that your customer would like to purchase. * These items are shown as line items in the Checkout interface and make up the total amount to be collected by Checkout. */ lineItems?: Array<{ /** * The ID of the price that the customer would like to purchase. SKU or plan IDs may also be used. */ price?: string; /** * The quantity of units for the item. */ quantity?: number; }>; /** * An array of objects representing the items that your customer would like to purchase. * These items are shown as line items in the Checkout interface and make up the total amount to be collected by Checkout. * * @deprecated */ items?: Array<{ /** * The ID of the SKU that the customer would like to purchase */ sku?: string; /** * The ID of the plan that the customer would like to subscribe to. */ plan?: string; /** * The quantity of units for the item. */ quantity?: number; }>; /** * The mode of the Checkout Session. Required if using lineItems. */ mode?: 'payment' | 'subscription'; /** * A unique string to reference the Checkout session. * This can be a customer ID, a cart ID, or similar. * It is included in the `checkout.session.completed` webhook and can be used to fulfill the purchase. */ clientReferenceId?: string; /** * The email address used to create the customer object. * If you already know your customer's email address, use this attribute to prefill it on Checkout. */ customerEmail?: string; /** * Specify whether Checkout should collect the customer’s billing address. * If set to `required`, Checkout will attempt to collect the customer’s billing address. * If not set or set to `auto` Checkout will only attempt to collect the billing address when necessary. */ billingAddressCollection?: 'auto' | 'required'; /** * Provides configuration for Checkout to collect a shipping address from a customer. */ shippingAddressCollection?: { /** * An array of two-letter ISO country codes representing which countries * Checkout should provide as options for shipping locations. The codes are * expected to be uppercase. Unsupported country codes: AS, CX, CC, CU, HM, IR, KP, MH, FM, NF, MP, PW, SD, SY, UM, VI. */ allowedCountries: string[]; }; /** * The [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag) of the locale to display Checkout in. * Default is `auto` (Stripe detects the locale of the browser). */ locale?: CheckoutLocale; /** * Describes the type of transaction being performed by Checkout in order to customize relevant text on the page, such as the **Submit** button. * `submitType` can only be specified when using using line items or SKUs, and not subscriptions. * The default is `auto`. */ submitType?: 'auto' | 'book' | 'donate' | 'pay'; }
Two APIs
The confusing part (for me at least) is that the Stripe documentation doesn't clearly delineate that there are (at least) two different APIs with distinct approaches that are not designed to work together.
The larger and full featured API is for a Node.js (or other common backend server) integration where the user interface makes payment calls to the application's Node.js backend which in turn invokes Stripe for payment processing.
The Stripe Checkout API is very limited and suited for integration directly from the client (e.g. to process a payment this API does not "initially" require any sort of backend to invoke Stripe payment processing methods). The reason that I qualify the Stripe Checkout statement as not needing to have a Node.js backend "initially" is because after Stripe brings up their simple/standard payment screen and the user makes a payment I would not recommend trying to complete the payment process via your user interface code and instead implement Stripe Webhooks (using a Node.js backend (or in my case AWS Lambda functions behind an AWS Gateway)). The Webhook endpoints that you register with Stripe which will be called as the payment is processed. In my case once the redirectToCheckout(options) function redirects to my application success or cancelled URLs the user interface is coded to poll one of my AWS endpoint Lambda functions which eventually confirms or does not confirm that payment was completed.
Faux Paus
A mistake that I made (learning experience I had) was attempting to use the Node.js Stripe libraries directly within my user interface application... (I was able to include the Stripe Checkout library into my application, why not also import the Node.js Stripe library?) I think that it might be able to be made to work, but the dependencies that the Stripe library expects to be available because it assumes a Node.js installation need to be also imported into your user interface... I quickly abandoned this approach because it seemed like I was buying a dependency maintenance nightmare for the long-term... other more intrepid developers might be willing/skillful enough to work through managing the dependencies... but not me.
Recommendation:
If you need to pass metadata for a Stripe payment I would recommend not using the Stripe Checkout redirectToCheckout(options) function... instead implement your own payment page that passes metadata to your backend which in turn uses the full Stripe API to process your payment.
Request for Enhancement
Perhaps at some point Stripe will expand Stripe Checkout functionality to support passing metadata and will also support payment types other than credit cards...
At a minimum I would recommend that Stripe liberally place cross-reference links (or warnings) within the documentation of both APIs such that it is obvious that these are not designed for combined usage from an application client.