#jeanbaptisten
1 messages · Page 1 of 1 (latest)
Thank you 🙂
I'm not sure of what is stripe connect, all I can say is that I'm using Stripe API
Have you followed any guides when integrating Stripe?
Not so much to be honest, we were 3 on this task and we mainly read the stripe docs
What Stripe docs exactly?
This:
https://stripe.com/docs/development/get-started
We read the whole docs before implementing it
Your diagram looks like a standard Stripe integration, but it's not enough for me to identify the problem. Does this happen all the time?
No, and that's why it's weird to me...
Everything works fine in a dev environnement using test cards, and it works for me and other members of our team when paying in the prod environnement
But for some reasons I can't identify, some users have the error you can see on the video
I can't even know if this error come from a call to Stripe API in our backend or frontend application
Could you please share a part of the code that does this?
Also, could you please share the PaymentIntent ID pi_xxx?
I can't do it right now sorry, I'll send you what I can as soon as possible
Thank you so much for your time thought 🙏
Happy to help.
Thank you ! I'm gonna send what I can
Well, first I'd like to send you two screenshots that I will use to explain the beginning of the user journey (not sure about the word "journey")
Here is what I can tell:
-
In the screenshots I've sent, you can see the beginning of the user process. The user give us informations about his booking then click on the button "Réservez" (meaning "booking" in french).
Then, a modal appears with the usual stripe payment interface.
When everything is good, the user can click on the blue button to book. -
This button will send a request to our api to create a booking :
// >> FRONT APP <<
const response = await useAPI().bookings.createNewBooking(
{
boxCategory: selectedBox.value,
centerId: centerId.value,
startingDay: selectedDayStr.value,
beginHour: form.selectedTime || '',
formula: selectedFormula.value,
creditCardId: selectedCreditCard.value?.id || '',
promoCodeLabel: promoData.value?.label
},
cardSubmitFunction
)
The cardSubmitFunction is a function returned by the card payment interface.
Here is the card payment interface component: (without the style, it's not necessary)
<template>
<section class="payment-container">
<div ref="paymentElementDOM" class="payment-element"></div>
</section>
</template>
<script lang="ts" setup>
const emit = defineEmits(['submitFunctionLoaded'])
const props = defineProps({
amount: {
type: Number,
default: 0
}
})
const paymentElementDOM = ref<HTMLElement>()
onMounted(async () => {
mountCardElem()
})
onBeforeUnmount(() => {
paymentElementDOM.value?.remove()
})
async function mountCardElem() {
if (!paymentElementDOM.value) return
emit('submitFunctionLoaded', await usePayment().mountPaymentInput(paymentElementDOM.value, props.amount))
}
</script>
The function given by the mountPaymentInput method is :
// >> FRONT APP <<
mountPaymentInput: async (elementWrapper: HTMLElement, amount: number): Promise<TPaymentSubmitFunction> => {
const stripeJs = await StripeHelper.getStripeJs()
const elements: StripeElements = stripeJs.elements({
mode: 'payment',
currency: 'eur',
payment_method_types: ['card'],
amount: amount * 100,
setup_future_usage: 'on_session'
})
const paymentElement: StripePaymentElement = elements.create('payment')
paymentElement.mount(elementWrapper)
//This function need to be called to validate and submit card informations
return StripeHelper.getPaymentSubmitFunction(elements, stripeJs)
},
We can get back to the payAndConfirmBooking function called by the click of the stripe payment interface.
The createNewbooking method is this one:
// >> FRONT APP <<
createNewBooking: async (
payload: IBookBoxPayload,
cardSubmitFunction: TPaymentSubmitFunction,
alert: IAlertControl = { mode: AlertModes.ON_ERROR }
): Promise<IRequestResult<IBookingResponse>> => {
if (!_session.isLoggedIn) {
useAlertStore().handleRequestResult(alert, NOT_LOGGED_IN_REQUEST_ERROR)
return { data: null, error: NOT_LOGGED_IN_REQUEST_ERROR }
}
const response = await _request.post<IBookingResponse>(_path, {
body: payload,
headers: { Authorization: `Bearer ${_session.accessToken}` },
alert
})
if (!response.data) return response
if (response.data.paymentStatus === PaymentProviderStatuses.REQUIRE_AUTHENTICATION && response.data.clientSecret) {
let error: IRequestError | null
if (payload.creditCardId) error = (await usePayment().confirmPayment(response.data.clientSecret, payload.creditCardId)).error
else error = (await cardSubmitFunction(response.data.clientSecret, { mode: 'all' })).error
if (error) return { data: null, error: { status: 400, message: error.message } }
}
return response
}
The first API call will create the booking, then store the payment response in the booking like this:
// >> BACK APP <<
// try to debit the card, else create a payment intent
if (creditCardId)
paymentResponse = await this.paymentsService.debitCreditCard(user, VmcCompanies.CENTER, creditCardId, booking.quote.totalTTC, documentData)
else {
paymentResponse = await this.paymentsService.createPaymentIntent(user, VmcCompanies.CENTER, booking.quote.totalTTC, documentData)
}
// set booking's payment information accordingly to the result
savedBooking.booker.setPaymentId(paymentResponse.paymentId)
savedBooking.setPaymentStatus(paymentResponse.status === PaymentProviderStatuses.SUCCESS ? PaymentStatuses.PAID : PaymentStatuses.NOT_PAID)
These informations will be sent to the front app and will be handled in the rest of the createNewBooking.
I hope all my explainations are good, I didn't implement the whole solution tbh, we were 3 on this task.
Feel free if you need anything else 🙂
Hey @gentle ermine, thanks for a detailed explanation, but unfortunately I won't have time to dive deep into such a question. Do you mind reaching out to Stripe Support and saying you talked to us on Discord, and my engineering team will take over and take the time to look into your issue carefully.
https://support.stripe.com/?contact=true
Thank you 🙏
Happy to help.