Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add specific type and min supported amount in error response for the payments below min supported amount #10112

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from

Conversation

mgascam
Copy link
Contributor

@mgascam mgascam commented Jan 8, 2025

Fixes #10092

Changes proposed in this Pull Request

This pull request enhances error handling for payment captures in the WooCommerce Payments plugin. The primary changes include adding support for a new error type related to capturing amounts that are too small, updating the associated error messages, and adding relevant tests.

Key changes:

Error Handling Enhancements

  • Introduced a new error type wcpay_capture_error_amount_too_small for cases where the capture amount is too small.
  • Updated the getErrorMessage function to handle the new error type and format the error message with the minimum amount and currency.

Documentation Updates

  • Added documentation for the new error type in the REST API documentation.

These changes provide clearer error messages and ensure that the system handles edge cases more gracefully.

Testing instructions

Pre-requisites

  1. As a merchant, enable the capture later feature in the Settings
  2. The minimum capturable amount for USD is $0.50, so I suggest having a product with a price below that limit.

Capturing an authorization

  1. As a shopper, purchase an order above the minimum amount limit. For instance, you can add several items of a product that has a unit price below the minimum amount.
  2. As a merchant, edit the recently purchased order so the order amount is below the limit. For instance, you can remove some items from the order.
  3. Navigate to Payments > Transactions > Uncaptured and locate the authorization.
  4. Click the capture button and observe a similar error to this one:
Screenshot 2025-01-09 at 17 27 11
  1. Navigate to the order details page and observe a similar note is added with the details of the error:
Screenshot 2025-01-09 at 17 29 27

Capturing a terminal payment

  1. Repeat steps 1 and 2 from the list above. Take a note of the Stripe payment intent id and the order id.
  2. Make a POST request to the capture_terminal_payment endpoint, making sure to use the correct payment intent id and order id:
curl -X POST https://example.com/wp-json/wc/v3/payments/orders/42/capture_terminal_payment \
	-u consumer_key:consumer_secret \
	-H "Content-Type: application/json" \
	-d '{
    "payment_intent_id": "pi_ZZZZZZZZZZZZZZZZAAAAAAAA"
}'
  1. Check that the response is similar to this one:
{
  "code": "wcpay_capture_error",
  "message": "Payment capture failed to complete with the following message: Minimum required amount was not reached. The minimum amount to capture is 0.5 USD.",
  "data": {
    "status": 400,
    "extra_details": {
      "minimum_amount": 50,
      "minimum_amount_currency": "USD"
    },
    "error_type": "amount_too_small"
  }
}

  • Run npm run changelog to add a changelog file, choose patch to leave it empty if the change is not significant. You can add multiple changelog files in one PR by running this command a few times.
  • Covered with tests (or have a good reason not to test in description ☝️)
  • Tested on mobile (or does not apply)

Post merge

@botwoo
Copy link
Collaborator

botwoo commented Jan 8, 2025

Test the build

Option 1. Jetpack Beta

  • Install and activate Jetpack Beta.
  • Use this build by searching for PR number 10112 or branch name add/10092-add-specific-type-and-min-supported-amount-in-error-response-for-the-payments-below-min-supported-amount in your-test.site/wp-admin/admin.php?page=jetpack-beta&plugin=woocommerce-payments

Option 2. Jurassic Ninja - available for logged-in A12s

🚀 Launch a JN site with this branch 🚀

ℹ️ Install this Tampermonkey script to get more options.


Build info:

  • Latest commit: f9d7ac1
  • Build time: 2025-01-16 18:09:29 UTC

Note: the build is updated when a new commit is pushed to this PR.

Copy link
Contributor

github-actions bot commented Jan 8, 2025

Size Change: +1.12 kB (0%)

Total Size: 1.36 MB

