#cristoffer_unexpected
1 messages ¡ Page 1 of 1 (latest)
đ Welcome to your new thread!
â˛ď¸ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
âąď¸ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
đ This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1243559195714781195
đ Have more to share? Add more details, code, screenshots, videos, etc. below.
My component looks like this
My Payment components (in docs App)
const Payment = ({ orderDetails, products }: PaymentProps) => {
const [loading, setLoading] = useState(true);
const [stripePromise, setStripePromise] =
useState<Promise<Stripe | null> | null>(null);
const [clientSecret, setClientSecret] = useState("");
// Effect to fetch publishable stripe key
useEffect(() => {
axios.get("/api/payment/config").then((res) => {
if (res.status === 200) {
setStripePromise(loadStripe(res.data.publishableKey));
}
});
}, []);
// Effect to check if order data collected from user is correct. Server side do the verifications.
useEffect(() => {
axios
.post("/api/payment/create-payment-intent", { ...orderDetails, products })
.then((res) => {
if (res.status === 200) {
const clientSecret = res.data.clientSecret;
setClientSecret(clientSecret);
}
})
.catch((err) => {
console.log(err);
})
.finally(() => setLoading(false));
}, []);
const appearance: Appearance = {
theme: "stripe",
};
const options: StripeElementsOptions = {
clientSecret,
appearance,
};
return (
<div className="payment">
{loading || !clientSecret || !stripePromise ? (
<Spinner />
) : (
<Elements options={options} stripe={stripePromise}>
<CheckoutForm />
</Elements>
)}
</div>
);
};
My CheckoutForm component
const CheckoutForm = () => {
const stripe = useStripe();
const elements = useElements();
const [message, setMessage] = useState<string | undefined>();
const [isProcessing, setIsProcessing] = useState(false);
const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
setIsProcessing(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `${window.location.origin}/completion`,
},
});
if (error.type === "card_error" || error.type === "validation_error") {
setMessage(error.message);
} else {
setMessage("unexpected-error");
}
setIsProcessing(false);
};
const paymentElementOptions: StripePaymentElementOptions = {
layout: "tabs",
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<PaymentElement id="payment-element" options={paymentElementOptions} />
<button disabled={isProcessing || !stripe || !elements} id="submit">
<span id="button-text">
{isProcessing ? (
<div className="spinner" id="spinner"></div>
) : (
"Pay now"
)}
</span>
</button>
{message && <div id="payment-message">{message}</div>}
</form>
);
};
And on my server-side payment controller and his endpoints
const OrderService = require("../services/modelServices/OrderService");
const orderService = new OrderService();
const { Stripe } = require("stripe");
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const createPaymentIntent = async (req, res) => {
const order = req.body;
const userId = res.locals?.user?.id;
try {
const verification = await orderService.verifyClientOrder(order, userId);
if (!verification.success) {
return res.status(400).json({ success: false, message: verification.message });
}
const paymentIntent = await stripe.paymentIntents.create({
currency: "pln",
amount: order.priceForAll,
automatic_payment_methods: { enabled: true },
});
return res.status(200).json({ success: true, message: verification.message, clientSecret: paymentIntent.client_secret });
} catch (err) {
console.log(err);
return res.status(400).json({ success: false, message: "unexpected-error" });
}
};
const getConfig = async (req, res) => {
return res.status(200).json({ publishableKey: process.env.STRIPE_PUBLISHABLE_KEY });
};
module.exports = { createPaymentIntent, getConfig };
Can you share the payment intent id?
Yeah, its : pi_3PJySNRrtsKOar1b1xvPZMw9
I'm on test mode working on local server without https. I don't know if it changes something. Also it's my very first experience with Stripe so propably it's something obvious i should have configured before
In the response for the payment intent creation request here: https://dashboard.stripe.com/test/logs/req_i33EiWdrHoqBce you can see:
"card",
],```
That indicates card is the only PM available even though you enabled automatic payment method. So, you'll need to enable more payment methods if you want them to show up: https://dashboard.stripe.com/test/settings/payment_methods
Yeah but this page shows me that cards, apple pay, Link, Blik, BanContact, etc are also active
In live mode?
Nevermind i have just switched P24 and refreshed my page and it works now
Does offered payment methods depends on client localisation? For example if customer from Australia will see methods which are popular there?
Yep it depends on currency, location of the customer, and location of your account
Oke, that's what important for me cause customer sells items to different countries
Can i show somehow on this component amount that have to be paid?
No you'll need to render it yourself somewhere on the page
Okey, thank you for help :))
No problem
I have another question if you are still there. How Stripe Elements are translated, do i have to provide translations to them or they are translated based on localisation or something else? Is there some prop which can change language of text's in component?