Laravel Cashier (Stripe) - Checkout - invoice.paid & invoice.payment_failed events (webhooks) - laravel

When using Checkout Subscriptions, the Stripe documentation states that the minimum event types to monitor are:
checkout.session.completed - When you receive a checkout.session.completed event, you can provision the subscription.
invoice.paid - Sent each billing interval when a payment succeeds.
invoice.payment_failed -Sent each billing interval if there is an issue with your customer’s payment method.
When you receive a checkout.session.completed event, you can provision the subscription. Continue to provision each month (if billing monthly) as you receive invoice.paid events. If you receive an invoice.payment_failed event, notify your customer and send them to the customer portal to update their payment method.
https://stripe.com/docs/billing/subscriptions/checkout#provision-and-monitor
However, I am still confused about invoice.paid and invoice.payment_failed events and Laravel Cashier:
Do I need to make handlers for these two events?
I'm asking this because Cashier already handles customer.subscription.updated and customer.subscription.deleted events and, if I understood well (correct me if I'm wrong):
if the payment (for renewal of the subscription) is successful next month, then I will receive customer.subscription.updated and the subscription will continue being 'active': $user->subscribed('default') will be true (in subscriptions table stripe_status will remain active).
However, if the payment fails next month (for example, due to insufficient funds) - then customer.subscription.updated event will be triggered/sent and Cashier will change the subscription's status (to canceled or incomplete?) and $user->subscribed('default') will be false, right?
If so, then I see no reason why I should make handlers for invoice.paid and invoice.payment_failed events. I will know if the payment (renewal of the subscription) was successful because customer.subscription.updated event will be triggered/sent and Cashier will update thestripe_status in subscriptions table.
Or I'm wrong and it won’t work that way? If I didn't understand well, then I guess that customer.subscription.updated event will not be triggered/sent at all, or it will but it will not contain information if the payment (renewal of the subscription) was successful or not (and if it failed, then in handler method for the invoice.payment_failed event I will have to update the stripe_status (in subscriptions table) to canceled or incomplete?

Whether the subscription gets deleted or not after a failed invoice payment is up to your subscription settings: https://dashboard.stripe.com/settings/billing/automatic
As such it's possible to get a invoice.payment_failed event and not have the subscription be cancelled.
Since those events are very different it's safest to listen to all of them and handle accordingly. customer.subscription.updated and invoice.paid are also very different, the former will fire whenever any update to the subscription is made (e.g. if you update the metadata) whereas the latter will only fire specifically when an invoice payment succeeds.
It's ultimately up to you, but to ensure that you don't miss any important events you should consider listening to all the aforementioned events.

Related

Facing issue with stripe failed payment webhook

I managing the failed payment webhook in my website but I am facing issue that invoice.payment_failed gets received before invoice.created which creating issue. Because on failed payment the system is updating the status of invoice but the invoice havnt been created becasue the invoice.created havent been called. I dont know what am I missing. I am using laravel ans spark for stripe management
Given the order of delivery for webhook events is not guaranteed, you have a couple options.
Create the record of the invoice in your system a soon as you receive a webhook notification for any new invoice ID.
When you receive a webhook notification, add it to a queue for processing, and process the queue in priority order where invoice.created events are processed first. You'll likely want to build in some sort of delay.

Laravel mail automation

I'm developing an invoicing system in order to collect payments from customers. Once I create an invoice, an email including invoice details will be sent to the customers with payment link. I want to re-generate payment token(once it expires) and send renewed payment link to the customers, and want to automate the process.
I created a Job called renewingInvoiceLinks and inside the handle method, I wrote code to retrieve all invoices with expired links and create new token and send new link. In App->Console->Kernel.php scheduled the job as following. I referred Laravel Docs but did not get the flow properly to dispatch the Job.
protected function schedule(Schedule $schedule)
{
$schedule->job(new renewingInvoiceLinks, 'renewingInvoiceLinks')->daily();
}
Things I want to get clear.
Whether using Job to do the requirement is correct or not? If not what is the perfect way to do it.
How to dispatch the Job? (Where I need to call the dispatch method?)
I think the question is quite subjective and opinionated, but here is how I'd do it:
Whenever the payment link is updated, I would fire a custom PaymentLinkUpdated event. You can either fire this event in the place(s) where you update the link, or you listen for the saving event on the affected model and fire the PaymentLinkUpdated event only when the original value is different from the updated one.
A custom event listener will listen for the update of payment links and will send out the email containing the payment details and link. This event listener should be a queued one so that emails are sent in the background and do not block the UI.
A scheduled job will check for expired payment links and will update the affected records, casting a PaymentLinkUpdated event where necessary. This will trigger step 2 (which is automatically queued again).
This way, you'd have all the logic implemented only once and sending mails would not be duplicated. If you have to send a different mail when the payment link is renewed, you can either do so in the event listener or you have to change your strategy entirely.

Braintree - proration upon subscription cancellation

I have a yearly recurring subscription set up on Braintree. I am trying to cater for a scenario where the user is able to have their subscription cancelled by an admin part way into the yearly cycle.
I am aware of the discounts feature but am unsure how I would apply it in this case or even whether its possible.
Is there a way in Braintree to prorate the amount for the rest of the cycle and credit it back to the user? I would not be doing any of this directly in Braintree but rather through the API via my application that uses Braintree for payments.
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support#braintreepayments.com.
You can cancel an annual subscription mid-cycle by using the API call to cancel a subscription. Here's a Ruby example, but the Braintree developer docs show examples in all supported SDKs.
result = Braintree::Subscription.cancel("the_subscription_id")
A proration would only apply if a change is made to the subscription price in the middle of a billing cycle. In the event of a cancellation, Braintree won't automatically issue a partial refund; you would have to process the refund yourself. Here is the API call for a partial refund:
result = Braintree::Transaction.refund("transaction_id", "amount")
Also, keep in mind that a cancelled annual subscription will not renew the next year. The only way to "reactivate" the subscription is to create a new one.

Is there a refund webhook for Braintree?

I didn't find some refund webhook in webhooks list.
Is Disbursment webhook something what can help me in this case https://developers.braintreepayments.com/reference/general/webhooks/disbursement/php ?
[Edit #1]: I've tested it and found that Disbursment webhook won't trigger after refund button is clicked in Braintree sandbox admin.
So I presume there is no webhook for refunds. For now I ended up with scheduled cron task to get refund transactions from the Braintree API:
$collection = Braintree_Transaction::search([
Braintree_TransactionSearch::createdAt()->greaterThanOrEqualTo($hourAgo),
Braintree_TransactionSearch::type()->is(Braintree_Transaction::CREDIT),
Braintree_TransactionSearch::refund()->is(true)
]);
P.S. hey, downvoters, why wouldn't you argument your opinion in comments? At least it could be helpful for someone who will find this thread.
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
Braintree does not have a webhook which fires when a refund is created. Webhooks are used for asynchronous events—in other words, events which are not triggered directly by an API call made by your integration. You get immediate feedback on the success or failure of a refund via the result object from the refund API call. Use that result to trigger whatever action you wanted to take when a refund occurs.
(If what you're actually looking for is to get information when a refund transaction disburses—i.e., when the funds for a refund are moved out of your bank account—then you actually do want a disbursement webhook. Disbursements represent the sum of your incoming and outgoing funds.)

How do I trigger a webhook for transaction settlement in sandbox?

I am trying to create a new webhook endpoint for my braintree integration. I added an endpoint to the Control Panel. I am using the Python library and created a a bunch of transactions. Also, I manually settled the transaction using the testing gateway. But I do not get a webhook notification. When is the webhook triggered and I=is there a way to trigger it manually?
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
The Transaction -> Disbursed webhook fires when the transaction has settled, and braintree has begun to move funds from their account to yours. While settling the transaction manually in the control panel would start the process, it does not fire the webhook right away.
There isn't any way to trigger this manually, instead braintree has sample payloads and a way to trigger a "Check" webhook in the control panel.
To answer the question in your comment, this type of webhook does not cover voided transactions.
I also asked a similar question to Braintree support, and the answer may be useful for others, even if I decided to go with Stripe (I found stripe much better after all my tests ;).
Question
Hi,
Just integrating Braintree, but can’t find how I’m supposed to get a transaction confirmation (meaning it has correctly been ‘charged’). When I create a ‘transaction.sale’ (including submitForSettlement) I always get (sandbox) a response indicating the status ‘Submitted for settlement’, and the transaction doesn't go to settled until Braintree globally process it (at night, etc.). I also can’t find a related webhook to get payment confirmation (as I currently get using PayPal - IPN).
When/how can I get a payment confirmation so I can then ‘deliver’ the purchased item? By checking your docs, it’s like you can’t get a confirmation until the next day… how does it work exactly?
Thank you!
Answer
Hi Nacho,
Thanks for reaching out!
A Braintree transaction goes through several steps, the second of which is submitted for settlement. This means that the money has begun to move from the customers account, and is the immediate state of a transaction after being submitted; it's not possible to get an immediate 'settled' or 'complete' status on submission. Transactions in this state will generally move to settling, then settled, within 24 hours, although it can depend on your processing bank.
It is very unusual for a transaction to fail when moving from submitted for settlement to settled, as the amount will have been authorised prior to submission for settlement. Authorising a transaction means the funds are put on hold within the customers account, for a certain timeframe depending on payment method, until they are settled. We get an immediate response from the processing bank as to whether an authorisation is successful, which you'll see in the Braintree_Transaction response object.
As such, you can generally rely on the status of submitted for settlement to reflect a valid, successful transaction.
We don't offer webhooks for transactions - instead, our API is designed to give instant feedback via the response object. Because of this, we only provide webhooks for asynchronous events that aren't triggered by an API call (such as billing a subscription or disbursing funds). We encourage our customers to avail of our API responses, in addition to our reporting system and comprehensive search call options, for any extra functionality they require.
If you wanted to pull updated statuses for transactions, such as to confirm a payment has settled, you could use the Transaction.find() API request, pass in the transaction ID and read in the status from the response object. This could easily be run as a script that iterates through the previous days transaction IDs and updates the status of each on a daily basis.
I hope this explains the transaction process with more clarity and offers you some options!
Kind regards,
Braintree
Just in case it helps!
I also asked kind of the same question to Braintree I got the same answer to check the transaction with Transaction::find() and then asked about the possibility of having a transaction not settled after having a SUBMITTED_FOR_SETTLEMENT status, here is the response, very clear and complete:
Hi Alban,
Thanks for reaching out with this question! Happy to help.
Settlement Declined:
In rare cases, a transaction that has been submitted for settlement will reach a status of settlement declined. For sales, this will only occur with a PayPal transaction. For refunds, this can occur with both PayPal and credit card transactions.
This occurs if the processor declined to settle the sale or refund request, and the result is unsuccessful. This can happen for a number of reasons, but the processor settlement response code may have more information about why the transaction was declined. Again, this status is rare, and only certain types of transactions can be affected.
PayPal sale: We recommend checking the settlement status of all PayPal sale transactions before shipping goods or providing services to customers. To reduce these types of declines, submit your PayPal transactions for settlement either upon creation or within 3 days of creation.
PayPal refund: We recommend contacting PayPal for details on refunds that are Settlement Declined.
Credit card refund: Settlement declines can be hard or soft, just as with authorizations. In regions that support an immediate decline response for refunds, we will return Settlement Declined if the processor responds with a hard decline.
Settlement Pending:
This status indicates that the transaction has not yet fully settled. This status is rare, and it does not always indicate a problem with settlement. Only certain types of transactions can be affected.
--> PayPal sale:
If using multiple partial settlements: Settlement Pending is a normal part of the transaction flow. The parent authorization will remain in this status until all child transactions are settled or the authorization expires. See the multiple partial settlement reference for more details.
If not using multiple partial settlements: Almost all Settlement Pending PayPal transactions will settle without intervention, so we always return a successful result. In general, you can expect these to be updated to Settled within a few days as we confirm their status with PayPal. In the rare case we can't settle a Settlement Pendingtransaction, we'll contact you to resolve the issue.
--> Credit card, Android Pay, and Apple Pay sales: You will only see Settlement Pendingauthorizations if you have contacted us to enable a specific API feature that uses this status.
I hope that provides insight, Alban! Let us know if further questions arise.
Best,
xxxxx.
Braintree

Resources