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

improve checkout validation & order creation (448) #513

Closed
InpsydeNiklas opened this issue Feb 24, 2022 · 31 comments
Closed

improve checkout validation & order creation (448) #513

InpsydeNiklas opened this issue Feb 24, 2022 · 31 comments
Labels
enhancement New feature or request
Milestone

Comments

@InpsydeNiklas
Copy link
Member

Describe the Bug

Following update 1.6.2, the PayPal order is created first and the WooCommerce checkout validation & order creation happens afterward. This change was done to improve plugin compatibility and prevent orders from incorrectly receiving a "Failed" status.
This change means users can leave all WooCommerce checkout fields empty and click the PayPal button. The user can log into their account, confirm the payment, and only after returning to the checkout page, the user would get an error about the checkout fields being empty. No confirmation email will be sent and it should be obvious the payment was not successful.

But users should not receive an error like this after confirming the payment screen. If possible, errors should always be thrown before.
Preferably, it should work like this instead:

  • the user clicks the PayPal button
  • validate WooCommerce checkout fields
  • create draft WC order and assign new WP user if registering in the process or Pending Payment order (PayPal Standard style)
  • create PayPal order
  • after completing the payment in PayPal, create persisting WooCommerce order

To Reproduce

  1. visit the checkout page
  2. leave required fields empty
  3. click PayPal smart button
  4. log in and confirm payment
  5. checkout page will reload with an error about the required fields missing
  6. user can either fill the required fields and start from step 3 again or reload the page, fill the fields and click the "Place order" button, as the payment has already been pre-authorized.

Expected Behavior

Checkout form field validation should occur immediately. Clicking the smart button when not all required fields are set throws an error immediately without letting the user log in to their PayPal account.

Actual Behavior

Checkout form field validation occurs after the PayPal order has been created, letting the user pre-authorize the payment without having provided all details. No WooCommerce order is created.

Environment

  • WordPress Version 5.9.1
  • WooCommerce Version 6.2.1
  • Plugin Version 1.7.0-test5

Additional Details

We can intercept the PayPal button click using its onclick parameter (like we do already) and then calling actions.reject() like discussed here if we need to stop the payment process (though it seems like it can be used only until this handler finishes, so e.g. sending a request to server and then rejecting will not work).

But the main problem is that it seems like WC does not provide any way to trigger validation. In checkout.js it performs some basic validation after user finishes input (e.g. input lost focus) and also there is validation on the server when submitting the form, probably here. I do not see any way to trigger the server validation separately, especially in a documented/stable way, I mean we can try to e.g. add our ajax endpoint and create a class inheriting WC_Checkout to call its protected methods but we cannot be sure that it works in all supported versions or at least will continue to work + it fires some actions which may break things if something uses them assuming that they happen only during form submission. And the JS validation is also not accessible outside the WC script.
In our view, WC should come up with some way to trigger validation.

@zerfl
Copy link

zerfl commented Mar 9, 2022

Great explanation of the issue. Is there perhaps a workaround available in the meantime while the team is working on this?

@Squeezemind
Copy link

Any news about this fix?

@ramonolivencia
Copy link

I have the same issue.

@simo-git
Copy link

Please increase the priority to solve this bug because the response message appearing at the top of the checkout page after the invalid submission is not helpful for a customer.

Plugin version: 1.7.0
WC checkut message: [INVALID_REQUEST] Request is not well-formed, syntactically incorrect, or violates schema. https://developer.paypal.com/docs/api/orders/v2/#error-INVALID_PARAMETER_SYNTAX

As temporary workaround I've seen that the minimum requirement to receive a correct answer to load the credit card form is verify the billing email is formally correct.

@cs9k
Copy link

cs9k commented Apr 12, 2022

I would love a solution for this as well, we run some code that prevents checkout if you're below a certain minimum order value, and as you described the validation only triggers after everything PayPal related is done and WooCommerce tries to submit the form, which might cause a lot of confusion depending on the customer, esp. if they get the feeling that they already paid for it.

@a-danae
Copy link
Contributor

a-danae commented Apr 22, 2022

@InpsydeNiklas I dug here a bit and pinged the core team. How about calling process_checkout when the PayPal button is clicked? Here, for example. Calling that method triggers the validation and closes the modal on failure.

The implementation in the extension handles the requests differently from how WC does it so this will require some refactoring. If we go this way, we'd have to integrate via the actions and filters inside the process_checkout method, which will probably increase the scope of this issue.

@AlexP11223
Copy link
Contributor

@a-danae we were doing this before, but it was causing some issues. #341

I think we need some function that only validates the data without creating the order etc.

And as I was saying above I don't see such public API available currently.

