Created
September 12, 2018 06:18
-
-
Save danielflippance/81d4229e0e65e8e58df434f3bcaee342 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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