forked from paypal/paypal-retail-node
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.js
267 lines (243 loc) · 10.9 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
var express = require('express'),
app = express(),
util = require('util'),
paypal = require('./');
/**
* These are the variables we will look for in the environment, from "most important" to least
*/
// A random value used to provide additional control to disable compromised versions of your app
var APP_SECURE_IDENTIFIER = process.env.APP_SECURE_IDENTIFIER;
var PAYPAL_LIVE_CLIENTID = process.env.PAYPAL_LIVE_CLIENTID;
var PAYPAL_LIVE_SECRET = process.env.PAYPAL_LIVE_SECRET;
var PAYPAL_SANDBOX_CLIENTID = process.env.PAYPAL_SANDBOX_CLIENTID;
var PAYPAL_SANDBOX_SECRET = process.env.PAYPAL_SANDBOX_SECRET;
// The base URL by which this server can be reached on the Internet (e.g. for token refresh)
var ROOT_URL = process.env.ROOT_URL;
// For third-party use, you will want this site to redirect to your app after the login flow completes.
// This URL will receive the access_token, refresh_url, and expires_in values as query arguments.
// If you don't set this value, this server essentially becomes "first party use only" as all it can do
// is refresh tokens generated with /firstParty
var APP_REDIRECT_URL = process.env.APP_REDIRECT_URL;
// If a PayPal representative gives you a custom environment string, set it as this env var
var PAYPAL_CUSTOM_ENVIRONMENT = process.env.PAYPAL_CUSTOM_ENVIRONMENT;
var errors, warnings, hasLive, hasSandbox;
validateEnvironment();
if (!errors) {
showStartupMessage();
}
// Pick it up from the request if it's not set, and wait to configure PayPal until we have it.
if (!ROOT_URL) {
app.use(function (req, res, next) {
if (!ROOT_URL) {
ROOT_URL = req.protocol + '://' + req.get('host');
showStartupMessage();
configurePayPal();
}
next();
});
} else {
configurePayPal();
}
/******************************** Express Routes and Server ********************************/
if (isSetupEnabled()) {
// Allow
app.get('/setup/:env', allErrorsAreBelongToUs, function (req, res) {
res.redirect(paypal.redirect(req.params.env, '/setup'));
});
app.get('/setup', function (req, res) {
res.send('<html><body><H1>InitializeMerchant Token</H1><p>This token requires this server to be running so it can ' +
'be refreshed automatically. It will work for about 8 hours before a refresh is required.</p><br/><textarea id="key" cols="100" rows="10">' +
req.query.sdk_token +
'</textarea><script type="text/javascript">document.getElementById("key").select();</script></body>');
});
}
if (APP_REDIRECT_URL) {
app.get('/toPayPal/:env', allErrorsAreBelongToUs, function (req, res) {
res.redirect(paypal.redirect(req.params.env, APP_REDIRECT_URL, !!req.query.returnTokenOnQueryString));
});
}
app.get('/returnFromPayPal', function (req, res) {
paypal.completeAuthentication(req.query, APP_SECURE_IDENTIFIER, function (error, destinationUrl) {
if (error) {
console.error(util.format('Failed to handle returnFromPayPal (%s): %s\n%s', error.env || 'unknown environment', error.message, error.stack));
return res.status(500).send(error.message);
}
res.redirect(destinationUrl);
});
});
app.get('/refresh', function (req, res) {
paypal.refresh(req.query, APP_SECURE_IDENTIFIER, function (error, token) {
if (error) {
return res.status(500).send(error.message);
}
res.json(token);
});
});
app.get('/', allErrorsAreBelongToUs, function (req, res) {
var ret = '<html><body><h1>Server is Ready</h1>';
if (isSetupEnabled()) {
if (hasLive) {
ret += '<a href="/setup/live">Setup a Live Account</a><br/>';
}
if (hasSandbox) {
ret += '<a href="/setup/sandbox">Setup a Sandbox Account</a><br/>';
}
}
ret += '</body></html>';
res.send(ret);
});
var server = app.listen(process.env.PORT || 3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('PayPal Retail SDK Service listening at http://%s:%s', host, port);
});
/******************************** The rest is just boring helpers ********************************/
function configurePayPal() {
if (hasLive) {
console.log('Configuring LIVE environment');
// This line adds the live configuration to the PayPal module.
// If you're going to write your own server, this is the money line
paypal.configure(paypal.LIVE, {
clientId: PAYPAL_LIVE_CLIENTID,
secret: PAYPAL_LIVE_SECRET,
returnUrl: combineUrl(ROOT_URL, 'returnFromPayPal'),
refreshUrl: combineUrl(ROOT_URL, 'refresh'),
scopes: process.env.SCOPES // This is optional, we have defaults in paypal-retail-node
});
checkScopes(paypal.LIVE);
}
if (hasSandbox) {
console.log('Configuring SANDBOX environment');
// This line adds the sandbox configuration to the PayPal module
paypal.configure(paypal.SANDBOX, {
clientId: PAYPAL_SANDBOX_CLIENTID,
secret: PAYPAL_SANDBOX_SECRET,
returnUrl: combineUrl(ROOT_URL, 'returnFromPayPal'),
refreshUrl: combineUrl(ROOT_URL, 'refresh'),
scopes: process.env.SCOPES // This is optional, we have defaults in paypal-retail-node
});
checkScopes(paypal.SANDBOX);
}
if (PAYPAL_CUSTOM_ENVIRONMENT) {
try {
var info = JSON.parse(new Buffer(PAYPAL_CUSTOM_ENVIRONMENT, 'base64').toString('utf8'));
console.log(JSON.stringify(info));
for (var envI = 0; envI < info.length; envI++) {
console.log('Configuring', info[envI].name, 'environment');
info[envI].returnUrl = info[envI].returnUrl || combineUrl(ROOT_URL, 'returnFromPayPal');
info[envI].refreshUrl = info[envI].refreshUrl || combineUrl(ROOT_URL, 'refresh');
paypal.configure(info[envI].name, info[envI]);
checkScopes(info[envI].name);
}
} catch (x) {
error('Invalid PAYPAL_CUSTOM_ENVIRONMENT: ' + x.message);
}
}
}
/**
* Environment validation and usage display
*/
function validateEnvironment() {
/**
* Analyze the environment and make sure things are setup properly
*/
if (!APP_SECURE_IDENTIFIER) {
error('The APP_SECURE_IDENTIFIER value is missing from the environment. It should be set to a reasonably long set of random characters (e.g. 32)');
}
if (!APP_REDIRECT_URL && !process.env.SETUP_ENABLED) {
error('Either APP_REDIRECT_URL (for third party merchant login) or SETUP_ENABLED (for first party token generation) must be set in the environment.');
}
if (!PAYPAL_LIVE_CLIENTID && !PAYPAL_SANDBOX_CLIENTID) {
error('The server must be configured for sandbox, live, or both. Neither PAYPAL_LIVE_CLIENTID or PAYPAL_SANDBOX_CLIENTID is set in the environment.');
} else {
if (!PAYPAL_LIVE_CLIENTID) {
warn('The server is only configured for Sandbox.');
} else {
if (!PAYPAL_LIVE_SECRET) {
error('PAYPAL_LIVE_CLIENTID is set, but PAYPAL_LIVE_SECRET is not. The app needs the client id and secret to function.');
} else {
hasLive = true;
}
}
if (!PAYPAL_SANDBOX_CLIENTID) {
warn('The server is only configured for live.');
} else {
if (!PAYPAL_SANDBOX_SECRET) {
error('PAYPAL_SANDBOX_CLIENTID is set, but PAYPAL_SANDBOX_SECRET is not. The app needs the client id and secret to function.');
} else {
hasSandbox = true;
}
}
}
if (!APP_REDIRECT_URL) {
warn('The APP_REDIRECT_URL value is missing from the environment. You will only be able to use this service to authenticate via /setup.');
}
if (!ROOT_URL) {
warn('The environment variable ROOT_URL should be set to the root URL of this server, such as http://mypaypalsdkserver.herokuapp.com');
}
}
function checkScopes(env) {
paypal.queryAvailableScopes(env, function (e, scopes) {
if (e && e.missing) {
error('\n\n\n!!! ' + e.message +
'\n!!! Please go to https://developer.paypal.com/developer/applications/ and ensure the appropriate\n!!! permissions are assigned to your application in the ' + env.toUpperCase() +
' environment.\n!!! Until this is done, you will not be able to login and get an SDK token.\n\n');
}
});
}
function allErrorsAreBelongToUs(req, res, next) {
if (errors && errors.length) {
res.send('<html><body><h1>Configuration Errors</h1><ul><li>' + errors.join('</li><li>') + '</li></pre></body>');
} else {
next();
}
}
function showStartupMessage() {
console.log('/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-');
if (!ROOT_URL) {
console.log(' *\n * ROOT_URL is not set, it will be set on first request and further configuration will happen then.');
} else {
if (isSetupEnabled()) {
console.log(' * To generate a token for your account, open the following URL in a browser:\n *');
if (hasLive) {
console.log(' * LIVE: ' + combineUrl(ROOT_URL || '/', 'setup/live'));
}
if (hasSandbox) {
console.log(' * SANDBOX: ' + combineUrl(ROOT_URL || '/', 'setup/sandbox'));
}
}
if (APP_REDIRECT_URL) {
console.log(' *\n * To begin the authentication flow in your app, open a browser or webview on the target device to:\n *');
if (PAYPAL_LIVE_CLIENTID) {
console.log(' * LIVE: ' + combineUrl(ROOT_URL || '/', 'toPayPal/live'));
}
if (PAYPAL_SANDBOX_CLIENTID || true) {
console.log(' * SANDBOX: ' + combineUrl(ROOT_URL || '/', 'toPayPal/sandbox'));
}
console.log(' * \n * When the flow is complete, this site will redirect to:\n * ');
console.log(' * ' + APP_REDIRECT_URL + (APP_REDIRECT_URL.indexOf('?') >= 0 ? '&' : '?') + 'sdk_token=[what you give to InitializeMerchant]');
console.log(' *\n * Your return url on developer.paypal.com must be set to:\n *');
console.log(' * ' + combineUrl(ROOT_URL, 'returnFromPayPal') + '\n *');
}
}
console.log(' *\n *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/');
}
function isSetupEnabled() {
return (process.env.SETUP_ENABLED || 'false').toLowerCase() === 'true';
}
function warn(msg) {
warnings = warnings || [];
warnings.push(msg);
console.log('WARNING', msg);
}
function error(msg) {
errors = errors || [];
errors.push(msg);
console.error('ERROR', msg);
}
function combineUrl(base, path) {
if (base[base.length - 1] === '/') {
return base + path;
}
return base + '/' + path;
}