Access Vue props from plain javascript - javascript

I am implementing Stripe inside my application and the Stripe docs show just plain JS so I have been putting the Stripe API calls in the script tag without any of the Vue methods. Things have been working fine until now. I am passing props down from the parent component and I cannot access the props from inside of the Script.
Some of the Stripe documentation says to implement the JS after the HTML has rendered so I am using the mounted method for some of this. Any ideas on how to get this working?
I have tried putting the API call in a method and calling the method on a click event on the form, but when I do that it doesn't know what the token is that Stripe uses.
towards the bottom of the code you can see where I try to get the this.plan that is in props.
<template>
<form method="post" id="payment-form">
<h4 class="h4Spacing">Billing Information</h4>
<div class="form-group">
<b-form-input type="email" class="form-control" id="email" placeholder="Email Address"/>
</div>
<div class="form-group">
<b-form-input type="text" class="form-control" id="name_on_card" name="name" placeholder="Name on Card"/>
</div>
<div class="form-group">
<b-form-input type="text" class="form-control" id="address" name="address_line1" placeholder="Address"/>
</div>
<div class="form-group">
<b-form-input type="text" class="form-control" id="city" name="address_city" placeholder="City"/>
</div>
<div class="form-group">
<b-form-input type="text" class="form-control" id="state" placeholder="State"/>
</div>
<div class="form-group">
<div id="card-element" class="form-control">
<!-- A Stripe Element will be inserted here. -->
</div>
<div v-if="successful">
<h3 class="title" data-tid="elements_examples.success.title">Payment successful</h3>
<p class="message"><span data-tid="elements_examples.success.message">Thanks for trying Stripe Elements. No money was charged, but we generated a token: </span><span class="token">tok_189gMN2eZvKYlo2CwTBv9KKh</span></p>
<a class="reset" href="#">
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path fill="#000000" d="M15,7.05492878 C10.5000495,7.55237307 7,11.3674463 7,16 C7,20.9705627 11.0294373,25 16,25 C20.9705627,25 25,20.9705627 25,16 C25,15.3627484 24.4834055,14.8461538 23.8461538,14.8461538 C23.2089022,14.8461538 22.6923077,15.3627484 22.6923077,16 C22.6923077,19.6960595 19.6960595,22.6923077 16,22.6923077 C12.3039405,22.6923077 9.30769231,19.6960595 9.30769231,16 C9.30769231,12.3039405 12.3039405,9.30769231 16,9.30769231 L16,12.0841673 C16,12.1800431 16.0275652,12.2738974 16.0794108,12.354546 C16.2287368,12.5868311 16.5380938,12.6540826 16.7703788,12.5047565 L22.3457501,8.92058924 L22.3457501,8.92058924 C22.4060014,8.88185624 22.4572275,8.83063012 22.4959605,8.7703788 C22.6452866,8.53809377 22.5780351,8.22873685 22.3457501,8.07941076 L22.3457501,8.07941076 L16.7703788,4.49524351 C16.6897301,4.44339794 16.5958758,4.41583275 16.5,4.41583275 C16.2238576,4.41583275 16,4.63969037 16,4.91583275 L16,7 L15,7 L15,7.05492878 Z M16,32 C7.163444,32 0,24.836556 0,16 C0,7.163444 7.163444,0 16,0 C24.836556,0 32,7.163444 32,16 C32,24.836556 24.836556,32 16,32 Z"></path>
</svg>
</a>
<div class="caption">
<span data-tid="elements_examples.caption.no_charge" class="no-charge">Your card won't be charged</span>
<a class="source" href="https://github.com/stripe/elements-examples/#example-1">
<svg width="16px" height="10px" viewBox="0 0 16 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M1,8 L12,8 C12.5522847,8 13,8.44771525 13,9 C13,9.55228475 12.5522847,10 12,10 L1,10 C0.44771525,10 6.76353751e-17,9.55228475 0,9 C-6.76353751e-17,8.44771525 0.44771525,8 1,8 L1,8 Z M1,4 L8,4 C8.55228475,4 9,4.44771525 9,5 C9,5.55228475 8.55228475,6 8,6 L1,6 C0.44771525,6 6.76353751e-17,5.55228475 0,5 C-6.76353751e-17,4.44771525 0.44771525,4 1,4 L1,4 Z M1,0 L15,0 C15.5522847,-1.01453063e-16 16,0.44771525 16,1 L16,1 C16,1.55228475 15.5522847,2 15,2 L1,2 C0.44771525,2 6.76353751e-17,1.55228475 0,1 L0,1 L0,1 C-6.76353751e-17,0.44771525 0.44771525,1.01453063e-16 1,0 L1,0 Z" fill="#AAB7C4"></path>
</svg>
<span data-tid="elements_examples.caption.view_source">View source on GitHub</span>
</a>
</div>
</div>
</div>
<!-- <form action="/charge" method="post" id="payment-form">-->
<!-- <div class="form-row">-->
<!-- <label for="name"> Name </label>-->
<!-- <div id="name"></div>-->
<!-- <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>-->
<!-- <b-button class="space" id="payment-request-button">Submit Payment</b-button>-->
<!-- </form>-->
<b-button class="space" type="submit" >Submit Payment</b-button>
</form>
</template>
<script>
import registerElements from './stripeMethods'
// Create a Stripe client.
let stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
// Create an instance of Elements.
let 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.)
let style = {
base: {
color: '#32325d',
fontFamily: '"Roboto", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
let card = undefined;
import Vue from 'vue';
import axios from 'axios';
import authService from '../../services/authService';
export default {
props: ['plan'],
data: function() {
return {
successful: false,
monthly: {
id: 'prod_Flp17jVPzNUfFz',
price: 25
},
annual: {
id: 'prod_Flp17jVPzNUfFz',
price: 215
}
}
},
methods: {
submitPayment: function () {
}
},
mounted() {
// Create an instance of the card Element.
let 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) {
let displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission.
let form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
console.log(event);
event.preventDefault();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the user if there was an error.
let errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
return result.token
}
}).then(function (result) {
console.log(result);
axios({
method: "POST",
url: '/api/checkout',
headers: {
Authorization: `Bearer ${authService.idToken}`,
},
data: {
subscription: result.id,
plan: this.plan <----------------------------------- not working
},
}).then(function (res) {
// alert("It went through! How? ")
}).catch(function (err) {
console.log(err)
});
});
});
// Submit the form with the token ID.
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
let form = document.getElementById('payment-form');
let hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
}
}
}
</script>

