#monarch_unexpected

1 messages · Page 1 of 1 (latest)

earnest plankBOT
#

👋 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/1372822515193221120

📝 Have more to share? Add more details, code, screenshots, videos, etc. below.

devout lake
#

Hi there, I don't understand the question. Can you rephrase?

rugged cloak
#

Hey, sure, just a min

#

I am in the process of building a subscription system that operates as follows: when a user initially signs up, I create a customer profile and assign them a subscription with a $0 cost. After the user logs in, they have the option to upgrade their subscription to one of two paid plans: $49 or $249.

To handle the payment flow, I am utilizing React’s payment elements to display the payment form and invoice. I rely on the payment_succeeded event in webhooks to trigger the subscription upgrade process. This setup works fine when a user completes the payment.

However, I've encountered an issue when a user attempts to upgrade their plan. The payment form appears with an option to cancel the upgrade. If the user decides to cancel, the current invoice is marked as void, which seems to be functioning as expected.

The problem arises when the user tries to upgrade again after canceling. In this case, the system automatically updates the subscription plan without prompting the user to make a new payment, due to "unused time" from the previous subscription. I have attached the invoice for reference. This is not the behavior I intend to have.

My question is: Is this behavior expected based on the current flow I’ve implemented? Or do I need to adjust the process in some way to ensure that the subscription plan is only upgraded after a successful payment, regardless of any unused time left on the previous plan?

devout lake
#

Ok, I think I understand you now. You want to avoid the prorated credits added to the customer as a result of subscription cancellation? Am I right?

rugged cloak
#

Yes exactly

#

What am I doing wrong ?

devout lake
#

Can you share with me the code that you wrote to cancel a subscription?

rugged cloak
#

Yes

