How to create payment indent for payment card with 3D Secure with stripe hosted invoice page - 3d-secure

No problems when I create an on-session payment, can't make a second charge when authentication required, don't understand an approach when Stripe must send a letter to the customer with a confirm link, the link lead to the Stripe hosted page like in Docs
The request with SCA required card I got a card error (authorization_required).
$intent = PaymentIntent::create([
'amount' => 1100,
'currency' => 'usd',
'payment_method_types' => ['card'],
'customer' => $customerId,
'payment_method' => $paymentMethodId,
'off_session' => true,
'confirm' => true,
]);
I found this approach here. Set settings in the Stripe dashboard for email.
Maybe must be a relation with invoice API, but I don't see a flow in docs.
Expected a success paymentIndent creation with requires_confirmation status. Email sent to the customer with a confirmation button.

As per new regulations of 3D secure, Need additional payment confirmation. You can achieve that using following code.
Pass intent to this Function (Server code)
const generate_payment_response = (intent) => {
if (
intent.status === 'requires_action' &&
intent.next_action.type === 'use_stripe_sdk'
) {
// Tell the client to handle the action
return {
requires_action: true,
payment_intent_client_secret: intent.client_secret
};
} else if (intent.status === 'succeeded') {
// The payment didn’t need any additional actions and completed!
// Handle post-payment fulfillment
return {
success: true
};
} else {
// Invalid status
return {
error: 'Invalid PaymentIntent status'
}
}
};
Prompt aditional 3D secure popup (Front-End Code)
function handleServerResponse(response) {
console.log(response, "handling response");
if (response.data.success) {
// Show error from server on payment form
alert("Paymemt successful");
} else if (response.data.requires_action) {
alert("require additional action");
// Use Stripe.js to handle required card action
stripe.handleCardAction(
response.data.payment_intent_client_secret
).then(function(result) {
if (result.error) {
// Show error in payment form
} else {
// The card action has been handled
// The PaymentIntent can be confirmed again on the server
let data = {
payment_intent_id: result.paymentIntent.id
}
axios.post(`${baseUrl}/confirmPayment`, data).then(response => {
handleServerResponse(response);
});
}
}).catch(error => {
console.log(error, "error");
alert("rejected payment");
})
} else {
// Show failed
alert("Failed transaction");
alert(response.data.message);
console.log(response.data, "error during payment confirmation");
}
}

Related

Missing value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}