This is a problem of scoping, nothing more. When you try to access this.plan, this is a reference to the callback being executed from the event listener. You need to declare a variable that is assigned the Vue instance to access these fields anywhere in a method:
mounted() {
var vm = this;
// Create an instance of the card Element.
let 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) {
let displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission.
let form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
console.log(event);
event.preventDefault();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the user if there was an error.
let errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
return result.token
}
}).then(function (result) {
console.log(result);
axios({
method: "POST",
url: '/api/checkout',
headers: {
Authorization: `Bearer ${authService.idToken}`,
},
data: {
subscription: result.id,
plan: vm.plan <----------------------------------- not working
},
}).then(function (res) {
// alert("It went through! How? ")
}).catch(function (err) {
console.log(err)
});
});
});

Related

Unable to redirect to homepage after posting a form - Express,EJS and JS

I have a view which contains a form and looks like this,
<form class="flex-form" id="form" method="">
<div class="form-component">
<label>Type</label>
<input type="text" id="type" name="type">
</div>
<div class="form-component">
<div class="form-component"><label><b>Contents</b></label></div>
<label>Savoury</label><input type="text" name="savoury" id="savoury">
<label>Fillings</label><input type="text" name="fillings" id="fillings">
<label>Amount</label><input type="text" name="amount" id="amount">
<div class="flex-component">
<button class="set-button" type="button" id="set">Set Item</button>
</div>
</div>
<div class="form-component">
<label class="description-label">Description</label>
<textarea class="fixed-textarea" id="description" name="description" cols="15" rows="10"></textarea>
</div>
<div class="form-component">
<label >Unit Price</label>
<input type="text" id="price" name="unit_price">
</div>
<div class="flex-component">
<button class="form-button" type="submit">Add</button>
</div>
</form>
I have a JavaScript that allows me to capture some intermediary information (via the Set Item button) from the form before the form gets submitted (via the Add Button). I want to handle the form's submission from the script since I need to capture the intermediary data.
let collectedItems = [];
let setter = document.getElementById('set');
let form = document.getElementById('form');
setter.addEventListener('click',getSetContent);
function getSetContent() {
let type = document.getElementById('savoury');
let fillings = document.getElementById('fillings');
let amount = document.getElementById('amount');
const content = {
type: type.value,
fillings: fillings.value.split(','),
amount: Number(amount.value)
};
collectedItems.push(content);
clearInputFields([type,fillings,amount]);
}
function clearInputFields(inputFields) {
inputFields.forEach(field => {
field.value = ''
});
console.log(collectedItems);
}
form.addEventListener('submit',submitForm);
function submitForm() {
const type = document.getElementById('type').value;
const desc = document.getElementById('description').value;
const price = Number(document.getElementById('price').value);
const content = collectedItems;
const data = {
type: type,
contents: content,
description: desc,
unit_price: price
};
post('http://localhost:8001/add/box',
{ 'Content-Type': 'application/json' },
JSON.stringify(data)
);
}
function post(endpoint,header,body) {
const response = fetch(endpoint,{ method: 'POST',headers: header,body: body });
response.then(
resp => {
if (resp.ok) {
console.log('form submitted');
} else {
console.log('form not submitted');
}
}
)
}
I then make a POST request using fetch() to an endpoint I have setup in Express which looks like this,
app.post('/add/box',(req,res) => {
const box: any = req.body;
console.log(box);
// DO SOME DB STUFF
res.redirect('/');
});
The form submission works as intended (logs to terminal using nodemon), however I am unable to redirect to the homepage. Instead I stay on the form page after the submission has occurred and I can't figure out why. Any help with this issue is much appreciated.

