I am using Vue component for my checkout form.
The stripe js (v3) file was included in the header section.
The form was in Component
This component has two section. One is to get payment details from the user and another is to submit card details.
<template>
<div class="payment_form">
<div id="payment_details" v-if="showPaymentDetails">
<!-- User input goes here. Like username phone email -->
</div>
<div id="stripe-form" v-if="showStripeForm">
<form action="/charge" method="post" id="payment-form" #submit.prevent="createStripeToken()">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- Used to display Element errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
</div>
</div>
</template>
<script>
import { Validator } from 'vee-validate';
export default {
data() {
return {
stripeToken: '',
showPaymentDetails: true,
showStripeForm: true,
}
},
created() {
},
methods: {
validateForm() {
self = this;
this.$validator.validateAll().then(result => {
if (result) {
// eslint-disable-next-line
alert('From Submitted!');
console.log(this.$data);
axios.post('/data',{
name:this.name,
})
.then(function (response) {
self.showStripeForm = true;
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return;
}
});
},
createStripeToken(){
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
window.stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
console.log(result.token);
}
});
});
},
initStripe(){
window.stripe = Stripe('stripe_test_key_here');
var elements = stripe.elements();
var style = {
base: {
// Add your base input styles here. For example:
fontSize: '16px',
lineHeight: '24px'
}
};
// Create an instance of the card Element
window.card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>
window.card.mount('#card-element');
}
},
mounted() {
this.initStripe();
setTimeout(function () {
this.showStripeForm = false;
},2000);
}
}
</script>
I try to load the stripe form on page load and try to disable the element via showStripeForm.
But vue unset the loaded stripe card form from the stripe server and saved the dom to its original state.
So i can't trigger the stripe form on the axios callback.
I don't want to user stripe checkout and stripe js v1(getting input on your own form is deprecated after this version).
In mounted. Change the setTimeout callback to an arrow function, otherwise, this will point to Window instead of Vue.
mounted() {
setTimeout(() => {
this.showStripeForm = false
}, 2000)
}
Also, the way you access the DOM is not so Vue-ish. You could use ref on the DOM element you want to use in your code. For example:
<form action="/charge" method="post" ref="payment-form" #submit.prevent="createStripeToken()">
Then access it from $refs like this:
var form = this.$refs['payment-form']
/*
Same result as document.getElementById('payment-form')
but without using an id attribute.
*/
Related
I am taking coding courses online, so I can build my app sometime next year...
Can you help me with this instant message code please?
a. I am supposed to display an alert message when the user is not logged in.
b. Display the usename in the header.
c. Display the username with his instant message.
Since insecure is removed, I have to use Meteor.methods and meteor.call. I cannot use Sessions. I keep getting weird errors...
Here is the javascript code I have tried based on the course module, but I get errors that don't make sense to me...
Messages = new Mongo.Collection("messages");
if (Meteor.isClient) {
// this will configure the sign up field so it
// they only need a username
Accounts.ui.config({
passwordSignupFields: 'USERNAME_ONLY',
});
Template.messageForm.events({
// this event listener is triggered when they click on
// the post! button on the message form template
'click .js-save-message': function (event) {
var messageText = $('#message-text-input').val();
// notice how tihs has changed since the lsat time
// now we read the username from the Meteor.user()
var messageNickname = "Anon";
if (Meteor.user()) {
messageNickname = Meteor.user().username;
}
var message = {
messageText: messageText,
nickname: messageNickname,
createdOn: new Date()
};
// HERE is where you come in ....
// call a meteor method
// on the server here instead of
if (Meteor.isServer) {
Meteor.methods({ // defines a method, adds extra security layer to app
insertMessage: function () {
var doc, user, euser;
doc = Message.findOne();
if (!doc) {
return;
} // no logged in user, give up
// now I have a doc and possibly a user
user = Meteor.user().profile;
eusers = insertMessage.findOne({ docid: doc._id });
if (!eusers) {
eusers = {
docid: doc._id,
users: {},
};
}
user.lastEdit = new Date();
eusers.users[this.userId] = user;
insertMessage.upsert({ _id: eusers._id }, eusers);
}
}
)
}
// comment out this code, which won't work as we removed insecure...
//Messages.insert(message); // the insecure way of doing it
// ... put code here that calls the
Meteor.call('insertMesage', message, function (err, res) {
if (!res) {
alert('You need to log in!');
}
});
Template.header.helpers({
// HERE is another one for you - can you
// complete the template helper for the 'header' template
// called 'nickname' that
// returns the nickname from the Session variable?, if they have set it
nickname: function () {
if (Meteor.user()) {
return Meteor.user().username;
}
},
});
Template.messageList.helpers({
// this helper provides the list of messages for the
// messageList template
messages: function () {
return Messages.find({}, { sort: { createdOn: -1 } })
}
});
},
});
}
Here is the html file
<body>
{{>header}}
{{>nicknameForm}}
{{>messageList}}
{{>messageForm}}
</body>
<template name="header">
<h1>Welcome to M-Instant {{nickname}}</h1>
</template>
<template name="messageList">
{{#each messages}}
{{>messageItem}}
{{/each}}
</template>
<template name="messageItem">
<h3>{{nickname}} - {{messageText}}</h3>
</template>
<template name="nicknameForm">
<div class="form-group">
<label for="nickname-input">Nickname:</label>
<input type="text" class="form-control" id="nickname-input"
placeholder="Type message here...">
<button type="submit" class="btn btn-default js-set-nickname">Set my
nickname!</button>
</div>
</template>
<template name="messageForm">
<div class="form-group">
<label for="message-text-input">Message:</label>
<input type="text" class="form-control" id="message-text-input"
placeholder="Type message here...">
<button type="submit" class="btn btn-primary js-save-message">Post!
</button>
</div>
</template>
Here is the Methods file
Meteor.methods({
'insertMessage':function(message){
console.log("If you manage to call the method, you'll see this
message in the server console");
if (!Meteor.user()){
return;
}
else {
return Messages.insert(message);
}
}
})
I am using VueJS with Laravel 6.0. What I'm trying to achieve is that to create global variables alertStatus and alertMsg, so that every time when an AJAX call is made, the global variables can be updated to display an alert message to user.
So I decided to use prototype variable for this case. The idea is that when AJAX call is success/fail in User.vue, the prototype variable should be updated, and Alerts.vue should display it accordingly.
However, it seems that the prototype variable display does not update when the data is changed in User.vue component.
I'm not sure if my methods are correct, would like to get some ideas from stackoverflow.
Thanks
main.js
Vue.prototype.$alertStatus = '';
Vue.prototype.$alertMsg = [];
Alerts.vue
<template>
<div class="alert alert-light alert-elevate" role="alert">
<div class="alert-icon">
<i class="flaticon-warning kt-font-brand"></i>
</div>
<div class="alert-text">
{{alertMsg}}
</div>
</div>
</template>
User.vue
<script>
export default {
mounted() {
var datatable = this.init();
datatable.on('kt-datatable--on-ajax-fail', function(event, data){
this.$alertStatus = data.responseJSON.status;
this.$alertMsg = data.responseJSON.msg;
});
},
}
</script>
I would consider using an event handler instead of the global prototype.
Event.js — credit to Jeffrey Way of https://laracasts.com/
class Event {
constructor() {
this.vue = new Vue();
}
fire(event, data = null) {
this.vue.$emit(event, data);
}
listen(event, callback) {
this.vue.$on(event, callback);
}
}
export default Event;
I have outlined the basic usage below, plus I added a v-if to your alert to hide it when not in use.
app.js
import Event from './Event';
window.Event = new Event;
Alerts.vue
<template>
<div v-if="show" class="alert alert-light alert-elevate" role="alert">
<div class="alert-icon"><i class="flaticon-warning kt-font-brand"></i></div>
<div class="alert-text">
{{ alert.msg }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
alert: {},
show: false,
}
},
mounted() {
// listen for a global event
Event.listen('show-alert',alert => {
this.alert = alert;
this.show = true;
});
},
}
</script>
User.vue
<script>
export default {
mounted() {
var datatable = this.init();
datatable.on('kt-datatable--on-ajax-fail', function(event, data){
// fire a global event
Event.fire('show-alert',{
status: data.responseJSON.status,
msg: data.responseJSON.msg,
});
});
},
}
</script>
I have a problem with stripe cashier; i get
This customer has no attached payment source
I don't know why, hope someone could help me... many thanks in advance.
I tried to look a lot of tutorials but i can't get any good results.
Maybe it's a problem with the token ? i tried to enter the credit card test :
4242 4242 4242 4242
I would like to use it as a subscription.
Here my view
<div class="container">
<div class="row">
<script src="https://js.stripe.com/v3/"></script>
<form action="{{route('checkout')}}" method="post" id="payment-form">
{{csrf_field()}}
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
</div>
</div>
</div>
<script>
// Create a Stripe client
var stripe = Stripe('.....');
// Create an instance of Elements
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element
var card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
stripeTokenHandler(result.token);
}
});
});
</script>
Here my checkout controller :
public function checkout(Request $request)
{
try {
Stripe::setApiKey('.....');
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($request->stripeToken);
return 'Subscription successful, you get the course!';
} catch (\Exception $ex) {
return $ex->getMessage();
}
}
I'm trying to implement modal windows with Vuejs.
The code below shows that after the user uploads the favorite photo,
then modal window appears, and photos which were uploaded so far and the newly registered photos are displayed
it will confirm when the user press the "confirm" button.
However, at present, data is not set in the modal window after fetching data with ajax after uploading.
How do I set the data in the modal window part?
<template>
<div>
<!-- upload -->
<div class="button__action">
<button type="button" #click="uploadData(originalData.image)">upload</button>
</div>
<!-- Modal window -->
<modal name="modal-view">
<div>
<div class="modal__box" v-if="modalList.list">
<img :src="modalList.list.url">
<p class="image__name">{{modalList.list.name}}</p>
</div>
<button type="button" #click="submit">Confirm</button>
</div>
</modal>
</div>
</template>
<script>
import { post } from './handler/api'
import { toFormat } from './handler/form'
export default {
props: {
originalData: {
type: Object,
required: true,
}
},
data: function(){
return {
modalList : {
list : [],
},
}
},
methods: {
showModal () {
this.$modal.show('modal-view');
},
uploadData() {
const form = toFormat({image: this.originalData.image})
post(`/api/upload/`, form)
.then((res) => {
if(res.data) {
Vue.set(this.$data, 'modalList', res.data.list);
this.$modal.show('modal-view');
}
})
.catch((err) => {
//error
})
},
submit() {
}
}
}
</script>
Try this:
uploadData() {
var vm = this;
post(`/api/upload/`, toFormat({image: this.originalData.image})).then(res => {
if(res.data) {
vm.modalList = res.data.list;
this.$modal.show('modal-view');
}
})
}
I've integrated reCAPTCHA and it is working fine, except for when the users are too quick to click the Submit button right after checking the "I'm not a robot" checkbox. It takes quite some time for reCAPTCHA to register the user action via Ajax, and if they click on Submit too quickly, the g-recaptcha-response is missing, and the validation fails.
Hence my question: how to I grey out the Submit button until g-recaptcha-response value is available?
<form id="capform" action="/captchaverify" method="POST">
<div class="g-recaptcha" data-sitekey="..."></div>
<p>
<input id="capsubmit" type="submit" value="Submit">
</form>
I ended up using the data-callback attribute as described in the documentation:
<form action="/captchaverify" method="POST">
<div class="g-recaptcha" data-sitekey="..." data-callback="capenable" data-expired-callback="capdisable"></div>
<p>
<input id="capsubmit" type="submit" value="Submit">
</form>
JavaScript (mootools-based, but the general idea should be clear):
function capenable() {
$('capsubmit').set('disabled', false);
}
function capdisable() {
$('capsubmit').set('disabled', true);
}
window.addEvent('domready', function(){
capdisable();
});
Here's an example that begins with the submit button disabled, and enables it once the callback is received from reCaptcha. It also uses jquery validate to ensure the form is valid before submitting.
var UserSubmitted = {
$form: null,
recaptcha: null,
init: function () {
this.$form = $("#form").submit(this.onSubmit);
},
onSubmit: function (e) {
if ($(this).valid()) {
var response = grecaptcha.getResponse();
if (!response) {
e.preventDefault();
alert("Please verify that you're a human!");
}
}
},
setupRecaptcha: function (key) {
UserSubmitted.recaptcha = grecaptcha.render('recaptcha', {
'sitekey': key,
'callback': UserSubmitted.verifyCallback
//'theme': 'light'//,
//'type': 'image'
});
},
verifyCallback: function (response) {
if (response) {
$(".visible-unverified").addClass("hidden");
$(".hidden-unverified").removeClass("hidden");
}
}
};
I call setupRecaptcha from the page with a named function that's part of the js include.
<script>
var recaptchaLoader = function () {
UserSubmitted.setupRecaptcha("yourkey");
};
</script>
<script src="https://www.google.com/recaptcha/api.js?onload=recaptchaLoader&render=explicit" async defer></script>
You could simplify this. I use it in a multi-tenant application with different keys, and UserSubmitted is actually part of a larger library. You can't usenamespaces (UserSubmitted.somefunction) as the onload param either (to my knowledge).