2
votes

I'm trying to integrate PayPal Express Checkout using their checkout.js and instructions provided here: https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/advanced-integration/

Whenever I click the 'PayPal' button, I get an error about indexOf of undefined coming from the checkout.js script. I do get the modal popup flash very quickly and then disappear.

TypeError: Cannot read property 'indexOf' of undefined
    at Object.onSuccess (https://www.paypalobjects.com/api/checkout.js:11449:30)
    at _loop2 (https://www.paypalobjects.com/api/checkout.js:1670:62)
    at SyncPromise.dispatch (https://www.paypalobjects.com/api/checkout.js:1700:29)
    at SyncPromise.then (https://www.paypalobjects.com/api/checkout.js:1718:18)
    at Component.buildUrl (https://www.paypalobjects.com/api/checkout.js:11448:40)
    at Object.onSuccess (https://www.paypalobjects.com/api/checkout.js:8770:57)
    at _loop2 (https://www.paypalobjects.com/api/checkout.js:1670:62)
    at SyncPromise.dispatch (https://www.paypalobjects.com/api/checkout.js:1700:29)
    at SyncPromise.then (https://www.paypalobjects.com/api/checkout.js:1718:18)
    at Function.syncPromiseTry [as try] (https://www.paypalobjects.com/api/checkout.js:1763:42)"

The line it's referencing is within checkout.js:

return props.payment().then(function(token) {
    if (token.indexOf("BA-") === 0) {

The code I'm using is straight from PayPal's documentation:

        paypal.Button.render({
            env: 'sandbox', // Specify 'sandbox' for the test environment
            payment: function() {
                var CREATE_PAYMENT_URL = '/api/checkout/create-express-payment';
                paypal.request.post(CREATE_PAYMENT_URL)
                    .then(function(data) {
                        console.log('Payment ID: ', data.paymentId);
                        resolve(data.paymentId);
                    })
                    .catch(function(err) {
                        reject(err);
                    });
            },

            onAuthorize: function(data, actions) {
                // Execute the payment here, when the buyer approves the transaction
            }

        }, '#paypal-button');

I can confirm data.paymentId is in fact coming back with a proper Payment ID, and I've even tried hard coding it into the resolve call. This error seems to occur before my internal API even gets called to get the payment ID (and then again after it gets called).

I have been trying to reach PayPal technical support for a week now and have not even received an acknowledgement after many requests. Additionally I can't test the REST APIs on Heroku because their sandbox takes over 30 seconds to respond and Heroku times out. This will be the last time I ever use PayPal on a project... and at this rate looks like I need to change from this project ASAP.

3

3 Answers

5
votes

I had this issue. My use case is Express Checkout (client) and Paypal SDK (server). Paypal refers to this as Advanced Integration.

I resolved this problem as follows:

I let my Server return a JSON object as response with the single property "paymentID". This is explained in the Advanced Integration page of the Paypal documentation, however its importance is not stated clearly enough.

Look at step 4 of that documentation, which makes clear what you need to return:

  1. Your server sends a response to your client to pass back the payment ID:

    {
        "paymentID": "PAY-0J356327TH335450NK56Y2PQ"
    }
    

The checkout.js processes this return value as the "token" parameter.

Here is the code that works for me:

 paypal.Button.render({
   env: 'sandbox',
   payment: function(resolve, reject) {
     var CREATE_PAYMENT_URL = 'http://localhost/api/payment/create';
     paypal.request.post(CREATE_PAYMENT_URL, {
     // js object used to create payment
     }).then(function(data) {
         resolve(data.paymentID);
     }).catch(function(err) {
         reject(err);
     });
   },
   onAuthorize: function(data) {
     var EXECUTE_PAYMENT_URL = 'http://localhost/api/payment/execute';
     paypal.request.post(EXECUTE_PAYMENT_URL, {
     // js object used to complete payment
     // needs to include properties for paymentID and payerID
     }).then(function(data) {
     // process results after transaction is completed
     }).catch(function(err) {});
   },
   onCancel: function(data) {}
 }, '#paypal-button');
3
votes

Right now you have:

payment: function() {

This needs to be:

payment: function(resolve, reject) {

Otherwise the resolve and reject functions aren't going to be available to pass the paymentID back to checkout.js.

0
votes

This solution was really helpful, but instead of the payid we can pass the token id received from paypal to resolve method, resolve(data.tokenid); and instead of paypal.request.post method we can make a ajax call, the code will be like,

<script src="https://www.paypalobjects.com/api/checkout.js"></script>
<div id="paypal-buttoncheckout"></div>
    paypal.Button.render({
        env: "sandbox", // Optional: specify 'sandbox' environment
        locale: //your respective locale,
        payment: function(resolve, reject) { 
            var CREATE_PAYMENT_URL = //URL for creating payment
            $.ajax({
               type:'POST',
               dataType : 'html',
               contentType: 'application/html',
               url : CREATE_PAYMENT_URL,
               success : function(data){    
                   resolve(data.tokenid);
               },
               error : function(response){

               }
           });

      },

            onAuthorize: function(data) {
                // Note: you can display a confirmation page before executing
                var EXECUTE_PAYMENT_URL = //reauthorize URL;
                paypal.request.post(EXECUTE_PAYMENT_URL,
                        { paymentID: data.paymentID, payerID: data.payerID,token: data.paymentToken })
                        .then(function(data) { window.location = //SUCCESS PAGE })
                        .catch(function(err) { window.location = //ERROR PAGE});
            },

            onCancel: function(data, actions) {
                window.location = //ON CANCEL URL
            }



        }, '#paypal-buttoncheckout');

Thanks, Manoj