Stripe 'card-element' is not displaying. Why?

Completely losing my mind with this...
I am creating a ecommerce website with django. I use Stripe as Payment Gateway Provider. I don't understand ...Stripe 'card-element' is not displaying. Why ?
For information, the card-element used to be displayed correclty in the previous days. I could simulate payments which were recorded in my Stripe account... The thing is, I have checked with older versions of my code which used to work (in case I did a mistake since). But none of them is working... It's driving me crazy.
Anything wrong with my code ? Any idea ?
checkout.html
{% extends 'main.html' %}
{% load static %}
{% block content %}
<script src="https://js.stripe.com/v3/"></script>
<div class="container">
<div class="row">
<div class="col d-flex justify-content-center align-items-center">
<h3 class="text-primary fw-bold">PAIEMENT PAR CARTE</h3>
</div>
</div>
<div class="row ">
<div class="col d-flex justify-content-center align-items-center">
<form id="payment-form" method='POST' class="col">
{% csrf_token %}
<div class="form-control" id="card-element"></div>
<div class="sr-field-error" id="card-errors" role="alert"></div>
<button id="payer-submit" class="btn btn-primary">
<div class="spinner-border spinner-border-sm text-light d-none" id="spinner" role="status"></div>
<span class="text-white" id="button-text">Pay</span>
<span class="text-white" id="order-amount"></span>
</button>
</form>
</div>
</div>
<div class="row ">
<div class="col d-flex justify-content-center align-items-center">
<form id="payload" method='POST' class="col" action="/payment/payment-bycard-complete">
{% csrf_token %}
<input id ="data-payload" type="hidden" name="payload"/>
</form>
</div>
</div>
</div>
<script type="text/javascript" src="{% static '/js/stripe.js' %}"></script>
{% endblock content %}
stripe.js
document.getElementById("payer-submit").disabled = true;
fetch("/payment/bycard", {
method: "POST",
headers: {
"Content-Type": "application/json",
}
})
.then(function(result) {
return result.json();
})
.then(function(data) {
return setupElements(data);
})
.then(function({ stripe, card, clientSecret }) {
document.getElementById("payer-submit").disabled = false;
// Handle form submission.
var form = document.getElementById("payment-form");
form.addEventListener("submit", function(event) {
event.preventDefault();
// Initiate payment when the submit button is clicked
pay(stripe, card, clientSecret);
});
});
// Set up Stripe.js and Elements to use in checkout form
var setupElements = function(data) {
stripe = Stripe(data.publishableKey);
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#aab7c4"
}
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a"
}
};
var card = elements.create("card", { style: style });
card.mount("#card-element");
return {
stripe: stripe,
card: card,
clientSecret: data.clientSecret
};
};
/*
* Calls stripe.confirmCardPayment which creates a pop-up modal to
* prompt the user to enter extra authentication details without leaving your page
*/
var pay = function(stripe, card, clientSecret) {
changeLoadingState(true);
// Initiate the payment.
// If authentication is required, confirmCardPayment will automatically display a modal
stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: card
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer
showError(result.error.message);
} else {
// The payment has been processed!
orderComplete(clientSecret);
}
});
};
/* ------- Post-payment helpers ------- */
/* Shows a success / error message when the payment is complete */
var orderComplete = function(clientSecret) {
//// Just for the purpose of the sample, show the PaymentIntent response object
stripe.retrievePaymentIntent(clientSecret).then(function(result) {
// var paymentIntent = result.paymentIntent;
// var paymentIntentJson = JSON.stringify(paymentIntent, null, 2);
// post data and show new page
// var input = document.getElementById("data-payload");
// input.value = paymentIntentJson;
var form2 =document.getElementById("payload");
form2.submit();
changeLoadingState(false);
});
};
var showError = function(errorMsgText) {
changeLoadingState(false);
var errorMsg = document.querySelector(".sr-field-error");
errorMsg.textContent = errorMsgText;
setTimeout(function() {
errorMsg.textContent = "";
}, 4000);
};
// Show a spinner on payment submission
var changeLoadingState = function(isLoading) {
if (isLoading) {
document.getElementById("payer-submit").disabled = true;
document.querySelector("#spinner").classList.remove("d-none");
document.querySelector("#button-text").classList.add("d-none");
} else {
document.getElementById("payer-submit").disabled = false;
document.querySelector("#spinner").classList.add("d-none");
document.querySelector("#button-text").classList.remove("d-none");
}
};
views.py
import stripe
from django.shortcuts import render, redirect
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.contrib import messages
from core.settings import STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY
from core.utils import display_price_in_euro_string
from basket.basket import Basket
from payment.checkout import calculate_order
from payment.save_order import create_invoice_xls, save_order_in_db, save_ordered_items_in_db, save_stripe_payment_in_db
from payment.send_mail import send_order_confirmation
def checkout(request):
if not request.session.get('cart', None):
return redirect('store:all_products')
elif not request.session.get('shipping-form', None):
return redirect('address:create_shipping_address')
else:
calculate_order(request)
context = {
'shipping_fees_in_euros': display_price_in_euro_string(request.session['order']['shipping_fees_in_cent']),
'cart_price_with_discount_in_euros': display_price_in_euro_string(request.session['order']['cart_price_in_cent_with_discount']),
'cart_price_without_discount_in_euros': display_price_in_euro_string(request.session['order']['cart_price_in_cent_without_discount']),
'order_total_in_euros': display_price_in_euro_string(request.session['order']['order_total_in_cent']),
'cart_discount_in_euros': display_price_in_euro_string(request.session['order']['cart_discount_in_cent']),
}
return render(request, 'payment/checkout.html', context)
#csrf_exempt
def create_stripe_payment_intent(request):
try:
total_to_pay = request.session['order']['order_total_in_cent']
stripe.api_key = STRIPE_SECRET_KEY
if request.method == "POST":
# Create a PaymentIntent with the order amount and currency
# https://stripe.com/docs/api/payment_intents/create
intent = stripe.PaymentIntent.create(
amount = total_to_pay,
currency = "eur",
metadata = {
"Adresse de livraison": request.session['shipping-form']['id'],
"Adresse de facturation": request.session['billing-form']['id'],
"Montant commande TTC (centimes)": request.session['order']['order_total_in_cent'],
"Panier avant réduction TTC (centimes)": request.session['order']['cart_price_in_cent_without_discount'],
"Panier après réduction TTC (centimes)": request.session['order']['cart_price_in_cent_with_discount'],
"Frais de livraison TTC (centimes)": request.session['order']['shipping_fees_in_cent'],
"TVA (centimes)": request.session['order']['tva_in_cent'],
"Montant commande HT (centimes)": request.session['order']['order_total_in_cent_HT'],
"SESSION KEY": request.session.session_key,
"PANIER": str(request.session['cart']),
},
receipt_email = 'monoi#laposte.net',
)
request.session['order']['stripe-intent'] = {}
request.session['order']['stripe-intent']['client-secret'] = intent.client_secret
request.session['order']['stripe-intent']['id'] = intent.id
request.session.modified = True
try:
return JsonResponse({
'publishableKey': STRIPE_PUBLISHABLE_KEY,
'clientSecret': intent.client_secret
})
except Exception as e:
return JsonResponse({'error': str(e)}, status = 403)
else:
return redirect('payment:checkout')
except:
return redirect('payment:checkout')
def payment_bycard_complete(request):
stripe.api_key = STRIPE_SECRET_KEY
payment_intent = stripe.PaymentIntent.retrieve(
request.session['order']['stripe-intent']['id'],
)
if payment_intent.status == "succeeded":
save_order_in_db(request, "STRIPE")
save_ordered_items_in_db(request)
save_stripe_payment_in_db(request)
is_invoice = create_invoice_xls(request)
is_mail = send_order_confirmation(request)
basket = Basket(request)
basket.clear()
request.session.flush()
messages.success(request, 'Merci pour votre achat')
return redirect('home_page:home')
else:
return redirect('home_page:error')
Versions :
Django 3.1.6
stripe 2.60.0
EDIT
Console
Promise { <state>: "pending" }
​
<state>: "fulfilled"
​
<value>: Object { publishableKey: "pk_test_51Ia5dlE8XikcpErQEGGuA56itbGvPLmnZSNALSKvVGK2syFR21CvPoYMCJtOjb2DArvAKT1hd1z2KSS4wZmpDqre00ZppDWLLl", clientSecret: "pi_3JPkJfE8XikcpErQ0NuBgyfq_secret_C94xiK71RDMFaZImXYc12c1rn" }
Uncaught (in promise) TypeError: Response.json: Body has already been consumed.
<anonymous> http://127.0.0.1:8000/static/js/stripe.js:16
promise callback* http://127.0.0.1:8000/static/js/stripe.js:13
stripe.js:16:21
According to debugger :
In
.then(function(result) {
return result.json();
})
return result.json() bugs
EDIT 2
I had console.log(result.json()) before return result.json(), which I think is not allowed with fetch. I have removed this console.log(result.json()).
There is still something I don't understand ... My code works well in Chrome (I am able to fill credit card number etc.) but not with Firefox. Any ideas ?
I had console.log(result.json()) before return result.json(), which is not allowed with fetch. I have removed this console.log(result.json()) and it works now.