Filename Size Change
release/woocommerce-payments/dist/index.js 302 kB +220 B (0%)
release/woocommerce-payments/dist/multi-currency-switcher-block.js 61.1 kB +189 B (0%)
release/woocommerce-payments/dist/multi-currency.js 57.8 kB +179 B (0%)
release/woocommerce-payments/dist/order.js 42.5 kB +171 B (0%)
release/woocommerce-payments/dist/payment-gateways.js 38.9 kB +183 B (0%)
release/woocommerce-payments/dist/settings.js 224 kB +179 B (0%)
ℹ️ View Unchanged
Filename Size
release/woocommerce-payments/assets/css/admin.css 1.37 kB
release/woocommerce-payments/assets/css/admin.rtl.css 1.37 kB
release/woocommerce-payments/assets/css/success.css 182 B
release/woocommerce-payments/assets/css/success.rtl.css 184 B
release/woocommerce-payments/dist/blocks-checkout-rtl.css 2.64 kB
release/woocommerce-payments/dist/blocks-checkout.css 2.64 kB
release/woocommerce-payments/dist/blocks-checkout.js 55.6 kB
release/woocommerce-payments/dist/cart-block.js 17.2 kB
release/woocommerce-payments/dist/cart.js 5.73 kB
release/woocommerce-payments/dist/checkout-rtl.css 1.13 kB
release/woocommerce-payments/dist/checkout.css 1.13 kB
release/woocommerce-payments/dist/checkout.js 33.6 kB
release/woocommerce-payments/dist/express-checkout-rtl.css 229 B
release/woocommerce-payments/dist/express-checkout.css 229 B
release/woocommerce-payments/dist/express-checkout.js 15.7 kB
release/woocommerce-payments/dist/frontend-tracks.js 854 B
release/woocommerce-payments/dist/index-rtl.css 39.5 kB
release/woocommerce-payments/dist/index.css 39.4 kB
release/woocommerce-payments/dist/multi-currency-analytics.js 1.08 kB
release/woocommerce-payments/dist/multi-currency-rtl.css 4.47 kB
release/woocommerce-payments/dist/multi-currency.css 4.47 kB
release/woocommerce-payments/dist/order-rtl.css 730 B
release/woocommerce-payments/dist/order.css 730 B
release/woocommerce-payments/dist/payment-gateways-rtl.css 1.33 kB
release/woocommerce-payments/dist/payment-gateways.css 1.33 kB
release/woocommerce-payments/dist/plugins-page-rtl.css 386 B
release/woocommerce-payments/dist/plugins-page.css 386 B
release/woocommerce-payments/dist/plugins-page.js 20.1 kB
release/woocommerce-payments/dist/product-details-rtl.css 433 B
release/woocommerce-payments/dist/product-details.css 436 B
release/woocommerce-payments/dist/product-details.js 12.5 kB
release/woocommerce-payments/dist/settings-rtl.css 11.6 kB
release/woocommerce-payments/dist/settings.css 11.5 kB
release/woocommerce-payments/dist/subscription-edit-page.js 703 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal-rtl.css 524 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal.css 524 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal.js 20.2 kB
release/woocommerce-payments/dist/subscription-product-onboarding-toast.js 730 B
release/woocommerce-payments/dist/subscriptions-empty-state-rtl.css 120 B
release/woocommerce-payments/dist/subscriptions-empty-state.css 120 B
release/woocommerce-payments/dist/subscriptions-empty-state.js 19.3 kB
release/woocommerce-payments/dist/tokenized-express-checkout-rtl.css 229 B
release/woocommerce-payments/dist/tokenized-express-checkout.css 229 B
release/woocommerce-payments/dist/tokenized-express-checkout.js 16.6 kB
release/woocommerce-payments/dist/tos-rtl.css 235 B
release/woocommerce-payments/dist/tos.css 235 B
release/woocommerce-payments/dist/tos.js 21.8 kB
release/woocommerce-payments/dist/woopay-direct-checkout.js 6.13 kB
release/woocommerce-payments/dist/woopay-express-button.js 25 kB
release/woocommerce-payments/dist/woopay-rtl.css 4.31 kB
release/woocommerce-payments/dist/woopay.css 4.28 kB
release/woocommerce-payments/dist/woopay.js 71 kB
release/woocommerce-payments/includes/subscriptions/assets/css/plugin-page.css 625 B
release/woocommerce-payments/includes/subscriptions/assets/js/plugin-page.js 814 B
release/woocommerce-payments/vendor/automattic/jetpack-assets/build/i18n-loader.js 2.46 kB
release/woocommerce-payments/vendor/automattic/jetpack-assets/build/jetpack-script-data.js 772 B
release/woocommerce-payments/vendor/automattic/jetpack-assets/src/js/i18n-loader.js 1.02 kB
release/woocommerce-payments/vendor/automattic/jetpack-assets/src/js/script-data.js 69 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/babel.config.js 163 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/identity-crisis.css 2.47 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/identity-crisis.js 14.2 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/identity-crisis.rtl.css 2.47 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-connection.css 10 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-connection.js 28.4 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-connection.rtl.css 10 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-admin-create-user.css 198 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-admin-create-user.js 280 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-admin-create-user.rtl.css 198 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-login.css 625 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-login.js 333 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-login.rtl.css 626 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-users.js 424 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/tracks-ajax.js 521 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/tracks-callables.js 585 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-admin-create-user.css 215 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-admin-create-user.js 521 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-login.css 721 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-login.js 412 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-users.js 632 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/about.css 1.04 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin-empty-state.css 294 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin-order-statuses.css 408 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin.css 3.59 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/checkout.css 301 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/modal.css 746 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/view-subscription.css 574 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/wcs-upgrade.css 414 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/admin-pointers.js 543 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/admin.js 9.4 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/jstz.js 6.78 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/jstz.min.js 3.84 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/meta-boxes-coupon.js 545 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/meta-boxes-subscription.js 2.52 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/moment.js 22.2 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/moment.min.js 11.7 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/payment-method-restrictions.js 1.29 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/wcs-meta-boxes-order.js 507 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/payment-methods.js 358 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/single-product.js 428 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/view-subscription.js 1.38 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/wcs-cart.js 782 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/modal.js 1.09 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/wcs-upgrade.js 1.26 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/build/index.css 391 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/build/index.js 3.04 kB

