React-stripe-checkout Getting the shipping address - reactjs

I want to put a property in the schema to receive the delivery address they could help me with what I need to be editing I didn't find it in any documentation, but something like metadata: args but I didn't find a way to put it in the code please help me thanks,
in models / Order.js I would like to register the shipping address in the Order, in the pages / api / checkout.js file when registering the new order:
await new Order ({
user: userId,
email: paymentData.email,
total: cartTotal,
products: cart.products
}). save ();
I would like to have an address field that will receive the delivery address of the checkout stripe form like this:
await new Order ({
user: userId,
email: paymentData.email,
address: "shipping address"
total: cartTotal,
products: cart.products
}). save ();
in the Order model:
import mongoose from 'mongoose';
import Product from './Product';
const {ObjectId, String, Number} = mongoose.Schema.Types;
const OrderSchema = new mongoose.Schema({
user: {
type: ObjectId,
ref: "User"
},
products: [
{
quantity: {
type: Number,
default: 1
},
product: {
type: ObjectId,
ref: Product
}
}
],
email: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
status: {
type: String,
required: true,
default: 'pending',
enum: ["pending", "delivered"]
}
}, {
timestamps: true
});
export default mongoose.models.Order || mongoose.model("Order", OrderSchema);
I want to put a property in the schema to receive the delivery address they could help me with what I need to be editing I didn't find it in any documentation, but something like metadata: args but I didn't find a way to put it in the code please help me thanks
import React from 'react';
import StripeCheckout from 'react-stripe-checkout';
import { Button, Segment, Divider } from 'semantic-ui-react';
import calculateCartTotal from '../../utils/calculateCartTotal';
const CartSummary = ({ products, handleCheckout, success }) => {
const [cartAmout, setCartAmaount] = React.useState(0);
const [stripeAmount, setStripeAmount] = React.useState(0);
const [isCartEmpty, setCartEmpty] = React.useState(false);
React.useEffect(() => {
const {cartTotal, stripeTotal} = calculateCartTotal(products);
setCartAmaount(cartTotal);
setStripeAmount(stripeTotal);
setCartEmpty(products.length === 0)
}, [products]);
return (
<React.Fragment>
<Divider />
<Segment clearing size="large">
<strong>Sub total:</strong>R$ {cartAmout}
<StripeCheckout
name="Conv. Calegari"
amount={stripeAmount}
image={products.length > 0 ? products[0].product.mediaUrl : ""}
currency="BRL"
shippingAddress={true}
billingAddress={true}
locale={'pt'}
zipCode={true}
stripeKey="pk_test_51H9DjUDvVpK1u7JvauE1t2VkVYyda6CGrlVy8az40uO9ELwXmQ3NFghlLIcTJDInZIazUlxLGMuUaMHhRjArCPB9008RvaFo7N"
token={handleCheckout}
triggerEvent="onClick"
>
<Button
icon="cart"
color="green"
floated="right"
content="Checkout"
disabled={isCartEmpty || success}
/>
</StripeCheckout>
</Segment>
</React.Fragment>
);
}
export default CartSummary;
Here I am using the stripe checkout and I need to somehow get this token with the address and save it in order

Related

The PaymentIntent requires a payment method — React, Django Rest

I have a React app and a Django Rest API.
My goal is to get the PaymentRequestButtonElement working.
In my Stripe dashboard (test mode) I get the following logs:
200 OK
POST
/v1/payment_intents
12:22:55 PM
200 OK
POST
/v1/payment_methods
12:22:54 PM
200 OK
POST
/v1/tokens
12:22:53 PM
But in the Payments tab, I get the following:
The PaymentIntent requires a payment method
Here is my React component:
import React, { useState, useEffect } from 'react';
// import { useNavigate } from 'react-router-dom';
// import { useShoppingCart } from 'use-shopping-cart';
import {
PaymentRequestButtonElement,
useStripe,
} from '#stripe/react-stripe-js';
const PaymentRequest = () => {
// const history = useNavigate();
// const { totalPrice, cartDetails, cartCount } = useShoppingCart();
const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
const price = 350;
const handleButtonClicked = (event) => {
// if (!cartCount) {
// event.preventDefault();
// alert('Cart is empty!');
// return;
// }
paymentRequest.on('paymentmethod', handlePaymentMethodReceived);
paymentRequest.on('cancel', () => {
paymentRequest.off('paymentmethod');
});
return;
};
const handlePaymentMethodReceived = async (event) => {
// Send the cart details and payment details to our function.
const paymentDetails = {
payment_method: event.paymentMethod.id,
shipping: {
name: event.shippingAddress.recipient,
phone: event.shippingAddress.phone,
address: {
line1: event.shippingAddress.addressLine[0],
city: event.shippingAddress.city,
postal_code: event.shippingAddress.postalCode,
state: event.shippingAddress.region,
country: event.shippingAddress.country,
},
},
};
const response = await fetch('https://my-api/create-payment-intent/', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// cartDetails,
paymentDetails,
amount: price,
currency: 'usd',
payment_method: 'card'
// automatic_payment_methods: true,
}),
}).then((res) => {
return res.json();
});
if (response.error) {
// Report to the browser that the payment failed.
console.log(response.error);
event.complete('fail');
} else {
// Report to the browser that the confirmation was successful, prompting
// it to close the browser payment method collection interface.
event.complete('success');
// Let Stripe.js handle the rest of the payment flow, including 3D Secure if needed.
const { error, paymentIntent } = await stripe.confirmCardPayment(
response.paymentIntent.client_secret
);
if (error) {
console.log(error);
return;
}
if (paymentIntent.status === 'succeeded') {
console.log('Payment succeeded!');
} else {
console.warn(
`Unexpected status: ${paymentIntent.status} for ${paymentIntent}`
);
}
}
};
useEffect(() => {
if (stripe && paymentRequest === null) {
const pr = stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'Demo total',
//
amount: price,
pending: true,
},
requestPayerName: true,
requestPayerEmail: true,
requestShipping: true,
shippingOptions: [
{
id: 'standard-global',
label: 'Global shipping',
detail: 'Handling and delivery fee',
amount: 350,
},
],
});
// Check the availability of the Payment Request API first.
pr.canMakePayment().then((result) => {
if (result) {
setPaymentRequest(pr);
}
});
}
}, [stripe,
paymentRequest,
// totalPrice
]);
useEffect(() => {
if (paymentRequest) {
paymentRequest.update({
total: {
label: 'Demo total',
amount: 350,
pending: false,
},
});
}
}, [
// totalPrice,
paymentRequest
]);
if (paymentRequest) {
return (
<div className="payment-request-button">
<PaymentRequestButtonElement
options={{ paymentRequest }}
onClick={handleButtonClicked}
/>
--- OR ---
</div>
);
}
return '';
};
export default PaymentRequest;
and here is my Django REST View
class PaymentIntentView(APIView):
def post(self, request, *args, **kwargs):
amount = request.data.get('amount')
currency = request.data.get('currency')
# automatic_payment_methods = request.data.get('automatic_payment_methods')
try:
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
# automatic_payment_methods={
# 'enabled': True,
# },
# You can also add other options like capture_method, setup_future_usage, etc.
)
return Response({'client_secret': intent.client_secret, 'id': intent.id})
except Exception as e:
return Response({'error': str(e)})
I've tried variations of passing automatic_payments as true and passing the payment_method as 'card', no joy
There's a couple of options that you can do in order to fix the problem here.
Option 1: Pass the PM in the backend
When you call fetch on https://my-api/create-payment-intent/ you are passing the paymentDetails that you're not using in your stripe.PaymentIntent.create method. For this to work, you need to first deserialize your request to get access to this information since it's nested (e.g. this guide). Then you need to pass payment_method to the stripe.PaymentIntent.create method. In this option you don't have to change anything in your frontend code.
Option 2: Pass the PM in the frontend
When you call stripe.confirmCardPayment you can pass in the payment_method as explained here. In this option you don't have to change anything in your backend code but you can remove the paymentDetails from the request to your backend.

How to set a value in input automatically

I'm using a hook that fills in my inputs automatically, according to the zip code the user enters. Then the user's address, street, etc are filled in automatically.
However, for the input to be filled in automatically, the component is re-rendering.
As my form is a modal it opens and closes again because of rendering. I need to make the user fill in the zip code, the inputs are filled in real time.
Can you help me with this?
useCEP Hook:
import { useState } from 'react'
import { api } from 'services/apiClient'
interface Cep {
bairro: string
logradouro: string
localidade: string
uf: string
}
export function useCep() {
const [checkCep, setCheckCep] = useState<Cep>()
const getCEP = async (e) => {
const cep = e.target.value.replace(/\D/g, '')
try {
const { data } = await api.get(`https://viacep.com.br/ws/${cep}/json/`)
setCheckCep(data)
} catch (err) {
console.log(err)
}
}
return { checkCep, getCEP }
}
Component:
const { control, formState, register, reset } = useFormContext()
const { checkCep, getCEP } = useCep()
useEffect(() => {
reset({
responsible: [
{
address: checkCep?.logradouro,
district: checkCep?.bairro,
city: checkCep?.localidade,
state: checkCep?.uf,
name: '',
email: '',
student_name: [],
cep: '',
residence: '',
telephone: '',
sex: ''
}
]
})
}, [checkCep])
<Input
name="cep"
type="number"
label="Cep"
{...register(`responsible.${index}.cep`)}
error={errors?.responsible?.[index]?.cep}
onBlur={(e) => getCEP(e)}
/>
{...}
Maybe you can try to use the setValue method instead of reset as it will reset the form.
https://react-hook-form.com/api/useform/setvalue

How to display selected option in select dropdown in React JS edit form

I am using react-hook-form npm module for my edit data form. Here below is my API response sample:
{
UUID: "xxxxxx-bd10-473e-a765-xxxxxx"
address1: "addr1"
address2: "addr2"
address3: ""
city: "xxxxx"
contact: "uxxxxxb"
country: "xxxxxx"
email: "xxxxxx#email.com"
links: {Company: 'xxxxxx-4689-4689-8812-xxxxxxxxx'}
name: "xxxxx"
phone: "xxxxxxxx"
state: "xxxxxxxx"
zip: "11111"
}
Here below is the required code lines from the edit component
import axios from 'axios'
import { useForm } from 'react-hook-form'
// Now getting the default list of all the companies
Edit.getInitialProps = async (context) => {
try {
const companyResponse = await axios(process.env.BASE_URL + '/v1/companies',{
headers : {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + process.env.TOKEN
}
})
if(companyResponse.status == 200) {
return {
companies : companyResponse.data.results
}
} else {
return {companies: []}
}
} catch (error) {
return {companies: []}
}
}
export default function Edit({...props}) {
const [selectedCompany, setSelectedCompany] = useState(0)
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({mode: 'onBlur' });
useEffect(() => {
// Here I am getting location existing value from API response i.e. the above json response
setSelectedCompany(locationDetails.links.Company) // setting state of existing company of location
})
return (
<>
<form className="g-3" onSubmit={handleSubmit(editLocation)} data-testid="editLocationForm">
<select {...register('company', { required: true })} className="form-control">
{(props.companies || []).map((company, index) => (
<option key={index} value={company.UUID} defaultValue={company.UUID === selectedCompany}>{company.name}</option>
))}
</select>
.....
</form>
</>
}
I need to display the existing value of company name as selected in the drop down.
From the react-hook-form documentation you have to use the setValue or reset methods of the hook, the setValue will only set the value of the field you specify, and the reset will reset the whole form and set initial values that you specify
https://react-hook-form.com/api/useform/setvalue
https://react-hook-form.com/api/useform/reset
SetValue Approach
useEffect(() => {
// Here I am getting location existing value from API response i.e. the above json response
setSelectedCompany(locationDetails.links.Company) // setting state of existing company of location
setValue('company', locationDetails.links.Company);
})
Reset approach
useEffect(() => {
// Here I am getting location existing value from API response i.e. the above json response
setSelectedCompany(locationDetails.links.Company) // setting state of existing company of location
reset('company', locationDetails.links.Company);
})

How can we index the keys from nested array of objects to our input set post data?

I'm trying to set course attribute from parameter to my input so that I can be able the insert the value on it but I cannot access the course attribute. ends_level and course becomes any instead of string means I haven't indexed properly the ends_level which the array of object course. I really need your eyes to see something that have missed or missed up. I hope I have explained it well. Thanks in advance y'all.
import { React, useState, useEffect } from 'react';
import Button from 'react-bootstrap/Button';
import { TextField } from '#material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { updateProfile } from '../../../actions/profile';
const Profile = ({ data }) => {
const dispatch = useDispatch();
const currentId = data._id;
const [postData, setPostData] = useState(
{
profile: {
name: "",
},
skills: [
{
end: "",
ends_level: [
{
course: "",
level: ""
}
]
},
],
}
);
const profile = useSelector((state) => currentId ? state.profile.find((p) => p._id === currentId) : null);
useEffect(() => {
if(profile) setPostData(profile);
}, [profile])
const handleSubmit = (e) => {
e.preventDefault();
if(currentId) {
dispatch(updateProfile(currentId, postData));
}
}
return (
<form onSubmit={handleSubmit}>
<TextField
value={postData.profile.name}
onChange={(e) =>
setPostData(
{
...postData, profile:
{
...postData.profile, name: e.target.value
}
}
)
}
/>
<TextField
onChange={(e) =>
setPostData(
{
...postData, skills:
{
...postData.skills, ends_level:
{
...postData.skills.ends_level, course: e.target.value
}
}
}
)
}
/>
<Button variant="primary" size="md" type="submit" block >Save</Button>
</form>
);
}
export default Profile;
The problem seems to be you are using spread operators to spread objects into positions where you originally had arrays. Also when trying to sort out an answer, I feel like you aren't addressing a plurality problem with these arrays. Namely, you have a single target value which may apply to an array full of skills and an array full of ends_levels in each skill, so you have to decide how/what you want the new changed to value to apply to.
Here is a good start, with TypeScript enhancements, that lays out the schema and attempts to craft a new "post data" object which looks like the old one. You will have to fill out the commented section below to affect the array(s) with the new value as you see fit.
interface SkillType {
end: string,
ends_level: [
{
course: string,
level: string
}
]
}
interface DataType {
profile: {
name: string
},
skills: SkillType[]
}
let postData: DataType = {
profile: {
name: "",
},
skills: [
{
end: "",
ends_level: [
{
course: "",
level: ""
}
]
},
],
};
// Create a new holder with all the old skills
let newSkills : SkillType[] = [... postData.skills ];
// Will have multiple skills per post-data
// Each post-data will have multiple "ends_levels" in it
// Do you want to merge it so all of these blocks now have the new value e.target.value ??
// If so, manipulate newSkills
let newPostData: DataType = {
...postData,
skills: newSkills
}

add inputs dynamically react redux

hello I will ask this question again because I still can’t find a answer
I try to create a form similar to google form with react and redux
each question is represented by: QuestionCard
contains a title and can have several types (short question, long question, choice ,multiple, single choice ...)
I manage to add cardQuestion and delete them
my problem is that when I select single choice I want to give the user the possibility to add the number of choices he wants but it does not work
this is my reducer
const initialState = {
formId: '',
form: {
title: '',
description: ''
},
basicForm: {
fullName: '',
age: '',
saved: false
},
cardQuestion: [{
title: '',
choix: [],
type: '',
}],
error: "",
loading: false,
}
const addRequestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_STATE':
return { ...state, ...action.payload }
default:
return state
}
}
i use a global state
and this how i try to add choices
1- i handle change of inputs (each choice) like this :
const { cardQuestion } = useSelector((state) => state.addRequest)
const choice = cardQuestion.map(e => e.choix)
const onChangeInput = (i, key, value) => {
console.log(value)
dispatch({
type: 'SET_STATE',
payload: {
cardQuestion: cardQuestion.map((elem, index) => {
console.log(i)
console.log(i === index)
if (i !== index) return elem
return { ...elem, [`${key}`]: value }
})
}
})
}
2- use the ChangeInptut like this
<div >{choice.map((elem, i) => <div key={i}>
<Input value={elem.choix} onChange={(e) => onChangeInput(i, 'choix', e.target.value)} />
</div>
3- button to add new input (choice)
<Button onClick={() => dispatch({
type: 'SET_STATE',
payload: {
cardQuestion: [...cardQuestion, { choix: '' }]
}
})} >Add</Button>
</div>
but it give me results like this :
and when i add a choice in question card1 it is also added in questioncard2
how can i resolve this , any help will be appreciated
This
cardQuestion: [...cardQuestion, { choix: '' }]
adds a new card without a title or type to the list of cards. If I understand you correctly, you want to add a new choice to an existing card, which will look something like
const newCards = [...cardQuestion]
newCards[currentCardPosition] = {...newCards[currentCardPosition]}
newCards[currentCardPosition].choix = [...newCards[currentCardPosition].choix, '' ]
but I don't see where currentCardPosition will come from

Resources