#marcus_api
1 messages ยท Page 1 of 1 (latest)
๐ 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/1418134026136588378
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
Here is my Java code for create and update a monthly stripe subscription
Hi! Pending updates only support specific parameters: https://docs.stripe.com/billing/subscriptions/pending-updates#supported-attributes
In the request you provided, I see unsupported parameters such as cancel_at_period_end which is why it failed.
you mean with the request Id?
yes but I fixed this in the meantime.
I have not yet retried the update - but I just recently tried a new subscription creation on prod - it also failed
hey there, taking over from my colleague, Sam, let me catch up
Could you share with me the request ID (req_xxxxx) where the request also failed?
no worries, i'll take a look
That's an event ID, the event is customer.subscription.created, it means that your subscription is successfully created
this API request req_3AZSJvAVLaINdW is the last request that errored in your Stripe account.
The next API request you made that creates a subscription is this: https://dashboard.stripe.com/logs/req_YaUYdNGC0ciUit, and the subscription is successfully created. This API request also correlates to the event ID for customer.subscription.created you just shared with me
It's still in status INCOMPLETE
I did not receive any payment it seems
the idea was - keep it INCOMPLETE until I receive an INVOICE PAID event
but there is never an invoice paid event
Yes, the subscription is in incomplete because you have not confirm the created PaymentIntent (pi_3S8bp7AfQ68zM8an1q7EhqZb)
You can use this link to view the payment in the Dashboard: https://dashboard.stripe.com/acct_17xOQjAfQ68zM8an/payments/pi_3S8bp7AfQ68zM8an1q7EhqZb
- Under Events and Logs, the PaymentIntent is shown as requires confirmation
You need to confirm the PaymentIntent using the Stripe API here: https://docs.stripe.com/api/payment_intents/confirm
how woudl I confirm it?
Earlier - we just activated it instantly, and the invoice paid event arrived, and the invoiuce was paid.
But.. for some fraudsters - they somehow never paid - maybe did not finish 3d Secure or so.. and then I had an activated business subscription (on my end) with a subcription incomplete on your end (stripe)
so I adjusted the process now - the idea is to wait for the invoice paid event until I switch the subscription
Wait - Before, my code was this:
public String createSubscription(String stripeCustomerId, String stripePriceId, Long endOfTrialTimestamp) {
try {
SubscriptionCreateParams.Builder builder = SubscriptionCreateParams.builder()
.addDefaultTaxRate(stripeTaxRateId)
.addItem(SubscriptionCreateParams.Item.builder()
.setPrice(stripePriceId)
.build())
.setCustomer(stripeCustomerId)
.setTrialEnd(endOfTrialTimestamp);
return Subscription.create(builder.build(), requestOptions).getId();
} catch (StripeException e) {
throw new TechnicalException(e);
}
(Without the " .setPaymentBehavior(SubscriptionCreateParams.PaymentBehavior.DEFAULT_INCOMPLETE)" setting)
and we did not hacve to confirm any payment intent I thinkl
maybe I should switch back to this code.. and just switch my BUSINESS subscription on my end when the invoice paid event arrives?
All I want is that it works.. but that I am sure the customer actually finished the payment, truly.
I see, thanks for providing the context. The reason why your new subscription (sub_1S8bp7AfQ68zM8anWCKlN5S9) needs an additional API call to confirm the PaymentIntent is because the subscription is created with payment_behavior: "default_incomplete" https://dashboard.stripe.com/logs/req_YaUYdNGC0ciUit
If you did not specify payment_behavior: "default_incomplete" when creating a subscription, the default payment behaviour from the Stripe API is allow_incomplete, which means Stripe will confirm the payment for you, and your server wouldn't need to make a new API request to confirm the PaymentIntent. This is also the payment behaviour your Stripe account was originally on before you updated the code to use "default_incomplete"
If you wish to better sync subscriptions' status between Stripe and your database without making another API call to confirm subscriptions' PaymentIntent โ yes, you can listen to the invoice.paid event, and only mark the subscription as paid in your database if you receive an invoice.paid event from Stripe for the related subscription.
We do have a guide on what common events that a Subscription will trigger that might be useful for you to synchronize subscription status between platforms: https://docs.stripe.com/billing/subscriptions/webhooks#events
so, in other words - I should just keep my backend subscription creation / subscription update logic as it was before - without auto payment confirmation from stripe - and then only change my backend logic in that on my end I do not switch to the new business plan before the invoice paid event arrives? Is invoice_paid the correct / best event?
GPT advises me to:
- Listen to "invoice.payment_succeeded" instead
- Check if stripeInvoice.getTotal() > 0 to be sure actual money was paid.
But.. if the customer has a positive balance.. I think it's totally fine if they don't pay "real money" and use their positive balance instead.. that would also mean I received money from the customer.
My main intention is to block fraudsters trying to circumvent actual payment.. which happend before ocasionally
๐ taking over for my colleague. Let me catch up.
My main intention is to block fraudsters trying to circumvent actual payment.. which happend before ocasionally
would you mind describing how that happened in the past?
well - in the past - I let stripe auto confirm the subscription / payment it seems - and then - no matter the state of the subscription - I activated the customer in my own business logic.
Some customers did - something - I assume for instance they maybe did not confirm 3D secure
I let stripe auto confirm the subscription / payment it seems
would you mind elaborating?
and then on stripe the subscription was in INCOMPLETE state
so the customer had never paid - but was fully able to use all my services on my ednd
this I want to end ๐
Old create subscription logic in my backend:
public String createSubscription(String stripeCustomerId, String stripePriceId, Long endOfTrialTimestamp) {
try {
SubscriptionCreateParams.Builder builder = SubscriptionCreateParams.builder()
.addDefaultTaxRate(stripeTaxRateId)
.addItem(SubscriptionCreateParams.Item.builder()
.setPrice(stripePriceId)
.build())
.setCustomer(stripeCustomerId)
.setTrialEnd(endOfTrialTimestamp);
return Subscription.create(builder.build(), requestOptions).getId();
} catch (StripeException e) {
throw new TechnicalException(e);
}
}
old update Subscription logic on my end:
public String changeSubscriptionPlan(String activeStripeSubscriptionId, SubscriptionPlan newSubscriptionPlan, long enrollmentTimestamp) {
String newStripePlanId = newSubscriptionPlan.stripePriceId();
Subscription subscription =
retrieveSubscription(activeStripeSubscriptionId, SubscriptionRetrieveParams.builder().build());
try {
SubscriptionUpdateParams.Builder builder = SubscriptionUpdateParams.builder()
.setCancelAtPeriodEnd(false)
.addDefaultTaxRate(stripeTaxRateId)
.addItem(
SubscriptionUpdateParams.Item.builder()
.setId(subscription.getItems().getData().get(0).getId())
.setPrice(newStripePlanId)
.build());
if (subscription.getStatus().equals(StripeSubscriptionStatus.TRIAL.value()) && !newSubscriptionPlan.hasTrial()) {
builder.setTrialEnd(SubscriptionUpdateParams.TrialEnd.NOW);
}
if (subscription.getStatus().equals(StripeSubscriptionStatus.ACTIVE.value()) || !newSubscriptionPlan.hasTrial()) {
/* TODO check if this is working when the customer switches from Basic plan WITH an active trial to a Corporate plan without any trial */
builder
.setProrationDate(enrollmentTimestamp)
.setBillingCycleAnchor(SubscriptionUpdateParams.BillingCycleAnchor.NOW)
.setProrationBehavior(SubscriptionUpdateParams.ProrationBehavior.CREATE_PRORATIONS);
}
return subscription.update(builder.build(), requestOptions).getId();
} catch (StripeException e) {
throw new StripeExternalException(e);
}
}
no - "SubscriptionCreateParams.PaymentBehavior.DEFAULT_INCOMPLETE)" set
And once create or update was called on the stripe api.. without an exception happening - I activated the customer on my end to the new plan
but that's not how subscriptions work on Stripe's side
Now - I want to wait for the invoice paid event to arrive
the subscription will be created no matter what
and then check it - and I added this part:
private void applyLocalSwitchIfNeeded(String authenticationId, Invoice stripeInvoice) {
StripeSubscription newSubscription = stripeSubscriptionClient.resolveSubscriptionFromInvoice(stripeInvoice);
if (newSubscription == null || !newSubscription.isActivated()) {
return;
}
String stripePriceId = newSubscription.stripePlan().stripePriceId();
SubscriptionPlan targetPlan = subscriptionPlanService.getByStripePriceId(stripePriceId);
Subscription currentSubscription = subscriptionRepository.getSubscription(authenticationId);
if (planDiffers(currentSubscription, targetPlan)) {
subscriptionRepository.changeToNewPlan(authenticationId, targetPlan.id(), newSubscription.stripeSubscriptionId());
if (currentSubscription.isFreePlan() && targetPlan.isPaidPlan()) {
lifeCycleEventService.onFreeToPaidConversion(authenticationId);
}
return;
}
if (isStripeIdAttachNeeded(currentSubscription, newSubscription)) {
subscriptionRepository.confirmPaidActivation(authenticationId, newSubscription.stripeSubscriptionId());
if (currentSubscription.isFreePlan() && targetPlan.isPaidPlan()) {
lifeCycleEventService.onFreeToPaidConversion(authenticationId);
}
}
}
yes it's the invoice that should give you a sense of whether the payment got through or not
the subscription will be created no matter what - yeah.. but it must be paid for me to give the customer the service they should pay for
exactly why we suggest listening to invoice.paid or invoice.payment_succeeded events
I'm still unsure what we're trying to solve here if we're both on the same page on that side
so which event is the correct or best one to listen to to activate my customer for the new business plan / services?
Invoice.paid or invoice.payment_succeeded?
or "invoice_payment.paid" ?
So whenever I can be sure the customer did not "fck me".. e.g. they paid.. or they for instances downgraded their plan to a cheaper plan, and "pay" their cheaper plan with the credit left they already did pay.. all that is fine.. then I want to activate the new plan for my cvustomer in my system and let them use it.
so which event is the correct or best one to listen to to activate my customer for the new business plan / services?
Invoice.paid or invoice.payment_succeeded?
either one works
I'm still unsure what we're trying to solve here
I want to avoid subscription / payment INCOMPLETE on stripe, with a customer using my service for free.
or "invoice_payment.paid" ?
if you're accepting multi payments on an invoice then yes for sure
We accept just a single payment. So it is always full.
please mind your language and remove the offensive word
but one idea would be - use invoice paid maybe to create the invoice, and another one to handle the swithc to the new plan.. to keep this a bit separated - if you say it's both the same anyway.
So.. if I understand you correctly - invoice paid means - SOME money has been paid, while "invoice.payment_succeeded" means ALL money has been paid - which, in my case is the same, since we do not offer multipayments ? e.g 10x 1 USD for a 10 USD subscription?
for downgrades you can simply downgrade at the end of the period
the difference between invoice.paid and invoice.payment_succeeded is that invoice.paid is triggered for paid-out-of-band invoices as well as all the other invoice.payment_succeeded events
so it's more englobing but if you're not accepting out-of-band payments then both events are similar
out of band payment.. does this mean that I click "customer paid me" because they would personally send or give me money?
invoice.paid will be fired once the total amount is paid
yes
well. We don't have that. But let's assume we know each other personally, and I say you paid, then you paid.. and if I activate in the stripe dashboard that you paid.. then of course I am fine with you using my business services.. so that shoudl be fine, I guess
I just dont want fraudsters to abuse me
I'm just explaining the difference between the two events
because you asked what event is better
yes. And I am explaining my use case, so you can better advise me which event you'd use for my case
as I said, regardless of how the customers try to overcome your logic, your logic should be executed after the invoice.paid event is triggered
the way you should think about this is the following
the invoice contains a very important field called billing_reason
depending on the billing_reason your code should address the different scenarios of the subscription
?
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
if the billing_reason is subscription_create then this is a new subscription that you want to start
if it's subscription_cycle this means that a new period have started
if it's subscription_update then the price has changed
and all of that can give you a sense of what exactly you need to do
that's on how you provision
on the other hand if you want to restrict your customers from being able to take advantage of the system, this needs to happen on the "subscription update" call
and not on the provision itself
one solution would be to automatically upgrade but to wait until the end of the period to downgrade
is it the same on all other browsers?
no it seems chrome is fine
took me a while to try chrome.. I did not know my password, had to log out of firefox, then show my password
and use this in chromer
would you mind reporting that directly to https://support.stripe.com/?contact=true
ok
For the invoice paid event.. I didn't know about the billing reason field that you just told me about.
So far - what I am doing is - I retrieve the current / new active subcription from stripe (remote) and compare it with the old/last subscription in my database - if those differ - it means I need to change something.
Then, I check if the subscription locally in my database is still the free plan - in which case it's a subscription create, and I activate the new subscription in my database -
otherwise I switch the subscription in my database to the new plan.
Is that better or worse or same as:
if(stripeInvoice.getBillingReason().equals("subscription_create")){
lifeCycleEventService.onFreeToPaidConversion(authenticationId);
} else if (stripeInvoice.getBillingReason().equals("subscription_update")){
subscriptionRepository.changeToNewPlan(authenticationId, targetPlan.id(), newSubscription.stripeSubscriptionId());
}
it's a different that uses less resources
Is that better or worse or same as:
I'll let you be the judge of that
I was guessing that ok.
Something else - I have a customer that has subscription cycle payments failing. How could I react on that in the best way?
I would like to - provide a payment link to stripe so the customer can manually finish / retry the failed cycle payment.
And then when I see that the new cycle keeps being unpaid.. e.g. for 10 days - cancel the customer.
What's the best practice for that?
Hey! Taking over for my colleague. Let me catch up.
I have a customer that has subscription cycle payments failing. How could I react on that in the best way?
I would like to - provide a payment link to stripe so the customer can manually finish / retry the failed cycle payment.
And then when I see that the new cycle keeps being unpaid.. e.g. for 10 days - cancel the customer.
What's the best practice for that?
I think you can achieve something similar with Customer Portal