FatFractal customer forums



Author Topic: PayPal Integration  (Read 3503 times)

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
PayPal Integration
« on: February 14, 2014, 07:57:01 PM »
Do you have any support or sample code to integrate PayPal transactions with NoServer?  I'm working on implementing these verifications and wondered if there was anything already done I could use:

https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #1 on: February 15, 2014, 07:34:24 PM »
A related question: can NoServer extensions send requests to other servers?  (I hope so.)  How?

gkc

  • Administrator
  • *****
  • Posts: 375
    • View Profile
Re: PayPal Integration
« Reply #2 on: February 15, 2014, 10:10:30 PM »
Hi Eric,

I haven't done any integration with PayPal myself, so have nothing to offer there I'm afraid

With regards to making requests to other servers - absolutely. FYI: NoServer leverages the wonderful RingoJS project for its CommonJS modules. Have a look at the ringojs site here: http://ringojs.org

Here's some code which (among other things) does a http get : https://gist.github.com/3917922

Here's some code which posts a request to Twilio
Code: [Select]
var ff = require ('ffef/FatFractal');
var hc = require('ringo/httpclient');

function postToTwilio (data) {
    var twilioAccount = '.....';
    var twilioAuthToken = '.....';
    var twilioFromNumber = '.....';

    var twilioRequestUrl = 'https://'
        + twilioAccount + ':' + twilioAuthToken + '@api.twilio.com'
        + '/2010-04-01/Accounts/' + twilioAccount + '/SMS/Messages.json';

    var twilioRequestBody = { From: '+' + twilioFromNumber, To: '+' + data.to, Body: data.body };

    ff.logger.info("POST to " + twilioRequestUrl + " : " + JSON.stringify(twilioRequestBody));

    var twilioResponse = hc.post(twilioRequestUrl, twilioRequestBody);

    ff.logger.info("Twilio response status: " + twilioResponse.status);
    ff.logger.info("Twilio response: " + JSON.stringify(twilioResponse.content, null, 2));

    return twilioResponse.status;
}
« Last Edit: February 15, 2014, 10:23:43 PM by gkc »

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #3 on: February 17, 2014, 08:18:55 PM »
I'm having some trouble translating from PayPal's documentation to the way to accomplish it with RingoJS.  I'm probably just lacking some basic intro-to-http stuff that is obvious to most people.  So, it may not be a topic for this forum, but in case someone will have mercy on me and answer:

I think figuring out this example would put me on the path I need.  On the PayPal docs they give this example cURL command (on https://developer.paypal.com/docs/integration/direct/make-your-first-call/):

Code: [Select]
curl -v https://api.sandbox.paypal.com/v1/oauth2/token \
  -H "Accept: application/json" \
  -H "Accept-Language: en_US" \
  -u "EOJ2S-Z6OoN_le_KS1d75wsZ6y0SFdVsY9183IvxFyZp:EClusMEUk8e9ihI7ZdVLF5cZ6y0SFdVsY9183IvxFyZp" \
  -d "grant_type=client_credentials"

I gather from the cURL documentation that "-H" specifies headers, "-u" specifies the username & password, and "-d" specifies POST data.  So, I'll be using ringo's post() function (http://ringojs.org/api/master/ringo/httpclient/#post).

That function has arguments for the url and the data, but what about the headers and the username/password?  How do I specify those?  I see RingoJS includes a Headers class (http://ringojs.org/api/master/ringo/utils/http/#Headers), but I do not see how to use that class, and that still does not address username/password.

dave

  • Administrator
  • *****
  • Posts: 52
    • View Profile
Re: PayPal Integration
« Reply #4 on: February 17, 2014, 10:48:29 PM »
Hi Eric,

To reproduce this example, you'll need to use the 'request' method of the Ringo httpclient, which gives the most flexibility (see docs at http://ringojs.org/api/v0.8/ringo/httpclient/#request). That method accepts a username and password, among other things. So, translating your curl example looks like this:

application.ffdl
Code: [Select]
CREATE EXTENSION /paypal UNSECURED AS javascript:require('scripts/test').paypal();

test.js
Code: [Select]
var ff = require('ffef/FatFractal');
var http = require('ringo/httpclient');

exports.paypal = function() {
    var request = {
        url: "https://api.sandbox.paypal.com/v1/oauth2/token",
        data: {
            grant_type: "client_credentials"
        },
        method: "POST",
        headers: {
            Accept: "application/json",
            "Accept-Language": "en_US"
        },
        username: "EOJ2S-Z6OoN_le_KS1d75wsZ6y0SFdVsY9183IvxFyZp",
        password: "EClusMEUk8e9ihI7ZdVLF5cZ6y0SFdVsY9183IvxFyZp"
    };

    var response = http.request(request);
   
    var r = ff.response();
    if (response) {
        r.result = JSON.parse(response.content);
        r.responseCode = 200;
        r.statusMessage = "Success";
    }
};

Dave
« Last Edit: February 17, 2014, 10:53:20 PM by dave »

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #5 on: February 18, 2014, 12:56:18 AM »
Awesome; this is going to be a huge help.  Thank you!

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #6 on: February 18, 2014, 07:30:13 PM »
Your code works beautifully when I put it in directly in the script specified for the extension in application.ffdl.  But it hangs when I move it to a module.  Is it ok to put http requests in modules?  I have it set up like this:

test.js
Code: [Select]
var paypal = require("scripts/PayPal")
exports.payPalTest = function() {
paypal.testLogin()
}

PayPal.js
Code: [Select]
var u = require("scripts/ServerUtils")
var http = require("ringo/httpclient")

function oathLogin() {
u.log("About to request paypal access token")
var response = http.request({
url: "https://api.sandbox.paypal.com/v1/oauth2/token",
data: {
grant_type: "client_credentials"
},
method: "POST",
headers: {
Accept: "application/json",
"Accept-Language": "en_US"
},
username: "EOJ2S-Z6OoN_le_KS1d75wsZ6y0SFdVsY9183IvxFyZp",
password: "EClusMEUk8e9ihI7ZdVLF5cZ6y0SFdVsY9183IvxFyZp"
})
u.log("Got OAuth token", response)
}

exports.testLogin = oathLogin

It logs the first message ("About to request paypal access token"), but not the second.  Instead it starts printing messages like this:
Quote
ERROR - com.fatfractal.module.common.interfaces.IModule - 2014.02.18 08 at 04:12:35.409 PM PST - Found long-running (32 seconds) request ( moduleExecutor [NoserverModuleExecutor : Domain [<snip>] Context [<snip>] Request URI [/<snip>/ff/ext/paypal-test]] ) in thread Thread[pool-3-thread-1,5,main]

Am I doing modules wrong?  Or should I not be doing modules?

kevin@fatfractal.com

  • Administrator
  • *****
  • Posts: 56
    • View Profile
Re: PayPal Integration
« Reply #7 on: February 19, 2014, 12:52:50 AM »
Hi Eric:

Dave and I have been trying to reproduce this issue and have not been able to :-( So far, your code seems to work just fine for us.

The only thing I can suggest is to get a little deeper into the PayPal response (you probably want to fill out error handling anyways).

Here is what you might want to try:

Code: [Select]
var paypal = require("scripts/PayPal")
exports.payPalTest = function() {
var r = ff.response();
var response = paypal.testLogin();
r.result = response.result;
r.responseCode=response.status;
r.statusMessage = response.message;
}

Code: [Select]
var u = require("scripts/ServerUtils")
var http = require("ringo/httpclient")

function oathLogin() {
u.log("About to request paypal access token");
var response = http.request({
url: "https://api.sandbox.paypal.com/v1/oauth2/token",
data: {
grant_type: "client_credentials"
},
method: "POST",
headers: {
Accept: "application/json",
"Accept-Language": "en_US"
},
username: "EOJ2S-Z6OoN_le_KS1d75wsZ6y0SFdVsY9183IvxFyZp",
password: "EClusMEUk8e9ihI7ZdVLF5cZ6y0SFdVsY9183IvxFyZp",
});
var responseObj;
if(response.status == 200) {
responseObj = {status:response.status, result:JSON.parse(response.content).data, message:"OK"}
u.log("Got OAuth token", responseObj);
} else {
responseObj = {status:response.status, result:null, message:response.message}
u.log("ERROR: " + response.status, response.message);
};
return responseObj;
}

exports.testLogin = oathLogin;



« Last Edit: February 19, 2014, 12:55:53 AM by kevin@fatfractal.com »

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #8 on: February 19, 2014, 01:33:32 AM »
Ah HA!  I got it.  Thank you for looking into this.  Copy-pasting your code got me on the right scent.  My problem was not that http.request() was hanging, but that my log command was hanging.  It turns out JSON.stringify(response) is a bad idea (as in, it just hangs forever).  Who knew?

Sorry for bothering you.

kevin@fatfractal.com

  • Administrator
  • *****
  • Posts: 56
    • View Profile
Re: PayPal Integration
« Reply #9 on: February 19, 2014, 11:08:21 AM »
Cool!

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #10 on: February 20, 2014, 09:21:45 PM »
Where does one go to get support for Ringo?  I tried googling some parts of this error message, but didn't find any useful results:
Quote
Done? true
<snip>
ERROR - STACK_TRACE - 2014.02.20 08 at 06:04:09.594 PM PST - /ff/ext/paypal-test - anonymous - Thread[pool-3-thread-1,5,main] - org.mozilla.javascript.EcmaError: TypeError: Cannot call method "getResponseCode" of null (ringo/httpclient.js#417)
        at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3691)
        <snip>
        at org.mozilla.javascript.gen.ringo_httpclient_js_18._c_anonymous_21(rin
go/httpclient.js:417)
        <snip>
        at org.mozilla.javascript.gen.PayPal_js_16._c_makeCall_2(PayPal.js:43)
      <snip>

The relevant portion of my code:
Code: [Select]
var response = http.request(etc)
u.log("Done? " + response.done)
if (response.status == 200) { // <-- The error is from this line.  Yes, really.
return JSON.parse(response.content)

Any ideas on where to go with this problem, or do you have thoughts?

kevin@fatfractal.com

  • Administrator
  • *****
  • Posts: 56
    • View Profile
Re: PayPal Integration
« Reply #11 on: February 21, 2014, 01:52:20 AM »
Couple of things:

I don't think that it is the cause, but you should probably be sure and end the statements with ;'s.
Code: [Select]
var response = http.request(etc); // < here
u.log("Done? " + response.done); // < and here
if (response.status == 200) { // <-- The error is from this line.  Yes, really.
return JSON.parse(response.content); // < and here
That said, I think the issue may actually be with this line:
Code: [Select]
return JSON.parse(response.content)
Try, instead
Code: [Select]
return JSON.parse(response.content).data;

Eric

  • Jr. Member
  • **
  • Posts: 61
    • View Profile
Re: PayPal Integration
« Reply #12 on: February 21, 2014, 01:57:05 AM »
Try, instead
Code: [Select]
return JSON.parse(response.content).data;

I will look into that, thank you.  However, that cannot be my problem because the error happens on the line above that.  Yes, it actually happens on:
Code: [Select]
if (response.status == 200) { // <-- The error is from this line.  Yes, really.

kevin@fatfractal.com

  • Administrator
  • *****
  • Posts: 56
    • View Profile
Re: PayPal Integration
« Reply #13 on: February 21, 2014, 01:58:17 AM »
Here is a full example of an extension that I wrote for comparison (hope this helps)...

Code: [Select]
exports.updateFoo = function() {
    var newObj = ff.getExtensionRequestData().httpContent;
    var r = ff.response();
    if(newObj.fooNumber) {
        var fooUrl = "https://sometest/" + newObj.guid;
        var obj = {summary:newObj.summary, details:newObj.details, fooNumber:newObj.fooNumber};
        try {
            var request = {
                url: fooUrl,
                data: JSON.stringify(obj),
                method: "PUT",
                contentType: "application/json",
            };
            var httpExchange = hc.request(request);
            if(httpExchange.status == 200) {
                var retObj = JSON.parse(httpExchange.content).data;
                print("propagateImage retObj " + JSON.stringify(retObj));
                retObj.guid = retObj.id.toString();
                retObj.createdBy = "system";
                retObj.clazz = "Foo";
                retObj.ffRL = "/Foos";
                retObj.ffUrl = "/ff/resources/Foos/" +  retObj.id.toString();
                r.result = retObj;
                r.responseCode="200";
                r.statusMessage = "Extensions.js updateFoo succeeded";
            } else {
                print("updateFoo httpExchange status " + httpExchange.status + ", error " + httpExchange.content);
                r.result = null;
                r.responseCode=httpExchange.status;
                r.statusMessage = httpExchange.content;
            }
        } catch(e) {
            print("updateFoo error " + JSON.stringify(e));
            r.result = null;
            r.responseCode="500";
            r.statusMessage = e;
        }
    } else {
        r.result = null;
        r.responseCode="405";
        r.statusMessage = "API requires a foo number to update a Foo.";
    }
};

kevin@fatfractal.com

  • Administrator
  • *****
  • Posts: 56
    • View Profile
Re: PayPal Integration
« Reply #14 on: February 21, 2014, 02:08:31 AM »
So - in that case, you may want to try a more granular approach...
Code: [Select]
var response = http.request(etc);
u.log("Done? " + response.done);
if (response) {
if (response.status) {
if (response.status == 200) { // <-- The error is from this line.  Yes, really.
return JSON.parse(response.content).data;
} else u.log("Got an error: " + response.status);
} else u.log("Did not receive a response.status");
} else u.log("Did not receive a response");

 

Copyright © FatFractal customer forums