clemlatz
clemlatz

Reputation: 8053

How can I prevent duplicate charge using Stripe?

I'm using Stripe to allow my customer to save a credit card and pay invoices (that have a unique id). On payment, I use a form to send a POST request with the card id (provided by stripe) to my server, and the controller charges it. Then I marked the invoiced as payed on my side.

Turns out that if I double click my form's submit button quick enough, I send two POST request and the cards ends up being charged twice, because when the second POST request arrives on my server, the POST request from my server to Stripe API (from the first click) isn't finished, thus the invoice has not been marked as payed yet.

I'm considering disabling the form submit button after first click, but I feel there may be a cleaner way.

Is there on Stripe's side to prevent duplicating charges (maybe using my invoice id)? If not what would the best back-end method to do this on my side?

Upvotes: 14

Views: 22777

Answers (3)

Pedro Lobito
Pedro Lobito

Reputation: 98871

As mentioned by other answers, you can use Idempotent Requests to prevent duplicate charges. Here's how I've implement it:

On checkout.php:

<?php
function rand_string( $length ) {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return substr(str_shuffle($chars),0,$length);
}
$idempotency = rand_string(8);
?>
 ...
<form method="post" action="payment.php">
<input type="hidden" name="idempotency" id="idempotency" value="<?=$idempotency?>">
...
</form>
...

On payment.php:

<?php
$idempotency = preg_replace('/[^a-z\d]/im', '', $_POST['idempotency']);
...

try {
    $charge = \Stripe\Charge::create(
        ["amount" => $price,
        "currency" => $currency,
        "description" => $invoiceid,
        "customer" => $customer->id ], 
    ["idempotency_key" => $idempotency,]);
}catch(Exception $e) {
    $api_error = $e->getMessage();
}

Upvotes: 3

koopajah
koopajah

Reputation: 25552

Disabling the form submission after the first click is definitely the easiest way to implement this.

Another approach is to use idempotent requests as documented here. The idea is that you pass a unique identifier when making a request to Stripe's API that ensures you can only run this transaction once. If you re-run the query with the exact same idempotency key you will get the original response from the first call which ensures you never have 2 charges created for the same "transaction" on your website.

Upvotes: 23

Rahul Sharma
Rahul Sharma

Reputation: 1431

yes idempotent request is correct way to implement this.

you can refer this here

https://stripe.com/docs/api#idempotent_requests

another simple solution which you can implement is use of state machine by keeping track of stripe api call.

Upvotes: 6

Related Questions