#walter_best-practices

1 messages ยท Page 1 of 1 (latest)

frigid spruceBOT
#

๐Ÿ‘‹ 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/1341216678461308930

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

formal oak
livid gazelle
#

hello! Can you share more on what specifically you're confused about?

formal oak
#

If there's no call to confirmPayment within the express checkout confirm event, we're seeing card errors in the payment element once elements.submit() is called

#

Our goal is to allow users to enter payment details via either the ECE or PE, and on the same page, show a show a cost breakdown.

livid gazelle
#

If there's no call to confirmPayment within the express checkout confirm event, we're seeing card errors in the payment element once elements.submit() is called - what's the specific error message you're seeing?

formal oak
#

after entering payment details the users would be able to add promo codes or change their billing address which would change the total cost shown in the price breakdown

#

The specific error is incomplete_number

#

Which is sort of true since the payment element is empty, but the user has already provided their payment details via the ECE

#

We're using a single elements instance currently

livid gazelle
#

can you share the relevant code snippets for how you're implementing things?

formal oak
#

sure, hang on

#

` const options = {
mode: 'subscription',
amount: this.product.price,
currency: 'usd',
setupFutureUsage: 'off_session',
}
// Set up Stripe.js and Elements to use in billing form.
this.elements = this.stripe.elements(options)

    // this.mountAddressElement()
    this.mountExpressPayElement()
    this.mountPaymentElement()`
#

Mounting the payment element...
`mountPaymentElement() {
const paymentElement = this.elements.create('payment', {
fields: {
billingDetails: 'never',
},
})

  paymentElement.on('ready', () => {
    if (this.elements.getElement('address')) {
      paymentElement.collapse()
    }
  })

  paymentElement.on('loaderror', function (event) {
    this.handleError(event.error)
  })

  paymentElement.on('change', (event) => {
    console.log('payment element change event: ', event)
    if (event.complete) {
      this.paymentCollected = true
      this.elements.getElement('expressCheckout').destroy()
      // $nextTick is needed to ensure computed properties are updated before calling mountAddressElement
      this.$nextTick(async () => {
        if (!this.elements.getElement('address')) {
          this.mountAddressElement()
        } else {
          const { error: submitError } = await this.elements.submit()
          if (submitError) {
            this.handleError(submitError)
            return
          }
        }
        this.mountExpressPayElement()
      })
    }
  })

  paymentElement.mount('#payment-element')
}`
livid gazelle
#

gimme a while to look into this

formal oak
#

` mountExpressPayElement() {
const expressCheckoutOptions = {
buttonType: {
applePay: 'buy',
googlePay: 'plain',
paypal: 'buynow',
klarna: 'pay',
},
paymentMethods: {
applePay: 'always',
googlePay: 'always',
},
}

  const expressCheckoutElement = this.elements.create(
    'expressCheckout',
    expressCheckoutOptions
  )

  expressCheckoutElement.on('click', (event) => {
    ...
  })

  expressCheckoutElement.on('confirm', async (event) => {
    this.makingPurchaseRequest = false
    this.paymentCollected = true
    this.address = event.billingDetails.address
    this.elements.getElement('payment').destroy()

    // Create a confirmation token for use summarizing the payment details later
    const { error, confirmationToken } =
      await this.stripe.createConfirmationToken({
        elements: this.elements,
        params: {
          payment_method_data: {
            billing_details: {
              name: event.billingDetails.name,
              address: event.billingDetails.address,
            },
          },
        },
      })
    this.stripeConfirmationToken = confirmationToken
    // $nextTick is needed to ensure computed properties are updated before calling mountAddressElement
    this.$nextTick(async () => {
      if (!this.elements.getElement('address')) {
        this.mountAddressElement(event.billingDetails.name)
      } else {
        // Trigger form validation and wallet collection
        const { error: submitError } = await this.elements.submit()
        this.mountPaymentElement()
        if (submitError) {
          ...
        }
      }
    })

    if (error) {
      ...
    }
  })

  expressCheckoutElement.mount('#express-checkout-element')
}`
#

