Skip to content

Instantly share code, notes, and snippets.

@danielflippance
Created September 12, 2018 06:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielflippance/81d4229e0e65e8e58df434f3bcaee342 to your computer and use it in GitHub Desktop.
Save danielflippance/81d4229e0e65e8e58df434f3bcaee342 to your computer and use it in GitHub Desktop.
'use strict';
const PRODUCTION_VERIFY_HOSTNAME = 'ipnpb.paypal.com';
const SANDBOX_VERIFY_HOSTNAME = 'ipnpb.sandbox.paypal.com';
var fs = require('fs');
var qs = require('querystring');
var path = require('path');
var Censor = require('../../shared/censor.debug.js');
//For PayPal
//https://stackoverflow.com/a/22263280/2325676
var rootCAs = require('ssl-root-cas/latest').create();
// rootCAs
// .addFile(__dirname + '/paypal-sandbox-intermediate-certificate-2.pem')
// .addFile(__dirname + '/paypal-sandbox-intermediate-certificate-1.pem')
// ;
var paypalSandboxIntermediateCertificate1 = `-----BEGIN CERTIFICATE-----
MIIGvzCCBaegAwIBAgIQCvb4f6RdZLUcn0SONY/MRTANBgkqhkiG9w0BAQsFADBEMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMR4wHAYDVQQDExVEaWdpQ2VydCBHbG9iYWwgQ0Eg
RzIwHhcNMTgwNjI5MDAwMDAwWhcNMjAwNzMxMTIwMDAwWjCBizELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBKb3NlMRUwEwYDVQQKEwxQYXlQYWwsIEluYy4x
GjAYBgNVBAsTEVBheVBhbCBQcm9kdWN0aW9uMSEwHwYDVQQDExhpcG5wYi5zYW5kYm94LnBheXBh
bC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJd5oSu6Syxu4ql5/Nl0XfasDA
EbgataLucM9pselCqNvto4aTQR6KyrjhW1zgOi/P6D4N7U9VuVVbqHgUB2jwiWmcAkeUnI90WAYv
6C3aWjIDfqzQ/DIh6ARqxFXwZaTzWGSD7Y9NNPH3PczcaO0ECNKov55YPereIMvhs0wEOzqT3F2U
yj36KHbl60fqA3y9Xx5BKlchHa4cmYG+LG7aKAa+dv0jKIC000aJdoOce8XgctlH+3i2hNrU5H2q
qZcMBIp/4Pj55gVnWa3KNQ3Kc6faP9BwvpsDTYa7sB73DX96xCnX6r0BOddqA9/7az76KfbFdxTw
fziMKyNsV1ilAgMBAAGjggNjMIIDXzAfBgNVHSMEGDAWgBQkbist0GqSUVElaQGqmkemiedAIDAd
BgNVHQ4EFgQUNzLnOTfXYmYdgPPkt2Y5nj2EeRUwIwYDVR0RBBwwGoIYaXBucGIuc2FuZGJveC5w
YXlwYWwuY29tMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
dwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFs
Q0FHMi5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbENB
RzIuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMHQGCCsGAQUFBwEBBGgwZjAkBggrBgEFBQcwAYYY
aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vY2FjZXJ0cy5kaWdp
Y2VydC5jb20vRGlnaUNlcnRHbG9iYWxDQUcyLmNydDAJBgNVHRMEAjAAMIIBfwYKKwYBBAHWeQIE
AgSCAW8EggFrAWkAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWRNeZDvAAAE
AwBHMEUCIQDOABfExDk/AjPDbUWqSGTXLD/O2GK2bgevH6m/d8j7KAIgU5zuHwNsKaFG87IXAj3X
4VYMSro6mpugWm+DCr5ZPTkAdgCHdb/nWXz4jEOZX73zbv9WjUdWNv9KtWDBtOr/XqCDDwAAAWRN
eZHHAAAEAwBHMEUCIHAm78hl+l6b5XhjthBR86An6giwaopGtUeYHAdUk523AiEA9nQEkOQQ2PZD
dzibQq5DYBhbNSbe7T4hwU8dtUui/I0AdwC72d+8H4pxtZOUI5eqkntHOFeVCqtS6BqQlmQ2jh7R
hQAAAWRNeZHkAAAEAwBIMEYCIQCmfk/Ax9v5k0pbcISLLpIKnxJeXsDiJTL7bSSzML0tyQIhANb5
f419fyLX/RRFlX2EnD1KmtHzSR1ovLJeJxJu+xcHMA0GCSqGSIb3DQEBCwUAA4IBAQB9UWlYT9Hu
ncXceoMoh7LSR1bPQvpjOeUVJsYoKHLzfcyGGnwmW57Y9qlEf04lwnAK9/lyPIPUQjK6v2yIs42Q
OO4tyQkwBC5tr41k3KYJYLYh91ozQ8mgGad0lX7tBoVbYsM2SAvBlK/kbbWV+q/6ibahf9oEnnt/
k/0D1kaYlVAPVtPvu0F2nGkAo0Y33ZW4E9OekvBe5Eqk+I4fEQJOlDsbypOmNzPx+48eDpSyLYXM
SJ1FPq2Ipj637CuBAET7UqNs8eeXejY2N7s7LfnMtZ2gjIeTFkUz1lNRWDMq1ZFYc1TP1fX0E2BF
NBphay7XFKzQEACbPyX5VO37HeIw
-----END CERTIFICATE-----`;
var paypalSandboxIntermediateCertificate2 = `-----BEGIN CERTIFICATE-----
MIIEizCCA3OgAwIBAgIQDI7gyQ1qiRWIBAYe4kH5rzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0yODA4MDEx
MjAwMDBaMEQxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxHjAcBgNVBAMTFURp
Z2lDZXJ0IEdsb2JhbCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNIfL7z
BYZdW9UvhU5L4IatFaxhz1uvPmoKR/uadpFgC4przc/cV35gmAvkVNlW7SHMArZagV+Xau4CLyMn
uG3UsOcGAngLH1ypmTb+u6wbBfpXzYEQQGfWMItYNdSWYb7QjHqXnxr5IuYUL6nG6AEfq/gmD6yO
TSwyOR2Bm40cZbIc22GoiS9g5+vCShjEbyrpEJIJ7RfRACvmfe8EiRROM6GyD5eHn7OgzS+8LOy4
g2gxPR/VSpAQGQuBldYpdlH5NnbQtwl6OErXb4y/E3w57bqukPyV93t4CTZedJMeJfD/1K2uaGvG
/w/VNfFVbkhJ+Pi474j48V4Rd6rfArMCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAw
DgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
aWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
aWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEW
HGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFCRuKy3QapJRUSVpAaqaR6aJ
50AgMB8GA1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA0GCSqGSIb3DQEBCwUAA4IBAQAL
OYSR+ZfrqoGvhOlaOJL84mxZvzbIRacxAxHhBsCsMsdaVSnaT0AC9aHesO3ewPj2dZ12uYf+QYB6
z13jAMZbAuabeGLJ3LhimnftiQjXS8X9Q9ViIyfEBFltcT8jW+rZ8uckJ2/0lYDblizkVIvP6hnZ
f1WZUXoOLRg9eFhSvGNoVwvdRLNXSmDmyHBwW4coatc7TlJFGa8kBpJIERqLrqwYElesA8u49L3K
Jg6nwd3jM+/AVTANlVlOnAM2BvjAjxSZnE0qnsHhfTuvcqdFuhOWKU4Z0BqYBvQ3lBetoxi6PrAB
DJXWKTUgNX31EGDk92hiHuwZ4STyhxGs6QiA
-----END CERTIFICATE-----`;
//========================================================
// PayPalIPNGateway
// Gateway for the PayPal IPN messages
//========================================================
module.exports = class PayPalIPNGateway {
//========================================================
// constructor
//========================================================
constructor(injected) {
console.log('+v PayPalIPNGateway.constructor');
this.https = injected && injected.https
? injected.https
: require('https');
if (this.https && this.https.globalAgent && this.https.globalAgent.options) {
//Load the paypal sandbox certs
rootCAs.push(paypalSandboxIntermediateCertificate2);
rootCAs.push(paypalSandboxIntermediateCertificate1);
//Use these certs in the https requests
console.log('++v Adding root SSL certificate authorities');
this.https.globalAgent.options.ca = rootCAs;
}
}
//========================================================
// verify
// https://github.com/paypal/ipn-code-samples/blob/master/javascript/googlecloudfunctions.js
//========================================================
verify(ipnTransactionMessage, mode, callback) {
console.log('+ PayPalIPNGateway.verify');
console.log('++ ipnTransactionMessage: ' + JSON.stringify(ipnTransactionMessage, null, 4));
console.log('++ mode: %s', mode);
const useSandbox = mode === 'live' ? false : true;
const host = useSandbox ? SANDBOX_VERIFY_HOSTNAME : PRODUCTION_VERIFY_HOSTNAME;
console.log('++ host: %s', host);
var self = this;
//Get the transaction message
// JSON object of the IPN message consisting of transaction details.
// Convert JSON ipn data to a query string since Google Cloud Function does not expose raw request data.
let formUrlEncodedBody = qs.stringify(ipnTransactionMessage);
// Build the body of the verification post message by prefixing 'cmd=_notify-validate'.
let verificationBody = `cmd=_notify-validate&${formUrlEncodedBody}`;
console.log(`++ verificationBody: ${verificationBody}`);
let options = {
method: 'POST',
port: 443,
uri: 'https://' + host + '/cgi-bin/webscr',
body: verificationBody,
agent: false, //connection: 'close'
secureProtocol: 'TLSv1_2_method',
ca: rootCAs,
headers: {
'User-Agent': 'NODEJS-IPN-VerificationScript',
}
};
console.log('++ options: ' + JSON.stringify(options, null, 4));
// POST verification IPN data to paypal to validate.
var request = self.https.request(options, function(response) {
console.log('++ response: ' + JSON.stringify(response, new Censor(response), 4));
console.log('++ response.headers: ' + JSON.stringify(response.headers, new Censor(response.headers), 4));
console.log('++ response.statusMessage: ' + JSON.stringify(response.statusMessage, new Censor(response.statusMessage), 4));
console.log('++ response.statusCode: ' + JSON.stringify(response.statusCode, new Censor(response.statusCode), 4));
var responseBody = '';
console.log(options.host + ':' + response.statusCode);
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log('++ chunk');
responseBody += chunk;
}).on('end', function() {
console.log('++ end');
// if (!error && response.statusCode == 200) {
if (response.statusCode == 200) {
// Check the response body for validation results.
if (responseBody === 'VERIFIED') {
console.log(`++ Verified IPN: IPN message for Transaction ID: ${ipnTransactionMessage.txn_id} is verified.`);
callback(null);
} else if (responseBody === 'INVALID') {
console.error(`++ M17 paypal Invalid IPN: IPN message for Transaction ID: ${ipnTransactionMessage.txn_id} is invalid.`);
callback('invalid');
} else {
console.error('Unexpected response body.');
callback('bad body');
}
} else {
console.log('++ M17 paypal status code error');
console.log('++ response.statusCode: %s', response.statusCode);
console.log('++ responseBody: %s', responseBody);
callback('bad status');
}
});
});
request.on('error', function(err) {
console.log('++ M17 paypal err: ' + err);
console.log('++ err: ' + JSON.stringify(err, null, 4));
callback('paypal error');
});
request.end();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment