Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates #38

Merged
merged 7 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,30 @@ You can sign up for a Realex account at https://developer.realexpayments.com
## Hosted Payment Page (HPP) JS Library

### Usage
The Javascript required to initialise the library is below. This code must only be executed when the DOM is fully loaded.
The Javascript required to initialise the library is below. This code must only be executed when the DOM is fully loaded. (default method: lightbox)
```javascript
RealexHpp.init(payButtonId, merchantUrl, jsonFromServerSdk);
RealexHpp.init(payButtonId, merchantUrl, jsonFromServerSdk[, options]);
```
* payButtonId - The ID of the button used to launch the lightbox.
* merchantUrl - The URL to which the JSON response from Realex will be posted.
* payButtonId - The ID of the button used to launch the lightbox. Set to "autoload" if you want to load without having to press a button
* merchantUrl - could be one of 2 types:
- string - The URL to which the JSON response from Realex will be posted.
- function - the callback function
* jsonFromServerSdk - The JSON output from the Realex HPP Server SDK.
* options/events
- onResize (iframe embed) Send resize iframe events so the parent frame can be adjusted

### Consuming the resulting POST
Once the payment has completed the Realex JSON response will be posted within to the supplied merchantUrl. The name of the field containing the JSON response is hppResponse.

If you prefer to handle response manually, provide your own callback function in "merchantUrl". The answer will be pre-parsed to an object ready to be used.

## Examples

* [embedded iFrame](examples/hpp/process-a-payment-embedded.html)
* [embedded iFrame autoload](examples/hpp/process-a-payment-embedded-autoload.html)
* [embedded iFrame autoload, callback](examples/hpp/process-a-payment-embedded-autoload-callback.html)
* [lightbox/modal](examples/hpp/process-a-payment-lightbox.html)

## Remote JS Library

### Validation functions
Expand Down
210 changes: 180 additions & 30 deletions dist/rxp-js.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
/*! rxp-js - v1.3.1 - 2018-08-30
/*! rxp-js - v1.4.0 - 2021-03-29
* The official Realex Payments JS Library
* https://github.com/realexpayments/rxp-js
* Licensed MIT
*/
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
};
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for(var i = this.length - 1; i >= 0; i--) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
};
var RealexHpp = (function () {

'use strict';
Expand All @@ -15,20 +25,141 @@ var RealexHpp = (function () {
hppUrl = url;
};

var mobileXSLowerBound = 360;
var setMobileXSLowerBound = function (lowerBound) {
mobileXSLowerBound = lowerBound;
};

var isWindowsMobileOs = /Windows Phone|IEMobile/.test(navigator.userAgent);
var isAndroidOrIOs = /Android|iPad|iPhone|iPod/.test(navigator.userAgent);
var isMobileXS = ( (((window.innerWidth > 0) ? window.innerWidth : screen.width) <= 360 ? true : false) || (((window.innerHeight > 0) ? window.innerHeight : screen.Height) <= 360 ? true : false)) ;
var isMobileXS = function () {
return (((window.innerWidth > 0) ? window.innerWidth : screen.width) <= mobileXSLowerBound ? true : false) ||
(((window.innerHeight > 0) ? window.innerHeight : screen.Height) <= mobileXSLowerBound ? true : false);
};

// Display IFrame on WIndows Phone OS mobile devices
var isMobileIFrame = isWindowsMobileOs;

// For IOs/Android and small screen devices always open in new tab/window
var isMobileNewTab = !isWindowsMobileOs && (isAndroidOrIOs || isMobileXS);
var isMobileNewTab = function () {
return !isWindowsMobileOs && (isAndroidOrIOs || isMobileXS());
};

var tabWindow;

var redirectUrl;

var internal = {

base64:{
encode:function(input) {
var keyStr = "ABCDEFGHIJKLMNOP" +
"QRSTUVWXYZabcdef" +
"ghijklmnopqrstuv" +
"wxyz0123456789+/" +
"=";
input = escape(input);
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;

do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);

enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;

if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}

output = output +
keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);

return output;
},
decode:function(input) {
if(typeof input === 'undefined') {
return input;
}
var keyStr = "ABCDEFGHIJKLMNOP" +
"QRSTUVWXYZabcdef" +
"ghijklmnopqrstuv" +
"wxyz0123456789+/" +
"=";
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;

// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
var base64test = /[^A-Za-z0-9\+\/\=]/g;
if (base64test.exec(input)) {
throw new Error("There were invalid base64 characters in the input text.\n" +
"Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
"Expect errors in decoding.");
}
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));

chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;

output = output + String.fromCharCode(chr1);

if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}

chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";

} while (i < input.length);