Currently, in an attempt to solve the issue with form validation errors appearing, we're destroying the payment element within ECE confirm event.

#

It is later re-mounted and collapse() is called, which hides the errors, but this is seeming like a not great solution that shouldn't be needed.

livid gazelle
#

this is likely going to take me a while to test out, would you prefer to write in via a ticket then I'll get back to you on the ticket instead so you don't have to wait around?

formal oak
#

I've had a support ticket open for slightly more than a week now with no help or solution ๐Ÿ˜…

#

I don't mind waiting if it means I get answers haha

livid gazelle
formal oak
#

Is this a public chat?

#

I'm not sure I should share my account id if so

#

I'd be happy to forward the email thread with support to you if I had your email

#

Or if you had a private method I could share my account id that isn't searchable here in discord

livid gazelle
#

it is a public thread, the account id isn't sensitive info, but let me see how to enable DMs so that you can send that info to me

#

can you try and see if you can send me a DM now?

formal oak
#

I tried

livid gazelle
#

okay, i found your support ticket too, I'm going to take over this ticket too

#

going back to looking into how this should be implemented

formal oak
#

Awesome thanks!

#

Let me know if you have any questions

livid gazelle
#

I very roughly threw this together, and i didn't run into that error you mentioned

#

checkout.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Accept a payment</title>
    <meta name="description" content="A demo of a payment on Stripe" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="checkout.css" />
    <script src="https://js.stripe.com/v3/"></script>
    <script src="checkout.js" defer></script>
     <!-- <script src="checkout-payment-element.js" defer></script> -->
  </head>
  <body>
    <form id="payment-form">
      <div id="express-checkout-element">
        <!-- Elements will create form elements here -->
      </div>
      <div id="payment-element">
        <!-- Elements will create form elements here -->
      </div>
      <button id="submit" >Submit</button>
      <div id="error-message">
        <!-- Display error message to your customers here -->
      </div>
    </form>
  </body>
</html>
#

checkout.js

const stripe = Stripe("pk_test_...");

const options = {
  mode: 'subscription',
  amount: 1099,
  currency: 'usd',
  // Fully customizable with appearance API.
  paymentMethodCreation : 'manual',
  appearance: {/*...*/},
};

// Set up Stripe.js and Elements to use in checkout form
const elements = stripe.elements(options);

// Create and mount the Express Checkout Element
const expressCheckoutElement = elements.create('expressCheckout');
expressCheckoutElement.mount('#express-checkout-element');

// Create and mount the Payment Element
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');

// const form = document.getElementById('express-checkout-element');
// const submitBtn = document.getElementById('submit');

const handleError = (error) => {
  const messageContainer = document.querySelector('#error-message');
  messageContainer.textContent = error.message;
}

expressCheckoutElement.on('confirm', async (event) => {

  const {error: submitError} = await elements.submit();
  if (submitError) {
    handleError(submitError);
    return;
  }

  stripe
  .createConfirmationToken({
    elements,
    params: {
      payment_method_data: {
        billing_details: {
          name: 'Jenny Rosen',
        },
      },
    },
  })
  .then(function(result) {
    // Handle result.error or result.confirmationToken
    console.log(result);
  });
});

paymentElement.on('confirm', async (event) => {

  const {error: submitError} = await elements.submit();
  if (submitError) {
    handleError(submitError);
    return;
  }

  stripe
  .createConfirmationToken({
    elements,
    params: {
      payment_method_data: {
        billing_details: {
          name: 'Jenny Rosen',
        },
      },
    },
  })
  .then(function(result) {
    // Handle result.error or result.confirmationToken
    console.log(result);
  });
});
formal oak
#

Hmm, at first glance that looks the same as what I have.

livid gazelle
#

i don't see your payment element mentioned in the code snippet you shared though

formal oak
#

I'll dig in tomorrow to figure out what's different it's late here and I need to sign off

#

Erm it's there in the mountPaymentElement function

livid gazelle
#

alright! Maybe take some time to compare mine against yours, feel free to reach out again on Discord, you'll start a new thread since this one will be closed when you come back after

formal oak
#

What are the hours for the discord? 9-5 SGT?