VeeValidate3: Custom validation always is true on submit

I am using Vue.js 2 and VeeValidate3 to validate my form. This from is also making an axios call to check if the username is already in use. If so, obviously the validation needs to be false.
So far so good. I also see the error message Dieser Name wird bereits verwendet when I type in a username which is already in use.
However, if I see the error message and nevertheless click the submit button, the error message disappears and I can see the message Submit submitCompleteNormalRegistrationForm which gets printed when the form gets submitted.
The question is, why does the form get submitted also there is an validation error with the name? What I am doing wrong?
Also, how can I set the validation for the name to true when the name is not in use?
This is my code so far:
<template>
<div>
<ValidationObserver ref="completeNormalRegistrationForm" v-slot="{ passes }" class="flex-column flex-grow-1 d-flex w-100">
<form #submit.prevent="passes(submitCompleteNormalRegistrationForm)" id="completeNormalRegistrationForm" class="flex-column flex-grow-1 d-flex w-100">
<div class="backButtonWrapper text-left">
<i id="backButtonRegistrationForm" #click="showLoginForm" class="far fa-arrow-alt-circle-left"></i>
</div>
<div class="form-wrapper margin-auto w-100">
<p class="rubik-bold" style="font-size: 1rem;">Registrieren</p>
<ValidationProvider vid="name" name="Nutzername" rules="required|alpha_dash" v-slot="{ errors }">
<input #keyup="completeNormalRegistrationFormUsernameExists" class="form-control search-username" v-model="registerForm.name" type="text" placeholder="Username">
<span v-if="errors[0]" class="username-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider vid="email" name="E-Mail" rules="required|email" v-slot="{ errors }">
<input class="form-control search-email" v-model="registerForm.email" type="email" placeholder="E-Mail">
<span v-if="errors[0]" class="email-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider vid="confirmation" name="Passwort" v-slot="{ errors }">
<input class="form-control" v-model="registerForm.password" type="password" placeholder="Passwort">
<span v-if="errors[0]" class="password-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider rules="confirmed:confirmation" name="Passwort" v-slot="{ errors }">
<input class="form-control" v-model="registerForm.passwordConfirmation" type="password" placeholder="Passwort wiederholen">
<span v-if="errors[0]" class="password-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<button type="submit" class="btn btn-primary btn-big big-letter-spacing text-uppercase rubik-bold login">Anmelden</button>
</div>
</form>
</ValidationObserver>
</div>
</template>
<script>
export default {
name: "NavbarAction",
data() {
return {
registerForm: {
name: '',
email: '',
password: '',
passwordConfirmation: '',
termsAndConditions: false,
},
}
},
methods: {
async completeNormalRegistrationFormUsernameExists() {
const nameValid = await this.usernameExists(this.registerForm.name);
if (nameValid) {
this.$refs.completeNormalRegistrationForm.setErrors({name: 'Dieser Name wird bereits verwendet'});
} else {
console.log('Set name is NOT in use!');
}
},
async usernameExists(name){
return await axios.post(window.routes.usernameExists, {value: name})
.then(r => {
return r.data;
});
},
submitCompleteNormalRegistrationForm(){
console.log('Submit submitCompleteNormalRegistrationForm');
console.log(this);
}
}
}
</script>
UPDATE (working with custom rule now):
extend('unique-email', (value) => {
return axios.post(this.routes.emailExists, { value: value })
.then((r) => {
// If email exists, axios response is true
if(r.data){
return {
valid: false,
data: { message: 'E-Mail wird bereits genutzt' }
};
}else{
return {
valid: true,
};
}
}, (err) => {
return {
valid: false,
data: { message: 'E-Mail wird bereits genutzt' }
};
})
},
)
You need to express your email validator as a vee-validate rule instead of trying to do it yourself on keyup. One of the many undocumented things in vee-validate is that if you return a promise as the result of a validation, vee-validate will handle it correctly, waiting to get the result before allowing validation to pass.
Here's an example to get you started:
mounted() {
extend('unique-email', (value) => {
return this.usernameExists(value)
.then((res) => {
return {
valid: true,
};
}, (err) => {
this.$refs.completeNormalRegistrationForm.setErrors({
name: ['Username already registered']
});
})
}, {
immediate: false
})
}
this is front-end validation. The only thing you can do is disable the button when the form is invalid. There is nothing preventing a smart kid trying to submit a form anyway. The true validation should be serverside.
something like:
<button type="submit" class="btn btn-primary btn-big big-letter-spacing text-uppercase rubik-bold login" :disabled="passes(submitCompleteNormalRegistrationForm)">Anmelden</button>
Finally also found a way to set custom error messages without using $refs:
extend('unique-email', (value) => {
return axios.post(window.laravel.emailExists, { value: value })
.then((r) => {
// If email exists, axios response is true
if(r.data){
return "E-Mail wird bereits genutzt";
}else{
return true;
}
}, (err) => {
return "E-Mail wird bereits genutzt";
})
},
);

Charging a custom amount with Stripe and Node.js

I'm going to try and be thorough as possible. So what I'm trying to do is charge a user a percentage of the overall total that is calculated. How do we get the total? Well, the total cost is depended upon the progression of questions the user answers.
If the user needs a specific service then the cost might increase a bit, say to $100, but we're wanting to charge just 10% of that total that constantly changes like stated before. Currently, the amount is hardcoded, but since the amount changes depending on their services select, I can't have it hard coded.
I'm new to Stripe and Node but is there an easy way such as 'document.getElementById'? of something similar? The charge and everything work correctly but I believe I'm getting confused on the routing.
My HTML form (with the area where the total will be displayed):
<div class="" style="margin-top: 60px;">
<h2 class="quote-info">Estimated total: $<span id="new_text"></span> USD</h2>
<h2 class="quote-info">Reservation deposit: $<span id="new_text2"></span> USD</h2>
</div>
<!-- Payment form -->
<form action="/charge" method="post" id="payment-form">
<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"></div>
</div>
<input type="hidden" name="totalAmount" value="1000">
<button>Submit Payment</button>
</form>
My script tag that's found at the bottom of my HTML file that contains the form above:
<script type="text/javascript">
//Create a Stripe client
var stripe = Stripe('my_key_should_go_here');
// 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: '24px',
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);
}
});
});
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
var formData = JSON.stringify({
mytoken: token.id
});
$.ajax({
type: "POST",
url: "/charge",
data: formData,
success: function(){alert("done")},
dataType: "json",
contentType: "application/json"
});
// alert("Created a token with value: "+token.id)
form.submit();
}
</script>
And lastly, my app.js file:
const express = require('express');
const stripe = require('stripe')('deleted_key');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
// Set Static Folder
app.use(express.static('public'));
// Index route
app.get('/charge', (req, res) => {
res.send();
});
// charge route
app.post('/charge', (req, res) => {
// const amount = 2500;
const amount = req.body.totalAmount;
stripe.customers.create({
email: "random#gmail.com",
source: req.body.mytoken
})
.then(customer => {
stripe.charges.create({
amount,
description:'specified service description here',
currency:'usd',
customer:customer.id
})})
.then(charge => res.send('success'));
});
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
My primary question is this, how would I go about obtaining the amount given in the 'new_text' area in my HTML file to use and charge the user in node?
To use the getElementById is to add an ID to your amount field first:
<input type="hidden" name="totalAmount" id="totalAmount" value="1000">
Then you can use the ID to get the value of the field:
const amount = document.getElementById("totalAmount").value;
Although, I can see that your input type is hidden - is that intentional?

Laravel\Vue - ajax file upload not working on the production server

I have a component where I am uploading a video file, everything works fine on my local machine, and it used to work fine on the production server, Namechap is where I host the project, until only recently I have done some work and made changes, that I saw it doesn't work on the production server anymore.
I am using Vue v. 1.0.28, and this is the upload component, where in the fileInputChange() method I am posting form data, to the /upload endpoint, which on the production server for some reason I can't read in the backend:
<template>
<div class="card-content col-md-10 col-md-offset-1">
<div v-if="!uploading">
<div class="col-md-12 Image-input__input-wrapper">
Upload video
<input type="file" name="video" id="video" #change="fileInputChange" class="Image-input__input" accept="video/*">
</div>
</div>
<div class="alert alert-danger video-upload-alert" v-if="failed">Something went wrong. Please check the video format and try again. If you need any help please contact our <a>support service.</a></div>
<div id="video-form">
<div class="alert alert-info" v-if="uploading && !failed && !uploadingComplete">
Please do not navigate away from this page, until the video has finished uploading. Your video will be available at {{ $root.url }}/videos/{{ uid }}, once uploaded.
</div>
<div class="alert alert-success" v-if="uploading && !failed && uploadingComplete">
Upload complete. Video is now processing. Go to your videos.
</div>
<div class="progress" v-if="uploading && !failed && !uploadingComplete">
<div class="progress-bar" v-bind:style="{ width: fileProgress + '%' }"></div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<label for="title" class="control-label">Title</label>
<input type="text" class="form-control" v-model="title">
</div>
<!--
<div class="col-md-12 form-group">
<label for="visibility" class="control-label">Visibility</label>
<select class="form-control" v-model="visibility">
<option value="private">Private</option>
<option value="unlisted">Unlisted</option>
<option value="public">Public</option>
</select>
</div>
-->
</div>
<div class="row">
<div class="col-md-12 form-group">
<label for="description" class="control-label">Description</label>
<textarea class="form-control" v-model="description"></textarea>
</div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<button type="submit" class="btn btn-submit" #click.prevent="update">Save</button>
</div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<span class="help-block pull-right">{{ saveStatus }}</span>
</div>
</div>
</div>
</template>
<script>
function initialState (){
return {
uid: null,
uploading: false,
uploadingComplete: false,
failed: false,
title: null,
link: null,
description: null,
visibility: 'private',
saveStatus: null,
fileProgress: 0
}
}
export default {
data: function (){
return initialState();
},
methods: {
fileInputChange() {
this.uploading = true;
this.failed = false;
this.file = document.getElementById('video').files[0];
var isVideo = this.isVideo(this.file.name.split('.').pop());
if (isVideo) {
this.store().then(() => {
var form = new FormData();
form.append('video', this.file);
form.append('uid', this.uid);
this.$http.post('/upload', form, {
progress: (e) => {
if (e.lengthComputable) {
this.updateProgress(e)
}
}
}).then(() => {
this.uploadingComplete = true
this.uploading = false
}, () => {
this.failed = true
this.uploading = false
});
}, () => {
this.failed = true
this.uploading = false
})
}
else {
this.failed = true
this.uploading = false
}
},
isVideo(extension) {
switch (extension.toLowerCase()) {
case 'm4v':
case 'avi':
case 'mpg':
case 'mp4':
case 'mp3':
case 'mov':
case 'wmv':
case 'flv':
return true;
}
return false;
},
store() {
return this.$http.post('/videos', {
title: this.title,
description: this.description,
visibility: this.visibility,
extension: this.file.name.split('.').pop()
}).then((response) => {
this.uid = response.json().data.uid;
});
},
update() {
this.saveStatus = 'Saving changes.';
return this.$http.put('/videos/' + this.uid, {
link: this.link,
title: this.title,
description: this.description,
visibility: this.visibility
}).then((response) => {
this.saveStatus = 'Changes saved.';
setTimeout(() => {
this.saveStatus = null
}, 3000)
}, () => {
this.saveStatus = 'Failed to save changes.';
});
},
updateProgress(e) {
e.percent = (e.loaded / e.total) * 100;
this.fileProgress = e.percent;
},
}
}
</script>
The problem is that on upload in my controller in the store function the request object is empty on the production server, which I got when I did dd($request->all()). Then it fails to find the video, in the network inspector I get a 404 error, which is returned by firstOrFail() method, because it can't find the Video model, since it is missing $request->uid.
No query results for model [App\Video].
This is the controller:
class VideoUploadController extends Controller
{
public function index()
{
return view('video.upload');
}
public function store(Request $request)
{
$player = $request->user()->player()->first();
$video = $player->videos()->where('uid', $request->uid)->firstOrFail();
$request->file('video')->move(storage_path() . '/uploads', $video->video_filename);
$this->dispatch(new UploadVideo(
$video->video_filename
));
return response()->json(null, 200);
}
}
I am not sure what is going on, since on inspecting the network tab in the console I am sending a request payload that looks like this:
------WebKitFormBoundarywNIkEqplUzfumo0A Content-Disposition: form-data; name="video"; filename="Football Match Play.mp4" Content-Type: video/mp4
------WebKitFormBoundarywNIkEqplUzfumo0A Content-Disposition: form-data; name="uid"
159920a7878cb2
------WebKitFormBoundarywNIkEqplUzfumo0A--
It is really frustrating since I have no idea how to fix this, when everything is fine on my local machine, not sure what is wrong on the production server and how to fix it?
Update
It started working again on its own, since I have created a ticket in the Namecheap support service, all of sudden after one day, it started working again, I haven't done any changes, and on asking the Namecheap support I have got an answer from them that they haven't done any changes either, so I have no idea what went wrong, which is still a bit frustrating since I would like to avoid that in the future, but at least everything is working now again as it should.
Laravel's $request->all() method only pulls from the input, thus in the store() method of VideoUploadController $request->all() return empty object.
In your script, it calls this.store() after checked the isVideo is true while file changes. As a result, there isn't a uid parameter or video parameter.

Categories

Resources