woocommerce / woocommerce-gateway-stripe

The official Stripe Payment Gateway for WooCommerce
https://wordpress.org/plugins/woocommerce-gateway-stripe/
232 stars 204 forks source link

Duplicate charges when placing order (if "Link By Stripe" was previously connected) #3299

Closed thomasjvu closed 2 months ago

thomasjvu commented 2 months ago

Describe the bug

While "Link by Stripe" is enabled and a customer is trying to make a new subscription purchase by Credit Card (Stripe) on an account that is already connected to "Link by Stripe", an error will be received after clicking on the place order button.

"Invalid or missing payment token fields."

A new payment intent will be generated each time the "place order" button is clicked. When going directly into the order. When looking at the order in the admin dashboard, you will see multiple payment intents (if the button was clicked multiple times) and that the order is in the "Pending payment" status.

However, if you go into the Stripe dashboard for the account, you will see that each of those multiple payments has succeeded. This is important because it can result in duplicate payments for certain customers who are purchasing subscription products, leading merchants to have to refund users and pay transaction fees out of pocket.

Here are some relevant tickets where I am investigating the matter for other users: 8442182-zen, 8428284-zen

To Reproduce

Pre-requisites:

Steps to reproduce the behavior:

  1. Create a subscription product.
  2. Ensure "Link by Stripe" is enabled within Stripe.
  3. Purchase the subscription product using "Link by Stripe" with the associated card.
  4. Try to purchase a different subscription product (or do a manual renewal of the existing product) using the Credit/Debit card method within Stripe instead.
  5. You will receive an error message: "Invalid or missing payment token fields." each time you press "Place Order".
  6. Go into the Orders page and see that a payment intent was created each time the "Place Order" button was pressed, and that the order status is "Pending Payment".
  7. Go into your Stripe dashboard and see that each payment intent was actually successfully paid for.

Expected behavior Instead, I would expect to be able to pay subscription orders using the Credit/Debit card method within Stripe even if "Link by Stripe" was used and not to be charged multiple times.

Screenshots Here's a video where I go through the entire process:

https://github.com/user-attachments/assets/661b6d29-753f-4cdb-8d7e-75c339829da5

Environment (please complete the following information):

