commerce-stripe
commerce-stripe copied to clipboard
Duplicate Charges
Description
We're seeing duplicate charges occasionally. I'm unable to reproduce it. It doesn't have to do with pressing the submit button multiple times (we disable it after the first click). I'm wondering if this plugin is properly passing an idempotency_key.
https://stripe.com/docs/api/idempotent_requests
Same scenario @codyjames. I've just posted this https://github.com/craftcms/commerce-stripe/issues/256. Have you seen any further duplicate charges?
@jonleverrier Yeah, we get them occasionally and just have to refund the buyer. We had one with four charges recently.
@codyjames Four charges!!! Were the timestamps for the 4 charges exactly the same? It's bizarre this kind of thing can happen.
I'm thinking about writing a module that will
- listen in on a payment event
- check the number of successful stripe payments
- if more than one successful card transaction, reject the other transactions?
Is there any updates on this @lukeholder we've had the same issue. Customer has been charged 3 times.
Apologies for the delay in response. We are looking into this but haven't been able to reproduce.
Could you all please let us know:
- the payment form html you are using (custom, paymentFormHtml() function)
- the timestamps on the transactions. Are they at the same time or a few seconds apart? We might need a DB dump sent to [email protected] referencing this ticket, if you can.
- the stripe API version you are on.
- if you are overriding any Mutex yii component?
- information on what your server hosting environment is. Are you doing any DB read/write splitting etc?
- what version of commerce and craft are you on?
- are you hooking into any commerce events?
Please let us know.
@lukeholder we've just had 2 instances of customers being charged twice.
- We are using a custom payment form (not
paymentFormHtml). - Stripe API version: 2020-08-27
- Not overriding mutex, but using Redis for sessions
- Hosted on Ubuntu 20.04.3 LTS, MariaDB 10.6.3
- Craft 4.6.0
- Commerce 4.4.0
- We are using a few commerce events, but nothing that mutates the order in a significant way.
LineItems::EVENT_POPULATE_LINE_ITEM- dynamically sets the line item priceOrder::EVENT_BEFORE_SAVE- sets a single field on the order based on the line item contentsOrder::EVENT_AFTER_ORDER_PAID- processes a Stripe Connect payout
This happened to us again today @lukeholder Pretty embarrassing to explain to customers.
@jaydensmith what hosting provider are you using?
Guessing based on this you're managing your own infrastructure?
Hosted on Ubuntu 20.04.3 LTS, MariaDB 10.6.3
@jaydensmith what hosting provider are you using?
Guessing based on this you're managing your own infrastructure?
Hosted on Ubuntu 20.04.3 LTS, MariaDB 10.6.3
@angrybrad It's managed via Laravel Forge
@jaydensmith can you share your config/app.php file? (redacting any sensitive bits of course)
@angrybrad sure!
<?php
/**
* Yii Application Config
*
* Edit this file at your own risk!
*
* The array returned by this file will get merged with
* vendor/craftcms/cms/src/config/app.php and app.[web|console].php, when
* Craft's bootstrap script is defining the configuration for the entire
* application.
*
* You can define custom modules and system components, and even override the
* built-in system components.
*
* If you want to modify the application config for *only* web requests or
* *only* console requests, create an app.web.php or app.console.php file in
* your config/ folder, alongside this one.
*/
use craft\helpers\App;
return [
'id' => App::env('APP_ID'),
'modules' => [
'site-module' => \modules\sitemodule\SiteModule::class,
'site-orders' => \modules\siteorders\SiteOrders::class,
'site-scripts' => \modules\sitescripts\SiteScripts::class,
],
'bootstrap' => [
'site-module',
'site-orders',
'site-scripts',
],
'components' => [
'queue' => [
'ttr' => 3600,
],
'addresses' => [
'class' => \craft\services\Addresses::class,
'formatter' => new \modules\sitemodule\formatters\Address(
new \CommerceGuys\Addressing\AddressFormat\AddressFormatRepository(),
new \CommerceGuys\Addressing\Country\CountryRepository(),
new \CommerceGuys\Addressing\Subdivision\SubdivisionRepository()
)
],
'session' => function() {
// Get the default component config:
$config = craft\helpers\App::sessionConfig();
// Replace component class:
$config['class'] = yii\redis\Session::class;
// Define additional properties:
$config['redis'] = [
'hostname' => App::env('REDIS_HOSTNAME') ?: 'localhost',
'port' => 6379,
'password' => App::env('REDIS_PASSWORD') ?: null,
];
// Return the initialized component:
return Craft::createObject($config);
}
],
];
@jaydensmith thanks for that.
Usually, when we see sporadic behaviors like this, the culprit has been using Redis as a mutex, which, as it turns out, doesn’t make a reliable distributed locking mechanism. https://redis.io/docs/manual/patterns/distributed-locks/
That doesn’t seem to be the case here, though.
I’m not sure what’s going on in your custom modules - it’s possible something in there is causing the behavior.
Guessing there’s no database read/write splitting based on the contents of config/app.php?
@jaydensmith I suppose as a next step, I'd update Craft and Commerce to the latest 4.x release to remove outdated versions as variables.
I have a hunch that this is related to failed payment intents. It seems like this happens when a customer has some sort of issue with their payment. Then they resolve the issue and a new payment intent is created and successfully processed, but perhaps some of the old payment intents also get re-processed. Or perhaps the errors happened on the Craft side, and blocked the payment intent from being processed, and then once the order is able to go through without errors, Craft just processes all of the payment intents.