I do not see any way to trigger the server validation separately, especially in a documented/stable way, I mean we can try to e.g. add our ajax endpoint and create a class inheriting WC_Checkout to call its protected methods (like validate_checkout) but we cannot be sure that it works in all supported versions or at least will continue to work + it fires some actions which may break things if something uses them assuming that they happen only during form submission. And the JS validation is also not accessible outside the WC script.

@perler
Copy link

perler commented Apr 28, 2022

As other plugins manage to do exactly this, maybe risk a look at their open source code? 🙄

@AlexP11223
Copy link
Contributor

@perler I will look into this, but as I can see so far, this plugin did not need to solve this problem. It does not have PayPal smart buttons like here, it simply uses the standard WC checkout workflow and after checkout is finished it redirects to PayPal.

@perler
Copy link

perler commented Apr 29, 2022

ok, but it checks the checkboxes before it forwards to paypal. isn't this the same routine?

@kwisatz
Copy link

kwisatz commented May 2, 2022

Chiming in here to say this is absolutely critical. I cannot understand how this plug-in could have been released in its current state. Even without additional plug-ins adding form fields that would need server-side validation, it seems essential to me that form validation happens before the paypal payment process is initiated which doesn't seem to be the case currently.

@InpsydeNiklas
Copy link
Member Author

The PayPal Plus integration and PayPal Payments have fundamental differences regarding the checkout flow. PayPal Payments implements the PayPal smart buttons with advanced functionality such as Vaulting, making this entire process more complex.
The checkout form field validation occurred immediately up until version 1.6.1. You can try it with this old plugin version if this is critical for you. But it's not something we would recommend as this behavior was changed deliberately to improve plugin compatibility until there is a better solution than the original implementation.

@perler
Copy link

perler commented May 2, 2022 via email

@kwisatz
Copy link

kwisatz commented May 3, 2022

Why not create a two-step process? The first step submits and validates the checkout form. Actually submitting it will allow any third party plug-ins to do their server-side validation too. If the form validates, then, and only then load the paypal sdk and their buttons.
I have recently implemented Paypal Payments too and while I agree that it's cumbersome – mostly due to confusing documentation – nothing prevents us from choosing when we want to initialize the process and display the buttons.

I think you've seen the feedback on this plug-in's Wordpress.org page and holding on to the current workflow isn't going to cut it.

@kaushikasomaiya
Copy link

User at (5186558-zen) got confused with this behavior.

@MysticMedusa
Copy link

It's so flaky at my end that we're only offering Stripe at checkout.

There was error after error after error - revenue down from Paypal payments unable to be processed and extra support time on understandably frustrated customers + myself and devs in fruitless hours spent going around in circles at Paypal.
I am extremely stressed by this and it makes me doubt Woo itself - this was forced upon Woo customers in place of a solution that worked. But a non-working checkout kills businesses.

WooCommerce did not disclose the issues with the plugin - if they had told me that even apart from all the bugs, customers would need to complete an extra step, I would have to cancel billing agreements with thousands of customers and that it does not work with Woo Subscriptions, I would have still needed to begin looking for another payment processor (having been with Woo Commerce since 2016) but I would have been doing so with significantly less stress.

@a-danae
Copy link
Contributor

a-danae commented May 16, 2022

Why not create a two-step process? The first step submits and validates the checkout form. Actually submitting it will allow any third party plug-ins to do their server-side validation too. If the form validates, then, and only then load the paypal sdk and their buttons.

Hey @kwisatz. Thanks for the suggestion.

If I'm getting you correctly, to trigger the first step, the customer would have to click on some "submit" button signaling that the form has been filled and ready to be submitted. Then the form would be submitted. Then they'd see and click on the "PayPal" button to make the payment, and the form would be submitted again with the payment information included.

As far as I see, doing this double submission would trigger a flow similar to what I proposed above bringing back the problems mentioned by @AlexP11223 above, and for which this approach was updated.

Glad to learn if that's not the case in your experience.

@AlexP11223
Copy link
Contributor

AlexP11223 commented May 17, 2022

@a-danae I think this suggestion is about doing it similarly to the old PayPal Plus plugin. First the user finishes the standard WC checkout, and then there is a separate page with PayPal buttons to make the payment for the created WC order.

@a-danae
Copy link
Contributor

a-danae commented May 17, 2022

I think this suggestion is about doing it similarly to the old PayPal Plus plugin.

@AlexP11223 gotcha. Then scratch my last comment.

From a short conversation with the core team and as another option... How about shifting to using the Store API? Basically, you would update the cart manually using the cart API, and then use the checkout's get checkout method to get draft order information. You can then process payment, and if the payment is successful, create the order using process order endpoint.

This would require some more effort. But even if the validation method gets public, this is the recommended approach in the long term.

@MysticMedusa
Copy link

