Some time ago, I was working on a project which needed to use a laravel cashier (stripe). After finishing the basic setup and building transaction flow, I encountered the issue of duplicate payments. If a user clicked twice on the pay button or something happened in the networks and the user clicked again to pay then the user can be charged twice.
After a little bit of research about it, I reached idempotency. So, what is this idempotency and what is the role of the idempotency key in the stripe transactions?
In general, Idempotency is a web API design principle and it helps to apply the same operation multiple times without changing the outcome. So, this term is useful to prevent duplicate payments.
Idempotency in stripe simply stores the result of the first request made by the particular idempotency key. After the first request, other requests with the same key return the same result, including 500 errors. Hence it prevents the duplicate payments or accidentally charges the user twice.
Idempotency key can be up-to 255 chars long and it should be unique like UUID v4 as suggested by the stripe.
Take an example of an e-commerce store.
As I was not able to find the solution to use idempotency key with laravel cashier package. This package has the Billable
trait to use with the User
model and this trait enables us to charge the user.
$user->charge($amount, $paymentMethod, $options);
As per stripe documentation we can pass an idempotency key in the second array parameter of the charge
object.
$stripe = new \Stripe\StripeClient("sk_test_your_key"); $charge = $stripe->charges->create([ 'amount' => 2000, 'currency' => 'usd', 'source' => 'tok_mastercard', // obtained with Stripe.js 'description' => 'My First Test Charge (created for API docs at https://www.stripe.com/docs/api)' ], [ 'idempotency_key' => 'ZvAFIiqjW9nGvrmC' ]);
So, I decided to override the charge method of billable trait, but after digging a little deeper, I found out that this charge method of billable trait uses stripe payment Intent to create and confirm the charge.
Now, again I reached the stripe documentation and understand how to pass idempotency key to the stripe payment intent.
It’s time to create our own trait and extend the Laravel cashier billable trait inside our custom billable trait. So, the charge method of billable trait uses the createPayment
method to execute the payment with stripe payment Intent and we need to override this method.
public function charge($amount, $paymentMethod, array $options = []) { $options = array_merge([ 'confirmation_method' => 'automatic', 'confirm' => true, ], $options); $options['payment_method'] = $paymentMethod; $payment = $this->createPayment($amount, $options); $payment->validate(); return $payment; }
As per Stripe\Service\PaymentIntentService::create
we can pass two parameters to stripe payment Intent and in the second parameter, we can pass idempotency key.
<?php namespace App\Traits; use Laravel\Cashier\Billable; use InvalidArgumentException; use Stripe\Charge as StripeCharge; use Laravel\Cashier\Payment; /** * extend Laravel Cashier Billable */trait SbsharmaBillable { use Billable; public function createPayment($amount, array $options = []) { $options = array_merge([ 'currency' => $this->preferredCurrency(), ], $options); $options['amount'] = $amount; if ($this->hasStripeId()) { $options['customer'] = $this->stripe_id; } $params = []; if ( array_key_exists('idempotency_key', $options) ) { $params['idempotency_key'] = $options['idempotency_key']; unset($options['idempotency_key']); } return new Payment( $this->stripe()->paymentIntents->create($options, $params) ); } }
Then I used this trait in the User
model (billable model) instead of the Laravel cashier Billable
trait, like given below.
class User { use SbsharmaBillable; //Billable }
Let’s charge the user with an idempotency key like given below in the example code.
$options = [ ‘description’ => ‘’, ‘metadata’ => [], ‘shipping’ => [ ], 'idempotency_key' => $request->idempotency_key ] $user->charge($amount, $payment_method, $options);
That’s it, the charge method will pass the idempotency key to createPayment
method and then it will create the payment Intent with idempotency key to prevent duplicate charges.
I hope you like it. See you in the next laravel learning corner.
Today we are going to learn about managing multiple PHP versions on ubuntu with xampp.…
Let's understand about how to use coding to improve your website's SEO. In today’s computerized…
Let's understand the most important linux commands for web developers. Linux, as an open-source and…
Today we are going to discuss top 75+ Laravel interview questions asked by top MNCs.Laravel,…
Today we will discuss about the Mailtrap integration with laravel 10 .Sending and receiving emails…
Today we are going to integrate FCM (Firebase Cloud Messaging) push notifications with ionic application.Firebase…
View Comments