Skip to content
This repository has been archived by the owner on May 12, 2020. It is now read-only.

Commit

Permalink
Merge branch 'rel-1.0.0'
Browse files Browse the repository at this point in the history
Merge in stable release branch
  • Loading branch information
avidas committed Sep 11, 2014
2 parents bbbf21a + 8bf8bc0 commit 0606c17
Show file tree
Hide file tree
Showing 91 changed files with 2,789 additions and 1,222 deletions.
2 changes: 1 addition & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = function (grunt) {
},
jshint: {
all: {
src: ["lib/*.js", "test/*.js", "samples/**/*.js"],
src: ["lib/*.js", "test/*.js", "samples/**/*.js", "lib/resources/*.js"],
options: {
jshintrc: ".jshintrc"
}
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ NPM status:

Repository for PayPal's Node SDK (node.js version >=0.6.x) and Node samples for REST API. Refer [Node.js Sample Reference App ](https://github.com/paypal/rest-api-sample-app-nodejs) for a sample web app implementing the REST APIs.

> **v1.0.0 notice**: If upgrading from paypal rest sdk 0.*, Please view Breaking Changes in release_notes.md
> **Before starting to use the sdk, please be aware of the [existing issues and currently unavailable or upcoming features](https://github.com/paypal/rest-api-sdk-python/wiki/Existing-Issues-and-Unavailable%5CUpcoming-features) for the REST APIs. (which the sdks are based on)**
## Usage
Expand All @@ -21,12 +23,12 @@ To write an app using the SDK
* Require 'paypal-rest-sdk' in your file

```js
var paypal_sdk = require('paypal-rest-sdk');
var paypal = require('paypal-rest-sdk');
```
* Create config options, with parameters (mode, client_id, secret).

```js
paypal_sdk.configure({
paypal.configure({
'mode': 'sandbox', //sandbox or live
'client_id': 'EBWKjlELKMYqRNQ6sYvFo64FtaRLRR5BdHEESmha49TM',
'client_secret': 'EO422dn3gQLgDbuwqTjzrFgFtaRLRR5BdHEESmha49TM'
Expand All @@ -45,7 +47,7 @@ To write an app using the SDK
"last_name": "Shopper"
};

paypal_sdk.credit_card.create(card_data, function(error, credit_card){
paypal.creditCard.create(card_data, function(error, credit_card){
if (error) {
console.log(error);
throw error;
Expand All @@ -60,36 +62,40 @@ To write an app using the SDK

```js
// OpenID configuration
paypal_sdk.configure({
paypal.configure({
'openid_client_id': 'CLIENT_ID',
'openid_client_secret': 'CLIENT_SECRET',
'openid_redirect_uri': 'http://example.com' });

// Authorize url
paypal_sdk.openid_connect.authorize_url({'scope': 'openid profile'});
paypal.openIdConnect.authorizeUrl({'scope': 'openid profile'});

// Get tokeninfo with Authorize code
paypal_sdk.openid_connect.tokeninfo.create("Replace with authorize code", function(error, tokeninfo){
paypal.openIdConnect.tokeninfo.create("Replace with authorize code", function(error, tokeninfo){
console.log(tokeninfo);
});

// Get tokeninfo with Refresh code
paypal_sdk.openid_connect.tokeninfo.refresh("Replace with refresh_token", function(error, tokeninfo){
paypal.openIdConnect.tokeninfo.refresh("Replace with refresh_token", function(error, tokeninfo){
console.log(tokeninfo);
});

// Get userinfo with Access code
paypal_sdk.openid_connect.userinfo.get("Replace with access_code", function(error, userinfo){
paypal.openIdConnect.userinfo.get("Replace with access_code", function(error, userinfo){
console.log(userinfo);
});

// Logout url
paypal_sdk.openid_connect.logout_url("Replace with tokeninfo.id_token");
paypal.openIdConnect.logoutUrl("Replace with tokeninfo.id_token");
```
* For creating [Subscription Payments](https://developer.paypal.com/docs/integration/direct/create-billing-plan/), check out the [samples](/samples/subscription) for creating planned sets of future recurring payments at periodic intervals.

* To create [Future Payments](https://developer.paypal.com/docs/integration/mobile/make-future-payment/), check out this [sample](/samples/payment/create_future_payment.js) for executing future payments for a customer who has granted consent on a mobile device.

* For [exploring additional payment capabilites](https://developer.paypal.com/docs/integration/direct/explore-payment-capabilities/), such as handling discounts, insurance, soft_descriptor and invoice_number, have a look at this [example](/samples/payment/create_with_paypal_further_capabilities.js). These bring REST payment functionality closer to parity with older Merchant APIs.

* For creating and managing [Orders](https://developer.paypal.com/webapps/developer/docs/integration/direct/create-process-order/#create-the-order), i.e. getting consent from buyer for a purchase but only placing the funds on hold when the merchant is ready to fulfill the [order](https://developer.paypal.com/webapps/developer/docs/api/#orders), have a look at [samples](/samples/order).

* For [Invoicing](https://developer.paypal.com/webapps/developer/docs/api/#invoicing), check out the [samples](/samples/invoice/) to see how you can use the node sdk to create, send and manage invoices.

## Running Samples
Expand Down Expand Up @@ -124,5 +130,3 @@ grunt test (timeout is specified in milliseconds eg: 15000ms)
## Contribution
* If you would like to contribute, please fork the repo and send in a pull request.
* Please ensure you run grunt before sending in the pull request.

[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/paypal/rest-api-sdk-nodejs/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
120 changes: 120 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* Copyright 2014 PayPal */
"use strict";

var client = require('./client');
var utils = require('./utils');
var configuration = require('./configure');

/* Merge user provided configurations otherwise use default settings */
var configure = exports.configure = function configure(options) {
if (options !== undefined && typeof options === 'object') {
configuration.default_options = utils.merge(configuration.default_options, options);
}
};

/**
* Generate new access token by making a POST request to /oauth2/token by
* exchanging base64 encoded client id/secret pair or valid refresh token.
*
* Otherwise authorization code from a mobile device can be exchanged for a long
* living refresh token used to charge user who has consented to future payments.
*/
var generateToken = exports.generateToken = function generateToken(config, cb) {

if (typeof config === "function") {
cb = config;
config = configuration.default_options;
} else if (!config) {
config = configuration.default_options;
} else {
config = utils.copyMissing(config, configuration.default_options);
}

var payload = 'grant_type=client_credentials';
if (config.authorization_code) {
payload = 'grant_type=authorization_code&response_type=token&redirect_uri=urn:ietf:wg:oauth:2.0:oob&code=' + config.authorization_code;
} else if (config.refresh_token) {
payload = 'grant_type=refresh_token&refresh_token=' + config.refresh_token;
}

var basicAuthString = 'Basic ' + new Buffer(config.client_id + ':' + config.client_secret).toString('base64');

var http_options = {
schema: config.schema || configuration.default_options.schema,
host: utils.getDefaultApiEndpoint(config.mode) || config.host || configuration.default_options.host,
port: config.port || configuration.default_options.port,
headers: {
'Authorization': basicAuthString,
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
};

client.invoke('POST', '/v1/oauth2/token', payload, http_options, function (err, res) {
var token = null;
if (res) {
if (!config.authorization_code) {
token = res.token_type + ' ' + res.access_token;
}
else {
token = res.refresh_token;
}
}
cb(err, token);
});
};

/* Update authorization header with new token obtained by calling
generateToken */
function updateToken(http_options, error_callback, callback) {
generateToken(http_options, function (error, token) {
if (error) {
error_callback(error, token);
} else {
http_options.headers.Authorization = token;
callback();
}
});
}

/**
* Make a PayPal REST API call, most REST functions wrap this function.
* Handles error and token expiration cases
*/
var executeHttp = exports.executeHttp = function executeHttp(http_method, path, data, http_options, cb) {
if (typeof http_options === "function") {
cb = http_options;
http_options = null;
}
if (!http_options) {
http_options = configuration.default_options;
} else {
http_options = utils.copyMissing(http_options, configuration.default_options);
}

//Get host endpoint using mode
http_options.host = utils.getDefaultApiEndpoint(http_options.mode) || http_options.host;

function retryInvoke() {
client.invoke(http_method, path, data, http_options, cb);
}

if (http_options.correlation_id) {
http_options.headers['Paypal-Application-Correlation-Id'] = http_options.correlation_id;
}

// Don't reprompt already authenticated user for login by updating Authorization header
// if token expires
if (http_options.headers.Authorization) {
client.invoke(http_method, path, data, http_options, function (error, response) {
if (error && error.httpStatusCode === 401 && http_options.client_id && http_options.headers.Authorization) {
http_options.headers.Authorization = null;
updateToken(http_options, cb, retryInvoke);
} else {
cb(error, response);
}
});
} else {
updateToken(http_options, cb, retryInvoke);
}
};
122 changes: 122 additions & 0 deletions lib/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* Copyright 2014 PayPal */
"use strict";

var http = require('http');
var https = require('https');
var querystring = require('querystring');
var uuid = require('node-uuid');
var configuration = require('./configure');

/* Wraps the http client, handles request parameters, populates request headers, handles response */
var invoke = exports.invoke = function invoke(http_method, path, data, http_options_param, cb) {
var client = (http_options_param.schema === 'http') ? http : https;

var request_data = data;

if (http_method === 'GET') {
//format object parameters into GET request query string
if (typeof request_data !== 'string') {
request_data = querystring.stringify(request_data);
}
if (request_data) {
path = path + "?" + request_data;
request_data = "";
}
} else if (typeof request_data !== 'string') {
request_data = JSON.stringify(request_data);
}

var http_options = {};

if (http_options_param) {

http_options = JSON.parse(JSON.stringify(http_options_param));

if (!http_options.headers) {
http_options.headers = {};
}
http_options.path = path;
http_options.method = http_method;
if (request_data) {
http_options.headers['Content-Length'] = Buffer.byteLength(request_data, 'utf-8');
}

if (!http_options.headers.Accept) {
http_options.headers.Accept = 'application/json';
}

if (!http_options.headers['Content-Type']) {
http_options.headers['Content-Type'] = 'application/json';
}

if (http_method === 'POST' && !http_options.headers['PayPal-Request-Id']) {
http_options.headers['PayPal-Request-Id'] = uuid.v4();
}

http_options.headers['User-Agent'] = configuration.userAgent;
}

var req = client.request(http_options);

req.on('error', function (e) {
console.log('problem with request: ' + e.message);
cb(e, null);
});

req.on('response', function (res) {
var response = '';
res.setEncoding('utf8');

res.on('data', function (chunk) {
response += chunk;
});

res.on('end', function () {
var err = null;

try {
//Set response to be parsed JSON object if data received is json
//expect that content-type header has application/json when it
//returns data
if (res.headers['content-type'] === "application/json") {
response = JSON.parse(response);
}
//Set response to an empty object if no data was received
if (response === '') {
response = {};
}
response.httpStatusCode = res.statusCode;

//TURN NODE_ENV to development to get access to paypal-debug-id
//for questions to merchant technical services. Similar convention
//to express.js
if (res.headers['paypal-debug-id'] !== undefined && process.env.NODE_ENV === 'development') {
console.log(res.headers['paypal-debug-id']);
}
} catch (e) {
err = new Error('Invalid JSON Response Received');
err.error = {
name: 'Invalid JSON Response Received, JSON Parse Error'
};
// response contains the full json description of the error
// that PayPal returns and information link
err.response = response;
err.httpStatusCode = res.statusCode;
response = null;
}

if (!err && (res.statusCode < 200 || res.statusCode >= 300)) {
err = new Error('Response Status : ' + res.statusCode);
err.response = response;
err.httpStatusCode = res.statusCode;
response = null;
}
cb(err, response);
});
});

if (request_data) {
req.write(request_data);
}
req.end();
};
18 changes: 18 additions & 0 deletions lib/configure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Copyright 2014 PayPal */
"use strict";

var sdkVersion = exports.sdkVersion = '0.10.0';
var userAgent = exports.userAgent = 'PayPalSDK/rest-sdk-nodejs ' + sdkVersion + ' (node ' + process.version + '-' + process.arch + '-' + process.platform + ')';

var default_options = exports.default_options = {
'mode': 'sandbox',
'schema': 'https',
'host': 'api.sandbox.paypal.com',
'port': '',
'openid_connect_schema': 'https',
'openid_connect_host': 'api.sandbox.paypal.com',
'openid_connect_port': '',
'authorize_url': 'https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize',
'logout_url': 'https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/endsession',
'headers': {}
};
Loading

0 comments on commit 0606c17

Please sign in to comment.