#naveed-web3auth_webhooks

1 messages ยท Page 1 of 1 (latest)

radiant pierBOT
#

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

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

meager cargo
#

Hi there ๐Ÿ‘‹ please give me a moment to catch up

viral kelp
#

This is the affected customer cus_T1Onjg2FvQ2D6L

#

their webhook history isnt long so you can see the events chronologically

meager cargo
#

I see, the two events you shared had different payment intent IDs
evt_3SltZmImFOsfVEtU3Vz1DNdV - pi_3SltZmImFOsfVEtU3bxKc42i
evt_3SltYjImFOsfVEtU1SxYgBwR - pi_3SltYjImFOsfVEtU1H6UQHU8

#

Both payment intents were created from different subscriptions. Based off the API logs, it looks like your server created two Subscriptions for this customer.

#

Give me awhile, I can share the API request ID that can be viewed in the Dashboard

#

For pi_3SltZmImFOsfVEtU3bxKc42i

For pi_3SltYjImFOsfVEtU1H6UQHU8

#

The two subscriptions above came from a request made by your backend server. They weren't created by Stripe automatically. In this case, you would want to check your web payment flow about the possibility of a customer having two payment session available. For example, the customer may have two browser tabs opened for payment and mistakenly checkout twice.

viral kelp
#

hmm yes i see that the two requests are a minute apart

#

so its not related to the failure recovery email?

#

I have a few more examples of customers who faced this problem, e.g. cus_QHbLr9X8l4VzoA

#

in this case the gap between the subscription.created events is much larger

#

evt_1SepoqImFOsfVEtUDJXOmLcA and evt_1Seq7mImFOsfVEtU8jFl7Hyw
Around 10 minutes apart

meager cargo
#

I see that a payment failure email notification was sent to cus_T1Onjg2FvQ2D6L on Jan 4, but the email was never clicked. Did the customer said they updated their payment method via the email?

viral kelp
#

No i dont have any info on that

#

oh interesting so the email wasnt clicked? Do you know that?

meager cargo
#

I can check cus_QHbLr9X8l4VzoA too, give me a moment

#

Yes we have records whether the recipient interacted with the email. In our logs, I only see that the email on Jan 4 was delivered

viral kelp
#

Finally one more customer, this is the most serious case with 3 subscriptions created cus_TeCIvOK2oCX8Ei

#

the ones spaced only a few seconds apart are no doubt probably due to something perhaps being done on the frontend

#

idk if its multiple calls to my create subscription point or something

#

Could you recommend me a test card to try so that I can try and reproduce it on my end

#

All these flows seem to have in common is a card failure

meager cargo
viral kelp
#

So the most likely cause would be some sort of frontend issue eh

#

im not sure if multiple tabs would be the cause idk if a user would actually do something like that

meager cargo
#

Is your frontend returning any error message that notifies the user of the payment failure?

viral kelp
#

code is something like this

if (!this.creditCard && this.stripeElements) {
          await billingModule.addCard({ stripeElements: this.stripeElements, teamId: this.selectedTeamId });
        }
        const subscriptionPlan = this.getPlanWithInterval(this.newPlan);
        let result;
        if (this.hasSubscription) {
          result = await billingModule.updateSubscription({
            teamId: this.selectedTeamId,
            subscriptionPlan,
          });
        } else {
          result = await billingModule.createSubscription({
            teamId: this.selectedTeamId,
            subscriptionPlan,
          });
        }
        /**
         * handling (double) 3d secure scenario
         * Normally it should already be handled during card confirmSetup above
         * But for some cards we face the situation where we need to confirm
         * 3d secure again for the transaction itself
         *
         * Most other cards do not require further 3d secure confirmation after
         * the initial card setup
         */
        if (result.client_secret) {
          const stripeClient = getStripeClient();
          const { error } = await stripeClient.confirmPayment({
            clientSecret: result.client_secret,
            redirect: "if_required",
          });
          if (error) throw error;
        }
radiant pierBOT
viral kelp
#

i first add card

#

and then create sub

woven dagger
#

๐Ÿ‘‹ Hi there! I'm taking over for my colleague

#

Where does hasSubscription come from in this case?

#

But since aure asked about errors, it looks like you're throwing an exception if there's a payment failure?

