duncanmcclean / simple-commerce

A simple, yet powerful e-commerce addon for Statamic.
https://statamic.com/simple-commerce
Other
143 stars 34 forks source link

Integrity constraint violation on findOrCreateCustomer #1048

Closed Web10-Joris closed 1 month ago

Web10-Joris commented 2 months ago

Description

It looks like the findOrCreateCustomer tries to create customers in the database with already existing emails.

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'example@email.nl' for key 'customers.customers_email_unique'

Stacktrace:

[stacktrace]
#0 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Connection.php(587): PDOStatement->execute()
#1 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Connection.php(816): Illuminate\\Database\\Connection->Illuminate\\Database\\{closure}()
#2 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Connection.php(783): Illuminate\\Database\\Connection->runQueryCallback()
#3 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Connection.php(576): Illuminate\\Database\\Connection->run()
#4 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Connection.php(540): Illuminate\\Database\\Connection->statement()
#5 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/Processor.php(32): Illuminate\\Database\\Connection->insert()
#6 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(3507): Illuminate\\Database\\Query\\Processors\\Processor->processInsertGetId()
#7 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(1969): Illuminate\\Database\\Query\\Builder->insertGetId()
#8 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1333): Illuminate\\Database\\Eloquent\\Builder->__call()
#9 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1298): Illuminate\\Database\\Eloquent\\Model->insertAndSetId()
#10 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1137): Illuminate\\Database\\Eloquent\\Model->performInsert()
#11 /home/ploi/example.nl/vendor/duncanmcclean/simple-commerce/src/Customers/EloquentCustomerRepository.php(122): Illuminate\\Database\\Eloquent\\Model->save()
#12 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(355): DuncanMcClean\\SimpleCommerce\\Customers\\EloquentCustomerRepository->save()
#13 /home/ploi/example.nl/vendor/duncanmcclean/simple-commerce/src/Customers/Customer.php(114): Illuminate\\Support\\Facades\\Facade::__callStatic()
#14 /home/ploi/example.nl/vendor/duncanmcclean/simple-commerce/src/Http/Controllers/Concerns/HandlesCustomerInformation.php(74): DuncanMcClean\\SimpleCommerce\\Customers\\Customer->save()
#15 /home/ploi/example.nl/vendor/duncanmcclean/simple-commerce/src/Http/Controllers/Concerns/HandlesCustomerInformation.php(37): DuncanMcClean\\SimpleCommerce\\Http\\Controllers\\CartController->findOrCreateCustomer()
#16 /home/ploi/example.nl/vendor/duncanmcclean/simple-commerce/src/Http/Controllers/CartController.php(36): DuncanMcClean\\SimpleCommerce\\Http\\Controllers\\CartController->handleCustomerInformation()
#17 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): DuncanMcClean\\SimpleCommerce\\Http\\Controllers\\CartController->update()
#18 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()
#19 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#20 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()
#21 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Router.php(806): Illuminate\\Routing\\Route->run()
#22 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#23 /home/ploi/example.nl/vendor/duncanmcclean/simple-commerce/src/Http/Middleware/EnsureFormParametersArriveIntact.php(66): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#24 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): DuncanMcClean\\SimpleCommerce\\Http\\Middleware\\EnsureFormParametersArriveIntact->handle()
#25 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#26 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()
#27 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#28 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()
#29 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#30 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()
#31 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#32 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#33 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()
#34 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#35 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
#36 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#37 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
#38 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#39 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Router.php(805): Illuminate\\Pipeline\\Pipeline->then()
#40 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Router.php(784): Illuminate\\Routing\\Router->runRouteWithinStack()
#41 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Router.php(748): Illuminate\\Routing\\Router->runRoute()
#42 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Routing/Router.php(737): Illuminate\\Routing\\Router->dispatchToRoute()
#43 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()
#44 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#45 /home/ploi/example.nl/vendor/statamic/cms/src/Http/Middleware/StopImpersonating.php(12): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#46 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\StopImpersonating->handle()
#47 /home/ploi/example.nl/vendor/statamic/cms/src/Http/Middleware/DisableFloc.php(17): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#48 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\DisableFloc->handle()
#49 /home/ploi/example.nl/vendor/statamic/cms/src/Http/Middleware/CheckMultisite.php(14): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#50 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\CheckMultisite->handle()
#51 /home/ploi/example.nl/vendor/statamic/cms/src/Http/Middleware/CheckComposerJsonScripts.php(14): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#52 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\CheckComposerJsonScripts->handle()
#53 /home/ploi/example.nl/vendor/statamic/cms/src/Http/Middleware/PoweredByHeader.php(18): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#54 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\PoweredByHeader->handle()
#55 /home/ploi/example.nl/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php(59): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#56 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Barryvdh\\Debugbar\\Middleware\\InjectDebugbar->handle()
#57 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#58 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#59 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()
#60 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#61 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#62 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#63 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#64 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
#65 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(99): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#66 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#67 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#68 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()
#69 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#70 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#71 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#72 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()
#73 /home/ploi/example.nl/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#74 /home/ploi/example.nl/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()
#75 {main}
"} 