#
    req: Request,
    res: Response,
): Promise<void> => {
    const { id } = req.params;
    console.log(
        `Controller: voidInvoiceHandler called for ID: ${id} with body:`,
        req.body,
    );
    try {
        const params: Stripe.InvoiceVoidInvoiceParams = req.body; // Body might be empty
        const invoice = await stripeService.voidInvoice(id, params);
        res.status(200).json(invoice);
    } catch (error: any) {
        console.error(`Error in voidInvoiceHandler for ID ${id}:`, error);
        res.status(error.statusCode || 500).json({
            message: "Failed to void invoice",
            error: error.message,
        });
    }
}; ```
#
  async voidInvoice(
    invoiceId: string,
    params?: Stripe.InvoiceVoidInvoiceParams,
  ): Promise<Stripe.Invoice> {
    return this.stripe.invoices.voidInvoice(invoiceId, params);
  }
#

I use this code to void the invoice not technically cancelling the subscription itself, that is used in downgrades, which works fine

devout lake
#

This is just to void the invoice, what triggers the subscription cancellation?

rugged cloak
#
export const cancelSubscription = async (
    req: Request,
    res: Response,
): Promise<void> => {
    console.log("Controller: cancelSubscription called for ID:", req.params.id);
    try {
        const { id } = req.params;
        const subscription = await stripeService.cancelSubscription(id);
        res.status(200).json(subscription);
    } catch (error: any) {
        console.error("Error in cancelSubscription:", error);
        res.status(500).json({
            message: "Failed to cancel subscription",
            error: error.message,
        });
    }
};

I only use this for downgrades

#

When I say cancel I mean a cancel button on my payment form, that just voids the invoice, since user did not go through the payment

devout lake
#

Ok, can you share with me an ID of an sample subscription that was canceled ?

rugged cloak
#

sub_1RPFoGQ5lokX8cUNsVrJZSpZ

#

Also I am using cancelSubscription to donwgrade subscription which works fine

#

when I click the cancel button the voidInvoice logic is called

devout lake
#

Hmm, the subscription that you provided is still active

rugged cloak
#

yes but I am not cancelling the subscription

#

I am just voiding the invoice

#

that is what i want, you can watch the video to understand better

devout lake
#

Thanks for the video/

rugged cloak
#

What to do when user clicks cancel button just update ui ? no changes/requests in backend ?

earnest plankBOT
flat holly
#

hi! I'm taking over this thread.

#

can you clarify your question? what exactly are you trying to achieve here?

rugged cloak
#

Hey, sure, just a min
I am in the process of building a subscription system that operates as follows: when a user initially signs up, I create a customer profile and assign them a subscription with a $0 cost. After the user logs in, they have the option to upgrade their subscription to one of two paid plans: $49 or $249.

To handle the payment flow, I am utilizing React’s payment elements to display the payment form and invoice. I rely on the payment_succeeded event in webhooks to trigger the subscription upgrade process. This setup works fine when a user completes the payment.

However, I've encountered an issue when a user attempts to upgrade their plan. The payment form appears with an option to cancel the upgrade. If the user decides to cancel, the current invoice is marked as void, which seems to be functioning as expected.

The problem arises when the user tries to upgrade again after canceling. In this case, the system automatically updates the subscription plan without prompting the user to make a new payment, due to "unused time" from the previous subscription. I have attached the invoice for reference. This is not the behavior I intend to have.

My question is: Is this behavior expected based on the current flow I’ve implemented? Or do I need to adjust the process in some way to ensure that the subscription plan is only upgraded after a successful payment, regardless of any unused time left on the previous plan?

flat holly
#

looks like yuo only want to update the subscription when the payment is successful? if so, you should use pending updated as mentioned above. this way there are no invoices to void.

rugged cloak
#

Okay let me try that

rugged cloak
#

It stil doesnt work

flat holly
#

what doesn't work?

rugged cloak
#

When i try to upgrade it still use the credits from previous invoice and updates subscription without payment

flat holly
#

are you talking about the Customer Balance? and you want to ignore the Customer Balance? if so, you can set it to 0 if needed.

rugged cloak
#

How ? if I void an invoice that invoice amount is used as credit for next upgrade and that way user is not charged, how to fix that ? is what I am asking

flat holly
rugged cloak
#

Okay, let me try that

#

Hey, it still does not work

flat holly
#

can you be more precise? what is not working exactly?

#

can you share a specific request ID or something that doesn't work?

rugged cloak
#

This is the customer id - cus_SJy0vOPH1lpcyb

#

This is what is happeing

flat holly
#

what's the issue with this screenshot?

rugged cloak
#

In my ui with the payment element form I have a cancel button (on press - void invoice, reset customer balance -0), when I try to upgrade to a plan with lesser price it uses the credit from voided invoice and considers it payment for the new price

flat holly
#

I don't understand why/how you have a "cancel button". can you share more details on this?

rugged cloak
#

I dont want that to happen, my entire flow is working except for the part when user clicks on upgrade but decides to not go through with the payment

flat holly
#

so you didn't follow the suggestion we gave you at the beginnng to use pending updates?

#

I really don't understand your payment flow. why do you show the payment element when they click upgrade?

earnest plankBOT
rugged cloak
#

When a user signs up to my app, they get a customer and subscription (0$) in stripe without payment method attached, when the user logs in they can upgrade to plans that require payment, I use the update method from this controller to create an invoice and send the payment intent and client secret to front based on if user has a payment method attached or not, during that in the payement element form the user can decide to not pay and just cancel - this is where all hell breaks loose, when they try again the price from previous is saved as credit and used as payment for the new price, which is what I dont want, I am voiding the invoice and resetting the customer balance on 'cancel' click still it did not work

#

I am using this -

 const updatePayload: Stripe.SubscriptionUpdateParams = {
                    items: [{ id: itemId, price: newPriceId }],
                    payment_behavior: "pending_if_incomplete",
                    proration_behavior: "always_invoice",
                };
rugged cloak
tender quail
#

Did you try the pending update fucntionality as noted? This does exactly as you describes – rolls back the update so the sub is reverted to the previous state if the payment is not made within a timeframe

rugged cloak
#

Yes but it does not work if the customer has no payment methods attached

tender quail
#

What do you mean by 'doesn't work'?

rugged cloak
#

which is the case when the user tries to update the subscription for the first time, since they have a 0$ subscription initially and I dont collect the payment method

tender quail
#

Yeah then you're likely going to have to adapt your flow a bit to collect payment details first before applying the update

rugged cloak
#

I cant make that change any alternative ?

#

If i suggest that now, most likely I lose the project

tender quail
rugged cloak
#

Will this flow work-
I dont create a subscription when the user signs up just a customer and then, when user tries to upgrade I check if they have a subscription with a payment method if yes I just update that if not then I create a new subscription with payment method and then the next time the upgrade checks for the subscription it finds the payment method attached and I use -

const updatePayload: Stripe.SubscriptionUpdateParams = {
                    items: [{ id: itemId, price: newPriceId }],
                    payment_behavior: "pending_if_incomplete",
                    proration_behavior: "always_invoice",
                };

to update the subscription, but meanwhile before making the pay the uer decides to cancel the payment and I void the invoice but then since I am using pending_if_incomplete it wont try to use credit from previously voided invoice and just make a new payment adn update the subscription ?

tender quail
#

Sounds like it could work, yes

rugged cloak
#

Can you please point out if anything could go wrong here? I need to be very sure to suggest this change, I cannot do trial and error anymore, or do you know of any examples or templates on github that implement recurring subsriptions using payment element that I can use for reference ?

#

The only part not working in my current flow is when I void the invoice, stripe still uses the price of that invoice to credit next invoice without capturing any real payments

tender quail
#

Nothing for your specific use case, no. You should just use test clocks to test out the variou scenarios you described to ensure it works – you can 'cheat' time with them

tender quail
rugged cloak
#

In the payment element form if user decides not to make payment, I allow them to cancel which just voids their invoice, but based on my current implementation I am collecting payment method on an existing subscription using this -

 proration_behavior: "none",
                    payment_behavior: "default_incomplete",
                    expand: ["latest_invoice.payment_intent"],
                    metadata: {
                        prev_price_id: currentSubscriptionItem.price.id,
                        current_price_id: newPriceId,
                    },

So with this config, if I void the invoice (i also reset customer balance to 0 alongside) the amount of that invoice is stored as credit, and used in the next invoice even though in this scenario no actual payment has been made. So if I switch to this flow -

I dont create a subscription when the user signs up just a customer and then, when user tries to upgrade I check if they have a subscription with a payment method if yes I just update that if not then I create a new subscription with payment method and then the next time the upgrade checks for the subscription it finds the payment method attached and I use -

const updatePayload: Stripe.SubscriptionUpdateParams = {
items: [{ id: itemId, price: newPriceId }],
payment_behavior: "pending_if_incomplete",
proration_behavior: "always_invoice",
};

to update the subscription, but meanwhile before making the pay the uer decides to cancel the payment and I void the invoice but then since I am using
pending_if_incomplete
it wont try to use credit from previously voided invoice and just make a new payment adn update the subscription ?

Will stripe not use the amount from void invoice as credit for next invoice ?

#

This flow tries to update existing subscription (0$) without payment method

tender quail
#

It'd be easier if you just gave us a sub_xxx ID

rugged cloak
#

Sure - sub_1RPIbkQ5lokX8cUNC6UmEdze

#

Look at the invoices in here

tender quail
#

I don't see where the customer balance is credited from the voiding of an invoice?

earnest plankBOT
rugged cloak
#

hey @tender quail ?

tender quail
#

I replied above?

rugged cloak
rugged cloak
#

Look at the applied balance

#

See the unused time ? thats the negative balance thats causing the issues

tender quail
rugged cloak
#

but my entire codebase has proration_behavior: "none", still it would prorate also is it because payment_behavior: "default_incomplete" even a voided invoice is considered as paid and hence the proration ?

#

How do you completely remove that ? no credit carry over and no proration ?

tender quail
#

Checking on this as I'm not sure what is expected or not

rugged cloak
#

Okay, let me know please

#

Also would this flow, I mentioned earlier work? -
I dont create a subscription when the user signs up just a customer and then, when user tries to upgrade I check if they have a subscription with a payment method if yes I just update that if not then I create a new subscription with payment method and then the next time the upgrade checks for the subscription it finds the payment method attached and I use -

const updatePayload: Stripe.SubscriptionUpdateParams = {
items: [{ id: itemId, price: newPriceId }],
payment_behavior: "pending_if_incomplete",
proration_behavior: "always_invoice",
};

to update the subscription, but meanwhile before making the pay the uer decides to cancel the payment and I void the invoice but then since I am using
pending_if_incomplete
it wont try to use credit from previously voided invoice and just make a new payment adn update the subscription ?

#

Thanks

tender quail
#

We can't answer every specific use case and all the nuances that might be problematic for you, that's where your testing comes in

#

Fundamentally your approach should work though

#

OK, apparently this is expected and you'd need to reset the billing anchor in your update call: billing_cycle_anchor: 'now' which should prevent any prorations for unpaid invoices

rugged cloak