3

I've got the basics working with Stripe payment intents. I am selling my original artwork. I want to be able to send metadata into the checkout process. The API says I can do that with payment_intents_data.metadata shown here: https://support.stripe.com/questions/using-metadata-with-checkout-sessions

const result = stripe.redirectToCheckout({ sessionId: session, metadata: { id: this.props.id, title: this.props.title } }); 

I get an error saying stripe.redirectToCheckout doesn't have metadata. The sessionId works fine.

How do I pass metadata? I see in the api it is possible, but no examples of how to do it.

3 Answers 3

8

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.

Sign up to request clarification or add additional context in comments.

Comments

0

If you want to include metadata in the PaymentIntent generated during a CheckoutSession you’ll have to include it during CheckoutSession creation, not in redirectToCheckout. When you create the CheckoutSession, you can use payment_intent_data.metadata (https://stripe.com/docs/api/checkout/sessions/create?lang=python#create_checkout_session-payment_intent_data-metadata). You can then continue to call redirectToCheckout with just the session ID.

6 Comments

regardless of what I try, I can't get my django view to accept metadata I get these errors: Received unknown parameter: line_items[0][metadata or Received unknown parameter: line_items[0][payment_intent_data.metadata] depending on if I add payment_intent_data.metadata or just plain metadata to the line item
There's your issue right there - platform_intent_data.metadata is not a child parameter of line_items. It is a separate thing, and should be passed in to the Create CheckoutSession call as its own parameter.
I think also, I am using checkout instead of payment intents. So using intents, i have to create an intent on the server as well as the session?
Nope, there's no need to create an intent on the server before the session. As long as you create the session in 'payment' mode, it'll generate the PaymentIntent for you. By passing in payment_intent_data.metadata you're just telling Stripe what should be included in the PaymentIntent it generates.
In case anyone is trying to do this in subscription mode, you can't. stripe.error.InvalidRequestError: Request req_xyz123: You can not pass payment_intent_data in subscription mode.
|
0

Use fields over which we have control:

In this case we've used query params to pass metadata. Example:

"success_url": "https://outrainer.com?success=1&anothermetadata=1337"

You might read it on frontend (but it's not safe!). Isntead we are using webhook integration so we listen for checkout.session.completed events and update metadata accordingly.

For more sophisticated metadata i.e. {success: true, longerText: "Let's encode it and pass as base64 string"}

you might want to encode whole metadata i.e. as base64 string and decode it later:

"success_url": "https://outrainer.com?metadata=e3N1Y2Nlc3M6IHRydWUsIGxvbmdlclRleHQ6ICJMZXQncyBlbmNvZGUgaXQgYW5kIHBhc3MgYXMgYmFzZTY0IHN0cmluZyJ9"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.