Steps to reproduce

  1. Database orders & customers enabled
  2. Update cart using POST request to https://example.nl/!/simple-commerce/cart

Environment

Environment Application Name: example Laravel Version: 10.47.0 PHP Version: 8.2.15 Composer Version: 2.7.2 Environment: production Debug Mode: OFF URL: example.nl Maintenance Mode: OFF

Cache Config: CACHED Events: NOT CACHED Routes: CACHED Views: CACHED

Drivers Broadcasting: log Cache: statamic Database: mysql Logs: stack / daily, flare Mail: smtp Queue: sync Session: file

Simple Commerce Currencies: EUR, EUR, EUR, EUR Gateways: Mollie Repository: Customer: DuncanMcClean\SimpleCommerce\Customers\EloquentCustomerRepository Repository: Order: DuncanMcClean\SimpleCommerce\Orders\EloquentOrderRepository Repository: Product: DuncanMcClean\SimpleCommerce\Products\EntryProductRepository Shipping Methods: MTC Pallet Zending, PostNL International Tax Engine: DuncanMcClean\SimpleCommerce\Tax\Standard\TaxEngine

Statamic Addons: 7 Antlers: runtime Sites: 4 (Nederlands, Duits, Engels, Pools) Stache Watcher: Enabled Static Caching: Disabled Version: 4.52.0 PRO

Statamic Addons aerni/advanced-seo: 2.4.4 duncanmcclean/simple-commerce: 6.0.10 heidkaemper/statamic-toolbar: 1.1.2 statamic-rad-pack/runway: 6.4.1 statamic/collaboration: 0.8.1 web10/back-in-stock-notifier: dev-main web10/language-switcher: dev-main

duncanmcclean commented 2 months ago

Sorry, I'm unable to reproduce this issue.

Whenever I provide the email for an existing customer, it uses the existing customer rather than creating a new one, as expected. 🤔

https://github.com/duncanmcclean/simple-commerce/blob/6.x/src/Http/Controllers/Concerns/HandlesCustomerInformation.php#L71-L72

https://github.com/duncanmcclean/simple-commerce/blob/6.x/tests/Http/Controllers/CartControllerTest.php#L361-391

Does the email you entered match exactly with the email of the existing customer in the database? Is the casing the same or different?

How are you providing the customer information? Can you provide the payload you're submitting to the endpoint?

Web10-Joris commented 2 months ago

Hi Duncan,

The emails are an exact match. Customer information is sent to /!/simple-commerce/cart with this body:

{
    "_token": "bgp7mte25uc1gUPzbWCpV8VkXIvw3EE4PIum0F7u",
    "_redirect": "/checkout/offsite/mollie",
    "_error_redirect": "/checkout",
    "_request": null,
    "name": "Name",
    "customer": {
        "phone": "1345678"
    },
    "email": "email@example.com",
    "shipping_name": null,
    "shipping_address": "street",
    "shipping_address_line2": "street2",
    "shipping_city": "city",
    "shipping_postal_code": "postal",
    "shipping_country": "NL",
    "use_shipping_address_for_billing": null
}

Looks like it is inserting the customer twice in a very short amount of time (within 1-2 sec), causing the second attempt to fail. Since this is the already existing record:

{
    "data":
    [
        {
            "id": 183,
            "name": "Name",
            "email": "email@example.com",
            "phone": "1345678",
            "data": "{\"published\": true}",
            "created_at": "2024-04-13 21:22:55",
            "updated_at": "2024-04-13 21:22:55"
        }
    ]
}

and this is the SQL statement of the error:

insert into
  `customers` (`email`, `data`, `updated_at`, `created_at`)
values
  (
    email@example.com,
    { "published": true },
    2024 -04 -13 21: 22: 55,
    2024 -04 -13 21: 22: 55
  ) 

They both have the same updated_at & created_at datetime. So probably my axios script is sending the data too quickly, is that possible? (it only sends it after the user changes Country or Region fields). Causing the HandlesCustomerInformation to create a new customer because it could not found one, while the other execution is injecting it into the database? Otherwise I also don't know where this could come from...

duncanmcclean commented 2 months ago

Hmm, are you sending multiple requests to the server at the same time? There must be some kind of race condition happening.

Web10-Joris commented 1 month ago

I continue to receive these errors intermittently and am unsure where it comes from. I'll close this issue for now. I'm in the process of building a new store and will monitor to see if the issue also shows up there.