return unescape(output);
}
},
decodeAnswer:function(answer){ //internal.decodeAnswer

var _r;

try {
_r=JSON.parse(answer);
} catch (e) {
_r = { error: true, message: answer };
}

try {
for(var r in _r){
if(_r[r]) {
_r[r]=internal.base64.decode(_r[r]);
}
}
} catch (e) { /** */ }
return _r;
},
createFormHiddenInput: function (name, value) {
var el = document.createElement("input");
el.setAttribute("type", "hidden");
Expand Down Expand Up @@ -127,20 +258,20 @@ var RealexHpp = (function () {
var form = document.createElement("form");
form.setAttribute("method", "POST");
form.setAttribute("action", hppUrl);

var versionSet = false;

for (var key in token) {
if (key === "HPP_VERSION"){
versionSet = true;
}
form.appendChild(internal.createFormHiddenInput(key, token[key]));
}

if (versionSet === false){
form.appendChild(internal.createFormHiddenInput("HPP_VERSION", "2"));
}

if (ignorePostMessage) {
form.appendChild(internal.createFormHiddenInput("MERCHANT_RESPONSE_URL", redirectUrl));
} else {
Expand Down Expand Up @@ -295,12 +426,12 @@ var RealexHpp = (function () {
if (!internal.isMessageFromHpp(event.origin, hppUrl)) {
return;
}

// check for iframe resize values
if (event.data && JSON.parse(event.data).iframe) {
if (!isMobileNewTab) {
var iframeWidth = JSON.parse(event.data).iframe.width;
var iframeHeight = JSON.parse(event.data).iframe.height;
var evtdata;
if (event.data && (evtdata=internal.decodeAnswer(event.data)).iframe) {
if (!isMobileNewTab()) {
var iframeWidth = evtdata.iframe.width;
var iframeHeight = evtdata.iframe.height;

var iFrame;
var resized = false;
Expand All @@ -310,6 +441,9 @@ var RealexHpp = (function () {
} else {
iFrame = document.getElementById("rxp-frame-" + randomId);
}
if(lightboxInstance.events && lightboxInstance.events.onResize) {
lightboxInstance.events.onResize(evtdata.iframe);
}

if (iframeWidth === "390px" && iframeHeight === "440px") {
iFrame.setAttribute("width", iframeWidth);
Expand Down Expand Up @@ -344,25 +478,34 @@ var RealexHpp = (function () {
}
}
} else {
if (isMobileNewTab && tabWindow) {
//Close the new window
tabWindow.close();
} else {
//Close the lightbox
lightboxInstance.close();
}
var _close=function(){
if (isMobileNewTab() && tabWindow) {
//Close the new window
tabWindow.close();
} else {
//Close the lightbox
lightboxInstance.close();
}
var overlay=document.getElementById("rxp-overlay-" + randomId);
if(overlay) {
overlay.remove();
}

};
var response = event.data;

//allow the script to intercept the answer, instead of redirecting to another page. (which is really a 90s thing)
if(typeof merchantUrl==='function'){
var answer=internal.decodeAnswer(response);
merchantUrl(answer,_close);
return;
}
_close();
//Create a form and submit the hpp response to the merchant's response url
var form = document.createElement("form");
form.setAttribute("method", "POST");
form.setAttribute("action", merchantUrl);

form.appendChild(internal.createFormHiddenInput("hppResponse", response));

document.body.appendChild(form);

form.submit();
}
};
Expand Down Expand Up @@ -391,7 +534,7 @@ var RealexHpp = (function () {

return {
lightbox: function () {
if (isMobileNewTab) {
if (isMobileNewTab()) {
tabWindow = internal.openWindow(token);
} else {
overlayElement = internal.createOverlay();
Expand Down Expand Up @@ -427,8 +570,12 @@ var RealexHpp = (function () {
//Get the lightbox instance (it's a singleton) and set the sdk json
var lightboxInstance = RxpLightbox.getInstance(serverSdkJson);

//if you want the form to load on function call, set to autoload
if(idOfLightboxButton==='autoload'){
lightboxInstance.lightbox();
}
// Sets the event listener on the PAY button. The click will invoke the lightbox method
if (document.getElementById(idOfLightboxButton).addEventListener) {
else if (document.getElementById(idOfLightboxButton).addEventListener) {
document.getElementById(idOfLightboxButton).addEventListener("click", lightboxInstance.lightbox, true);
} else {
document.getElementById(idOfLightboxButton).attachEvent('onclick', lightboxInstance.lightbox);
Expand Down Expand Up @@ -492,17 +639,20 @@ var RealexHpp = (function () {

//Set the hpp token
instance.setToken(hppToken);

return instance;
},
init: function (idOfEmbeddedButton, idOfTargetIframe, merchantUrl, serverSdkJson) {
init: function (idOfEmbeddedButton, idOfTargetIframe, merchantUrl, serverSdkJson,events) {
//Get the embedded instance (it's a singleton) and set the sdk json
var embeddedInstance = RxpEmbedded.getInstance(serverSdkJson);
embeddedInstance.events=events;

embeddedInstance.setIframe(idOfTargetIframe);

//if you want the form to load on function call, set to autoload
if(idOfEmbeddedButton==='autoload'){
embeddedInstance.embedded();
}
// Sets the event listener on the PAY button. The click will invoke the embedded method
if (document.getElementById(idOfEmbeddedButton).addEventListener) {
else if (document.getElementById(idOfEmbeddedButton).addEventListener) {
document.getElementById(idOfEmbeddedButton).addEventListener("click", embeddedInstance.embedded, true);
} else {
document.getElementById(idOfEmbeddedButton).attachEvent('onclick', embeddedInstance.embedded);
Expand Down Expand Up @@ -594,11 +744,11 @@ var RealexHpp = (function () {
init: RxpRedirect.init
},
setHppUrl: setHppUrl,
setMobileXSLowerBound: setMobileXSLowerBound,
_internal: internal
};

}());

var RealexRemote = (function() {

'use strict';
Expand Down
4 changes: 2 additions & 2 deletions dist/rxp-js.min.js

Large diffs are not rendered by default.

Loading