Paypal-checkout-components: ppxo_unhandled_error when calling reject method of paypal.Promise

Created on 16 Feb 2018  路  18Comments  路  Source: paypal/paypal-checkout-components

Description

I have the following code:

        payment() {
            return new paypal.Promise((resolve, reject) => {
                reject(new Error('Invalid form!'));
            });
        },

for the payment handler.
This is a simplified version of what I have. In reality there's an ajax request being done to the server (as I use the server-side REST method of integrating PayPal) and if the form validation fails (server response has errors) I call reject else I call resolve.

Everything seems to be working ok, the only problem is that in the browser console I get this error: ppxo_unhandled_error

window.console.(anonymous function) | @ | index.js:48
-- | -- | --
聽 | print | @ | logger.js:244
聽 | log | @ | init.js:34
聽 | error | @ | checkout.js:151
聽 | (anonymous) | @ | checkout.js:25202
聽 | dispatchPossiblyUnhandledError | @ | promise.js:334
聽 | (anonymous) | @ | child.js:33
聽 | setTimeout (async) | 聽 | 聽
聽 | ZalgoPromise.reject | @ | child.js:23
聽 | (anonymous) | @ | fallback.js:33
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.reject | @ | child.js:36
聽 | (anonymous) | @ | component.js:26
聽 | (anonymous) | @ | fallback.js:34
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.reject | @ | child.js:36
聽 | (anonymous) | @ | fallback.js:33
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.reject | @ | child.js:36
聽 | (anonymous) | @ | fallback.js:35
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.reject | @ | child.js:36
聽 | (anonymous) | @ | componentTemplate.jsx:109
聽 | (anonymous) | @ | fallback.js:34
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.reject | @ | child.js:36
聽 | (anonymous) | @ | promise.js:410
聽 | respond | @ | browser.js:35
聽 | _RECEIVE_MESSAGE_TYPE.(anonymous function) | @ | checkout.js:4024
聽 | receiveMessage | @ | server.js:132
聽 | messageListener | @ | clean.js:16
聽 | postMessage (async) | 聽 | 聽
聽 | (anonymous) | @ | types.js:30
聽 | SEND_MESSAGE_STRATEGIES.(anonymous function) | @ | types.js:26
聽 | (anonymous) | @ | strategies.js:55
聽 | ZalgoPromise.try | @ | hacks.js:53
聽 | (anonymous) | @ | strategies.js:49
聽 | ZalgoPromise.map | @ | hacks.js:40
聽 | (anonymous) | @ | strategies.js:45
聽 | ZalgoPromise.try | @ | hacks.js:53
聽 | sendMessage | @ | checkout.js:4031
聽 | respond | @ | checkout.js:4008
聽 | (anonymous) | @ | checkout.js:4019
聽 | (anonymous) | @ | fallback.js:34
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.then | @ | containerTemplate.jsx:50
聽 | _RECEIVE_MESSAGE_TYPE.(anonymous function) | @ | checkout.js:4016
聽 | receiveMessage | @ | server.js:132
聽 | messageListener | @ | clean.js:16
聽 | postMessage (async) | 聽 | 聽
聽 | (anonymous) | @ | types.js:30
聽 | SEND_MESSAGE_STRATEGIES.(anonymous function) | @ | types.js:26
聽 | (anonymous) | @ | strategies.js:55
聽 | ZalgoPromise.try | @ | hacks.js:53
聽 | (anonymous) | @ | strategies.js:49
聽 | ZalgoPromise.map | @ | hacks.js:40
聽 | (anonymous) | @ | strategies.js:45
聽 | ZalgoPromise.try | @ | hacks.js:53
聽 | sendMessage | @ | checkout.js:4031
聽 | (anonymous) | @ | browser.js:137
聽 | ZalgoPromise | @ | promise.js:403
聽 | (anonymous) | @ | browser.js:162
聽 | (anonymous) | @ | fallback.js:12
聽 | ZalgoPromise.dispatch | @ | componentTemplate.jsx:113
聽 | ZalgoPromise.then | @ | containerTemplate.jsx:50
聽 | (anonymous) | @ | browser.js:150
聽 | ZalgoPromise.try | @ | hacks.js:53
聽 | request | @ | browser.js:95
聽 | _send | @ | amd-define.js:1
聽 | wrapper | @ | log.js:11
聽 | memoizeWrapper | @ | util.js:22
聽 | (anonymous) | @ | config.js:289
聽 | obj.(anonymous function) | @ | checkout.js:25202
聽 | callOriginal | @ | checkout.js:25202
聽 | (anonymous) | @ | misc.js:17
聽 | obj.(anonymous function) | @ | checkout.js:25202
聽 | renderCheckout | @ | checkout.js:129
聽 | clickButton | @ | button.js:50
聽 | (anonymous) | @ | button.js:77

screen shot 2018-02-16 at 20 27 36

Is this normal? Is there anyway that I can reject the payment request (so the PayPal window gets closed) but not have this error in the console?

So to reiterate I based my code on the following example: https://github.com/paypal/paypal-checkout/blob/master/docs/button.md#advanced-integration

Steps to reproduce

Follow this example https://github.com/paypal/paypal-checkout/blob/master/docs/button.md#advanced-integration and just call reject method.

Affected browsers

  • [x] Chrome
  • [x] Safari
  • [x] Firefox
outdated

All 18 comments

This can also be reproduced here https://developer.paypal.com/demo/checkout/#/pattern/server
with following code:

<!DOCTYPE html>

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://www.paypalobjects.com/api/checkout.js"></script>
</head>

