This repository has been archived by the owner on Mar 7, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
sagepay-server-util.js
131 lines (119 loc) · 4.07 KB
/
sagepay-server-util.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
"use strict";
const assert = require("assert");
const bodyParser = require("body-parser");
const crypto = require("crypto");
const extend = require("extend");
const request = require("request-promise-native");
const parser = bodyParser.urlencoded({extended: false});
// Alternative interface without express integrtion
class SagepayServerUtil {
/*
SagepayServerUtil(options)
@options Optional. Contains connetion options.
@options.gatewayUrl
Required. The URL to the Sage Pay gateway.
@options.vendor
Required. The vendor name provided by Sage Pay.
*/
constructor(options) {
assert(options, "options is required");
assert(options.gatewayUrl, "options.gatewayUrl is required.");
assert(options.vendor, "options.vendor is required.");
this._options = options;
}
register(transaction) {
var form = extend({
VPSProtocol: "3.00",
Vendor: this._options.vendor,
TxType: "PAYMENT"
}, transaction);
return request({
uri: this._options.gatewayUrl,
method: "POST",
form: form
}).then(data => {
var resultBuffer = {};
data.split("\r\n").map(a => a.split("=")).forEach(a => {
resultBuffer[a[0]] = a[1];
});
// Sage Pay provides a value called NextURL, but it is not
// complete. This fact is not documented by Sage Pay. To save
// implementers the hassle, we fix it here.
resultBuffer.NextURL = [
resultBuffer.NextURL,
"=",
resultBuffer.VPSTxId
].join("");
return resultBuffer;
});
}
parseNotification(stream) {
return new Promise(function(accept, reject) {
parser(stream, {}, function(err) {
if (err)
reject(err);
else
accept(stream.body);
});
});
}
calculateNotificationSignature(vpsTxId, securityKey, notification) {
var message = [
vpsTxId,
notification.VendorTxCode,
notification.Status,
notification.TxAuthNo,
this._options.vendor,
notification.AVSCV2,
securityKey,
notification.AddressResult,
notification.PostCodeResult,
notification.CV2Result,
notification.GiftAid,
notification["3DSecureStatus"],
notification.CAVV,
notification.AddressStatus,
notification.PayerStatus,
notification.CardType,
notification.Last4Digits,
notification.DeclineCode,
notification.ExpiryDate,
notification.FraudResponse,
notification.BankAuthCode
].join("");
var hash = crypto.createHash("md5");
hash.update(message, "utf8");
var expectedSignature = hash.digest("hex").toUpperCase();
return expectedSignature;
}
validateNotificationSignature(vpsTxId, securityKey, notification) {
var expectedSignature = this.calculateNotificationSignature(
vpsTxId,
securityKey,
notification
);
var actualSignature = notification.VPSSignature;
return actualSignature === expectedSignature;
}
formatNotificationResponse(response) {
return Object.keys(response)
.map(key => [key, response[key]])
.map(pair => pair.join("="))
.join("\r\n");
}
// This is used for testing purposes only, normally Sage Pay will parse
// the notification response.
parseNotificationResponse(response) {
var ret = {};
response
.split("\r\n")
.forEach(line => {
var splitIndex = line.indexOf("=");
var key = line.substr(0, splitIndex);
var value = line.substr(splitIndex + 1);
ret[key] = value;
});
return ret;
}
}
module.exports = SagepayServerUtil;