I'm working on an e-commerce web app using Laravel and Vuejs. I chose Stripe's API to accept and manage payments.
In my Vue component, which contains the payment form, and before making it visible(I'm using a multi-step form), I send a post request to my payments store controller function to 'initialize' Stripe and get the clientSecret variable. As follows:
axios
.post('http://127.0.0.1:8000/payments', {
headers: {
'content-type': 'multipart/form-data'
},
'total': this.total,
'mode_payment': this.mode_payment
})
This is how the store function looks like in my PaymentController:
public function store(Request $request)
{
$payment = new Payment;
$payment->montant_payment = $request->total;
$payment->mode = $request->mode_payment;
$payment->date_payment = Carbon::now();
$payment->statut_payment = false;
$payment->save();
Stripe::setApiKey(env('STRIPE_SECRET'));
header('Content-Type: application/json');
$intent = PaymentIntent::create([
'amount' => $payment->montant_payment,
'currency' => 'usd',
]);
$clientSecret = Arr::get($intent, 'client_secret');
$amount = $payment->montant_payment;
return response()->json($clientSecret);
}
As you can see, the store function sends back a JSON response containing the clientSecret variable. This response is then captured by the same Vue component discussed above.
This is how the Vue component captures the response:
.then((response) => {
this.clientSecret = response.data;
var stripe =
Stripe('pk_test_51JL3DSLJetNHxQ3207t13nuwhCB1KPvUJJshapsOQATnZn79vA4wK3p9Hf2yi2uUXfXXWdAtLZGRepfJGvRnd7oI006Kw6rFTC');
document.querySelector("button").disabled = true;
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
}
};
this.card = elements.create("card", { style: style });
this.card.mount("#card-element");
this.card.on('change', ({error}) => {
let displayError = document.getElementById('card-error');
if (error) {
displayError.classList.add('alert', 'alert-warning');
displayError.textContent = error.message;
} else {
displayError.classList.remove('alert', 'alert-warning');
displayError.textContent = '';
}
});
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(ev) {
ev.preventDefault();
// If the client secret was rendered server-side as a data-secret attribute
// on the <form> element, you can retrieve it here by calling `form.dataset.secret`
stripe.confirmCardPayment(String(this.clientSecret), {
payment_method: {
card: this.card,
billing_details: {
name: "testan testo"
}
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer (e.g., insufficient funds)
console.log(result.error.message);
}
else {
// The payment has been processed!
if (result.paymentIntent.status === 'succeeded') {
console.log(result.paymentIntent);
window.location.href = 'http://127.0.0.1:8000/payment-success/';
}
}
});
});
})
Using Vue web dev tools I checked that the clientSecret variable has been successfully passed from the laravel store controller function to the payment Vue component, thanks to the execution of the commmand : this.clientSecret = response.data;.
However, when clicking the pay button, I get the following error in my console:
Uncaught IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}.
at X ((index):1)
at Q ((index):1)
at lo ((index):1)
at (index):1
at (index):1
at e.<anonymous> ((index):1)
at e.confirmCardPayment ((index):1)
at HTMLFormElement.eval (ComLivPay.vue?5598:339)
I guess the problem then is in the next portion of code:
stripe.confirmCardPayment(String(this.clientSecret), {
payment_method: {
card: this.card,
billing_details: {
name: "testan testo"
}
}
})
EDIT:
Since the problem seems to arise from the string form of the this.clientSecret variable. Here's how it looks like in Vue dev tools:

Cancel previous call made using fetch API in Vue JS

I am working in laravel + VueJS application.In that there is a search functionality through which user can search any text then API will called to get data based on search text.So to call API in laravel "Fetch API" in VueJS is used.So now when any user press any keyword then everytime the method will be called to get the result.So due to that multiple request has been send.I just like to do that when any user type any text then everytime previous request must be cancelled.The code i have attached below which is used to call the API and fetch the data.
searchProperties(search) {
if (search != null && search != '') {
this.review = false;
this.clearSes = false;
fetch('/search/' + search)
.then(res => res.json())
.then(res => {
if (res.status === 200) {
this.properties = res.data.properties;
}
});
} else {
this.clearSuggestions();
}
}
Thanks in advance!
You could you an abortable fetch like this
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => controller.abort(), 5000);
fetch(url, { signal }).then(response => {
return response.text();
}).then(text => {
console.log(text);
});
See docs for more details.

Cashier throws error "A parameter provided in the URL (payment_method) was repeated as a GET or POST parameter." while adding payment method

I am using cashier for stripe in laravel 7, getting this error while adding payment method.
A parameter provided in the URL (payment_method) was repeated as a GET or POST parameter. You can only provide this information as a portion of the URL.
I am passing payment intent to the blade like this,
'intent' => $user->createSetupIntent()
In js,
cardButton.addEventListener('click', async (e) => {
e.preventDefault();
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Display "error.message" to the user...
} else {
send('save', false, {
data: {
payment_method: setupIntent
}
});
// The card has been verified successfully...
}
});
then adding
$paymentMethod=$request['payment_method'];
$user->updateDefaultPaymentMethod($paymentMethod);
Can anyone help me find out what is the issue.
It was a fault from my side,I passed complete setup intent instead of payment method in updateDefaultPaymentMethod.
I need to set data as payment_method: setupIntent.payment_method

Loader icon in Bot Framework Webchat