System Status Report ``` ### WordPress Environment ### WordPress address (URL): [Redacted] Site address (URL): [Redacted] WC Version: 9.1.2 Legacy REST API Package Version: The Legacy REST API plugin is not installed on this site. Action Scheduler Version: ✔ 3.7.4 Log Directory Writable: ✔ WP Version: 6.6 WP Multisite: – WP Memory Limit: 512 MB WP Debug Mode: – WP Cron: ✔ Language: en_US External object cache: ✔ ### Server Environment ### Server Info: nginx PHP Version: 8.2.21 PHP Post Max Size: 2 GB PHP Time Limit: 1200 PHP Max Input Vars: 6144 cURL Version: 8.7.1 OpenSSL/3.0.13 SUHOSIN Installed: – MySQL Version: 10.6.18-MariaDB-log Max Upload Size: 2 GB Default Timezone is UTC: ✔ fsockopen/cURL: ✔ SoapClient: ✔ DOMDocument: ✔ GZip: ✔ Multibyte String: ✔ Remote Post: ✔ Remote Get: ✔ ### Database ### [REDACTED] ### Post Type Counts ### attachment: 1 page: 7 post: 1 product: 6 revision: 6 shop_order_placehold: 67 wp_global_styles: 1 wp_navigation: 1 ### Security ### Secure connection (HTTPS): ✔ Hide errors from visitors: ✔ ### Active Plugins (3) ### WooCommerce Stripe Gateway: by WooCommerce – 8.5.1 WooCommerce Subscriptions: by WooCommerce – 6.5.0 WooCommerce: by Automattic – 9.1.2 ### Inactive Plugins (4) ### Akismet Anti-spam: Spam Protection: by Automattic - Anti-spam Team – 5.3.3 Jetpack: by Automattic – 13.7-a.3 Jetpack Protect: by Automattic - Jetpack Security team – 2.2.0 Pressable OnePress Login: by Pressable – 1.3.2 ### Dropin Plugins () ### advanced-cache.php: advanced-cache.php object-cache.php: Memcached ### Settings ### API Enabled: – Force SSL: – Currency: USD ($) Currency Position: left Thousand Separator: , Decimal Separator: . Number of Decimals: 2 Taxonomies: Product Types: external (external) grouped (grouped) simple (simple) subscription (subscription) variable (variable) variable subscription (variable-subscription) Taxonomies: Product Visibility: exclude-from-catalog (exclude-from-catalog) exclude-from-search (exclude-from-search) featured (featured) outofstock (outofstock) rated-1 (rated-1) rated-2 (rated-2) rated-3 (rated-3) rated-4 (rated-4) rated-5 (rated-5) Connected to WooCommerce.com: – Enforce Approved Product Download Directories: ✔ HPOS feature enabled: ✔ Order datastore: Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore HPOS data sync enabled: – ### Logging ### Enabled: ✔ Handler: Automattic\WooCommerce\Internal\Admin\Logging\LogHandlerFileV2 Retention period: 30 days Level threshold: – Log directory size: 549 KB ### WC Pages ### Shop base: #5 - /shop/ Cart: #6 - /cart/ - Contains the woocommerce/cart block Checkout: #7 - /checkout/ - Contains the woocommerce/checkout block My account: #8 - /my-account/ Terms and conditions: ❌ Page not set ### Theme ### Name: Storefront Version: 4.6.0 Author URL: https://woocommerce.com/ Child Theme: ❌ – If you are modifying WooCommerce on a parent theme that you did not build personally we recommend using a child theme. See: How to create a child theme WooCommerce Support: ✔ ### Templates ### Overrides: – ### Subscriptions ### WCS_DEBUG: ✔ No Subscriptions Mode: ✔ Live Subscriptions Live URL: https://test-stripe.mystagingwebsite.com Subscriptions-core Library Version: 7.3.0 Subscription Statuses: trash: 5 wc-active: 4 wc-pending: 5 WooCommerce Account Connected: ❌ No Report Cache Enabled: ✔ Yes Cache Update Failures: ✔ 0 failure ### Store Setup ### Country / State: United States (US) — Massachusetts ### Subscriptions by Payment Gateway ### Cash on delivery: trash: 1 Stripe: trash: 4 wc-active: 4 wc-pending: 5 ### Payment Gateway Support ### Cash on delivery: products Stripe: products refunds tokenization add_payment_method subscriptions subscription_cancellation subscription_suspension subscription_reactivation subscription_amount_changes subscription_date_changes subscription_payment_method_change subscription_payment_method_change_customer subscription_payment_method_change_admin multiple_subscriptions ### Admin ### Enabled Features: activity-panels analytics product-block-editor coupons core-profiler customize-store customer-effort-score-tracks import-products-task experimental-fashion-sample-products shipping-smart-defaults shipping-setting-tour homescreen marketing mobile-app-banner navigation onboarding onboarding-tasks product-custom-fields remote-inbox-notifications remote-free-extensions payment-gateway-suggestions shipping-label-banner subscriptions store-alerts transient-notices woo-mobile-welcome wc-pay-promotion wc-pay-welcome-page launch-your-store Disabled Features: experimental-blocks minified-js pattern-toolkit-full-composability product-pre-publish-modal printful settings async-product-editor-category-field product-editor-template-system Daily Cron: ✔ Next scheduled: 2024-07-20 13:41:13 +00:00 Options: ✔ Notes: 65 Onboarding: skipped ### Action Scheduler ### Complete: 183 Oldest: 2024-07-16 13:41:28 +0000 Newest: 2024-07-19 17:44:10 +0000 Failed: 3 Oldest: 2024-07-16 14:04:33 +0000 Newest: 2024-07-18 20:47:20 +0000 Pending: 10 Oldest: 2024-07-19 19:31:55 +0000 Newest: 2024-10-19 17:46:58 +0000 ### Status report information ### Generated at: 2024-07-19 19:27:12 +00:00 ```

Additional context I believe what is happening here is that the system is detecting this associated card within Link by Stripe and making it necessary for the user to pay for subscriptions with that method moving forward. Thus, when a regular Credit/Debit card method is used within Stripe, the "Invalid or missing payment token fields." message occurs.

I was not able to get the issue to occur with accounts that did not have the associated card shown next to their "Link by Stripe". I've tried making other users, but it only shows on my main account. All that said, disabling "Link by Stripe" fixes the issue temporarily.

Please also note that in WooCommerce, the payment appears as "Credit/Debit" but as "Link" in the Stripe Dashboard.

Edit: I added a video (which I thought I added previously but didn't) and some additional notes for context.

icepicknz commented 2 months ago

I just came here to check on this error.

I had a customer go to place an order yesterday, also using Link; WooCommerce saw 3 different payment intents and a single charge (shown on the invoice). Looking at Stripe portal payments, I can see all 3 Link payment intents were actually charged, thus the customer has paid 3 times.

I havent looked into this too much as I just came here to see if it had happened to anyone else and this item was top of the list. Running the latest plugin, latest Woo, latest WP.

While my customer is not a subscription customer, rather a one off payment, they are a signed in member using Link.

stefanoginella commented 2 months ago

This happened also to a client of mine in the last few weeks. We temporarily disabled Link By Stripe because of that.

The website is selling subscription products with the WooCommerce Subscriptions plugin, and for a few orders we had multiple charges to the client when the Link payment method was selected (in the Stripe dashboard those were the only transactions with the Link logo). We had to refund the client, but that's a costly operation because of the fees.

This happened ONLY with Link. When the user paid normally with a credit card (I assume because they aren't signed up with Link) everything went smoothly.

a-danae commented 2 months ago

👋 hey! Thanks for the report and the replication steps!

We've just released Stripe 8.5.2, which includes the fix for this behavior, so I'm marking this issue as closed.