After a long to and fro with Woo, went back to Paypal Standard which works perfectly but of course Woo said they don't support it and it could break with any Woo update. They said they will continue to "recommend" paypal payments for its "superior features"

So if, like most Woo customers, you want to use Paypal with Woo, they present you with a choice between:

(1) A functioning plugin that collects revenue/retains customers but could break at any time because Woo no longer support it

(2) A semi-functional plugin that works haphazardly, loses customers who understandably don't want three-step checkouts, adds several hours of customer support problem solving time a week, generates wonky data in Woo reports - eg: money not collected shows as revenue, does not work with Woo Subscriptions unless you cancel all your members and re-subscribe them.

Having said that, I don't want to do this with thousands of members but i tried on staging and it didn't work anyway.

Woo support were really pleasant but really, this is apparently as good as it gets - they should have warnings on Woo Subs that you won't be able to do it using Paypal.

@bwalger
Copy link

bwalger commented May 30, 2022

+1 waiting for the update. We lose many orders due to this strange behaviour of plugin. I can understand that it is the "fault" of the user not checking or filling out necessary fields. But a good and usability tested plugin should not allow going to checkout in this status.

--> In my eyes this is a critical missing feature and we cannot wait for WC for a solution. We cannot wait and drink tea :-(

@a-danae a-danae removed their assignment May 30, 2022
@Dinamiko Dinamiko added this to the 1.8.2 milestone Jun 14, 2022
@Dinamiko
Copy link
Contributor

Fixed by #675 and #680.

@InpsydeNiklas
Copy link
Member Author

There's a new pre-release for the 1.8.2 update with a basic validation available here.
This basic validation should help prevent confusing behavior while a better long-term solution remains under investigation.

@bizrockman
Copy link

bizrockman commented Jul 17, 2022

I have version 1.9.0 installed, but still I can hit the paypal button without filling out any of the required fields. What leads to that issue descriped in this topic.

I also tried version 1.9.1-test1, but without any change.

As a quick hack I activated the old PayPal option in Woocommerce -
functions.php (wp-content/themes/YourTheme/functions.php)

add_filter( 'woocommerce_should_load_paypal_standard', '__return_true' );

Not that nice like this module, but it works as a fallback and quick hack until this issue is solved.

@anton-esin
Copy link

anton-esin commented Oct 19, 2022

@InpsydeNiklas
What is the basic validation? We leave the address and name fields empty and it will still allow to hit paypal buttons. Is it intended behavior right? We have Plugin version 1.9.3. Wordpress: 6.0.3. Woocommerce 6.7.0.

@AlexP11223
Copy link
Contributor

@anton-esin Are these the standard WC fields? Do you see any errors in the DevTools console?

@anton-esin
Copy link

@AlexP11223
Yes those are standard WC fields. And no I don't see any errors in the DevTools console.

@MysticMedusa
Copy link

MysticMedusa commented Oct 19, 2022 via email

@nroccogit
Copy link

nroccogit commented Jun 8, 2023

Hi, I'm using the action "woocommerce_checkout_process" to validate some custom checkout fields.
For example I've date of birth to check because customer needs to be at least 18 years old to buy.
The user can click the Paypal Buttons on checkout and proceed with the order because the custom validation hooked on action "woocommerce_checkout_process" doesn't run as usual with other payments methods that use the "standard woocommerce #place_order" button.
Now I need to disable Paypal.
Does anyone has a fix for this?

This is a sample of my code:

add_action('woocommerce_checkout_process', 'my_custom_checkout_validation', 1);
function my_custom_checkout_validation() {
	if( isset($_POST['billing_birth_date']) && ! empty($_POST['billing_birth_date']) ){
		// Get customer age from birthdate
		$age = date_diff(date_create($_POST['billing_birth_date']), date_create('now'))->y;
		// Checking age and display an error notice avoiding checkout 
		if( $age < 18 ){
			wc_add_notice('Age ERROR', "error" );
		}
	}
}

@AlexP11223
Copy link
Contributor

@nroccogit try using woocommerce_after_checkout_validation instead.

@nroccogit
Copy link

nroccogit commented Jun 8, 2023

@nroccogit try using woocommerce_after_checkout_validation instead.
@AlexP11223 Thanks!!! It Works!!

If anyone is interested (you can use also $_POST):

add_action('woocommerce_after_checkout_validation', 'my_customer_checkout_validation', 10, 2);
function my_customer_checkout_validation($data,$errors) {
	if( isset($data['billing_birth_date']) && ! empty($data['billing_birth_date']) ){
		// Get customer age from birthdate
		$age = date_diff(date_create($data['billing_birth_date']), date_create('now'))->y;
		// Checking age and display an error notice avoiding checkout 
		if( $age < 18 ){
			wc_add_notice('Age ERROR', "error" );
		}
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests