#florin_best-practices

1 messages ยท Page 1 of 1 (latest)

hybrid ravenBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1313835078450937938

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

bitter ore
#

Hi

Does stripe provide a dropdown component with the list of countries and these codes?
I'm affraid no, but have you had the chance to check Stripe Address Element ?

past warren
wispy mantle
#

๐Ÿ‘‹ taking over for my colleague. Let me catch up.

hybrid ravenBOT
wispy mantle
#

do you need to update the billing or the shipping details?

past warren
#

I need to update both of them

#

this is my billing address form for example

wispy mantle
#

we will collect all the details for you

past warren
#

can I not force all the fields to be required? I see that I can only set the phone field to be required always

wispy mantle
#

only the country is required in the Address

past warren
#

yea, I wanted to be required for my app, since I am using a custom checkout

wispy mantle
#

all the fields with a ? following their name are not required

#

you can do that by doing your own validation

past warren
#

I see that if I only provide a single country in the allowedCOuntries array the dropdown wont show, so I guess the value for it will be that specific country

wispy mantle
#

in that case you can pass it in the defaultValues as well

#

so it will get pre-populated

past warren
#

hmm yea, my issue is that my app will only ship to the UK, but UK consists of 4 countries, so my users will basically have to specify their Town / City without specifying the exact country

#

actually, it's fine I only need to specify the UK*

wispy mantle
#

yes that's correct

past warren
#

hmm so I am doing something wrong, I have a component which renders the AddressElement alongside with other logic. I am rendering this component twice each having a type of either shipping or billing.

This is the AddressElement inside <AddressElement
options={{
mode: type,
fields: { phone: 'always' },
...(type === 'shipping' && {
allowedCountries: ['GB'],
defaultValues: {
address: {
country: 'GB',
},
},
}),
validation: {
phone: { required: 'always' },
},
}}
/>

and inside the component I have a test handler in which I am trying to get the values of the element.

const testHandler = async () => {
if (!elements) {
return;
}
console.log('handler');

console.log(await elements.getElement('address', { mode: type }));
const addressElement = elements.getElement('address', { mode: type });
addressElement
  ?.getValue()
  .then(function (result) {
    if (result.complete) {
      console.log(result.value);
      console.log(result.complete);

      // Allow user to proceed to the next step
      // Optionally, use value to store the address details
    } else {
      console.log({ result });
    }
  })
  .catch((error) => {
    console.log(error);
  });

};

My issue is that I can only see the data on the element of type shipping, and not of the type billing

#

type: 'shipping' | 'billing';

hybrid ravenBOT
sacred quarry
#

to be clear, you can only have one instance of <AddressElement> per <Elements> component.
when you say "My issue is that I can only see the data on the element of type shipping, and not of the type billing", what specifically are you seeing with getValue?

past warren
#

{
"result": {
"isNewAddress": true,
"complete": false,
"value": {
"name": "dsasadsda",
"phone": "+447400123456",
"address": {
"line1": "dsadad",
"line2": null,
"city": "",
"country": "GB",
"postal_code": "E1 6AN",
"state": ""
}
}
}
}

sacred quarry
#

and what did you expect to see instead?

past warren
#

so basically I have created a custom AddressList component, this component receives a type prop type AddressListProps = {
addresses: ShippingAddressType[] | BillingAddressType[];
formData: ShippingAddressType | BillingAddressType;
onSubmit: () => void;
onChange: (key: keyof BillingAddressType | keyof ShippingAddressType, value: string) => void; // Modified type
onSelect: (address: ShippingAddressType | BillingAddressType) => void;
selectedAddress?: ShippingAddressType | BillingAddressType;
type: 'shipping' | 'billing';
loading: boolean;
};

and based on that type I am creating the <AddressElement
options={{
mode: type,
fields: { phone: 'always' },
...(type === 'shipping' && {
allowedCountries: ['GB'],
defaultValues: {
address: {
country: 'GB',
},
},
}),
validation: {
phone: { required: 'always' },
},
}}
/>

#

and in the test handler I want to see the form data for each address element const testHandler = async () => {
if (!elements) {
return;
}
console.log('handler');

console.log(await elements.getElement('address', { mode: type }));
const addressElement = elements.getElement('address', { mode: type });
addressElement
  ?.getValue()
  .then(function (result) {
    if (result.complete) {
      console.log(result.value);
      console.log(result.complete);

      // Allow user to proceed to the next step
      // Optionally, use value to store the address details
    } else {
      console.log({ result });
    }
  })
  .catch((error) => {
    console.log(error);
  });

};

sacred quarry
#

I'm with you so far in terms of context, I think, but not following what specific issue you're running into and what result you're getting versus what you expect to get

past warren
#

I can only get the value of the shipping address Element

#

the billing address element is null

sacred quarry
#

when you say it's null, what specific variable is null that you're expecting not to be? The return value of elements.getElement ? One of the parameters returned from getValue() ? Something else?

past warren
#

the return fo elements.getElement

#

the first console log is from the billing address element and the second one is from the shipping one

sacred quarry
#

ok. Well remember what I said, you can only have one AddressElement per instance of Elements. Maybe the elements variable is not the right one.

past warren
#

hmm, but doesn't this mean that I have 2 different instances of Elements?

const AddressList = ({
addresses,
formData,
onSubmit,
onChange,
onSelect,
selectedAddress,
type,
loading,
}: AddressListProps) => {
const [showAddressesModal, setShowAddressesModal] = useState(false);
const [showAddAddressForm, setShowAddAddressForm] = useState(false);

const onAddNewAddress = () => {
setShowAddAddressForm(true);
setShowAddressesModal(false);
};
const elements = useElements();

const testHandler = async () => {
if (!elements) {
return;
}
console.log('handler');

console.log(elements.getElement('address', { mode: type }));

const addressElement = elements.getElement('address', { mode: type });
addressElement
  ?.getValue()
  .then(function (result) {
    if (result.complete) {
      console.log(result.value);
      console.log(result.complete);

      // Allow user to proceed to the next step
      // Optionally, use value to store the address details
    } else {
      console.log({ result });
    }
  })
  .catch((error) => {
    console.log(error);
  });

};

as I instantiate it in each AddressList component?

sacred quarry
#

you don't instantiate it, you're getting a reference to it from the useElements hook. That gets the instance from the "StripeProvider" that is higher up in the component tree,
<Elements options=foo>
<ComponentThatUsesAddressElement> // useElements called inside here gets the elements instance with foo options
</Elements>

<Elements options=bar>
<ComponentThatUsesAddressElement> // useElements called inside here gets the elements instance with bar options
</Elements>

past warren
#

so what can I do to have 2 instances of elements here ?

sacred quarry
#

structure it like I have there, with multiple <Elements> components

past warren
#

ok, so I wrapped both of my AddressLists in <Elements stripe={stripePromise}>
but I also have <Elements stripe={stripePromise}> in the root file of my app, is that a problem?

#

it seems that the address elements are now working

sacred quarry
#

that would probably have been a problem , possibly

past warren
#

so should I do anything with the Elements tag I have wrapped around my root ? I was using it for the CardElement, but now that I added 2 more elements tags around my addresses lists, can anything go bad?

sacred quarry
#

it really depends. Normally I would say you should have the AddressElement and the PaymentElement in the same <Elements> so that the PaymentElement automatically pulls in the address entered and submits it with the payment, but that logic AFAIK doesn't exist for the legacy CardElement. So I suppose it doesn't matter.

past warren
#

I am passing the address when I am using the card element, so I think that shouldn't be an issue ?

#

const { error: stripePaymentError, paymentIntent } = await stripe.confirmCardPayment(
clientSecret,
{
payment_method: selectedPaymentMethod
? selectedPaymentMethod.id
: {
card: cardElement,
billing_details: {
name: billingDetails.name,
address: {
city: billingDetails.city,
country: billingDetails.country,
line1: billingDetails.address,
postal_code: billingDetails.postalCode,
state: billingDetails.state,
},
phone: billingDetails.phoneNumber,
},
},
}
);

sacred quarry
#

yes if you're just manually pulling the values into your own variables and passing those manually into the API call for confirmCardPayment then it doesn't matter

past warren
#

I don't know what happened, but now it stopped working.

const submitHandler = async () => {
if (!elements) {
return;
}

console.log('here');

const addressElement = elements.getElement('address', { mode: type });

if (!addressElement) {
  return;
}

console.log('asdsad');
console.log('1234', await addressElement.getValue());
console.log('here2', addressElement);
const result = await addressElement.getValue();

console.log('here3');

console.log(result);
if (!result?.complete) {
  return;
}

console.log('ajunge aici?');
const stripeAddress = result.value;
console.log('abc', stripeAddress);
onSubmit({
  address: {
    name: stripeAddress.name,
    city: stripeAddress.address.city,
    country: stripeAddress.address.country,
    state: stripeAddress.address.state,
    phoneNumber: stripeAddress.phone ?? '',
    postalCode: stripeAddress.address.postal_code,
    address: stripeAddress.address.line1,
  },
});

};

basically when I am calling the .getValue() method everything stops there, and it doesn't seem to throw an error since I wrapped it in a try catch block.

#

I can see that the element is here

sacred quarry
#

might be easier to open a support case with a minimal reproduction of the problem

past warren
#

how do I do that

sacred quarry
#

ideally put together a simple page on something like Codesandbox or just your own site that reproduces the problem using the multiple <Elements>, and open a support case at https://support.stripe.com/?contact=true sharing a link to that and a description of the problem. Otherwise I can try to help here, it's just getting extremely difficult and abstract without seeing this for myself in my own browser and seeing all the code

past warren
#

hmm, maybe let's try one last time? In my Checkout page I have 2

<Elements stripe={stripePromise}>
<AddressList
addresses={user?.user_shipping_addresses || []}
onSubmit={addShippingAddressMutate}
onSelect={setSelectedShippingAddress}
selectedAddress={selectedShippingAddress}
type="shipping"
loading={addShippingAddressPending}
/>
</Elements>

<Elements stripe={stripePromise}>
<AddressList
addresses={user?.user_billing_addresses || []}
onSubmit={addBillingAddressMutate}
onSelect={setSelectedBillingAddress}
selectedAddress={selectedBillingAddress}
type={'billing'}
loading={addBillingAddressPending}
/>
</Elements>

#

and inside the AddressList component I have this submitHandler
const submitHandler = async () => {
try {
if (!elements) {
return;
}

  console.log('here');

  const addressElement = elements.getElement('address', { mode: type });

  if (!addressElement) {
    return;
  }

  // this is where everything stops, no error is being thrown, the function just stops
  const result = await addressElement.getValue();

  console.log('here');

  console.log(result);
  if (!result?.complete) {
    return;
  }

  const stripeAddress = result.value;
  onSubmit({
    address: {
      name: stripeAddress.name,
      city: stripeAddress.address.city,
      country: stripeAddress.address.country,
      state: stripeAddress.address.state,
      phoneNumber: stripeAddress.phone ?? '',
      postalCode: stripeAddress.address.postal_code,
      address: stripeAddress.address.line1,
    },
  });
} catch (error) {
  console.error(error);
}

};

#

all I did was to remove some unrelated code, and now the addressElement.getValue stops the execution of the submit handler

#

it worked before

sacred quarry
#

maybe add the "unrelated code" back and slowly remove parts again until finding where the difference is?
maybe capture an event variable in the submit handler and call event.preventDefault() in submitHandler

I can't think of any specific reason for getValue to "crash", if that is what's happening I'd need to look at an actual reproduction to figure out what's happening

hybrid ravenBOT
past warren
#

I managed to fix it, it was clearly a mistake on my part, I was hiding the modal by mistake and it would stop the submission. sorry!