I'm following the docs here (Set up future payments - Custom payment flow):
https://stripe.com/docs/payments/save-and-reuse?platform=web
But the user payment method doesn't get stored.
On the backend the card payment method returns empty array.
On the client side:
"StripeElements Component"
import { useStripe } from '@stripe/react-stripe-js';
import { stripePaymentStart, stripePaymentSuccess } from 'redux/features/adCreation.slice';
import PaymentForm from './PaymentForm';
const StripeElements = () => {
useEffect(() => {
dispatch(stripePaymentStart());
}, [dispatch]);
useEffect(() => {
if (!stripe) return;
stripe.retrieveSetupIntent(stripeClientSecret).then(({ setupIntent }) => {
switch (setupIntent.status) {
case 'succeeded':
setMessage('succeeded');
dispatch(stripePaymentSuccess());
break;
case 'processing':
setMessage('processing');
break;
case 'requires_payment_method':
// Redirect your user back to your payment page to attempt collecting
// payment again
setMessage('requires_payment_method');
// setMessage('Failed to process payment details. Please try another payment method.');
break;
default:
console.log(setupIntent.status);
}
});
}, [stripe, stripeClientSecret, stripeConfirm, dispatch]);
if (message === 'succeeded' && stripeConfirm) {
return (
<Container>
<Box className={classes.box}>
<CheckCircle className={classes.icon} />
<Typography align="center">Success! Your payment method has been saved.</Typography>
</Box>
</Container>
);
}
if (message === 'succeeded') {
return (
<Container>
<Box className={classes.box}>
<Typography align="center">Choose saved credit card / debit card.</Typography>
</Box>
</Container>
);
}
if (message === 'processing') {
return (
<Container>
<Box className={classes.box}>
<LoadingSpinner
in={true}
mountOnEnter
unmountOnExit
timeout={400}
size={32}
color="#663399"
/>
<Typography align="center">
Processing payment details. We'll update you when processing is complete.
</Typography>
</Box>
</Container>
);
}
return <PaymentForm onStripeConfirm={() => setStripeConfirm(true)} />;
};
export default StripeElements;
"Payment Form Component"
import { PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js';
const PaymentForm = ({ onStripeConfirm }) => {
const handleSubmit = async event => {
event.preventDefault();
if (!stripe || !elements) return;
setIsLoading(true);
const { error } = await stripe.confirmSetup({
elements,
redirect: 'if_required',
});
if (error) {
setErrorMessage(error.message);
dispatch(stripePaymentFailure(error.message));
} else {
onStripeConfirm();
}
};
return (
<form className={classes.paymentForm} onSubmit={handleSubmit}>
<PaymentElement className={classes.paymentElement} />
<Button
type="submit"
variant="contained"
color="primary"
disabled={isLoading || !stripe || !elements}
className={classes.submitButton}
>
{isLoading ? (
<LoadingSpinner in={isLoading} mountOnEnter unmountOnExit timeout={400} size={30} />
) : (
'Submit'
)}
</Button>
{/* Show error message to your customers */}
{errorMessage && <Typography align="center">{errorMessage}</Typography>}
</form>
);
};
export default PaymentForm;
Response of retrieveSetupIntent (setupIntent):
{
"id": "",
"object": "setup_intent",
"cancellation_reason": null,
"client_secret": "",
"created": 1660222675,
"description": null,
"last_setup_error": null,
"livemode": false,
"next_action": null,
"payment_method": "*********************************",
"payment_method_types": [
"card"
],
"status": "succeeded",
"usage": "off_session"
}
P.S I deleted most of the imports, and hooks usage, So I could post the reset of the codes.
Thanks