I'm trying to implement Stripe Elements Payment Intents into my React app using Netlify Functions. Submitting the payment form returns the error: Unhandled Rejection (IntegrationError): Invalid value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}. You specified: .
I can see the client_secret value in the paymentIntent object, so the serverless function is returning the client_secret, but I must not implementing it appropriate in the client. If I pass data.clientSecret
as the parameter to stripe.confirmCardPayment
, then my payment completes successfully, but the Elements form does not reset which indicates to me that there is a problem with this implementation.
Is setClientSecret not updating state on clientSecret?
Here's my Netlify function:
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
exports.handler = async (event) => {
const paymentObject = JSON.parse(event.body);
const amount = paymentObject.amount;
const donorName = paymentObject.metadata.donorName;
const address = paymentObject.metadata.address;
const city = paymentObject.metadata.city;
const state = paymentObject.metadata.state;
const zip = paymentObject.metadata.zip;
const email = paymentObject.metadata.email
try {
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency: 'usd',
description: 'USS Idaho Donation',
receipt_email: email,
metadata: {
donorName,
address,
city,
state,
zip,
email,
},
});
return {
statusCode: 200,
body: JSON.stringify({
paymentIntent,
clientSecret: paymentIntent.client_secret
})
};
} catch (error) {
console.log({ error });
return {
statusCode: 400,
body: JSON.stringify({ error }),
};
}
};
handleSubmit function:
const handleSubmit = async ev => {
ev.preventDefault();
setProcessing(true);
window
.fetch("/.netlify/functions/charge", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
amount,
receipt_email: email,
metadata: {
donorName: donorName,
address: address,
city: city,
state: state,
zip: zip,
email: email
}
}
)
})
.then(res => {
return res.json();
})
.then(data => {
setClientSecret(data.clientSecret)
console.log(`clientSecret: ${clientSecret}`)
const payload = stripe.confirmCardPayment(data.clientSecret, {
payment_method: {
card: elements.getElement(CardElement)
}
});
if (payload.error) {
setError(`Payment failed: ${payload.error.message}`);
setProcessing(false);
} else {
setError(null);
setProcessing(false);
setSucceeded(true);
reset();
}
})
};
The stripe.confirmCardPayment
function returns a Promise, so you need wait for that Promise to resolve before updating your component state:
In your code, this would look something like:
const handleSubmit = async ev => {
ev.preventDefault();
setProcessing(true);
window
.fetch("/.netlify/functions/charge", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
amount,
receipt_email: email,
metadata: {
donorName: donorName,
address: address,
city: city,
state: state,
zip: zip,
email: email
}
}
)
})
.then(res => {
return res.json();
})
.then(data => {
setClientSecret(data.clientSecret);
return stripe.confirmCardPayment(data.clientSecret, {
payment_method: {
card: elements.getElement(CardElement)
}
});
})
.then((paymentResult) => {
if (paymentResult.error) {
setError(`Payment failed: ${payload.error.message}`);
setProcessing(false);
} else {
if (paymentResult.paymentIntent.status === 'succeeded') {
setError(null);
setProcessing(false);
setSucceeded(true);
reset();
}
}
});
};