Adding cardholder name in Stripe - javascript

So the problem I am facing is this. Here I have created PaymentForm with Stripe. So when I am not entering the input value of CardHolder name, and when I press the Purchase button it should display the <h1>Please enter your cardholder name</h1> but it is not doing it. I just want to create a test of Cardholder name, I know the documentation of Stripe is showing real approaches. Where could the error be located?
Payment form
import React,{useContext, useEffect, useState} from 'react'
import {CardElement, useStripe, useElements } from"#stripe/react-stripe-js"
import { CartContext } from '../../context/cart'
import { useHistory, Link } from "react-router-dom";
const PaymentForm = () => {
const { total} = useContext(CartContext)
const {cart, cartItems}= useContext(CartContext)
const history = useHistory()
const {clearCart} = useContext(CartContext)
const [nameError, setNameError]=useState(null)
const [name, setName] = React.useState("");
const [succeeded, setSucceeded] = useState(false);
const [error, setError] = useState(null);
const [processing, setProcessing] = useState('');
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState('');
const stripe = useStripe();
const elements = useElements();
useEffect(() => {
// Create PaymentIntent as soon as the page loads
window
.fetch("http://localhost:5000/create-payment-intent", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({items: [{cart}]})
})
.then(res => {
return res.json();
})
.then(data => {
setClientSecret(data.clientSecret);
});
}, [cart]);
const cardStyle = {
style: {
base: {
color: "#32325d",
fontFamily: 'Arial, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d"
}
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a"
}
}
};
const handleChange = async (event) => {
setDisabled(event.empty);
setError(event.error ? event.error.message : "");
};
const handleChangeInput = async (event) => {
setDisabled(event.empty);
setNameError(event.nameError ? event.nameError.message : "");
setName(event.target.value)
};
const handleSubmit = async ev => {
ev.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement)
}
});
if (payload.error) {
setError(`Payment failed ${payload.error.message}`);
setProcessing(false);
} else {
setNameError(null)
setError(null);
setProcessing(false);
setSucceeded(true)
clearCart()
}
};
useEffect(()=>{
setTimeout(() => {
if(succeeded){
history.push('/')
}
}, 3000);
},[history, succeeded])
console.log(name)
return (
<form id="payment-form" onSubmit={handleSubmit}>
<h2>Checkout</h2>
<div className='payment__cont'>
<label>Cardholder Name </label>
<input
placeholder='Please enter your Cardholder name'
type="text"
id="name"
value={name}
onChange={handleChangeInput}
/>
</div>
<div className="stripe-input">
<label htmlFor="card-element">
Credit or Debit Card
</label>
<p className="stripe-info">
Test using this credit card : <span>4242 4242 4242 4242</span>
<br />
enter any 5 digits for the zip code
<br />
enter any 3 digits for the CVC
</p>
</div>
<CardElement id="card-element" options={cardStyle} onChange={handleChange} />
<div className='review__order'>
<h2>Review Order</h2>
<h4>Total amount to pay ${total}</h4>
<h4>Total amount of items {cartItems}</h4>
<button
className='purchase__button'
disabled={processing || disabled || succeeded}
id="submit"
>
<span id="button-text">
{processing ? (
<div className="spinner" id="spinner"></div>
) : (
"Complete purchase"
)}
</span>
</button>
<button className='edit__button'onClick={()=> {history.push('/cart')}}>Edit Items</button>
</div>
{error && (
<div className="card-error" role="alert">
{error}
</div>
)}
{nameError && (
<div className="card-error" role="alert">
<h1>Please enter yor card holder name</h1>
</div>
)}
<p className={succeeded ? "result-message" : "result-message hidden"}>
Payment succeeded
{''}
<h1>Redirecting you yo the home</h1>
</p>
</form>
);
}
export default PaymentForm

You still need to validate your name input yourself. Stripe doesn't do that for you
Your handleChangeInput handler only fires when you write to your name input, and you're treating the event as if it's fired from a Stripe component, but it's not, so try this:
// Validates name input only
const handleChangeInput = async (event) => {
const { value } = event.target;
// Disable when value is empty
setDisabled(!value);
// Set the error if value is empty
setNameError(value ? "" : "Please enter a name");
// Update form value
setName(value)
};