#

Does this result in a message to the user? Or what happens here?

viral kelp
#

yes, but thus far it seems that it did not go to the throw

#

because those errors wouldve been sent to sentry

#

but i dont see any of those

#

has subscription is from server fetching on mount

#
hasSubscription() {
      return Boolean(this.subscription && this.subscription?.customer && this.subscription.customer?.subscriptions.length > 0);
    },
#

i think it would not have any subscription until success

#

as our webhook handler only writes to db on success

#
func (s *billingService) handleEventSubscriptionCreated(tx *gorm.DB, event stripe.Event) error {
    // only process active status
    subscriptionStatus := event.Data.Object["status"].(string)
    if subscriptionStatus != string(stripe.SubscriptionStatusActive) && subscriptionStatus != string(stripe.SubscriptionStatusPastDue) {
        return nil
    }
#

so it would ignore if subscription status isnt active in webhook handler

woven dagger
#

What happens when you use a test card to simulate a failed payment?

viral kelp
#

should i refactor my payment code actually

#

am i doing things in a suboptimal way?

woven dagger
#

If you can see that your backend is sending multiple Subscription creation requests to Stripe, then that's not optimal

#

It's not clear to us exactly what sequence of events happens

#

If you're saying that you see problems with extra subscriptions being created when there's a payment failure, this is something you should be able to test using the test cards, and then see what API calls you're making

viral kelp
#

what i notice as a pattern thus far is customer is able to set up card. Then step 2 the payment fails.

#

Hmm i think what i need to do is maybe store the incomplete subscription* in memory somewhere

#

cuz i think that after payment failure when they try again with a working card a new sub gets created

#

i mean i dont think theres an issue with that per se

#

we can just let the older incomplete sub to expire naturally

#

idk but that payment intent attached to that previous sub also gets confirmed

woven dagger
#

If a payment fails, you should just be showing the error.message you get from confirming the payment. The customer should then be able to retry. You don't need to create a new Subscription or Payment Intent

viral kelp
#
async confirmSubscription() {
      try {
        this.paymentError = "";
        this.confirmingSubscription = true;
        if (!this.creditCard && this.stripeElements) {
          await billingModule.addCard({ stripeElements: this.stripeElements, teamId: this.selectedTeamId });
        }
        const subscriptionPlan = this.getPlanWithInterval(this.newPlan);
        let result;
        if (this.hasSubscription) {
          result = await billingModule.updateSubscription({
            teamId: this.selectedTeamId,
            subscriptionPlan,
          });
        } else {
          result = await billingModule.createSubscription({
            teamId: this.selectedTeamId,
            subscriptionPlan,
          });
        }
        /**
         * handling (double) 3d secure scenario
         * Normally it should already be handled during card confirmSetup above
         * But for some cards we face the situation where we need to confirm
         * 3d secure again for the transaction itself
         *
         * Most other cards do not require further 3d secure confirmation after
         * the initial card setup
         */
        if (result.client_secret) {
          const stripeClient = getStripeClient();
          const { error } = await stripeClient.confirmPayment({
            clientSecret: result.client_secret,
            redirect: "if_required",
          });
          if (error) throw error;
        }

      } catch (e) {
        log.error(e);

        // analytics
        
        this.paymentError = (e as Error).message;
      }
#

i set the payment error state upon failure

#

is there a way to add card and subscribe in one flow?

#

instead of doing what im doing here where i set up card and then save the card as default method?

woven dagger
#

You can create a Subscription like this for example:

const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{
    price: priceId,
  }],
  payment_behavior: 'default_incomplete',
  payment_settings: { save_default_payment_method: 'on_subscription' },
  expand: ['latest_invoice.confirmation_secret'],
});

If you pass the subscription.latest_invoice.confirmation_secret.client_secret to the frontend and use that in the Payment Element, it will attach the card to the customer.

radiant pierBOT
viral kelp
#

I wonder if it has something to do with redirecting?

#

redirect due to 3d secure?

#

how can i test that out?

elfin bronze
#

hi! I'm taking over this thread.

#

can you clarify your question?

viral kelp
#

how can I test out the redirect url flow for 3d secure

#

i have a feeling it may be the cause behind duplicate subscriptions being created for customers

elfin bronze