compressed-size-action

@mgascam mgascam changed the title [WIP] Add specifics to the amount too small exception Add specific type and min supported amount in error response for the payments below min supported amount Jan 9, 2025
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
@mgascam mgascam marked this pull request as ready for review January 9, 2025 17:05
@mgascam mgascam requested a review from a team January 9, 2025 17:05
@mgascam mgascam self-assigned this Jan 9, 2025
Copy link
Contributor

@deepakpathania deepakpathania left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments.

const errorMessages: Record< string, string > = {
const getAmountTooSmallError = ( error: WCPayError ): string => {
const currency =
error.data?.extra_details?.minimum_amount_currency ?? 'USD';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel rather than falling back to these defaults, maybe we could omit the message in case the server doesn't provide the extra_details. Or do we see any benefits of this approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Instead of using defaults, I'm falling back to this message 'The payment amount is too small to be processed.' when the server doesn't provide the extra_details 7cf9528. WDYT?

return sprintf(
/* translators: %1$s: minimum amount, %2$s: currency code */
__(
'The minimum amount to capture is %1$s %2$s.',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capture seems like an internal term, to the end shopper, we shouldn't expose this directly IMO. 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right—capture does feel like an internal term. I've switched to using process in this commit 01795d0.

return new WP_Error(
'wcpay_capture_error',
'amount_too_small' === $error_code ? 'wcpay_capture_error_amount_too_small' : 'wcpay_capture_error',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how I feel about having a separate error code just for this scenario, maybe there can be some other meta field that indicates this to the clients and the error code remains the same for all? Open to thoughts here since I don't have a strong opinion either ways.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe there can be some other meta field that indicates this to the clients and the error code remains the same for all?

This is a valid point @deepakpathania . Instead of creating a new error code, we could use metadata to handle this specific case:

{
  "code": "wcpay_capture_error",
  "message": "Payment capture failed to complete with the following message: Minimum required amount was not reached. The minimum amount to capture is 0.5 USD.",
  "data": {
    "status": 400,
    "error_type": "amount_too_small",
    "extra_details": {
      "minimum_amount": 50,
      "minimum_amount_currency": "usd"
    }
  }
}

While I implemented the specific wcpay_capture_error_amount_too_small code as requested in the issue, I like your suggestion of using metadata. It would reduce the number of error codes we need to maintain and provide more flexibility for future error types.

Before making this change though, I'd like to check with @kidinov (who reported the issue) to ensure this approach would meet their needs.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgascam @deepakpathania 👋 Thanks for working on that!

I am still validating this with the iOS folks so that I will give a definite answer on Monday.

A quick question here. minimum_amount are those cents? What will it be in other currency subunit systems, e.g., when there are no subdivisions or it's 1/1000 currency?

Also, minimum_amount_currency is this ISO 4217 or something else? It looks like that, but usually, it's in upper case, not lower case therefore confirming

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @kidinov for your quick response!

I am still validating this with the iOS folks so that I will give a definite answer on Monday.

Great 👍 Looking forward to the confirmation.

minimum_amount are those cents? What will it be in other currency subunit systems, e.g., when there are no subdivisions or it's 1/1000 currency?

Yes, the amounts are in the currency's minor unit, following Stripe's convention. For example, in USD, the minimum_amount is expressed in cents, so it should be converted to the main unit (by dividing by 100) if needed.

For currencies with no subdivisions, such as JPY, the amount is already expressed in the main unit, so no conversion is necessary. Regarding currencies with a 1/1000 subdivision, I don't believe any are currently supported, but please correct me if I'm mistaken.

Additionally, if it simplifies your workflow, we can provide the amounts in the currency's main unit to avoid conversion on your end. We have a utility function, WC_Payments_Utils::interpret_stripe_amount, that handles this. Just let me know your preference.

Also, minimum_amount_currency is this ISO 4217 or something else? It looks like that, but usually, it's in upper case, not lower case therefore confirming

Yes, the value follows ISO 4217, and you're right—it should be in uppercase. I'll ensure the necessary updates are made to include this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgascam

Thanks for the clarification!

iOS folks confirmed that the API is fine

The only point that came up is that the same changes needed to be done for the Stripe plugin; otherwise, it'll be a regression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sharing more context @kidinov. Let me clarify a few things, based on my own understanding:

  1. You're right that both WooPayments and Stripe plugins need to handle this case consistently for the best app experience. This change is specifically for WooPayments, and a similar change would need to be made in the Stripe plugin (woocommerce-gateway-stripe).

  2. Regarding potential regressions:

    • This change adds additional error details (minimum_amount and currency) while maintaining backwards compatibility
    • Older versions of WooPayments will continue to return the basic error response. So I'd suggest that
      the apps can implement progressive enhancement - use the additional details when available, fall back to basic error handling when not. Please correct me if I'm wrong, but this aproach ensures no regression for stores using either plugin or different versions. WDYT?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgascam

That's slightly more complicated than that, unfortunately. Android app was handling it using an error that comes from Stripe's SDK processing payment step until that was broken by a change, when for some reason for Woo payments account:

we enabled a special gate on the WooPayments account that would allow for very small charges under the 50cent limit documented

p1736206944981919/1735228071.598689-slack-C01G168NFC2

And the error started to come without any details from the "capture" step. So the current state of the android app is that the error handled in a general way, and users don't have a clear picture why the error happens. Missing implementation of this error in the Stripe plugin won't cause a regression, it will be just as right now for the Stripe plugin users.

iOS app handled it differently. They hardcoded the values from different countries and checked them locally. So, for them, it will be a regression, as they will have to remove their locally built logic for this case and switch to the handling of the error based on the error provided by the backend. So for them, missing change in Stripe plugin will be regression

In any case, if the change for stripe will be released, ideally at a similar timing with WooPayments, this will be ideal 🙏

Copy link
Contributor Author

@mgascam mgascam Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kidinov thanks for the additional context. I created woocommerce/woocommerce-gateway-stripe#3728 to tackle the Stripe plugin change. I'll try to apply the same changes we have in this PR and request a review of the team that maintains the Stripe plugin. EDIT: The codebases are somewhat similar, which initially led me to overestimate how quickly I could introduce the changes. After taking a closer look, I believe it would be better for someone more familiar with the codebase to implement the changes.

About the release of the changes, I'd say we should first release the change in the Stripe plugin, to avoid the iOS app regression. What do you think? But one thing is not clear: how this change would affect previous versions of the mobile apps? Would they break?

cc: @deepakpathania

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think? But one thing is not clear: how this change would affect previous versions of the mobile apps? Would they break?

The old versions will behave the same way they behave now, as the API change is not breaking

The issue lies with the new versions of the iOS app, which will expect the changed API but lack client-side handling for the case. If they operate against Stripe or WooPayments, which have not yet implemented the change, so as a result, users will only see a generic error. We will make the necessary changes to the iOS app with some delay to minimize the number of instances like this. Considering that the issue is relatively minor and will eventually resolve with updates to the plugins, I believe this approach is acceptable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stripe gateway isn't managed by us directly so someone from @Automattic/quark would have to port these changes to that plugin as well in case there is preference to release them at the same time.

However I do agree that there should be a progressive approach followed by apps in case this additional information is provided from our end since the change doesn't break backward compatibility.

…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
…rted-amount-in-error-response-for-the-payments-below-min-supported-amount
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add specific type and min supported amount in error response for the payments below min supported amount
4 participants