├── .gitignore
├── .npmignore
├── LICENSE.txt
├── README.md
├── lib
└── paypal-adaptive.js
├── package.json
└── test
└── api.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test
2 | lib-cov
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Gonzalo Aguirre
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Adaptive Payments and Adaptive Accounts SDK
2 |
3 | Node.js sdk for Paypal Adaptive Payments and Paypal Adaptive Accounts APIs, without dependencies
4 |
5 | ### Usage
6 | * Add dependency 'paypal-adaptive' in your package.json file.
7 | * Require 'paypal-adaptive' in your file.
8 | ```js
9 | var Paypal = require('paypal-adaptive');
10 |
11 | var paypalSdk = new Paypal({
12 | userId: 'userId',
13 | password: 'password',
14 | signature: 'signature',
15 | sandbox: true //defaults to false
16 | });
17 | ```
18 | * Call to sdk methods or to the generic method callApi. If you get an error, you can check the response too for better error handling.
19 | ```js
20 | var requestData = {
21 | requestEnvelope: {
22 | errorLanguage: 'en_US',
23 | detailLevel: 'ReturnAll'
24 | },
25 | payKey: 'AP-1234567890'
26 | };
27 |
28 | paypalSdk.callApi('AdaptivePayments/PaymentDetails', requestData, function (err, response) {
29 | if (err) {
30 | // You can see the error
31 | console.log(err);
32 | //And the original Paypal API response too
33 | console.log(response);
34 | } else {
35 | // Successful response
36 | console.log(response);
37 | }
38 | });
39 | ```
40 |
41 | ### API
42 | #### GetPaymentOptions
43 |
44 | ```js
45 | var payKey = 'AP-1234567890';
46 |
47 | paypalSdk.getPaymentOptions(payKey, function (err, response) {
48 | if (err) {
49 | console.log(err);
50 | } else {
51 | // payments options for this payKey
52 | console.log(response);
53 | }
54 | });
55 | ```
56 |
57 | #### PaymentDetails
58 | ```js
59 | // One of this params is required
60 | // The payKey
61 | var params = {
62 | payKey: 'AP-1234567890'
63 | };
64 | // Or the transactionId
65 | var params = {
66 | transactionId: 'AP-1234567890'
67 | };
68 | // Or the trackingId
69 | var params = {
70 | trackingId: 'AP-1234567890'
71 | };
72 |
73 | paypalSdk.paymentDetails(params, function (err, response) {
74 | if (err) {
75 | console.log(err);
76 | } else {
77 | // payments details for this payKey, transactionId or trackingId
78 | console.log(response);
79 | }
80 | });
81 | ```
82 |
83 | #### Pay
84 | ```js
85 | var payload = {
86 | requestEnvelope: {
87 | errorLanguage: 'en_US'
88 | },
89 | actionType: 'PAY',
90 | currencyCode: 'USD',
91 | feesPayer: 'EACHRECEIVER',
92 | memo: 'Chained payment example',
93 | cancelUrl: 'http://test.com/cancel',
94 | returnUrl: 'http://test.com/success',
95 | receiverList: {
96 | receiver: [
97 | {
98 | email: 'primary@test.com',
99 | amount: '100.00',
100 | primary:'true'
101 | },
102 | {
103 | email: 'secondary@test.com',
104 | amount: '10.00',
105 | primary:'false'
106 | }
107 | ]
108 | }
109 | };
110 |
111 | paypalSdk.pay(payload, function (err, response) {
112 | if (err) {
113 | console.log(err);
114 | } else {
115 | // Response will have the original Paypal API response
116 | console.log(response);
117 | // But also a paymentApprovalUrl, so you can redirect the sender to checkout easily
118 | console.log('Redirect to %s', response.paymentApprovalUrl);
119 | }
120 | });
121 | ```
122 |
123 | #### Preapproval
124 | ```js
125 | var payload = {
126 | currencyCode: 'USD',
127 | startingDate: new Date().toISOString(),
128 | endingDate: new Date('2020-01-01').toISOString(),
129 | returnUrl: 'http://your-website.com',
130 | cancelUrl: 'http://your-website.com',
131 | ipnNotificationUrl: 'http://your-ipn-listener.com',
132 | maxNumberOfPayments: 1,
133 | displayMaxTotalAmount: true,
134 | maxTotalAmountOfAllPayments: '100.00',
135 | requestEnvelope: {
136 | errorLanguage: 'en_US'
137 | }
138 | }
139 |
140 | paypalSdk.preapproval(payload, function (err, response) {
141 | if (err) {
142 | console.log(err);
143 | } else {
144 | // Response will have the original Paypal API response
145 | console.log(response);
146 | // But also a preapprovalUrl, so you can redirect the sender to approve the payment easily
147 | console.log('Redirect to %s', response.preapprovalUrl);
148 | }
149 | });
150 | ```
151 |
152 | **Note:**
153 | The other API methods has default behavior by now: you send a payload and obtains the Paypal original response.
154 |
155 | ```js
156 | var payload = {
157 | requestEnvelope: {
158 | errorLanguage: 'en_US'
159 | },
160 | // another data required by API method
161 | };
162 |
163 | var callback = function (err, response) {
164 | if (err) {
165 | // Handle error
166 | console.log(err);
167 | } else {
168 | // Paypal response
169 | console.log(response)
170 | }
171 | };
172 |
173 | // For Adaptive Payments
174 | paypalSdk.cancelPreapproval(payload, callback);
175 |
176 | paypalSdk.convertCurrency(payload, callback);
177 |
178 | paypalSdk.executePayment(payload, callback);
179 |
180 | paypalSdk.getFundingPlans(payload, callback);
181 |
182 | paypalSdk.getShippingAddresses(payload, callback);
183 |
184 | paypalSdk.preapprovalDetails(payload, callback);
185 |
186 | paypalSdk.setPaymentOptions(payload, callback);
187 |
188 | // For Adaptive Accounts
189 | paypalSdk.addBankAccount(payload, callback);
190 |
191 | paypalSdk.addPaymentCard(payload, callback);
192 |
193 | paypalSdk.checkComplianceStatus(payload, callback);
194 |
195 | paypalSdk.createAccount(payload, callback);
196 | // To use this method you can set X-PAYPAL-SANDBOX-EMAIL-ADDRESS and X-PAYPAL-DEVICE-IPADDRESS headers passing 'sandboxEmailAddress' and 'deviceIpAddress' properties on config
197 |
198 | paypalSdk.getUserAgreement(payload, callback);
199 |
200 | paypalSdk.getVerifiedStatus(payload, callback);
201 |
202 | paypalSdk.setFundingSourceConfirmed(payload, callback);
203 |
204 | paypalSdk.updateComplianceStatus(payload, callback);
205 | ```
206 |
207 | ### Tests
208 | Tests can be ran with:
209 |
210 | ```sh
211 | mocha
212 | ```
213 |
214 | ### Reference
215 | Paypal Adaptive Payments
216 | Paypal Adaptive Accounts
217 |
218 | ### License
219 | Copyright (c) 2014 Gonzalo Aguirre. See the LICENSE file for license rights and limitations (MIT).
--------------------------------------------------------------------------------
/lib/paypal-adaptive.js:
--------------------------------------------------------------------------------
1 | var https = require('https')
2 | , util = require('util');
3 |
4 | var adaptiveAccountsMethods = [
5 | 'AddBankAccount',
6 | 'AddPaymentCard',
7 | 'CheckComplianceStatus',
8 | 'CreateAccount',
9 | 'GetUserAgreement',
10 | 'GetVerifiedStatus',
11 | 'SetFundingSourceConfirmed',
12 | 'UpdateComplianceStatus'
13 | ];
14 |
15 | var adaptivePaymentsMethods = [
16 | 'CancelPreapproval',
17 | 'ConvertCurrency',
18 | 'ExecutePayment',
19 | 'GetFundingPlans',
20 | 'GetShippingAddresses',
21 | 'PreapprovalDetails',
22 | 'SetPaymentOptions'
23 | ];
24 |
25 | function merge(a, b) {
26 | for (var p in b) {
27 | try {
28 | if (b[p].constructor === Object) {
29 | a[p] = merge(a[p], b[p]);
30 | } else {
31 | a[p] = b[p];
32 | }
33 | } catch (e) {
34 | a[p] = b[p];
35 | }
36 | }
37 | return a;
38 | }
39 |
40 | function defaultPayload() {
41 | return {
42 | requestEnvelope: {
43 | errorLanguage: 'en_US',
44 | detailLevel: 'ReturnAll'
45 | }
46 | };
47 | }
48 |
49 | function httpsPost(options, callback) {
50 | options.method = 'POST';
51 | options.headers = options.headers || {};
52 |
53 | var data = (typeof options.data !== 'string') ? JSON.stringify(options.data) : options.data;
54 |
55 | options.headers['Content-Length'] = Buffer.byteLength(data);
56 |
57 | var req = https.request(options);
58 |
59 | req.on('response', function (res) {
60 | var response = '';
61 | //do not setEndcoding with browserify
62 | if (res.setEncoding) {
63 | res.setEncoding('utf8');
64 | }
65 |
66 | res.on('data', function (chunk) {
67 | response += chunk;
68 | });
69 |
70 | res.on('end', function () {
71 | return callback(null, {
72 | statusCode: res.statusCode,
73 | body: response
74 | });
75 | });
76 | });
77 |
78 | req.on('error', function (e) {
79 | callback(e);
80 | });
81 |
82 | if (data) {
83 | req.write(data);
84 | }
85 |
86 | req.end();
87 | }
88 |
89 | var Paypal = function (config) {
90 | if (!config) throw new Error('Config is required');
91 | if (!config.userId) throw new Error('Config must have userId');
92 | if (!config.password) throw new Error('Config must have password');
93 | if (!config.signature) throw new Error('Config must have signature');
94 | if (!config.appId && !config.sandbox) throw new Error('Config must have appId');
95 |
96 | var defaultConfig = {
97 | requestFormat: 'JSON',
98 | responseFormat: 'JSON',
99 | sandbox: false,
100 | productionHostname: 'svcs.paypal.com',
101 | sandboxHostname: 'svcs.sandbox.paypal.com',
102 | appId: 'APP-80W284485P519543T',
103 | approvalUrl: 'https://www.paypal.com/cgi-bin/webscr?cmd=_ap-payment&paykey=%s',
104 | sandboxApprovalUrl: 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_ap-payment&paykey=%s',
105 | preapprovalUrl: 'https://www.paypal.com/webscr?cmd=_ap-preapproval&preapprovalkey=%s',
106 | sandboxPreapprovalUrl: 'https://www.sandbox.paypal.com/webscr?cmd=_ap-preapproval&preapprovalkey=%s'
107 | };
108 |
109 | this.config = merge(defaultConfig, config);
110 | };
111 |
112 | Paypal.prototype.callApi = function (apiMethod, data, callback) {
113 | var config = this.config;
114 |
115 | var options = {
116 | hostname: config.sandbox ? config.sandboxHostname : config.productionHostname,
117 | port: 443,
118 | path: '/' + apiMethod,
119 | data: data,
120 | headers: {
121 | 'X-PAYPAL-SECURITY-USERID': config.userId,
122 | 'X-PAYPAL-SECURITY-PASSWORD': config.password,
123 | 'X-PAYPAL-SECURITY-SIGNATURE': config.signature,
124 | 'X-PAYPAL-APPLICATION-ID': config.appId,
125 | 'X-PAYPAL-REQUEST-DATA-FORMAT': config.requestFormat,
126 | 'X-PAYPAL-RESPONSE-DATA-FORMAT': config.responseFormat
127 | }
128 | };
129 |
130 | if (config.sandboxEmailAddress)
131 | options.headers['X-PAYPAL-SANDBOX-EMAIL-ADDRESS'] = config.sandboxEmailAddress;
132 |
133 | if (config.deviceIpAddress)
134 | options.headers['X-PAYPAL-DEVICE-IPADDRESS'] = config.deviceIpAddress;
135 |
136 | if (config.subject)
137 | options.headers['X-PAYPAL-SECURITY-SUBJECT'] = config.subject;
138 |
139 | httpsPost(options, function (error, response) {
140 | if (error) { return callback(error); }
141 |
142 | var body = response.body;
143 | var statusCode = response.statusCode;
144 |
145 | if (config.responseFormat === 'JSON') {
146 | try {
147 | body = JSON.parse(body);
148 | } catch (e) {
149 | var err = new Error('Invalid JSON Response Received');
150 | err.response = body;
151 | err.httpStatusCode = response.statusCode;
152 | return callback(err);
153 | }
154 | }
155 |
156 | if (statusCode < 200 || statusCode >= 300) {
157 | error = new Error('Response Status: ' + statusCode);
158 | error.response = body;
159 | error.httpStatusCode = statusCode;
160 | return callback(error);
161 | }
162 |
163 | body.httpStatusCode = statusCode;
164 |
165 | if (/^(Success|SuccessWithWarning)$/.test(body.responseEnvelope.ack)) {
166 | callback(null, body);
167 | } else {
168 | var err = new Error('Response ack is ' + body.responseEnvelope.ack + '. Check the response for more info');
169 | return callback(err, body);
170 | }
171 | });
172 | };
173 |
174 | // Paypal Adaptive Payments API methods
175 | Paypal.prototype.getPaymentOptions = function (payKey, callback) {
176 | if (!payKey) {
177 | return callback(new Error('Required "payKey"'));
178 | }
179 |
180 | var data = defaultPayload();
181 | data.payKey = payKey;
182 |
183 | this.callApi('AdaptivePayments/GetPaymentOptions', data, callback);
184 | };
185 |
186 | Paypal.prototype.paymentDetails = function (params, callback) {
187 | if (!params.payKey && !params.transactionId && !params.trackingId) {
188 | return callback(new Error('Required "payKey" or "transactionId" or "trackingId" on first param'));
189 | }
190 |
191 | var data = merge(defaultPayload(), params);
192 |
193 | this.callApi('AdaptivePayments/PaymentDetails', data, callback);
194 | };
195 |
196 | Paypal.prototype.pay = function (data, callback) {
197 | var config = this.config;
198 |
199 | this.callApi('AdaptivePayments/Pay', data, function (err, res) {
200 | if (err) { return callback(err, res); }
201 |
202 | if (res.paymentExecStatus === 'CREATED') {
203 | var url = config.sandbox ? config.sandboxApprovalUrl : config.approvalUrl;
204 | res.paymentApprovalUrl = util.format(url, res.payKey);
205 | }
206 |
207 | return callback(null, res);
208 | });
209 | };
210 |
211 | Paypal.prototype.preapproval = function (data, callback) {
212 | var config = this.config;
213 |
214 | this.callApi('AdaptivePayments/Preapproval', data, function (err, res) {
215 | if (err) { return callback(err, res); }
216 |
217 | if (res.preapprovalKey) {
218 | var url = config.sandbox ? config.sandboxPreapprovalUrl : config.preapprovalUrl;
219 | res.preapprovalUrl = util.format(url, res.preapprovalKey);
220 | }
221 |
222 | return callback(null, res);
223 | });
224 | };
225 |
226 | Paypal.prototype.refund = function (params, callback) {
227 | if (!params.payKey && !params.transactionId && !params.trackingId) {
228 | return callback(new Error('Required "payKey" or "transactionId" or "trackingId" on first param'));
229 | }
230 |
231 | var data = merge(defaultPayload(), params);
232 |
233 | this.callApi('AdaptivePayments/Refund', data, callback);
234 | };
235 |
236 | adaptivePaymentsMethods.forEach(function (method) {
237 | var prototypeMethodName = method.charAt(0).toLowerCase() + method.slice(1);
238 | var apiMethodName = 'AdaptivePayments/' + method;
239 | Paypal.prototype[prototypeMethodName] = function (data, callback) {
240 | this.callApi(apiMethodName, data, callback);
241 | };
242 | });
243 |
244 | adaptiveAccountsMethods.forEach(function (method) {
245 | var prototypeMethodName = method.charAt(0).toLowerCase() + method.slice(1);
246 | var apiMethodName = 'AdaptiveAccounts/' + method;
247 | Paypal.prototype[prototypeMethodName] = function (data, callback) {
248 | this.callApi(apiMethodName, data, callback);
249 | };
250 | });
251 |
252 | module.exports = Paypal;
253 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "paypal-adaptive",
3 | "version": "0.4.1",
4 | "author" : "Gonzalo Aguirre",
5 | "description" : "Paypal Adaptive (Payments & Accounts) SDK in node.js",
6 | "main": "./lib/paypal-adaptive.js",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/gaguirre/paypal-adaptive-sdk-nodejs.git"
10 | },
11 | "keywords": [
12 | "api"
13 | , "paypal"
14 | , "adaptive"
15 | , "payments"
16 | , "accounts"
17 | , "sdk"
18 | , "node"
19 | ],
20 | "devDependencies": {
21 | "mocha": "1.10.0",
22 | "nock": "0.18.2"
23 | },
24 | "engine" : {
25 | "node": ">=0.6"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/api.js:
--------------------------------------------------------------------------------
1 | var nock = require('nock')
2 | , assert = require('assert')
3 | , Paypal = require('..');
4 |
5 | var paypalConfig = {
6 | userId: 'mockUserId',
7 | password: 'mockPassword',
8 | signature: 'mockSignature',
9 | sandbox: true
10 | };
11 |
12 | describe('callApi method', function() {
13 | it('should POST with correct header', function(done) {
14 | var mockResponse = { jjx: 'jjx' };
15 |
16 | var mockHttp = nock('https://svcs.sandbox.paypal.com')
17 | .matchHeader('X-PAYPAL-SECURITY-USERID', 'mockUserId')
18 | .matchHeader('X-PAYPAL-SECURITY-PASSWORD', 'mockPassword')
19 | .matchHeader('X-PAYPAL-SECURITY-SIGNATURE', 'mockSignature')
20 | .matchHeader('X-PAYPAL-APPLICATION-ID', 'APP-80W284485P519543T')
21 | .matchHeader('X-PAYPAL-REQUEST-DATA-FORMAT', 'JSON')
22 | .matchHeader('X-PAYPAL-RESPONSE-DATA-FORMAT', 'JSON')
23 | .post('/just-for-check-header', {})
24 | .reply(400, mockResponse);
25 |
26 | var api = new Paypal(paypalConfig);
27 |
28 | api.callApi('just-for-check-header', {}, function(err, res) {
29 | assert.equal(err.httpStatusCode, 400);
30 | assert.deepEqual(err.response, mockResponse);
31 |
32 | mockHttp.done();
33 | done();
34 | });
35 | });
36 |
37 | it('should POST with header additional headers X-PAYPAL-SANDBOX-EMAIL-ADDRESS and X-PAYPAL-DEVICE-IPADDRESS if were provided on config', function(done) {
38 | var mockResponse = { jjx: 'jjx' };
39 |
40 | var mockHttp = nock('https://svcs.sandbox.paypal.com')
41 | .matchHeader('X-PAYPAL-SECURITY-USERID', 'mockUserId')
42 | .matchHeader('X-PAYPAL-SECURITY-PASSWORD', 'mockPassword')
43 | .matchHeader('X-PAYPAL-SECURITY-SIGNATURE', 'mockSignature')
44 | .matchHeader('X-PAYPAL-APPLICATION-ID', 'APP-80W284485P519543T')
45 | .matchHeader('X-PAYPAL-REQUEST-DATA-FORMAT', 'JSON')
46 | .matchHeader('X-PAYPAL-RESPONSE-DATA-FORMAT', 'JSON')
47 | .matchHeader('X-PAYPAL-SANDBOX-EMAIL-ADDRESS', 'mockEmailAddress')
48 | .matchHeader('X-PAYPAL-DEVICE-IPADDRESS', 'mockIpAddress')
49 | .post('/just-for-check-extra-headers', {})
50 | .reply(400, mockResponse);
51 |
52 | var api = new Paypal({
53 | userId: 'mockUserId',
54 | password: 'mockPassword',
55 | signature: 'mockSignature',
56 | sandbox: true,
57 | sandboxEmailAddress: 'mockEmailAddress',
58 | deviceIpAddress: 'mockIpAddress'
59 | });
60 |
61 | api.callApi('just-for-check-extra-headers', {}, function(err, res) {
62 | assert.equal(err.httpStatusCode, 400);
63 | assert.deepEqual(err.response, mockResponse);
64 |
65 | mockHttp.done();
66 | done();
67 | });
68 | });
69 |
70 | it('should return error when status is not 200', function(done) {
71 | var mockResponse = { jjx: 'jjx' };
72 |
73 | var mockHttp = nock('https://svcs.sandbox.paypal.com')
74 | .post('/not-200', {})
75 | .reply(400, mockResponse);
76 |
77 |
78 | var api = new Paypal(paypalConfig);
79 |
80 | api.callApi('not-200', {}, function(err, res) {
81 | assert.equal(err.httpStatusCode, 400);
82 | assert.deepEqual(err.response, mockResponse);
83 |
84 | mockHttp.done();
85 | done();
86 | });
87 | });
88 |
89 | it('should return error when Paypal response ack is not Success or SuccessWithWarning', function(done) {
90 | var failureResponse = {
91 | responseEnvelope: {
92 | ack: 'NotSuccess'
93 | },
94 | error: 'errorMock'
95 | };
96 |
97 | var mockHttp = nock('https://svcs.sandbox.paypal.com')
98 | .post('/failure-response', {})
99 | .reply(200, failureResponse);
100 |
101 |
102 | var api = new Paypal(paypalConfig);
103 |
104 | api.callApi('failure-response', {}, function(err, res) {
105 | assert.notEqual(err, null);
106 |
107 | failureResponse.httpStatusCode = 200;
108 | assert.deepEqual(res, failureResponse);
109 |
110 | mockHttp.done();
111 | done();
112 | });
113 | });
114 |
115 | it('should return OK when Paypal response ack is Success', function(done) {
116 | var okResponse = {
117 | responseEnvelope: {
118 | ack: 'Success'
119 | },
120 | mock: 'mock'
121 | };
122 |
123 | var mockHttp = nock('https://svcs.sandbox.paypal.com')
124 | .post('/ok-response', {})
125 | .reply(200, okResponse);
126 |
127 |
128 | var api = new Paypal(paypalConfig);
129 |
130 | api.callApi('ok-response', {}, function(err, res) {
131 | assert.equal(err, null);
132 |
133 | okResponse.httpStatusCode = 200;
134 | assert.deepEqual(res, okResponse);
135 |
136 | mockHttp.done();
137 | done();
138 | });
139 | });
140 |
141 | it('should return OK when Paypal response ack is SuccessWithWarning', function(done) {
142 | var okResponse = {
143 | responseEnvelope: {
144 | ack: 'SuccessWithWarning'
145 | },
146 | mock: 'mock'
147 | };
148 |
149 | var mockHttp = nock('https://svcs.sandbox.paypal.com')
150 | .post('/ok-response', {})
151 | .reply(200, okResponse);
152 |
153 |
154 | var api = new Paypal(paypalConfig);
155 |
156 | api.callApi('ok-response', {}, function(err, res) {
157 | assert.equal(err, null);
158 |
159 | okResponse.httpStatusCode = 200;
160 | assert.deepEqual(res, okResponse);
161 |
162 | mockHttp.done();
163 | done();
164 | });
165 | });
166 | });
167 |
--------------------------------------------------------------------------------