Dear sirs,
I've been tried this for a while now, but it doesn't seem to work.
My team and I are working on the new payment flow to meet the new standard criteria. This means we save a card, then we try to process the payment in the backend and if it fails, we run
the handleCardPayment function, like this:
...
props.stripe.handleCardPayment(props.clientSecret)
...
Unfortunately, what we get is this error:
Error: Could not find a CardElement or CardNumberElement which which perform handleCardPayment

As far as I have seen in your documentation (https://stripe.com/docs/stripe-js/reference#stripe-handle-card-payment-no-element), though, I can send the client secret only to toggle further steps.
Is there a way to fix this?
Thank you so much.
Hmm, this looks like a bug where we are requiring the second data argument. It should work if you call it with an empty object like so:
props.stripe.handleCardPayment(props.clientSecret, {})
Also, it sounds like you are using the manual confirmation flow. If so, you should instead use stripe.handleCardAction.
Thank you so much! It seems adding an empty object does the trick! However, I endend up using stripe.handleCardAction as you suggested.
Thank you so much for the prompt response.
We're having a similar issue.
You can see the commented out code that used to work with Stripe Elements and the new code that is using the paymentIntent API.
We are getting the following error:
code: "payment_intent_incompatible_payment_method"
doc_url: "https://stripe.com/docs/error-codes/payment-intent-incompatible-payment-method"
message: "A card payment method was expected to be present, but this PaymentIntent does not have a payment method and none was provided. Try again with a payment method or card data.
When we revert back to the normal createToken flow it works and finds the element automatically.
pay = ev => {
const { stripe, setLoading, clientSecret } = this.props
const { nameOnCard } = this.state
ev.preventDefault()
setLoading(true)
if (stripe) {
stripe.handleCardPayment(
clientSecret,
{} // tried both WITH and WITHOUT this empty object
).then(function(result) {
console.log(result)
if (result.error) {
// Display error.message in your UI.
} else {
// The payment has succeeded. Display a success message.
}
});
// stripe.createToken({ name: nameOnCard })
// .then(this.takeTokenToServer)
// .then(this.finishUp)
// .catch(this.errorHandlePayFlow)
} else {
console.log("Stripe.js hasn't loaded yet.")
}
}
Hmm, @sverraest, that code looks reasonable to me. Can you share a bit more about what context this was used in? What is the relationship of this function to the React tree and the Card Element?
So we're using a custom components where we inject Stripe according to a previous guide.
What's strange is that the previous code with the createToken works perfectly in this context.
However with trying to switch to Payment Intents for the new SCA it's not working:
Imports:
import React, { Component } from 'react'
import { isEmpty } from 'lodash'
import ReactDOM from 'react-dom'
import {
injectStripe,
Elements,
CardNumberElement,
CardExpiryElement,
CardCVCElement,
PostalCodeElement,
StripeProvider
} from 'react-stripe-elements'
We define the form class in _CardPaymentForm (lowest level) and _CardPaymentForm has the pay function as described in my previous post and a render method as follows:
render () {
const { errors } = this.state
return (
<PaymentFormComponent>
<CardNumberContainer
className={errors.cardNumber && 'hasError'}
>
<CardNumberElement
placeholder={constants.CARD_NUMBER_PLACEHOLDER}
onChange={this.onFieldChange('cardNumber')}
style={{ base: { fontSize: '15px' } }}
/>
</CardNumberContainer>
<Row>
<CardExpiryContainer
className={errors.expirationDate && 'hasError'}>
<CardExpiryElement
placeholder={constants.EXPIRATION_DATE_PLACEHOLDER}
onChange={this.onFieldChange('expirationDate')}
style={{ base: { fontSize: '15px' } }}
/>
</CardExpiryContainer>
<CardCVCContainer
className={errors.CVC && 'hasError'}>
<CardCVCElement
placeholder={constants.CVC_PLACEHOLDER}
onChange={this.onFieldChange('CVC')}
onFocus={this.showTooltip}
onBlur={this.hideTooltip}
style={{ base: { fontSize: '15px' } }}
/>
</CardCVCContainer>
...
</PaymentFormComponent>
)
}
Then we inject Stripe using injectStripe from imports
const CardPaymentForm = injectStripe(_CardPaymentForm)
Then we have Stripe specific form that uses this CardPaymentForm:
In StripePaymentForm (highest element):
render () {
return (
<StripeProvider stripe={this.state.stripe}>
<Elements>
<CardPaymentForm clientSecret={this.state.clientSecret} {...this.props} />
</Elements>
</StripeProvider>
)
}
So this works perfectly already in production with the normal previous createToken flow but gives an error when switching to PaymentIntents.
The clientSecret is definitely there.
code: "payment_intent_incompatible_payment_method"
doc_url: "https://stripe.com/docs/error-codes/payment-intent-incompatible-payment-method"
message: "A card payment method was expected to be present, but this PaymentIntent does not have a payment method and none was provided. Try again with a payment method or card data.
@sverraest Are you using the latest version of the library? v4? Some PaymentIntent-related improvements were made then and I could see this error happening if you were on an earlier version.
Updating to v4 for react-stripe-elements indeed solved the issue.
Most helpful comment
Updating to v4 for react-stripe-elements indeed solved the issue.