<body>
    <div id="paypal-button-container"></div>

    <script>
        paypal.Button.render({

            env: 'sandbox', // sandbox | production

            // Show the buyer a 'Pay Now' button in the checkout flow
            commit: true,

            // payment() is called when the button is clicked
            payment: function() {
               return new paypal.Promise(function(resolve, reject) {
                    reject(new Error("Err!"));                   
               });
            },

            // onAuthorize() is called when the buyer approves the payment
            onAuthorize: function(data, actions) {

                // Set up a url on your server to execute the payment
                var EXECUTE_URL = '/demo/checkout/api/paypal/payment/execute/';

                // Set up the data you need to pass to your server
                var data = {
                    paymentID: data.paymentID,
                    payerID: data.payerID
                };

                // Make a call to your server to execute the payment
                return paypal.request.post(EXECUTE_URL, data)
                    .then(function (res) {
                        window.alert('Payment Complete!');
                    });
            }

        }, '#paypal-button-container');
    </script>
</body>

Rejecting the promise in payment() is counted as an error case, and logged as such. Ideally form validation should be done prior to the button click, as the form changes state. There's an example of that here: https://developer.paypal.com/demo/checkout/#/pattern/validation

@bluepnume even if we catch it? (as in implement promise.catch()).
Then it should be up to the user to handle it. As now it's a bit confusing why we get ppxo_unhandled_error (unhandled - when we do handle it). I would expect to get unhandled only if I don't catch it.

Can you share the code you're running to catch the code?

<!DOCTYPE html>

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://www.paypalobjects.com/api/checkout.js"></script>
</head>

<body>
    <div id="paypal-button-container"></div>

    <script>
        paypal.Button.render({

            env: 'sandbox', // sandbox | production

            // Show the buyer a 'Pay Now' button in the checkout flow
            commit: true,

            // payment() is called when the button is clicked
            payment: function() {
               var paymentPromise = new paypal.Promise(function(resolve, reject) {
                    reject(new Error("Err!"));                   
               });
               paymentPromise.catch(function() {
                   console.log("Handled!");
               });
               return paymentPromise;
            },

            // onAuthorize() is called when the buyer approves the payment
            onAuthorize: function(data, actions) {

                // Set up a url on your server to execute the payment
                var EXECUTE_URL = '/demo/checkout/api/paypal/payment/execute/';

                // Set up the data you need to pass to your server
                var data = {
                    paymentID: data.paymentID,
                    payerID: data.payerID
                };

                // Make a call to your server to execute the payment
                return paypal.request.post(EXECUTE_URL, data)
                    .then(function (res) {
                        window.alert('Payment Complete!');
                    });
            }

        }, '#paypal-button-container');
    </script>
</body>

Now in the console you get "Handled!" and then after that still get ppxo_unhandled_error

Also looking at the promise implementation of the reject method here https://github.com/krakenjs/zalgo-promise/blob/master/src/promise.js#L107

seems that dispatchPossiblyUnhandledError(error) is always called as this.errorHandled is never set to true.

Can you please try adding an onError callback to the button?

Same thing with:

<!DOCTYPE html>

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://www.paypalobjects.com/api/checkout.js"></script>
</head>

<body>
    <div id="paypal-button-container"></div>

    <script>
        paypal.Button.render({

            env: 'sandbox', // sandbox | production

            // Show the buyer a 'Pay Now' button in the checkout flow
            commit: true,

            // payment() is called when the button is clicked
            payment: function() {
               return new paypal.Promise(function(resolve, reject) {
                    reject(new Error("Err!"));                   
               });
            },

            onError: function(err) {

            },

            // onAuthorize() is called when the buyer approves the payment
            onAuthorize: function(data, actions) {

                // Set up a url on your server to execute the payment
                var EXECUTE_URL = '/demo/checkout/api/paypal/payment/execute/';

                // Set up the data you need to pass to your server
                var data = {
                    paymentID: data.paymentID,
                    payerID: data.payerID
                };

                // Make a call to your server to execute the payment
                return paypal.request.post(EXECUTE_URL, data)
                    .then(function (res) {
                        window.alert('Payment Complete!');
                    });
            }

        }, '#paypal-button-container');
    </script>
</body>

Greetings !

To correct this problem, check that the PHP runtime environment is set to "Stable" and not "Legacy" or "Testing". Check this on your hosting configuration.

With kind regards.

@Squamifer huh, what? What PHP environment? We're not using PHP.

This message, if analyzed, means that there is an error in the environment of your Hosting. However, if you are not in PHP, try to integrate this code into the class of your PayPalPayment file (where the functions and classes are located):

_$ch = curl_init();_

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

With kind regards.

This has nothing to do with my hosting. It's a client side error (JavaScript) so I don't really see how the hosting is involved. You will get same error even if you just have a simple HTML page (implement the client side REST option for PayPal integration).

Thanks for trying to help but what you are suggesting has nothing to do with the actual issue.

As I said in my previous comment this seems to be a problem with https://github.com/krakenjs/zalgo-promise/blob/master/src/promise.js#L107

If a user implements the catch method then dispatchPossiblyUnhandledError(error) should not be called.

Hi @Squamifer -- appreciate you taking the time to support, but this issue isn't anything to do with php. This is a client side script.

@danechitoaie -- I'll try to take the time this week to repro this problem and give you an update. Thanks for your patience.

@bluepnume No problem. Thank you for your support.

This is now resolved -- if you have an onError, the error will not be thrown globally.

How is it going now? I'm facing the same problem.

Any update will be high appreciated

Thank you,

Please open a new issue if you're still facing a problem.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JeromeDeLeon picture JeromeDeLeon  路  4Comments

Wr4i7h picture Wr4i7h  路  4Comments

gastonyelmini picture gastonyelmini  路  3Comments

Soulpancake000 picture Soulpancake000  路  5Comments

domtripodi picture domtripodi  路  5Comments