I'm not sure what you want to do is possible with Stripe elements as cardholder name isn't actually part of it. You'll need to handle it yourself.
An alternative way of ensuring the name field is entered prior to the card element being pressed would be to force input into cardholder name field before displaying/enabling the Stripe Card Element. This way you can be more certain the the name has been entered (and then you can do what you like with it) before the card element is pressed. You could do this a lot of ways, but for example in your render:
{name.length >= 10 ? ( //Check length of name
<CardElement id="card-element" options={cardStyle} onChange={handleChange} />
) : (
<span>Please enter your cardholder name.</span> // do nothing if check not passed
)}
This is a really simple example but it checks the length of name and then if greater than or equals ten characters makes the card element visible. You could instead use your handleChangeInput to set a boolean state (true or false) on button press or something; it would be better to make this more robust.
edit: some clarifications.

Related

React: [Label]-[input]-[edit btn]-[save btn-(hidden)]

As the title states, Ive got a simple crud operation I could use some help with the useEffect/useState hook in how to implement.
I've got the input set to disabled by default, displaying the previous value via the placeholder. The state is then updated via a useState/useEffect hooks. Also, the second button (save) is set to hidden by default.
Goal:
essentially, just trying to setup an event listener for each edit-input button: hide the edit button, enable the input, unhide the save button.
with a separate (2nd) event listener on the save button: to hide the save button, unhide the edit button, return the input to disabled, turn the placeholder to the new value, and submit the value(I've got a good idea of the last part)
JSX:
<label className="col">
<div className="row">
<p className="bold nowrap inline-value-title" >Test Name: </p>
<input
id="display-name-change"
type="text"
className="form-reset inline-input"
onChange={(e) => setDisplayName(e.target.value)}
value={displayName}
placeholder={user.displayName}
disabled
/>
<button type="button" className="inline-button edit-val-btn">
<FontAwesomeIcon icon={faPenToSquare} />
</button>
<button type="button" className="hidden inline-button save-val-btn">.
<FontAwesomeIcon icon={faCircleCheck} />
</button>
</div>
</label>
My Javascript: (as you can probably tell it's still very Vanilla and I think that's the problem)...
const editValueBtns = document.querySelectorAll('.edit-val-btn');
const saveValueBtns = document.querySelectorAll('.save-val-btn');
useEffect(() => {
editValueBtns.forEach((button) => {
button.addEventListener('click', (e) => {
button.classList.add('hidden')
button.nextSibling.classList.remove('hidden')
button.parentElement.children[1].removeAttr("disabled") ;
})
})
saveValueBtns.forEach((button) => {
button.addEventListener('click', (e) => {
button.classList.add('hidden')
button.previousSibling.classList.remove('hidden')
button.parentElement.children[1].addAttr("disabled") ;
})
})
}, []);
EDIT: Showing the inputs being submitted to Firebase/Firestore
const handleSubmit = async (e) => {
e.preventDefault();
let selectedFile = document.querySelector('#thumbnailInput')
// displayname
if(displayName.length == 0){console.log('No change of name')}
else {
console.log('change to displayname')
updateProfile(user, { displayName })
setDoc(doc(db, 'users', user.uid), { displayName }, { merge: true })
}
// phone Number
if (phoneNo.length == 0){console.log('No change of phone no')}
else {
console.log('change to phone')
updateProfile(user, { phoneNo })
setDoc(doc(db, 'users', user.uid), { phoneNo }, { merge: true })
}
// title
if (title.length == 0){console.log('No change of title')}
else {
console.log('change to title')
updateProfile(user, { title })
setDoc(doc(db, 'users', user.uid), { title }, { merge: true })
}
// avatar thumbnail
if(selectedFile.files[0] == undefined){
console.log('no change to thumbnail')
} else {
console.log('change to thumbnail')
// pass the path in ref to create a StorageReference
const storageRef = ref(storage,`thumbnails/${user.uid}/${displayName}`) //thumbnail.name
// upload image, file is a blob here
await uploadBytes(storageRef, thumbnail);
const downloadUrl = await getDownloadURL(storageRef);
// this function returns promise too, add await
await updateProfile(user, { photoURL: downloadUrl })
updateProfile(user, { photoURL: downloadUrl})
setDoc(doc(db, 'users', user.uid), {
photoURL: downloadUrl,
}, { merge: true })
}
// clear all form inputs
const inputs = e.target.querySelectorAll('.form-reset')
inputs.forEach((input) => {
input.value=""
})
}
I see your vanilla js way, and raise you the react way. In react, you shouldn't have to use document.querySelector, previousSibling, parentElement, classList.add, classList.remove, addAttr or button.addEventListener. See solution in CodeSandbox or below:
App.jsx
import { Row } from "./components/Row";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Row placeholder="input 1" />
<Row placeholder="input 2" />
<Row placeholder="input 3" />
</div>
);
}
Row.jsx
import { useState } from "react";
export const Row = ({ defaultValue, placeholder }) => {
const [value, setValue] = useState(defaultValue);
const [disabled, setDisabled] = useState(true);
const handleEditClick = () => {
setDisabled(false);
};
const handleSaveClick = () => {
setDisabled(true);
// save logic goes here
};
return (
<label className="col">
<div className="row">
<p className="bold nowrap inline-value-title">Test Name:</p>
<input
type="text"
className="form-reset inline-input"
onChange={(e) => {
setValue(e.target.value);
}}
value={value}
placeholder={placeholder}
disabled={disabled}
/>
{disabled && (
<button
type="button"
onClick={handleEditClick}
className="inline-button edit-val-btn"
>
edit
</button>
)}
{!disabled && (
<button
type="button"
onClick={handleSaveClick}
className="hidden inline-button save-val-btn"
>
save
</button>
)}
</div>
</label>
);
};
small tweaks.. it appears that logging the console kept it from doing what it did. also removed the e from the parenthesis after each 'click'. the addAttr and removeAttr also were replaced... the rest of the functionality can be placed in either of the listeners..
EDIT: added a 2nd input and it appears to only work in the 1st input....
...yes I'm talking to myself.
EDIT 2: It worked fine until the page refreshed... removed the dependency array for it to work every time. I feel like i still need a cleanup function, but I can't just place the event listeners into a function can i? Really, if you're reading this i would love some more experienced input... :)
useEffect(() => {
editValueBtns.forEach((button) => {
button.addEventListener('click', () => {
button.classList.add('hidden')
button.nextSibling.classList.remove('hidden')
button.parentElement.children[1].disabled = false ;
})
})
saveValueBtns.forEach((button) => {
button.addEventListener('click', () => {
button.classList.add('hidden')
button.previousSibling.classList.remove('hidden')
button.parentElement.children[1].disabled = true ;
})
})
});

How to make phone input with country code in React JS

For reference you can see this image I need something like this
I am new and learning React JS and I want to take the phone input in +917896541230 or +91 78965412330 in this format, not in this +91 (123)789 654 0 or not in any other format ("I gave +91 as a exam of country code but it will accept all countries code around the world") because then number stored in the database is in above format. I try to include jQuery but it did not work. I don't understand How to do this the npm packages I got all are in a different format which does not work for me.
This is my code
import React from 'react'
import firebase from './firebase'
import "./App.css";
import { getDatabase, ref, child, get } from "firebase/database";
// import PhoneInput from 'react-phone-number-input'
class Login extends React.Component {
// jQuery code
// var phoneNumber = window.intlTelInput(document.querySelector("#phoneNumber"), {
// separateDialCode: true,
// preferredCountries:["in"],
// hiddenInput: "full",
// utilsScript: "//cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/js/utils.js"
// });
// $("#getCode").click(function() {
// var full_num = phoneNumber.getNumber(intlTelInputUtils.numberFormat.E164);
// $("input[name='phoneNumber[full]'").val(full_num);
// localStorage.setItem("Phone_No", full_num)
// });
//
handleChange = (e) =>{
const {name, value } = e.target
this.setState({
[name]: value
})
this.setState({ phoneNumber: value }, () => {
console.log(this.state.phoneNumber);
});
}
configureCaptcha = () =>{
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': (response) => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
this.onSignInSubmit();
// console.log("Recaptca varified")
},
// defaultCountry: "IN"
}
);
}
onSignInSubmit = (e) => {
e.preventDefault()
this.configureCaptcha()
const phoneNumber = this.state.mobile
const appVerifier = window.recaptchaVerifier;
const dbRef = ref(getDatabase());
get(child(dbRef, `Users/${phoneNumber}`)).then((snapshot) => {
if (snapshot.exists()){
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then((confirmationResult) => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
alert('An OTP has been sent to your registered mobile number')
localStorage.setItem("Phone_No", phoneNumber )
console.log(localStorage.getItem('Phone_No'));
}).catch((error) => {
console.error(error);
alert("Oops! Some error occured. Please try again.")
});
}
else{
alert('Sorry, this mobile number is not registered with us. Please use your registered mobile number.');
}
})
}
onSubmitOTP = (e) =>{
e.preventDefault()
const code = this.state.otp
console.log(code)
window.confirmationResult.confirm(code).then((result) => {
// User signed in successfully.
const Users = result.user;
console.log(JSON.stringify(Users))
this.props.history.push("/home");
}).catch((error) => {
alert("You have entered wrong code")
});
}
render() {
return (
<div className="Main-header">
<img src="./55k-logo.png" alt="Company Logo" style={{height: "80px", width: "200px"}} />
<br/>
<div>
<h2>Login Form</h2>
<p>Limtless Water. From Unlimited Air.</p>
<form onSubmit={this.onSignInSubmit}>
<div id="sign-in-button"></div>
{/* <PhoneInput */}
<label>Mobile Number</label> <br/>
{/* for="phoneNumber" */}
<input type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange}/>
<div className="buttons">
<button type="submit">Submit</button>
</div>
</form>
</div>
<div>
<form onSubmit={this.onSubmitOTP}>
<label >Code</label> <br/>
{/* for="code" */}
<input type="number" name="otp" placeholder="Enter The 6 Digit OTP" required onChange={this.handleChange}/>
<div className="buttons" >
<button type="submit">Submit</button>
</div>
</form>
</div>
</div>
)
}
}
export default Login;
try React Moduel
first install package
npm i react-phone-input-2
second require it on page
import PhoneInput from 'react-phone-input-2'
third import css
import 'react-phone-input-2/lib/style.css'
in side return (
<PhoneInput
country={"in"}
/>
)

How to delete OTP user input on Backspace key press

I've coded a OTP component but it has two issues that I couldn't solve.
The first issue is, when I press Backspace I want to delete the input value and then go to the previous input field to delete it.
The second issue is, when I delete a input (i.e the 3rd box) my code makes me focus on the next input field.
How can I implement Backspace key press properly and go to the previous input field when a input is deleted?
Here's what I've done so far:
import React, { useState } from "react";
import css from '../components/css/OTP.css'
const Axios = require('axios')
const OTP = () => {
const [otp, setOtp] = useState(new Array(4).fill(""));
const handleChange = (element, index) => {
if (isNaN(element.value)) return false;
setOtp([...otp.map((d, idx) => (idx === index ? element.value : d))]);
//Focus next input
if (element.nextSibling) {
element.nextSibling.focus();
}
}
//eğer girilen OTP backendden gelen OTP ile aynıysa matchleştiğini göster ve kullanıcıyı verifike et daha sonra dashboarda aktar.
const checkOTP = async () =>{
try
{
const url = "http://localhost:5000/auth/otp"
const response = await Axios.post(url , {otp} , {withCredentials: true})
if(response.status!==200){
alert("Invalid OTP entry")
}
else{
alert("OTP successful!")
}
}
catch(err)
{
console.log(err)
}
}
return (
<>
<div className="otp-window">
<div className="otp-modal">
<div className="row">
<div className="info">
<p className="otp-info">Please enter the OTP sent to your email</p>
{otp.map((data, index) => {
return (
<input
className="otp-field"
type="text"
name="otp"
maxLength="1"
key={index}
value={data}
onChange={e => handleChange(e.target, index)}
onFocus={e => e.target.select()}
/>
);
})}
<p className="otp-entered">OTP : {otp.join("")}</p>
<p>
<button
className="clear-button"
onClick={e => setOtp([...otp.map(v => "")])}
>
Clear
</button>
<button
className="verify-button"
onClick={e =>
alert("Entered OTP is " + otp.join(""))
}
onClick={checkOTP}>
Verify OTP
</button>
</p>
</div>
</div>
</div>
</div>
</>
);
};
export default OTP;
You can use an input event and check the event.inputType property of the event object like this. You might have to change the onChange prop to onInput though.
if (event.inputType === 'deleteContentBackward' && event.target.value === '') {
// Focus on the previous field
}

Display data from input fields in react

Good afternoon everyone.
I have a dropdown with two input fields inside. Name and Price.
I would like to display the name and price after I click Set button that it appears in the same dropdown but on top of input fields.
Here is how it looks in my app currently, I enter name and price by myself.
As you can see in a first field there is a name and in the second there is a number and I wan't to store it under Price Alert History after clicking Set button.
Here is how I wish it will look. It's just an example which was made in photoshop. The main thing that I want to see name and price on top of input field.
CODE HERE
import React from "react";
import { Button} from "react-bootstrap";
const symbols = [
"ADABTC",
"AIONBTC",
"ALGOBTC",
"ARDRBTC",
"KAVABTC",
"ETHBTC",
"ETCBTC"
];
function PriceTriggerField() {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchSymbol, setSearchSymbol] = React.useState([]);
const handleChangeTerm = event => {
setSearchTerm(event.target.value);
};
const handleChangeSymbol = event => {
setSearchSymbol(event.target.value);
};
React.useEffect(() => {
const results = symbols.filter(symbols =>
symbols.toUpperCase().includes(searchTerm)
);
setSearchSymbol(results);
}, [searchTerm]);
return (
<div className="App">
<h6>Price Alert History</h6>
<input
type="text"
placeholder="Symbol"
value={searchTerm}
onChange={handleChangeTerm}
/>
<input
type="number"
placeholder="Price"
/>
{
searchTerm.length > 0 && searchSymbol.map(item => <li onClick={(() => setSearchTerm(item) )}>{item}</li>)
}
<Button variant="secondary">Set</Button>
</div>
);
}
export default PriceTriggerField;
this is just a simple example with only one variable, but of course, you can do that for as many variables as you wish.
import React, { useState } from "react";
export default function App() {
const [name, setName] = useState(null);
let tmpName;
const onChange = e => {
tmpName = e.target.value;
}
return (
<div className="App">
<input onChange={onChange} />
<button onClick={() => setName(tmpName)}>set</button>
name: {name}
</div>
);
}

How to remove error when cancel button is clicked

I have a form inside which i am showing edit,save and cancel button logically, so initially edit button is visible and all inputs are disabled, and when I click on edit I am making my save and cancel button visible and edit not visible.
So after filling some data when user click on save I am checking the validation like required fields, so if error then user can see.
After then on click of edit if user do not want to save then I am filling the data in site for to the initial values, but if there is error on click of save and I am clicking cancel still the error is there it is not going away,
What I am doing wrong
I think on click when i am filling my formdata to initial value.
if above point is correct then why error is still visible
my code
import React, { useState, useEffect } from "react";
import "./styles.css";
import { useForm } from "react-hook-form";
// mock for useQuery
const useQuery = query => {
const [loading, setIsLoading] = useState(true);
const [data, setData] = useState({});
useEffect(() => {
setTimeout(() => {
setIsLoading(false);
setData({ firstname: "steve", lastname: "smith" });
}, 1000);
}, []);
return { loading, data };
};
export default function App() {
const { register, handleSubmit, errors } = useForm();
const [disabled, setdisabled] = useState(true);
const [editBtn, seteditBtn] = useState(true);
const [initialData, setinitialData] = useState({});
const { loading, data } = useQuery("some qraphql query here"); // getting data from graphql
const [formData, setFormData] = useState({});
useEffect(() => {
setFormData(data);
setinitialData(data);
}, [data]);
const edit = () => {
setdisabled(false);
seteditBtn(false);
};
const cancel = () => {
setFormData(initialData);
setdisabled(true);
seteditBtn(true);
};
const onSubmit = () => {
console.log(formData);
};
const handleChange = e => {
const name = e.target.name;
const value = e.target.value;
console.log(name, value);
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<div className="container-fluid">
<form onSubmit={handleSubmit(onSubmit)}>
{editBtn === true && (
<div align="right">
<button
className="btn white_color_btn"
type="button"
onClick={edit}
>
Edit
</button>
</div>
)}
{editBtn === false && (
<div>
<button className="btn white_color_btn" type="submit">
Save
</button>
<button
className="btn white_color_btn"
type="submit"
onClick={cancel}
>
Cancel
</button>
</div>
)}
<div className="row">
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
id="firstname"
name="firstname"
onChange={handleChange}
value={formData.firstname}
disabled={disabled}
ref={register({ required: true })}
/>
{errors.firstname && (
<span className="text-danger">first name required</span>
)}
<br />
<label htmlFor="emp_designation">First name</label>
</div>
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
id="lastname"
name="lastname"
value={formData.lastname}
onChange={handleChange}
disabled={disabled}
ref={register({ required: true })}
/>
{errors.lastname && (
<span className="text-danger">last name required</span>
)}
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</form>
</div>
);
}
To check the issue follow this points
click on edit -> empty the field -> then click save -> it will throw error -> then click cancel.
on cancel click I want error should go away
Working code codesandbox
The errors are present because they're managed by useForm. The hook exposes a function reset that should fix your problem. Here is an example that leverage the function.
const { register, handleSubmit, reset, errors } = useForm();
// ...
const cancel = () => {
setFormData(initialData);
setdisabled(true);
seteditBtn(true);
reset();
};
A simple pattern is you set errors in state and clear the values of errors object to null or empty upon clicking cancel Button or when a valid input is typed. Here you can initialize errors and reset on cancel button's click. So you should update errors every time input value is changed or cancel button is clicked.

Categories