stripe / stripe-node

Node.js library for the Stripe API.
https://stripe.com
MIT License
3.73k stars 728 forks source link

Stripe.paymentIntents missing `payment_method_details` type but is included in the returned data object. #2124

Closed holahoon closed 1 week ago

holahoon commented 1 week ago

Describe the bug

Using NextJS 14. I'm trying to get the last 4 digits of the card on payment success event. I set up my code to create a payment intent from the server with confirm: true and allow the stripe webhook to listen for charge.succeeded type and process other database related logics within the block. So I checked the console to see what type of data I get back and I noticed the payment_method_details object which includes the card information, very similar to what stripe.paymentMethods.retrieve() would return. I was trying to use this card info, but quickly realized that this payment_method_details property type doesn't exist in the PaymentIntent type. I'm curious if this field was never intended to be included inside the PaymentIntent object or it's just missing a type?

To Reproduce

  1. Submit element and create confirmation token on the frontend (with useElements() and useStripe() React hooks).

    const onCheckout = async () => {
    if (!stripe || !elements) return
    
    const { error: elementSubmitError } = await elements.submit()
    if (elementSubmitError) {
      // handle error
    }
    
    const { confirmationToken, error: confirmationError } =
      await stripe.createConfirmationToken({
        elements,
      })
    if (confirmationError) {
      // handle error
    }
    
    if (confirmationToken)
      execute({...})
    }
  2. Take the confirmation_token from the frontend and create a payment intent from the server.

    ...
      const paymentIntent = await stripe.paymentIntents.create({
        confirm: true,
        amount,
        currency,
        automatic_payment_methods: { enabled: true },
        confirmation_token: confirmationTokenId,
        return_url: '...',
        customer: stripeCustomerId,
        metadata: {...},
      })
    ...
  3. Allow webhook to listen and for charge.succeeded event type, trigger db logics.

    ...
    try(event.type)
    ...
        case 'charge.succeeded':
          data = object as Stripe.PaymentIntent
          console.log(`💰 Charge status: ${data.status}`)
    
          // cannot access data.payment_method_details 
          // even though the data is actually being returned
    
          break
    ...

Expected behavior

Expect payment_method_details object type to be included in the PaymentIntent object type.

Code snippets

No response

OS

macOS

Node version

Node v20.12.2

Library version

stripe v16.1.0

API version

2023-10-16

Additional context

No response

remi-stripe commented 1 week ago

@holahoon PaymentIntent doesn't have the property payment_method_details anywhere (and never did). That property lives on the Charge object which is a completely separate API resource.

A PaymentIntent has the property latest_charge which is set to the id of the latest Charge associated with it, if any. It's an id but you can use our Expand feature (see this video) to get the full Charge object.

Note that on older API versions before 2022-11-15, a PaymentIntent had the charges property which was a sub-list with the most recent Charge object on it. That might be what made you think that it has the property. This was deprecated 2 years ago and it still confirms that this property was on the Charge object.

If you use an old API version then you would see that charges property but the types in stripe-node always match the latest version of our API. So if you are looking for that information, you need to look at the types for Charge and you can see it defined in our code here

holahoon commented 1 week ago

Yeah, you are right. I must've had a long week 🤦🏻 I was asserting Stripe.PaymentIntent type to the data object for charge.succeeded event type. This data type should've been of type Stripe.Charge

   try(event.type)
   ...
        case 'charge.succeeded':
          data = object as Stripe.PaymentIntent // <-- this should be of type `Stripe.Charge`
          console.log(`💰 Charge status: ${data.status}`)

          // cannot access data.payment_method_details 
          // even though the data is actually being returned

          break

Thanks for clarifying 👍