stripe first time the user pays for a subscription - stripe-payments

If this is the first time this specific customer pays for this specific subscription.
I must set the cancel_at field of the subscription based on the chosen plan ID. (I don't want to set it upon the creation of the subscription). To tackle this first time , I have captured the invoice.payment_succeeded event and I did the following:
$invoices = \Stripe\Invoice::all([
"limit" => 3, //no need to get all of the invoices just checking if their number is = 1.
"status" => 'paid',
"customer" => $customer_id,
"subscription" => $subscription_id,
"expand" => ["data.charge"],
]);
if ( count($invoices->data) == 1 ) {
//First time this user pays for this specific subscription;
Can You just tell me if it is right or if I missed anything as I am new to php as well as stripe. should I be doing count($invoices) instead of what i did ?

Your code looks fine, but the only way to tell if it'll work or not is for you to run it and see if it does what you expect it to do. You can use your test mode API keys and the Stripe CLI to test the flow:
stripe trigger customer.subscription.created
This will create a test customer, a plan and a subscription. The latter will immediately create and pay an invoice, so you should get a invoice.payment_succeeded webhook event to test with.

Related

Given a Stripe payment intent ID, how can I find the checkout session ID?

For a successful payment, how can I find the related checkout session ID given the payment intent ID?
In that case you would just use the Checkout Session List API and pass in the Payment Intent ID for the payment_intent parameter. That will return an array with the related Checkout Session in it.
Given a Stripe payment intent ID, you can find the checkout session ID like so:
From Stripe CLI
stripe checkout sessions list --payment-intent=pi_abc --limit=1 --expand=data.line_items
Line items
Taking it a step further, this PHP example will return the line items for a given payment intent:
/**
* Returns an array of line items. On each, can access properties `price`,
* `quantity` and more. e.g.:
* - getLinesItemsFromPaymentIntent('pi_3MCJzWBkWO5ojRjq1zBX6gA3')[0]->price->id
* - getLinesItemsFromPaymentIntent('pi_3MCJzWBkWO5ojRjq1zBX6gA3')[0]->quantity
*/
function getLinesItemsFromPaymentIntent($payment_intent_id) {
$stripe = new \Stripe\StripeClient(
getenv('STRIPE_API_SECRET_KEY')
);
// https://stripe.com/docs/api/checkout/sessions/list?lang=cli#list_checkout_sessions-payment_intent
$session = $stripe->checkout->sessions->all([
'limit' => 1,
'payment_intent' => $payment_intent_id,
'expand' => ['data.line_items'],
]);
return $session->data[0]->line_items->data ?? false;
}
Limitations
In passing, note that this will only work with products ordered through a Stripe Payment Link AKA Stripe-hosted payment page AKA checkout sessions in the API.
If/when using your own payment form, you will need to use yet another approach to retrieve line items / what was ordered by your customer 🙄
Ergo, the way to make things more consistent when using multiple types of order forms is to set/read metadata on the Subscription, payment_intent_data or whatever handles your orders.

How to get default payment method from Stripe invoice?

I am listening to Stripe's invoice.payment_failed webhook with my web app and I would like to get the default_payment_method from each invoice but for some reason it always returns nothing but an empty array!
When I query for a Stripe invoice on the command line and expand on the default_payment_method like this...
Stripe::Invoice.retrieve('in_3K6dIY2KgYRkshw2LAzya63P', :expand => "default_payment_method")
...I also get empty arrays. This surprises me because all my Stripe customers do have a default payment method associated with them.
What am I missing here?
Thanks for any help.
There are three independent places a default payment method can be set. From more specific to less specific they go :
invoice.default_payment_method (which you are looking at)
subscription.default_payment_method
customer.invoice_settings.default_payment_method
Stripe charges the most specific one if it's set. When reading from the API, those values don't inherit from the level above, they can all be set individually, if they are not set explicitly then they are null. So that's why you see it as null on the Invoice level.
Instead you likely want to look at the Subscription object or the Customer object(and can leverage the expand feature for that), depending on how you built your integration and which one it sets.
Overall though, you probably actually want the PaymentMethod used in the invoice payment though? That would be from the last_payment_error.
inv = Stripe::Invoice.retrieve({
id: 'in_1K8iiKJoUivz182DMzSkuBgp',
expand: ["customer.invoice_settings.default_payment_method",
"subscription.default_payment_method",
"payment_intent"]
}
)
print("invoice : #{inv.default_payment_method} \n")
print("subscription : #{inv.subscription.default_payment_method} \n")
print("customer : #{inv.customer.invoice_settings.default_payment_method} \n")
print("failed charge : #{inv.payment_intent.last_payment_error.payment_method} \n")

Stripe immediate payment on each quantity update

We are using Stripe as the payment gateway, and we have a yearly plan i.e .billing cycle of the plan is 1 year.
Within the billing cycle, the user can update opt for more seats which will result in increasing the quantity for the subscribed plan.
Subscription subscription = Subscription.retrieve(paymentDetails.getSubscriptionId());
int currentQuentity = subscription.getQuantity();
Map<String, Object> updateParams = new HashMap<String, Object>();
updateParams.put("quantity", (currentQuentity + changeInQuantity));
subscription.update(updateParams);
So for any update stripe refunds(if quantity reduces) and charges(if quantity increases) at the end of billing cycle prorated.
In our business logic, we need immediate payment (charges or refunds) on each quantity update rather then on end of billing cycle. Is there any way in the stripe to achieve so.
I spent a lot of time trying to figure this one out, I hope I can save someone else some time. To take payment for a change of quantity or subscription plan, you need to do the following:
Update subscription with the new changes.
Create an invoice.
The thing that confused me by this is that Stripe magically knows to only invoice for the updated subscription items, and not for upcoming billing cycle items.
Retrieve the newly created invoice
Finalise the invoice
Take payment for the invoice This assumes that your customer has a payment method stored.
Only when all of the steps have been completed, you've successfully charged for the subscription change (and charged for the change only). You can do it all of it in one go. The customer should get an invoice email from Stripe at the end of it.
What makes matters complicated for me, is that reading from the documentation, I got the impression that Stripe will automatically do all of this for me as long as the subscription is set to "billing": "charge_automatically". This is not the case, and in Stripe's defence, they mention it at the bottom of the documentation page related to upgrading and downgrading plans:
If you want the customer to immediately pay the price difference when switching to a more expensive plan on the same billing cycle, you need to generate an invoice after making the switch.
To charge immediately after subscription update, you need to create an invoice just after the subscription update. This will result in an immediate charge
To create invoice you can simply call:
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4UWQfQ2";
Map<String, Object> invoiceParams = new HashMap<String, Object>();
invoiceParams.put("customer", "cus_D2XUIsncG7YUX");
Invoice.create(invoiceParams);
Ref (https://stripe.com/docs/api#update_subscription):
The accepted answer is the solution if you want to maintain the same billing dates for the subscription.
A simpler solution if you need to push the billing cycle forward to that of the date of the change is to simply set the 'billing_cycle_anchor' to 'now' when updating the subscription. Stripe will immediately charge the user and apply and prorations.
example in python
stripe.Subscription.modify(
subscription_id,
billing_cycle_anchor = 'now',
......
)
You can also set proration_behavior to always_invoice and Stripe will apply both a proration and immediately invoice your customer (source).
Here's an example in curl:
curl https://api.stripe.com/v1/subscriptions/sub_HE... \
-u "sk_test_MI...:" \
-d "items[0][id]=si_Il3Z..." \
-d "items[0][quantity]"=2 \
-d "proration_behavior=always_invoice"

Why is stripe charging an account before invoice created returns

On signing up a user to a subscription, I would like to charge a different rate if the user has credit in my system. However, when the user signs up for the subscription, Stripe is charging them right away instead of waiting for me to mutate the invoice object on the invoice created webhook.
My understanding based on the docs is that charging the user will wait until after invoice create returns with a 200. However, my invoice created webhook is returning 500's so I know this isn't true.
My current code looks like this:
stripe.Subscription.create(
customer=user.stripe_customer_id,
plan=subscription_plan.stripe_product_id,
trial_period_days=trial_period,
idempotency_key=key)
My webhook looks like:
def invoice_created(request, *args, **kwargs):
user = request.user
invoice = request.data['data']['object']
stripe_customer = stripe.Customer.retrieve(user.stripe_customer_id)
if request.user.credit:
stripe_customer.add_invoice_item(
amount=-user.credit,
currency='usd',
description='Applied ${:.2f} of credit for subscription.'.format(user.credit),
invoice=invoice['id'],
)
return Response(status=200)
When I try to update the invoice, it says the invoice can't be modified. The error I'm getting is:
stripe.error.InvalidRequestError: Request "": Invalid invoice: This invoice is no longer editable
What am I supposed to do here?
My understanding based on the docs is that charging the user will wait until after invoice create returns with a 200.
This is not true for the first Invoice of a Subscription, which is always charged immediately.
If you want to add items to that first one, you'll want to create the Invoice Items on the Customer first, before you create the Subscription.

Stripe: Create a Charge with Coupon

Could you guy please show me how to create a Stripe Charge which can apply the discount automatically.
I have a valid coupon code (expired in far future, forever use, discount $2).
I create a new Stripe user and assign that Coupon Code for him.
I make a Charge with that customer and some money says: amount = $10.
All the thing works. When I login to Stripe Dashboard, I still see the new user in the list and he is using that Coupon Code. However in the payment, he still pay me $10 instead of $8.
I would like to make a Charge with amount = $10, however Stripe will do discounting so the true Charge will pay $8 only.
$myCard = array(
'number' => '4242424242424242',
'exp_month' => 12,
'exp_year' => 16
);
$coupon = Coupon::retrieve('6868');
//Valid coupon
$stripe_customer = Customer::create(array(
'card' => $myCard,
'email' => 'cus#info.com',
'coupon' => $coupon
));
$charge = Charge::create(array(
'customer' => $stripe_customer->id,
'amount' => 1000,
'currency' => 'usd'
));
Stripe does not allow coupons/discounts for one time payments because you don't really need Stripe to handle your coupons for one-time payments.
You need coupons for subscriptions because Stripe generates a subscription's charges automatically, by itself, on its own schedule. That means your code has no opportunity to modify the amount of the charge itself. Instead, you have to modify the subscription, which then modifies the charges it generates in the future. Stripe offers three simple ways to do this: changing the plan, setting the account balance, and adding invoice items. However, none of them allow you to specify a percentage discount or apply a modification for more than just the next invoice but less than forever. Coupons fix that issue.
A one-time charge, on the other hand, is always generated by you, so you always have an opportunity to modify the amount of the charge yourself. The problem that coupons are trying to solve doesn't exist here, so they aren't offered as an option.
reference : click here

Resources