I get this error when trying to checkout with stripe:
Uncaught (in promise) IntegrationError: Invalid value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id} secret ${secret}. You specified: .....
I use stripe on my website and I've implemented it with firebase functions. When I run my website and firebase functions locally I do not get this error, but when I have it on my firebase hosting it does not work and I get that error. Locally I would run these commands: npm start
to start the website and then I cd inside the functions folder and then run npm run serve
. How can I fix this? Here is the index.js file that is being run with firebase functions: index.js
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const stripe = require('stripe')(secret_key)
const app = express();
app.use(cors({
origin: true
}));
app.use(express.json());
app.post('/payments/create', async (req, res) => {
try {
const { amount, shipping } = req.body;
const paymentIntent = await stripe.paymentIntents.create({
shipping,
amount,
currency: 'eur'
});
res
.status(200)
.send(paymentIntent.client_secret);
}catch(err) {
res
.status(500)
.json({
statusCode: 500,
message: err.message
});
}
})
app.get('*', (req, res) => {
res
.status(404)
.send('404, Not Found');
});
exports.api = functions.https.onRequest(app);
Here's the package.json
{
"name": "evelinas-art-store",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.11.2",
"@stripe/react-stripe-js": "^1.1.2",
"@stripe/stripe-js": "^1.11.0",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.6.0",
"axios": "^0.21.1",
"ckeditor4-react": "^1.3.0",
"firebase": "^8.2.1",
"moment": "^2.29.1",
"node-sass": "^4.14.1",
"react": "^17.0.1",
"react-country-region-selector": "^3.0.1",
"react-dom": "^17.0.1",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"redux": "^4.0.5",
"redux-logger": "^3.0.6",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"web-vitals": "^0.2.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
File that has stripe payment
import React, { useState, useEffect } from 'react';
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
import FormInput from './../forms/FormInput';
import Button from './../forms/Button';
import { CountryDropdown } from 'react-country-region-selector';
import { apiInstance } from './../../Utils';
import { selectCartTotal, selectCartItemsCount, selectCartItems } from './../../redux/Cart/cart.selectors';
import { saveOrderHistory } from './../../redux/Orders/orders.actions';
import { createStructuredSelector } from 'reselect';
import { useSelector, useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import './styles.scss';
const initialAddressState = {
line1: '',
line2: '',
city: '',
state: '',
postal_code: '',
country: '',
};
const mapState = createStructuredSelector({
total: selectCartTotal,
itemCount: selectCartItemsCount,
cartItems: selectCartItems,
});
const PaymentDetails = () => {
const stripe = useStripe();
const elements = useElements();
const history = useHistory();
const { total, itemCount, cartItems } = useSelector(mapState);
const dispatch = useDispatch();
const [billingAddress, setBillingAddress] = useState({ ...initialAddressState });
const [shippingAddress, setShippingAddress] = useState({ ...initialAddressState });
const [recipientName, setRecipientName] = useState('');
const [nameOnCard, setNameOnCard] = useState('');
useEffect(() => {
if (itemCount < 1) {
history.push('/dashboard');
}
}, [itemCount]);
const handleShipping = evt => {
const { name, value } = evt.target;
setShippingAddress({
...shippingAddress,
[name]: value
});
};
const handleBilling = evt => {
const { name, value } = evt.target;
setBillingAddress({
...billingAddress,
[name]: value
});
}
const handleFormSubmit = async evt => {
evt.preventDefault();
const cardElement = elements.getElement('card');
if (
!shippingAddress.line1 || !shippingAddress.city ||
!shippingAddress.state || !shippingAddress.postal_code ||
!shippingAddress.country || !billingAddress.line1 ||
!billingAddress.city || !billingAddress.state ||
!billingAddress.postal_code || !billingAddress.country ||
!recipientName || !nameOnCard
) {
return;
}
apiInstance.post('/payments/create', {
amount: total * 100,
shipping: {
name: recipientName,
address: {
...shippingAddress
}
}
}).then(({ data: clientSecret }) => {
stripe.createPaymentMethod({
type: 'card',
card: cardElement,
billing_details: {
name: nameOnCard,
address: {
...billingAddress
}
}
}).then(({ paymentMethod }) => {
stripe.confirmCardPayment(clientSecret, {
payment_method: paymentMethod.id
})
.then(({ paymentIntent }) => {
const configOrder = {
orderTotal: total,
orderItems: cartItems.map(item => {
const { documentID, productThumbnail, productName,
productPrice, quantity } = item;
return {
documentID,
productThumbnail,
productName,
productPrice,
quantity
};
})
}
dispatch(
saveOrderHistory(configOrder)
);
});
})
});
};
const configCardElement = {
iconStyle: 'solid',
style: {
base: {
fontSize: '16px'
}
},
hidePostalCode: true
};
return (
<div className="paymentDetails">
<form onSubmit={handleFormSubmit}>
<div className="group">
<h2>
Shipping Address
</h2>
<FormInput
required
placeholder="Recipient Name"
name="recipientName"
handleChange={evt => setRecipientName(evt.target.value)}
value={recipientName}
type="text"
/>
<FormInput
required
placeholder="Line 1"
name="line1"
handleChange={evt => handleShipping(evt)}
value={shippingAddress.line1}
type="text"
/>
<FormInput
placeholder="Line 2"
name="line2"
handleChange={evt => handleShipping(evt)}
value={shippingAddress.line2}
type="text"
/>
<FormInput
required
placeholder="City"
name="city"
handleChange={evt => handleShipping(evt)}
value={shippingAddress.city}
type="text"
/>
<FormInput
required
placeholder="State"
name="state"
handleChange={evt => handleShipping(evt)}
value={shippingAddress.state}
type="text"
/>
<FormInput
required
placeholder="Postal Code"
name="postal_code"
handleChange={evt => handleShipping(evt)}
value={shippingAddress.postal_code}
type="text"
/>
<div className="formRow checkoutInput">
<CountryDropdown
required
onChange={val => handleShipping({
target: {
name: 'country',
value: val
}
})}
value={shippingAddress.country}
valueType="short"
/>
</div>
</div>
<div className="group">
<h2>
Billing Address
</h2>
<FormInput
required
placeholder="Name on Card"
name="nameOnCard"
handleChange={evt => setNameOnCard(evt.target.value)}
value={nameOnCard}
type="text"
/>
<FormInput
required
placeholder="Line 1"
name="line1"
handleChange={evt => handleBilling(evt)}
value={billingAddress.line1}
type="text"
/>
<FormInput
placeholder="Line 2"
name="line2"
handleChange={evt => handleBilling(evt)}
value={billingAddress.line2}
type="text"
/>
<FormInput
required
placeholder="City"
name="city"
handleChange={evt => handleBilling(evt)}
value={billingAddress.city}
type="text"
/>
<FormInput
required
placeholder="State"
name="state"
handleChange={evt => handleBilling(evt)}
value={billingAddress.state}
type="text"
/>
<FormInput
required
placeholder="Postal Code"
name="postal_code"
handleChange={evt => handleBilling(evt)}
value={billingAddress.postal_code}
type="text"
/>
<div className="formRow checkoutInput">
<CountryDropdown
required
onChange={val => handleBilling({
target: {
name: 'country',
value: val
}
})}
value={billingAddress.country}
valueType="short"
/>
</div>
</div>
<div className="group">
<h2>
Card Details
</h2>
<CardElement
options={configCardElement}
/>
</div>
<Button
type="submit"
>
Pay Now
</Button>
</form>
</div>
);
}
export default PaymentDetails;```
According to the documentation https://stripe.com/docs/api/payment_intents/create for stripe.PaymentIntent.create()
You need to pass this:
import stripe
stripe.api_key = "sk_test_51I5EU6DbwDQYqmKoHRVYU2jw4jtzB8aQa6byuVIMyfDvYl3lxHOzmIRUZ6SabMmk1TV0jNu4w9akIgPY4E3krUbj00ewcroCvC"
const PaymentIntentVar = stripe.PaymentIntent.create(
amount=2000,
currency="usd",
payment_method_types=["card"],
)
You have a typo, I guess? PaymentIntents? After please try to:
console.log(PaymentIntentVar)
in index.js To see if you are getting the correct response? Would you please share that !
Also in " File that has stripe payment":
instead of:
const cardElement = elements.getElement('card');
and this:
stripe.createPaymentMethod({
type: 'card',
card: cardElement,
billing_details: {
name: nameOnCard,
address: {
...billingAddress
}
}
})
do this:
stripe.createPaymentMethod({
type: 'card',
card: elements.getElement(CardElement),
billing_details: {
name: nameOnCard,
address: {
...billingAddress
}
}
})
also check you are passing the correct public and secret keys on your front and backend by console.log() on front and backend
Also instead useStripe, try this
import {loadStripe} from '@stripe/stripe-js';
const stripe = loadStripe('secret_key');
Here are the possible scenarios and how I understand the configuration.
So first you attempt a payment from the front end
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: 'card',
card: cardElement,
billing_details: {
name: `${stateProps.userProfile.firstName} ${stateProps.userProfile.lastName}`,
email: stateProps.user.email
}
});
from that paymentMethod, you retrieve paymentMethod.Id and send that to your server to make a payment - at this point we are assuming the payment will work and not require 3d secure.
In my case, I need is that paymentMethod.Id because I have put all the billing details in a different object.
In the backend, here is what I have using C#
try
{
if (request.PaymentMethodId != null)
{
var createOptions = new PaymentIntentCreateOptions
{
PaymentMethod = request.PaymentMethodId,
Amount = request.total,
Currency = "gbp",
ConfirmationMethod = "manual",
Confirm = true,
//ReturnUrl = "",
Metadata = new Dictionary<string, string>
{
{ "OrderId", request.orderId.ToString() },
}
};
paymentIntent = paymentIntentService.Create(createOptions);
}
if (request.PaymentIntentId != null)
{
var confirmOptions = new PaymentIntentConfirmOptions { };
paymentIntent = paymentIntentService.Confirm(
request.PaymentIntentId,
confirmOptions
);
}
}
catch (StripeException e)
{
return new ViewModels.ResponseObject
{
Success = false,
Title = "An Error Occcured.",
Message = e.Message,
Data = e.Message
};
}
if (paymentIntent.Status == "requires_action" && paymentIntent.NextAction.Type == "use_stripe_sdk")
{
return new ViewModels.ResponseObject
{
Success = false,
Data = new
{
requires_action = true,
payment_intent_client_secret = paymentIntent.ClientSecret
},
Title = "Requires Action",
Message = "Requires Action"
};
}
else if (paymentIntent.Status == "succeeded")
{
return new ViewModels.ResponseObject
{
Success = true,
Title = "Payment Successful.",
Message = "Thank you. Your payment has been successful. A confirmation message has been sent to you by email.",
Data = null
};
}
else
{
return new ViewModels.ResponseObject
{
Success = false,
Title = "An Error Occcured.",
Message = "An error has occured with your payment. A representative from our store has been notified and will be in touch with you to resolve the issue.",
Data = null
};
}
So if you read the code, you will see that it tries to make a payment and if that fails, THEN it gives you the clientSecret and you can return that to the client.
This is what you can now use on the front end to construct a payment intent (which we already know will fail due to 3d secure) that will automatically pull up the 3d secure authentication window.
In my opinion it seems a waste to do the roundtrip twice but that's how I managed to beat it into submission.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.