I am using Bot Framework Webchat. There are few user related data which I am posting using back channel post activity through the store option to greet the user.
<ReactWebChat
activityMiddleware={ activityMiddleware }
directLine={ window.WebChat.createDirectLine( this.state.token ) }
store = {this.handleGetStore()}
styleOptions={styleOptions}
/>
handleGetStore returns the store data:
handleGetStore(){
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'userDetail',
value: this.state.userDetail
}
});
}
return next(action);
});
return store;
}
When the connection initiates the loader appears.
After that there is delay of about 3-5 seconds before the welcome message appears and in the meantime the Webchat seems ready for the user.
A slight delay of 3 seconds is acceptable but quite often the delay is upto 10 seconds or more. I understand that this can be slightly improved by using the Always On feature of the App Service and scaling up the plan. Is there a way I can wait for the back channel welcome message to appear and show the loader until then?
Reference: https://github.com/microsoft/BotFramework-WebChat/pull/1866
Unfortunately, the connection status display relies on events received from DirectLineJs and Web Chat does not support customizing its behavior at the moment. That being said, there is a hacky way to accomplish what you're trying to do by dispatching pseudo DirectLine events.
Here are the steps below:
Create a flag that will indicate whether or not the bot has sent a welcome message - received_welcome_message.
When Web Chat dispatches a connection fulfilled event, check the flag
to ensure a welcome message has been received. If the bot has not
sent a welcome message, dispatch the welcome event to the bot and reset the
connection status to fulfilling.
When Web Chat receives an activity
from the bot, check if it is a welcome message. I would recommend
adding a name attribute to message on the bot side to check - await
context.sendActivity({ text: 'Welcome', name: 'welcome'}). If the
activity is a welcome message, dispatch a connection fulfilled event and set the flag to true.
For more details take a look at the code snippets below.
let received_welcome_message = false;
const store = createStore(
{},
({ dispatch}) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
if (!received_welcome_message) {
dispatch({
type: 'DIRECT_LINE/CONNECT_FULFILLING'
});
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: { name: 'webchat/join' }
});
return
}
} else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' && action.payload.activity.name === 'welcome') {
received_welcome_message = true;
dispatch({
type: 'DIRECT_LINE/CONNECT_FULFILLED',
});
}
return next(action);
}
);
Edit
A less hacky approach is to dispatch a post activity pending event when the connection to the bot is fulfilled to mimic the bot sending a welcome message. Note, that the bot is unaware of the mimicked activity. See the code snippet below.
const store = createStore(
{},
({ dispatch}) => next => action => {
console.log(action)
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'DIRECT_LINE/POST_ACTIVITY_PENDING',
meta: { method: 'keyboard' },
payload: {
activity: {
from: { role: "bot" },
text: "Welcome Message",
textFormat: "plain",
timestamp: new Date().toString(),
type: "message"
}
}
})
}
return next(action);
}
Hope this helps!

VueJS: Is there an easy way to validate email and password on client side based on the server side's validator?

I'm new to VueJS. I'm creating signup and login page and users are supposed to send the email and password to the back-end (I'm using Django) to check if the data is valid. I'd like to show error messages on form if one of them are not valid.
I saw some documentation about validation and seems like I have to write a bunch of validation code. Now I'm wondering if there's an easy way to do it.
I'd like to validate them based on the server side's validators.
Login.vue
export default {
data() {
return {
form: {
email: '',
password: '',
}
}
},
methods: {
onSubmit(event) {
event.preventDefault()
// validate the inputs here and shows error messages if they are not valid
const path = `http://127.0.0.1:8000/users/login/`
axios.post(path, this.form).then((resp) => {
location.href = '/'
})
.catch((err) => {
console.log(err)
})
}
}
}
Can anyone give me tips?
Yes, Here is the code you can follow.
In data make a reg object like this.
data(){
return{
email:null,
reg: /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,24}))$/
}
},
add then in your submit method
if(this.email == null || this.email == '')
{
this.errorEmail = "Please Enter Email";
}
else if(!this.reg.test(this.email))
{
this.errorEmail = "Please Enter Correct Email";
}

Resources