From 6e8319903838a2ccad4d20e9b9a633ede6fcc0b4 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 29 Mar 2021 21:38:13 +0300 Subject: [PATCH 01/38] TR-92: add 3DS --- .../globalpayments-secure-payment-fields.css | 4 + assets/frontend/js/globalpayments-3ds.js | 733 ++++++++++++++++++ assets/frontend/js/globalpayments-3ds.min.js | 16 + .../globalpayments-secure-payment-fields.js | 203 ++++- src/Gateways/AbstractGateway.php | 43 +- src/Gateways/Clients/SdkClient.php | 52 +- src/Gateways/GpApiGateway.php | 155 +++- src/Gateways/Requests/AbstractRequest.php | 6 + .../Requests/AuthorizationRequest.php | 7 +- src/Gateways/Requests/RequestArg.php | 1 + .../ThreeDSecure/CheckEnrollmentRequest.php | 42 + .../InitiateAuthenticationRequest.php | 102 +++ src/Gateways/Requests/VerifyRequest.php | 9 + 13 files changed, 1353 insertions(+), 20 deletions(-) create mode 100644 assets/frontend/js/globalpayments-3ds.js create mode 100644 assets/frontend/js/globalpayments-3ds.min.js create mode 100644 src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php create mode 100644 src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php diff --git a/assets/frontend/css/globalpayments-secure-payment-fields.css b/assets/frontend/css/globalpayments-secure-payment-fields.css index 0a4ce6a..dbb420b 100644 --- a/assets/frontend/css/globalpayments-secure-payment-fields.css +++ b/assets/frontend/css/globalpayments-secure-payment-fields.css @@ -1,3 +1,7 @@ .globalpayments iframe { min-height: 3.6rem; } + +div[id^="GlobalPayments-overlay-"] { + z-index: 1001 !important; +} \ No newline at end of file diff --git a/assets/frontend/js/globalpayments-3ds.js b/assets/frontend/js/globalpayments-3ds.js new file mode 100644 index 0000000..d638fdf --- /dev/null +++ b/assets/frontend/js/globalpayments-3ds.js @@ -0,0 +1,733 @@ +this.GlobalPayments = this.GlobalPayments || {}; +this.GlobalPayments.ThreeDSecure = (function (exports) { + 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + } + + (function (AuthenticationSource) { + AuthenticationSource["Browser"] = "BROWSER"; + AuthenticationSource["MobileSDK"] = "MOBILE_SDK"; + AuthenticationSource["StoredRecurring"] = "STORED_RECURRING"; + })(exports.AuthenticationSource || (exports.AuthenticationSource = {})); + (function (AuthenticationRequestType) { + AuthenticationRequestType["AddCard"] = "ADD_CARD"; + AuthenticationRequestType["CardholderVerification"] = "CARDHOLDER_VERIFICATION"; + AuthenticationRequestType["InstalmentTransaction"] = "INSTALMENT_TRANSACTION"; + AuthenticationRequestType["MaintainCard"] = "MAINTAIN_CARD"; + AuthenticationRequestType["PaymentTransaction"] = "PAYMENT_TRANSACTION"; + AuthenticationRequestType["RecurringTransaction"] = "RECURRING_TRANSACTION"; + })(exports.AuthenticationRequestType || (exports.AuthenticationRequestType = {})); + (function (ChallengeRequestIndicator) { + ChallengeRequestIndicator["ChallengeMandated"] = "CHALLENGE_MANDATED"; + ChallengeRequestIndicator["ChallengePreferred"] = "CHALLENGE_PREFERRED"; + ChallengeRequestIndicator["NoChallengeRequested"] = "NO_CHALLENGE_REQUESTED"; + ChallengeRequestIndicator["NoPreference"] = "NO_PREFERENCE"; + })(exports.ChallengeRequestIndicator || (exports.ChallengeRequestIndicator = {})); + (function (ChallengeWindowSize) { + ChallengeWindowSize["FullScreen"] = "FULL_SCREEN"; + ChallengeWindowSize["Windowed250x400"] = "WINDOWED_250X400"; + ChallengeWindowSize["Windowed390x400"] = "WINDOWED_390X400"; + ChallengeWindowSize["Windowed500x600"] = "WINDOWED_500X600"; + ChallengeWindowSize["Windowed600x400"] = "WINDOWED_600X400"; + })(exports.ChallengeWindowSize || (exports.ChallengeWindowSize = {})); + (function (MessageCategory) { + MessageCategory["NonPayment"] = "NON_PAYMENT_AUTHENTICATION"; + MessageCategory["Payment"] = "PAYMENT_AUTHENTICATION"; + })(exports.MessageCategory || (exports.MessageCategory = {})); + (function (MethodUrlCompletion) { + MethodUrlCompletion["Unavailable"] = "UNAVAILABLE"; + MethodUrlCompletion["No"] = "NO"; + MethodUrlCompletion["Yes"] = "YES"; + })(exports.MethodUrlCompletion || (exports.MethodUrlCompletion = {})); + (function (TransactionStatus) { + TransactionStatus["AuthenticationAttemptedButNotSuccessful"] = "AUTHENTICATION_ATTEMPTED_BUT_NOT_SUCCESSFUL"; + TransactionStatus["AuthenticationCouldNotBePerformed"] = "AUTHENTICATION_COULD_NOT_BE_PERFORMED"; + TransactionStatus["AuthenticationFailed"] = "AUTHENTICATION_FAILED"; + TransactionStatus["AuthenticationIssuerRejected"] = "AUTHENTICATION_ISSUER_REJECTED"; + TransactionStatus["AuthenticationSuccessful"] = "AUTHENTICATION_SUCCESSFUL"; + TransactionStatus["ChallengeRequired"] = "CHALLENGE_REQUIRED"; + })(exports.TransactionStatus || (exports.TransactionStatus = {})); + (function (TransactionStatusReason) { + TransactionStatusReason["CardAuthenticationFailed"] = "CARD_AUTHENTICATION_FAILED"; + TransactionStatusReason["UnknownDevice"] = "UNKNOWN_DEVICE"; + TransactionStatusReason["UnsupportedDevice"] = "UNSUPPORTED_DEVICE"; + TransactionStatusReason["ExceedsAuthenticationFrequencyLimit"] = "EXCEEDS_AUTHENTICATION_FREQUENCY_LIMIT"; + TransactionStatusReason["ExpiredCard"] = "EXPIRED_CARD"; + TransactionStatusReason["InvalidCardNumber"] = "INVALID_CARD_NUMBER"; + TransactionStatusReason["InvalidTransaction"] = "INVALID_TRANSACTION"; + TransactionStatusReason["NoCardRecord"] = "NO_CARD_RECORD"; + TransactionStatusReason["SecurityFailure"] = "SECURITY_FAILURE"; + TransactionStatusReason["StolenCard"] = "STOLEN_CARD"; + TransactionStatusReason["SuspectedFraud"] = "SUSPECTED_FRAUD"; + TransactionStatusReason["TransactionNotPermittedToCardholder"] = "TRANSACTION_NOT_PERMITTED_TO_CARDHOLDER"; + TransactionStatusReason["CardholderNotEnrolledInService"] = "CARDHOLDER_NOT_ENROLLED_IN_SERVICE"; + TransactionStatusReason["TransactionTimedOutAtTheAcs"] = "TRANSACTION_TIMED_OUT_AT_THE_ACS"; + TransactionStatusReason["LowConfidence"] = "LOW_CONFIDENCE"; + TransactionStatusReason["MediumConfidence"] = "MEDIUM_CONFIDENCE"; + TransactionStatusReason["HighConfidence"] = "HIGH_CONFIDENCE"; + TransactionStatusReason["VeryHighConfidence"] = "VERY_HIGH_CONFIDENCE"; + TransactionStatusReason["ExceedsAcsMaximumChallenges"] = "EXCEEDS_ACS_MAXIMUM_CHALLENGES"; + TransactionStatusReason["NonPaymentTransactionNotSupported"] = "NON_PAYMENT_TRANSACTION_NOT_SUPPORTED"; + TransactionStatusReason["ThreeriTransactionNotSupported"] = "THREERI_TRANSACTION_NOT_SUPPORTED"; + })(exports.TransactionStatusReason || (exports.TransactionStatusReason = {})); + function colorDepth(value) { + var result = ""; + switch (value) { + case 1: + return "ONE_BIT"; + case 2: + result += "TWO"; + break; + case 4: + result += "FOUR"; + break; + case 8: + result += "EIGHT"; + break; + case 15: + result += "FIFTEEN"; + break; + case 16: + result += "SIXTEEN"; + break; + case 24: + case 30: + result += "TWENTY_FOUR"; + break; + case 32: + result += "THIRTY_TWO"; + break; + case 48: + result += "FORTY_EIGHT"; + break; + default: + throw new Error("Unknown color depth '" + value + "'"); + } + return result + "_BITS"; + } + function dimensionsFromChallengeWindowSize(options) { + var height = 0; + var width = 0; + switch (options.size || options.windowSize) { + case exports.ChallengeWindowSize.Windowed250x400: + height = 250; + width = 400; + break; + case exports.ChallengeWindowSize.Windowed390x400: + height = 390; + width = 400; + break; + case exports.ChallengeWindowSize.Windowed500x600: + height = 500; + width = 600; + break; + case exports.ChallengeWindowSize.Windowed600x400: + height = 600; + width = 400; + break; + } + return { height: height, width: width }; + } + function messageCategoryFromAuthenticationRequestType(authenticationRequestType) { + switch (authenticationRequestType) { + case exports.AuthenticationRequestType.AddCard: + case exports.AuthenticationRequestType.CardholderVerification: + case exports.AuthenticationRequestType.MaintainCard: + return exports.MessageCategory.NonPayment; + case exports.AuthenticationRequestType.InstalmentTransaction: + case exports.AuthenticationRequestType.PaymentTransaction: + case exports.AuthenticationRequestType.RecurringTransaction: + default: + return exports.MessageCategory.Payment; + } + } + + var GPError = /** @class */ (function (_super) { + __extends(GPError, _super); + function GPError(reasons, message) { + var _this = _super.call(this, message || "Error: see `reasons` property") || this; + _this.error = true; + _this.reasons = reasons; + return _this; + } + return GPError; + }(Error)); + + function handleNotificationMessageEvent(event, data, origin) { + if (window.parent !== window) { + window.parent.postMessage({ data: data, event: event }, origin || window.location.origin); + } + } + + function makeRequest(endpoint, data) { + return __awaiter(this, void 0, void 0, function () { + var headers, rawResponse, _a, e_1, reasons; + var _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + headers = { + "Content-Type": "application/json", + }; + _c.label = 1; + case 1: + _c.trys.push([1, 6, , 7]); + return [4 /*yield*/, fetch(endpoint, { + body: JSON.stringify(data), + credentials: "omit", + headers: typeof Headers !== "undefined" ? new Headers(headers) : headers, + method: "POST", + })]; + case 2: + rawResponse = _c.sent(); + if (!!rawResponse.ok) return [3 /*break*/, 4]; + _a = GPError.bind; + _b = { + code: rawResponse.status.toString() + }; + return [4 /*yield*/, rawResponse.text()]; + case 3: throw new (_a.apply(GPError, [void 0, [ + (_b.message = _c.sent(), + _b) + ], rawResponse.statusText]))(); + case 4: return [4 /*yield*/, rawResponse.json()]; + case 5: return [2 /*return*/, _c.sent()]; + case 6: + e_1 = _c.sent(); + reasons = [{ code: e_1.name, message: e_1.message }]; + if (e_1.reasons) { + reasons = reasons.concat(e_1.reasons); + } + throw new GPError(reasons); + case 7: return [2 /*return*/]; + } + }); + }); + } + + // most of this module is pulled from the legacy Realex Payments JavaScript library + 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); + // For IOs/Android and small screen devices always open in new tab/window + // TODO: Confirm/implement once sandbox support is in place + var isMobileNewTab = !isWindowsMobileOs && (isAndroidOrIOs || isMobileXS); + // Display IFrame on WIndows Phone OS mobile devices + var isMobileIFrame = isWindowsMobileOs || isMobileNewTab; + var randomId = Math.random() + .toString(16) + .substr(2, 8); + function createLightbox(iFrame, options) { + // Create the overlay + var overlayElement = createOverlay(); + // Create the spinner + var spinner = createSpinner(); + document.body.appendChild(spinner); + var _a = dimensionsFromChallengeWindowSize(options), height = _a.height, width = _a.width; + // Configure the iframe + if (height) { + iFrame.setAttribute("height", height + "px"); + } + iFrame.setAttribute("frameBorder", "0"); + if (width) { + iFrame.setAttribute("width", width + "px"); + } + iFrame.setAttribute("seamless", "seamless"); + iFrame.style.zIndex = "10001"; + iFrame.style.position = "absolute"; + iFrame.style.transition = "transform 0.5s ease-in-out"; + iFrame.style.transform = "scale(0.7)"; + iFrame.style.opacity = "0"; + overlayElement.appendChild(iFrame); + if (isMobileIFrame || options.windowSize === exports.ChallengeWindowSize.FullScreen) { + iFrame.style.top = "0px"; + iFrame.style.bottom = "0px"; + iFrame.style.left = "0px"; + iFrame.style.marginLeft = "0px;"; + iFrame.style.width = "100%"; + iFrame.style.height = "100%"; + iFrame.style.minHeight = "100%"; + iFrame.style.WebkitTransform = "translate3d(0,0,0)"; + iFrame.style.transform = "translate3d(0, 0, 0)"; + var metaTag = document.createElement("meta"); + metaTag.name = "viewport"; + metaTag.content = + "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"; + document.getElementsByTagName("head")[0].appendChild(metaTag); + } + else { + iFrame.style.top = "40px"; + iFrame.style.left = "50%"; + iFrame.style.marginLeft = "-" + width / 2 + "px"; + } + iFrame.onload = getIFrameOnloadEventHandler(iFrame, spinner, overlayElement, options); + } + function closeModal() { + Array.prototype.slice.call(document + .querySelectorAll("[target$=\"-" + randomId + "\"],[id$=\"-" + randomId + "\"]")) + .forEach(function (element) { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + }); + } + function createOverlay() { + var overlay = document.createElement("div"); + overlay.setAttribute("id", "GlobalPayments-overlay-" + randomId); + overlay.style.position = "fixed"; + overlay.style.width = "100%"; + overlay.style.height = "100%"; + overlay.style.top = "0"; + overlay.style.left = "0"; + overlay.style.transition = "all 0.3s ease-in-out"; + overlay.style.zIndex = "100"; + if (isMobileIFrame) { + overlay.style.position = "absolute !important"; + overlay.style.WebkitOverflowScrolling = "touch"; + overlay.style.overflowX = "hidden"; + overlay.style.overflowY = "scroll"; + } + document.body.appendChild(overlay); + setTimeout(function () { + overlay.style.background = "rgba(0, 0, 0, 0.7)"; + }, 1); + return overlay; + } + function createCloseButton(options) { + if (document.getElementById("GlobalPayments-frame-close-" + randomId) !== null) { + return; + } + var closeButton = document.createElement("img"); + closeButton.id = "GlobalPayments-frame-close-" + randomId; + closeButton.src = + // tslint:disable-next-line:max-line-length + "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUJFRjU1MEIzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUJFRjU1MEMzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQkVGNTUwOTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQkVGNTUwQTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHco5QAAAHpSURBVHjafFRdTsJAEF42JaTKn4glGIg++qgX4AAchHAJkiZcwnAQD8AF4NFHCaC2VgWkIQQsfl/jNJUik8Duzs/XmW9mN7Xb7VRc5vP5zWKxaK5Wq8Zmu72FqobfJG0YQ9M0+/l8/qFQKDzGY1JxENd1288vLy1s786KRZXJZCLber1Wn7MZt4PLarVnWdZ9AmQ8Hncc17UvymVdBMB/MgPQm+cFFcuy6/V6lzqDf57ntWGwYdBIVx0TfkBD6I9M35iRJgfIoAVjBLDZbA4CiJ5+9AdQi/EahibqDTkQx6fRSIHcPwA8Uy9A9Gcc47Xv+w2wzhRDYzqdVihLIbsIiCvP1NNOoX/29FQx3vgOgtt4FyRdCgPRarX4+goB9vkyAMh443cOEsIAAcjncuoI4TXWMAmCIGFhCQLAdZ8jym/cRJ+Y5nC5XCYAhINKpZLgSISZgoqh5iiLQrojAFICVwGS7tCfe5DbZzkP56XS4NVxwvTI/vXVVYIDnqmnnX70ZxzjNS8THHooK5hMpxHQIREA+tEfA9djfHR3MHkdx3Hspe9r3B+VzWaj2RESyR2mlCUE4MoGQDdxiwHURq2t94+PO9bMIYyTyDNLwMoM7g8+BfKeYGniyw2MdfSehF3Qmk1IvCc/AgwAaS86Etp38bUAAAAASUVORK5CYII="; + closeButton.style.transition = "all 0.5s ease-in-out"; + closeButton.style.opacity = "0"; + closeButton.style.float = "left"; + closeButton.style.position = "absolute"; + closeButton.style.left = "50%"; + closeButton.style.zIndex = "99999999"; + closeButton.style.top = "30px"; + var width = dimensionsFromChallengeWindowSize(options).width; + if (width) { + closeButton.style.marginLeft = width / 2 - 8 /* half image width */ + "px"; + } + setTimeout(function () { + closeButton.style.opacity = "1"; + }, 500); + if (isMobileIFrame || options.windowSize === exports.ChallengeWindowSize.FullScreen) { + closeButton.style.float = "right"; + closeButton.style.top = "20px"; + closeButton.style.left = "initial"; + closeButton.style.marginLeft = "0px"; + closeButton.style.right = "20px"; + } + return closeButton; + } + function createSpinner() { + var spinner = document.createElement("img"); + spinner.setAttribute("src", + // tslint:disable-next-line:max-line-length + "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iIzAwNzBiYSIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iI2MwZGNlZSIgZmlsbC1vcGFjaXR5PSIwLjI1IiB0cmFuc2Zvcm09InJvdGF0ZSgyMjUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMjcwIDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDMxNSA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzQ1IDY0IDY0OzkwIDY0IDY0OzEzNSA2NCA2NDsxODAgNjQgNjQ7MjI1IDY0IDY0OzI3MCA2NCA2NDszMTUgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMjgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="); + spinner.setAttribute("id", "GlobalPayments-loader-" + randomId); + spinner.style.left = "50%"; + spinner.style.position = "fixed"; + spinner.style.background = "#FFFFFF"; + spinner.style.borderRadius = "50%"; + spinner.style.width = "30px"; + spinner.style.zIndex = "200"; + spinner.style.marginLeft = "-15px"; + spinner.style.top = "120px"; + return spinner; + } + function getIFrameOnloadEventHandler(iFrame, spinner, overlayElement, options) { + return function () { + iFrame.style.opacity = "1"; + iFrame.style.transform = "scale(1)"; + iFrame.style.backgroundColor = "#ffffff"; + if (spinner.parentNode) { + spinner.parentNode.removeChild(spinner); + } + var closeButton; + closeButton = createCloseButton(options); + if (closeButton) { + overlayElement.appendChild(closeButton); + closeButton.addEventListener("click", function () { + if (closeButton) { + closeModal(); + } + }, true); + } + }; + } + + function postToIframe(endpoint, fields, options) { + return new Promise(function (resolve, reject) { + var timeout; + if (options.timeout) { + timeout = setTimeout(function () { + ensureIframeClosed(timeout); + reject(new Error("timeout reached")); + }, options.timeout); + } + var iframe = document.createElement("iframe"); + iframe.id = iframe.name = "GlobalPayments-3DSecure-" + randomId; + iframe.style.display = options.hide ? "none" : "inherit"; + var form = createForm(endpoint, iframe.id, fields); + switch (options.displayMode) { + case "redirect": + // TODO: Add redirect support once sandbox environment respects configured + // challengeNotificationUrl instead of hardcoded value + ensureIframeClosed(timeout); + reject(new Error("Not implemented")); + return; + case "lightbox": + createLightbox(iframe, options); + break; + case "embedded": + default: + if (!handleEmbeddedIframe(reject, { iframe: iframe, timeout: timeout }, options)) { + // rejected + return; + } + break; + } + window.addEventListener("message", getWindowMessageEventHandler(resolve, { + origin: options.origin, + timeout: timeout, + })); + document.body.appendChild(form); + form.submit(); + }); + } + function createForm(action, target, fields) { + var form = document.createElement("form"); + form.setAttribute("method", "POST"); + form.setAttribute("action", action); + form.setAttribute("target", target); + for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) { + var field = fields_1[_i]; + var input = document.createElement("input"); + input.setAttribute("type", "hidden"); + input.setAttribute("name", field.name); + input.setAttribute("value", field.value); + form.appendChild(input); + } + return form; + } + function ensureIframeClosed(timeout) { + if (timeout) { + clearTimeout(timeout); + } + try { + Array.prototype.slice.call(document + .querySelectorAll("[target$=\"-" + randomId + "\"],[id$=\"-" + randomId + "\"]")) + .forEach(function (element) { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + }); + } + catch (e) { + /** */ + } + } + function getWindowMessageEventHandler(resolve, data) { + return function (e) { + var origin = data.origin || window.location.origin; + if (origin !== e.origin) { + return; + } + ensureIframeClosed(data.timeout || 0); + resolve(e.data); + }; + } + function handleEmbeddedIframe(reject, data, options) { + var targetElement; + if (options.hide) { + targetElement = document.body; + } + else if (typeof options.target === "string") { + targetElement = document.querySelector(options.target); + } + else { + targetElement = options.target; + } + if (!targetElement) { + ensureIframeClosed(data.timeout || 0); + reject(new Error("Embed target not found")); + return false; + } + var _a = dimensionsFromChallengeWindowSize(options), height = _a.height, width = _a.width; + if (data.iframe) { + data.iframe.setAttribute("height", height ? height + "px" : "100%"); + data.iframe.setAttribute("width", width ? width + "px" : "100%"); + targetElement.appendChild(data.iframe); + } + return true; + } + + /** + * Retrieves client browser runtime data. + */ + function getBrowserData() { + var now = new Date(); + return { + colorDepth: screen && colorDepth(screen.colorDepth), + javaEnabled: navigator && navigator.javaEnabled(), + javascriptEnabled: true, + language: navigator && navigator.language, + screenHeight: screen && screen.height, + screenWidth: screen && screen.width, + time: now, + timezoneOffset: now.getTimezoneOffset() / 60, + userAgent: navigator && navigator.userAgent, + }; + } + /** + * Facilitates backend request to merchant integration to check the enrolled 3DS version for the consumer's card. + * + * @param endpoint Merchant integration endpoint responsible for performing the version check + * @param data Request data to aid in version check request + * @throws When an error occurred during the request + */ + function checkVersion(endpoint, data) { + return __awaiter(this, void 0, void 0, function () { + var response, e_1, reasons; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + data = data || {}; + _a.label = 1; + case 1: + _a.trys.push([1, 4, , 5]); + return [4 /*yield*/, makeRequest(endpoint, data)]; + case 2: + response = (_a.sent()); + return [4 /*yield*/, handle3dsVersionCheck(response, data.methodWindow)]; + case 3: return [2 /*return*/, _a.sent()]; + case 4: + e_1 = _a.sent(); + reasons = [{ code: e_1.name, message: e_1.message }]; + if (e_1.reasons) { + reasons = reasons.concat(e_1.reasons); + } + throw new GPError(reasons); + case 5: return [2 /*return*/]; + } + }); + }); + } + /** + * Facilitates backend request to merchant integration to initiate 3DS 2.0 authentication workflows with the consumer. + * + * @param endpoint Merchant integration endpoint responsible for initiating the authentication request + * @param data Request data to aid in initiating authentication + * @throws When an error occurred during the request + */ + function initiateAuthentication(endpoint, data) { + return __awaiter(this, void 0, void 0, function () { + var response, e_2, reasons; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 3, , 4]); + data.authenticationSource = + data.authenticationSource || exports.AuthenticationSource.Browser; + data.authenticationRequestType = + data.authenticationRequestType || + exports.AuthenticationRequestType.PaymentTransaction; + data.messageCategory = + data.messageCategory || + messageCategoryFromAuthenticationRequestType(data.authenticationRequestType); + data.challengeRequestIndicator = + data.challengeRequestIndicator || exports.ChallengeRequestIndicator.NoPreference; + // still needs ip address and accept header from server-side + data.browserData = data.browserData || getBrowserData(); + return [4 /*yield*/, makeRequest(endpoint, data)]; + case 1: + response = (_a.sent()); + return [4 /*yield*/, handleInitiateAuthentication(response, data.challengeWindow)]; + case 2: return [2 /*return*/, _a.sent()]; + case 3: + e_2 = _a.sent(); + reasons = [{ code: e_2.name, message: e_2.message }]; + if (e_2.reasons) { + reasons = reasons.concat(e_2.reasons); + } + throw new GPError(reasons); + case 4: return [2 /*return*/]; + } + }); + }); + } + /** + * Handles response from merchant integration endpoint for the version check request. When a card is enrolled and a + * method URL is present, a hidden iframe to the method URL will be created to handle device fingerprinting + * requirements. + * + * @param data Version check data from merchant integration endpoint + * @param options Configuration options for the method window + * @throws When a card is not enrolled + */ + function handle3dsVersionCheck(data, options) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!data.enrolled) { + throw new Error("Card not enrolled"); + } + options = options || {}; + options.hide = typeof options.hide === "undefined" ? true : options.hide; + options.timeout = + typeof options.timeout === "undefined" ? 30 * 1000 : options.timeout; + if (!data.methodUrl) return [3 /*break*/, 2]; + return [4 /*yield*/, postToIframe(data.methodUrl, [{ name: "threeDSMethodData", value: data.methodData }], options)]; + case 1: + _a.sent(); + _a.label = 2; + case 2: return [2 /*return*/, data]; + } + }); + }); + } + /** + * Handles response from merchant integration endpoint for initiating 3DS 2.0 authentication flows with consumer. If a + * challenge is mandated, an iframe will be created for the issuer's necessary challenge URL. + * + * @param data Initiate authentication data from merchant integration endpoint + * @param options Configuration options for the challenge window + * @throws When a challenge is mandated but no challenge URL was supplied + * @throws When an error occurred during the challenge request + */ + function handleInitiateAuthentication(data, options) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!data.challengeMandated) return [3 /*break*/, 2]; + data.challenge = data.challenge || {}; + if (!data.challenge.requestUrl) { + throw new Error("Invalid challenge state. Missing challenge URL"); + } + return [4 /*yield*/, postToIframe(data.challenge.requestUrl, [ + { name: "creq", value: data.challenge.encodedChallengeRequest }, + ], options)]; + case 1: + response = _a.sent(); + data.challenge.response = response; + _a.label = 2; + case 2: return [2 /*return*/, data]; + } + }); + }); + } + /** + * Assists with notifying the parent window of challenge status + * + * @param data Raw data from the challenge notification + * @param origin Target origin for the message. Default is `window.location.origin`. + */ + function handleChallengeNotification(data, origin) { + handleNotificationMessageEvent("challengeNotification", data, origin); + } + /** + * Assists with notifying the parent window of method status + * + * @param data Raw data from the method notification + * @param origin Target origin for the message. Default is `window.location.origin`. + */ + function handleMethodNotification(data, origin) { + handleNotificationMessageEvent("methodNotification", data, origin); + } + + exports.checkVersion = checkVersion; + exports.colorDepth = colorDepth; + exports.dimensionsFromChallengeWindowSize = dimensionsFromChallengeWindowSize; + exports.getBrowserData = getBrowserData; + exports.handle3dsVersionCheck = handle3dsVersionCheck; + exports.handleChallengeNotification = handleChallengeNotification; + exports.handleInitiateAuthentication = handleInitiateAuthentication; + exports.handleMethodNotification = handleMethodNotification; + exports.initiateAuthentication = initiateAuthentication; + exports.messageCategoryFromAuthenticationRequestType = messageCategoryFromAuthenticationRequestType; + + Object.defineProperty(exports, '__esModule', { value: true }); + + return exports; + +}({})); +//# sourceMappingURL=globalpayments-3ds.js.map diff --git a/assets/frontend/js/globalpayments-3ds.min.js b/assets/frontend/js/globalpayments-3ds.min.js new file mode 100644 index 0000000..e30f385 --- /dev/null +++ b/assets/frontend/js/globalpayments-3ds.min.js @@ -0,0 +1,16 @@ +this.GlobalPayments=this.GlobalPayments||{},this.GlobalPayments.ThreeDSecure=function(e){"use strict"; +/*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */var t,n,i,a,r,o,s,c,l=function(e,t){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function u(e,t,n,i){return new(n||(n=Promise))((function(a,r){function o(e){try{c(i.next(e))}catch(e){r(e)}}function s(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?a(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,s)}c((i=i.apply(e,t||[])).next())}))}function d(e,t){var n,i,a,r,o={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return r={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(r[Symbol.iterator]=function(){return this}),r;function s(r){return function(s){return function(r){if(n)throw new TypeError("Generator is already executing.");for(;o;)try{if(n=1,i&&(a=2&r[0]?i.return:r[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,r[1])).done)return a;switch(i=0,a&&(r=[2&r[0],a.value]),r[0]){case 0:case 1:a=r;break;case 4:return o.label++,{value:r[1],done:!1};case 5:o.label++,i=r[1],r=[0];continue;case 7:r=o.ops.pop(),o.trys.pop();continue;default:if(!(a=o.trys,(a=a.length>0&&a[a.length-1])||6!==r[0]&&2!==r[0])){o=0;continue}if(3===r[0]&&(!a||r[1]>a[0]&&r[1]0?window.innerWidth:screen.width)<=360||(window.innerHeight>0?window.innerHeight:screen.height)<=360,E=M||!M&&(m||T),C=Math.random().toString(16).substr(2,8);function w(t,n){var i=function(){var e=document.createElement("div");e.setAttribute("id","GlobalPayments-overlay-"+C),e.style.position="fixed",e.style.width="100%",e.style.height="100%",e.style.top="0",e.style.left="0",e.style.transition="all 0.3s ease-in-out",e.style.zIndex="100",E&&(e.style.position="absolute !important",e.style.WebkitOverflowScrolling="touch",e.style.overflowX="hidden",e.style.overflowY="scroll");return document.body.appendChild(e),setTimeout((function(){e.style.background="rgba(0, 0, 0, 0.7)"}),1),e}(),a=function(){var e=document.createElement("img");return e.setAttribute("src","data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iIzAwNzBiYSIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iI2MwZGNlZSIgZmlsbC1vcGFjaXR5PSIwLjI1IiB0cmFuc2Zvcm09InJvdGF0ZSgyMjUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMjcwIDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDMxNSA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzQ1IDY0IDY0OzkwIDY0IDY0OzEzNSA2NCA2NDsxODAgNjQgNjQ7MjI1IDY0IDY0OzI3MCA2NCA2NDszMTUgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMjgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="),e.setAttribute("id","GlobalPayments-loader-"+C),e.style.left="50%",e.style.position="fixed",e.style.background="#FFFFFF",e.style.borderRadius="50%",e.style.width="30px",e.style.zIndex="200",e.style.marginLeft="-15px",e.style.top="120px",e}();document.body.appendChild(a);var r=h(n),o=r.height,s=r.width;if(o&&t.setAttribute("height",o+"px"),t.setAttribute("frameBorder","0"),s&&t.setAttribute("width",s+"px"),t.setAttribute("seamless","seamless"),t.style.zIndex="10001",t.style.position="absolute",t.style.transition="transform 0.5s ease-in-out",t.style.transform="scale(0.7)",t.style.opacity="0",i.appendChild(t),E||n.windowSize===e.ChallengeWindowSize.FullScreen){t.style.top="0px",t.style.bottom="0px",t.style.left="0px",t.style.marginLeft="0px;",t.style.width="100%",t.style.height="100%",t.style.minHeight="100%",t.style.WebkitTransform="translate3d(0,0,0)",t.style.transform="translate3d(0, 0, 0)";var c=document.createElement("meta");c.name="viewport",c.content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0",document.getElementsByTagName("head")[0].appendChild(c)}else t.style.top="40px",t.style.left="50%",t.style.marginLeft="-"+s/2+"px";t.onload=function(t,n,i,a){return function(){var r;t.style.opacity="1",t.style.transform="scale(1)",t.style.backgroundColor="#ffffff",n.parentNode&&n.parentNode.removeChild(n),(r=function(t){if(null!==document.getElementById("GlobalPayments-frame-close-"+C))return;var n=document.createElement("img");n.id="GlobalPayments-frame-close-"+C,n.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUJFRjU1MEIzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUJFRjU1MEMzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQkVGNTUwOTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQkVGNTUwQTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHco5QAAAHpSURBVHjafFRdTsJAEF42JaTKn4glGIg++qgX4AAchHAJkiZcwnAQD8AF4NFHCaC2VgWkIQQsfl/jNJUik8Duzs/XmW9mN7Xb7VRc5vP5zWKxaK5Wq8Zmu72FqobfJG0YQ9M0+/l8/qFQKDzGY1JxENd1288vLy1s786KRZXJZCLber1Wn7MZt4PLarVnWdZ9AmQ8Hncc17UvymVdBMB/MgPQm+cFFcuy6/V6lzqDf57ntWGwYdBIVx0TfkBD6I9M35iRJgfIoAVjBLDZbA4CiJ5+9AdQi/EahibqDTkQx6fRSIHcPwA8Uy9A9Gcc47Xv+w2wzhRDYzqdVihLIbsIiCvP1NNOoX/29FQx3vgOgtt4FyRdCgPRarX4+goB9vkyAMh443cOEsIAAcjncuoI4TXWMAmCIGFhCQLAdZ8jym/cRJ+Y5nC5XCYAhINKpZLgSISZgoqh5iiLQrojAFICVwGS7tCfe5DbZzkP56XS4NVxwvTI/vXVVYIDnqmnnX70ZxzjNS8THHooK5hMpxHQIREA+tEfA9djfHR3MHkdx3Hspe9r3B+VzWaj2RESyR2mlCUE4MoGQDdxiwHURq2t94+PO9bMIYyTyDNLwMoM7g8+BfKeYGniyw2MdfSehF3Qmk1IvCc/AgwAaS86Etp38bUAAAAASUVORK5CYII=",n.style.transition="all 0.5s ease-in-out",n.style.opacity="0",n.style.float="left",n.style.position="absolute",n.style.left="50%",n.style.zIndex="99999999",n.style.top="30px";var i=h(t).width;i&&(n.style.marginLeft=i/2-8+"px");setTimeout((function(){n.style.opacity="1"}),500),(E||t.windowSize===e.ChallengeWindowSize.FullScreen)&&(n.style.float="right",n.style.top="20px",n.style.left="initial",n.style.marginLeft="0px",n.style.right="20px");return n}(a))&&(i.appendChild(r),r.addEventListener("click",(function(){r&&Array.prototype.slice.call(document.querySelectorAll('[target$="-'+C+'"],[id$="-'+C+'"]')).forEach((function(e){e.parentNode&&e.parentNode.removeChild(e)}))}),!0))}}(t,a,i,n)}function b(e,t,n){return new Promise((function(i,a){var r;n.timeout&&(r=setTimeout((function(){p(r),a(new Error("timeout reached"))}),n.timeout));var o=document.createElement("iframe");o.id=o.name="GlobalPayments-3DSecure-"+C,o.style.display=n.hide?"none":"inherit";var s=function(e,t,n){var i=document.createElement("form");i.setAttribute("method","POST"),i.setAttribute("action",e),i.setAttribute("target",t);for(var a=0,r=n;a { + e.preventDefault(); + + try { + versionCheckData = await checkVersion(this.threedsecure.checkEnrollmentUrl, { + tokenResponse: this.tokenResponse, + amount: this.order.amount, + currency: this.order.currency, + }); + + // Card holder not enrolled in 3D Secure, continue the Magento flow. + if (versionCheckData.enrolled === "NOT_ENROLLED") { + this.placeOrder(); + return; + } + + //Something went wrong with CheckEnrolment request on server side + if (versionCheckData.error) { + this.showPaymentError( versionCheckData.reasons ); + return; + } + } catch (e) { + this.showPaymentError(e.reasons); + return; + } + + try { + authenticationData = await initiateAuthentication(this.threedsecure.initiateAuthenticationUrl, { + tokenResponse: this.tokenResponse, + versionCheckData: versionCheckData, + challengeWindow: { + windowSize: ChallengeWindowSize.Windowed500x600, + displayMode: 'lightbox', + }, + order: this.order, + }); + + switch (authenticationData.result) { + case "SUCCESS_AUTHENTICATED": + case "AUTHENTICATION_SUCCESSFUL": + // frictionless authentication success + this.createInputElement( 'serverTransId', authenticationData.serverTransactionId ); + this.placeOrder(); + return; + break; + case "CHALLENGE_REQUIRED": + // challenge authentication success + if (authenticationData.challenge.response.data.transStatus == "Y") { + this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId); + this.placeOrder(); + return; + } + // challenge authentication failure + this.showPaymentError('3DS Authentication failed. Please try again!'); + return; + case "NOT_AUTHENTICATED": + case "FAILED": + default: + this.showPaymentError('3DS Authentication failed. Please try again!'); + return; + } + } catch (e) { + console.error(e); + this.showPaymentError( e.reasons ); + return; + } + + return false; + }; + + checkVersionButton.addEventListener('click', start3DS); + checkVersionButton.click(); + }, + + createInputElement: function ( name, value ) { + var inputElement = (document.getElementById( this.id + '-' + name )); + + if ( ! inputElement) { + inputElement = document.createElement( 'input' ); + inputElement.id = this.id + '-' + name; + inputElement.name = this.id + '[' + name + ']'; + inputElement.type = 'hidden'; + this.getForm().appendChild( inputElement ); + } + + inputElement.value = value; }, /** @@ -307,6 +464,28 @@ $( '.' + this.id + '.' + fieldType + ' .validation-error' ).show(); }, + /** + * Shows payment error and scrolls to it + * + * @param {string} message Error message + * + * @returns + */ + showPaymentError: function ( message ) { + var $form = $( this.getForm() ); + + this.unblockOnError(); + + // Remove notices from all sources + $( '.woocommerce-error, .woocommerce-message' ).remove(); + + $form.prepend( '
' + message + '
' ); + + $( 'html, body' ).animate( { + scrollTop: ( $form.offset().top - 100 ) + }, 1000 ); + }, + /** * Handles errors from the payment field iframes * @@ -610,7 +789,7 @@ } }; - new GlobalPaymentsWooCommerce( globalpayments_secure_payment_fields_params ); + new GlobalPaymentsWooCommerce( globalpayments_secure_payment_fields_params, globalpayments_secure_payment_threedsecure_params ); }( /** * Global `jQuery` reference @@ -630,10 +809,22 @@ * @type {any} */ (window).GlobalPayments, + /** + * Global `GlobalPayments` reference + * + * @type {any} + */ + (window).GlobalPayments.ThreeDSecure, + /** + * Global `globalpayments_secure_payment_fields_params` reference + * + * @type {any} + */ + (window).globalpayments_secure_payment_fields_params, /** * Global `globalpayments_secure_payment_fields_params` reference * * @type {any} */ - (window).globalpayments_secure_payment_fields_params + (window).globalpayments_secure_payment_threedsecure_params || {} )); diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index cf38f81..5cff269 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -5,6 +5,7 @@ defined( 'ABSPATH' ) || exit; use Exception; +use GlobalPayments\Api\Entities\Enums\GatewayProvider; use GlobalPayments\Api\Entities\Exceptions\ApiException; use GlobalPayments\Api\Entities\Reporting\TransactionSummary; use WC_Payment_Gateway_CC; @@ -48,6 +49,10 @@ abstract class AbstractGateway extends WC_Payment_Gateway_Cc { //gp-api requests const TXN_TYPE_GET_ACCESS_TOKEN = 'getAccessToken'; + //3DS requests + const TXN_TYPE_CHECK_ENROLLMENT = 'checkEnrollment'; + const TXN_TYPE_INITIATE_AUTHENTICATION = 'initiateAuthentication'; + /** * Gateway provider. Should be overriden by individual gateway implementations * @@ -278,6 +283,39 @@ public function tokenization_script() { 'field_options' => $this->secure_payment_fields(), ) ); + + // Global Payments scripts for handling 3DS + if ( GatewayProvider::GP_API !== $this->gateway_provider ) { + return; + } + + wp_enqueue_script( + 'globalpayments-threedsecure-lib', + Plugin::get_url( '/assets/frontend/js/globalpayments-3ds' ) + . ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ) . '.js', + array( 'globalpayments-secure-payment-fields-lib' ), + WC()->version, + true + ); + wp_localize_script( + 'globalpayments-secure-payment-fields', + 'globalpayments_secure_payment_threedsecure_params', + array( + 'threedsecure' => array( + 'methodNotificationUrl' => $this->get_api_url('threedsecure_methodnotification'), + 'challengeNotificationUrl' => $this->get_api_url('threedsecure_challengenotification'), + 'checkEnrollmentUrl' => $this->get_api_url('threedsecure_checkenrollment'), + 'initiateAuthenticationUrl' => $this->get_api_url('threedsecure_initiateauthentication'), + ), + 'order' => array ( + 'amount' => $this->get_session_amount(), + 'currency' => get_woocommerce_currency(), + 'billingAddress' => $this->get_billing_address(), + 'shippingAddress' => $this->get_shipping_address(), + 'customerEmail' => $this->get_customer_email(), + ), + ) + ); } /** @@ -528,7 +566,7 @@ public static function capture_credit_card_authorization( $order_id ) { case "globalpayments_genius": $gateway = new GeniusGateway(); break; - case "globalpayments_gpapi": + case GpApiGateway::GATEWAY_ID: $gateway = new GpApiGateway(); break; }; @@ -592,6 +630,8 @@ protected function prepare_request( $txn_type, WC_Order $order = null ) { self::TXN_TYPE_REPORT_TXN_DETAILS => Requests\TransactionDetailRequest::class, self::TXN_TYPE_CAPTURE => Requests\CaptureAuthorizationRequest::class, self::TXN_TYPE_GET_ACCESS_TOKEN => Requests\GetAccessTokenRequest::class, + self::TXN_TYPE_CHECK_ENROLLMENT => Requests\ThreeDSecure\CheckEnrollmentRequest::class, + self::TXN_TYPE_INITIATE_AUTHENTICATION => Requests\ThreeDSecure\InitiateAuthenticationRequest::class, ); if ( ! isset( $map[ $txn_type ] ) ) { @@ -696,4 +736,5 @@ public static function addCaptureOrderAction( $actions ) $actions['capture_credit_card_authorization'] = 'Capture credit card authorization'; return $actions; } + } diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index 32b88f5..c65c6a9 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -5,6 +5,7 @@ use GlobalPayments\Api\Builders\TransactionBuilder; use GlobalPayments\Api\Entities\Address; use GlobalPayments\Api\Entities\Enums\GpApi\Channels; +use GlobalPayments\Api\Entities\GpApi\AccessTokenInfo; use GlobalPayments\Api\Entities\Transaction; use GlobalPayments\Api\Entities\Enums\AddressType; use GlobalPayments\Api\Entities\Enums\CardType; @@ -19,6 +20,7 @@ use GlobalPayments\Api\ServiceConfigs\Gateways\PorticoConfig; use GlobalPayments\Api\ServiceConfigs\Gateways\TransitConfig; use GlobalPayments\Api\Services\ReportingService; +use GlobalPayments\Api\Services\Secure3dService; use GlobalPayments\Api\ServicesContainer; use GlobalPayments\WooCommercePaymentGatewayProvider\Data\PaymentTokenData; use GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\AbstractGateway; @@ -62,6 +64,10 @@ class SdkClient implements ClientInterface { AbstractGateway::TXN_TYPE_VOID, ); + protected $access_token_permissions = array( + 'PMT_POST_Create_Single', + ); + /** * Card data * @@ -77,10 +83,7 @@ class SdkClient implements ClientInterface { protected $previous_transaction = null; public function set_request( RequestInterface $request ) { - $this->args = array_merge( - $request->get_default_args(), - $request->get_args() - ); + $this->prepare_request_args( $request ); $this->prepare_request_objects(); return $this; @@ -98,6 +101,9 @@ public function execute() { } $this->prepare_builder( $builder ); + if ( $this->threedsecure_is_enabled() ) { + $this->set_threedsecure_data(); + } $response = $builder->execute(); if ( ! is_null( $this->card_data ) && $response instanceof Transaction && $response->token ) { $this->card_data->token = $response->token; @@ -107,6 +113,13 @@ public function execute() { return $response; } + public function submit_request( RequestInterface $request ) { + $this->prepare_request_args( $request ); + $this->configure_sdk(); + + $request->do_request(); + } + protected function prepare_builder( TransactionBuilder $builder ) { foreach ( $this->builder_args as $name => $args ) { $method = 'with' . ucfirst( $name ); @@ -149,6 +162,13 @@ protected function get_transaction_builder() { return $subject->{$this->get_arg( RequestArg::TXN_TYPE )}(); } + protected function prepare_request_args( RequestInterface $request ) { + $this->args = array_merge( + $request->get_default_args(), + $request->get_args() + ); + } + protected function prepare_request_objects() { if ( $this->has_arg( RequestArg::AMOUNT ) ) { $this->builder_args['amount'] = array( $this->get_arg( RequestArg::AMOUNT ) ); @@ -255,6 +275,21 @@ protected function prepare_card_data( WC_Payment_Token_CC $token = null ) { } } + protected function threedsecure_is_enabled() { + return $this->has_arg( RequestArg::SERVER_TRANS_ID ); + } + + protected function set_threedsecure_data() { + $threeDSecureData = Secure3dService::getAuthenticationData() + ->withServerTransactionId( $this->get_arg( RequestArg::SERVER_TRANS_ID ) ) + ->execute(); + + if ( ! in_array( $threeDSecureData->eci, ["01", "02", "05", "06"] ) ) { + throw new ApiException( __( '3DS authentication failed' ) ); + } + $this->card_data->threeDSecure = $threeDSecureData; + } + protected function prepare_address( $address_type, array $data ) { $address = new Address(); $address->type = $address_type; @@ -288,9 +323,18 @@ protected function configure_sdk() { $servicesConfig = $this->args[ RequestArg::SERVICES_CONFIG ]; $gatewayConfig->setAppId( $servicesConfig['AppId'] ); $gatewayConfig->setAppKey( $servicesConfig['AppKey'] ); + $gatewayConfig->setMethodNotificationUrl($servicesConfig['methodNotificationUrl']); + $gatewayConfig->setChallengeNotificationUrl($servicesConfig['challengeNotificationUrl']); $gatewayConfig->setChannel( Channels::CardNotPresent ); + if ( in_array( $this->get_arg( RequestArg::TXN_TYPE ), $this->client_transactions, true ) ) { + $accessTokenInfo = new AccessTokenInfo(); + $accessTokenInfo->setPermissions($this->access_token_permissions); + $gatewayConfig->setAccessTokenInfo($accessTokenInfo); + } unset( $this->args[ RequestArg::SERVICES_CONFIG ]['gatewayProvider'] ); + unset( $this->args[ RequestArg::SERVICES_CONFIG ]['methodNotificationUrl'] ); + unset( $this->args[ RequestArg::SERVICES_CONFIG ]['challengeNotificationUrl'] ); break; } diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index 238bfd8..acaa2ea 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -4,10 +4,15 @@ use GlobalPayments\Api\Entities\Enums\Environment; use GlobalPayments\Api\Entities\Enums\GatewayProvider; +use GlobalPayments\WooCommercePaymentGatewayProvider\Plugin; defined( 'ABSPATH' ) || exit; class GpApiGateway extends AbstractGateway { + /** + * Gateway ID + */ + const GATEWAY_ID = 'globalpayments_gpapi'; /** * SDK gateway provider * @@ -45,13 +50,13 @@ class GpApiGateway extends AbstractGateway { public $developer_id = ''; public function configure_method_settings() { - $this->id = 'globalpayments_gpapi'; + $this->id = self::GATEWAY_ID; $this->method_title = __( 'GP-API', 'globalpayments-gateway-provider-for-woocommerce' ); $this->method_description = __( 'Connect to the Global Payments API (GP-API) gateway', 'globalpayments-gateway-provider-for-woocommerce' ); } public function get_first_line_support_email() { - return 'securesubmitcert@e-hps.com'; + return 'api.integrations@globalpay.com'; } public function get_gateway_form_fields() { @@ -91,10 +96,12 @@ public function get_frontend_gateway_options() { public function get_backend_gateway_options() { return array( - 'AppId' => $this->app_id, - 'AppKey' => $this->app_key, - 'developerId' => '', - 'environment' => $this->is_production ? Environment::PRODUCTION : Environment::TEST, + 'AppId' => $this->app_id, + 'AppKey' => $this->app_key, + 'developerId' => '', + 'environment' => $this->is_production ? Environment::PRODUCTION : Environment::TEST, + 'methodNotificationUrl' => $this->get_api_url('threedsecure_methodnotification'), + 'challengeNotificationUrl' => $this->get_api_url('threedsecure_challengenotification'), ); } @@ -105,6 +112,19 @@ protected function get_access_token() { return $response->token; } + protected function add_hooks() { + parent::add_hooks(); + + /** + * The WooCommerce API allows plugins make a callback to a special URL that will then load the specified class (if it exists) + * and run an action. This is also useful for gateways that are not initialized. + */ + add_action( 'woocommerce_api_threedsecure_checkenrollment', array( $this, 'process_threeDSecure_checkEnrollment' ) ); + add_action( 'woocommerce_api_threedsecure_methodnotification', array( $this, 'process_threeDSecure_methodNotification' ) ); + add_action( 'woocommerce_api_threedsecure_initiateauthentication', array( $this, 'process_threeDSecure_initiateAuthentication' ) ); + add_action( 'woocommerce_api_threedsecure_challengenotification', array( $this, 'process_threeDSecure_challengeNotification' ) ); + } + public function mapResponseCodeToFriendlyMessage( $responseCode ) { if ( 'DECLINED' === $responseCode ) { return __( 'Your card has been declined by the bank.', 'globalpayments-gateway-provider-for-woocommerce' ); @@ -112,4 +132,127 @@ public function mapResponseCodeToFriendlyMessage( $responseCode ) { return __( 'An error occurred while processing the card.', 'globalpayments-gateway-provider-for-woocommerce' ); } + + public function process_threeDSecure_checkEnrollment() + { + $request = $this->prepare_request( parent::TXN_TYPE_CHECK_ENROLLMENT ); + $this->client->submit_request( $request ); + } + + public function process_threeDSecure_initiateAuthentication() + { + $request = $this->prepare_request( parent::TXN_TYPE_INITIATE_AUTHENTICATION ); + $this->client->submit_request( $request ); + } + + public function process_threeDSecure_methodNotification() + { + if ( ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) ) { + return; + } + if ( 'application/x-www-form-urlencoded' !== $_SERVER['CONTENT_TYPE'] ) { + return; + } + + $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); + + $convertedThreeDSMethodData = json_decode( base64_decode( $_POST['threeDSMethodData'] ) ); + $response = json_encode([ + 'threeDSServerTransID' => $convertedThreeDSMethodData->threeDSServerTransID, + ]); + $script = << + + + + + + +EOT; + header("Content-Type: text/html"); + echo $script; + exit(); + } + + public function process_threeDSecure_challengeNotification() + { + if ( ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) ) { + return; + } + if ( 'application/x-www-form-urlencoded' !== $_SERVER['CONTENT_TYPE'] ) { + return; + } + + try { + $response = new \stdClass(); + + $convertedCRes = json_decode(base64_decode($_POST['cres'])); + + $response = json_encode([ + 'threeDSServerTransID' => $convertedCRes->threeDSServerTransID, + 'transStatus' => $convertedCRes->transStatus ?? '', + ]); + + $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); + $script = << + + + + + + +EOT; + header("Content-Type: text/html"); + echo $script; + exit(); + + } catch (Exception $e) { + $response = array('error' => TRUE, 'message' => $e->getMessage()); + } + } + + protected function get_session_amount() { + $cart_totals = WC()->session->get('cart_totals'); + return round($cart_totals['total'], 2); + } + + protected function get_customer_email() { + return WC()->customer->get_billing_email(); + } + + protected function get_billing_address() + { + return [ + 'streetAddress1' => WC()->customer->get_billing_address_1(), + 'streetAddress2' => WC()->customer->get_billing_address_2(), + 'city' => WC()->customer->get_billing_city(), + 'state' => WC()->customer->get_billing_state(), + 'postalCode' => WC()->customer->get_billing_postcode(), + 'country' => WC()->customer->get_billing_country(), + 'countryCode' => '', + ]; + } + + protected function get_shipping_address() + { + return [ + 'streetAddress1' => WC()->customer->get_shipping_address_1(), + 'streetAddress2' => WC()->customer->get_shipping_address_2(), + 'city' => WC()->customer->get_shipping_city(), + 'state' => WC()->customer->get_shipping_state(), + 'postalCode' => WC()->customer->get_shipping_postcode(), + 'country' => WC()->customer->get_shipping_country(), + 'countryCode' => '', + ]; + } + + protected function get_api_url($path) { + $url = get_home_url( null, "wc-api/", is_ssl() ? 'https' : 'http' ); + if ( ! empty( $path ) && is_string( $path ) ) { + $url .= ltrim( $path, '/' ); + } + + return $url; + } } diff --git a/src/Gateways/Requests/AbstractRequest.php b/src/Gateways/Requests/AbstractRequest.php index 52ce27b..1007d15 100644 --- a/src/Gateways/Requests/AbstractRequest.php +++ b/src/Gateways/Requests/AbstractRequest.php @@ -67,6 +67,12 @@ public function get_default_args() { public function get_request_data( $key = null ) { if ( null === $key ) { + if ( ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) ) { + return null; + } + if ( 'application/json' === $_SERVER['CONTENT_TYPE'] ) { + return json_decode( file_get_contents( 'php://input' ) ); + } // WooCommerce should verify nonce during its checkout handling // phpcs:ignore WordPress.Security.NonceVerification return $_POST; diff --git a/src/Gateways/Requests/AuthorizationRequest.php b/src/Gateways/Requests/AuthorizationRequest.php index 0da07ac..60a2523 100644 --- a/src/Gateways/Requests/AuthorizationRequest.php +++ b/src/Gateways/Requests/AuthorizationRequest.php @@ -16,9 +16,10 @@ public function get_args() { $token = ( new PaymentTokenData( $this ) )->get_token(); return array( - RequestArg::AMOUNT => null !== $this->order ? $this->order->get_total() : null, - RequestArg::CURRENCY => null !== $this->order ? $this->order->get_currency() : null, - RequestArg::CARD_DATA => $token, + RequestArg::AMOUNT => null !== $this->order ? $this->order->get_total() : null, + RequestArg::CURRENCY => null !== $this->order ? $this->order->get_currency() : null, + RequestArg::CARD_DATA => $token, + RequestArg::SERVER_TRANS_ID => $this->data[ $this->gateway_id ]['serverTransId'] ?? null; ); } } diff --git a/src/Gateways/Requests/RequestArg.php b/src/Gateways/Requests/RequestArg.php index 592a15f..65c16cc 100644 --- a/src/Gateways/Requests/RequestArg.php +++ b/src/Gateways/Requests/RequestArg.php @@ -8,6 +8,7 @@ abstract class RequestArg { const CARD_DATA = 'CARD_DATA'; const CARD_HOLDER_NAME = 'CARD_HOLDER_NAME'; const CURRENCY = 'CURRENCY'; + const SERVER_TRANS_ID = 'SERVER_TRANS_ID'; const SERVICES_CONFIG = 'SERVICES_CONFIG'; const SHIPPING_ADDRESS = 'SHIPPING_ADDRESS'; const TXN_TYPE = 'TXN_TYPE'; diff --git a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php new file mode 100644 index 0000000..95d4251 --- /dev/null +++ b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php @@ -0,0 +1,42 @@ +data; + $tokenResponse = json_decode($requestData->tokenResponse); + + $paymentMethod = new CreditCardData(); + $paymentMethod->token = $tokenResponse->paymentReference; + + $threeDSecureData = Secure3dService::checkEnrollment($paymentMethod) + ->withAmount($requestData->amount) + ->withCurrency($requestData->currency) + ->execute(); + $response["enrolled"] = $threeDSecureData->enrolled ?? self::NOT_ENROLLED; + $response['version'] = $threeDSecureData->getVersion(); + $response["serverTransactionId"] = $threeDSecureData->serverTransactionId ?? ''; + $response["methodUrl"] = $threeDSecureData->issuerAcsUrl ?? ''; + $response['methodData'] = $threeDSecureData->payerAuthenticationRequest ?? ''; + + wp_send_json($response); + } +} \ No newline at end of file diff --git a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php new file mode 100644 index 0000000..d331737 --- /dev/null +++ b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php @@ -0,0 +1,102 @@ +data; + $tokenResponse = json_decode($requestData->tokenResponse); + $responseJson = []; + + $paymentMethod = new CreditCardData(); + $paymentMethod->token = $tokenResponse->paymentReference; + + $threeDSecureData = new ThreeDSecure(); + $threeDSecureData->serverTransactionId = $requestData->versionCheckData->serverTransactionId; + $methodUrlCompletion = ( $requestData->versionCheckData->methodData && $requestData->versionCheckData->methodUrl ) ? + MethodUrlCompletion::YES : MethodUrlCompletion::NO; + + $threeDSecureData = Secure3dService::initiateAuthentication( $paymentMethod, $threeDSecureData ) + ->withAmount( $requestData->order->amount ) + ->withCurrency( $requestData->order->currency ) + ->withOrderCreateDate( date( 'Y-m-d H:i:s' ) ) + ->withAddress( $this->mapAddress( $requestData->order->billingAddress ), AddressType::BILLING ) + ->withAddress( $this->mapAddress( $requestData->order->shippingAddress ), AddressType::SHIPPING ) + ->withCustomerEmail( $requestData->order->customerEmail ) + ->withAuthenticationSource( $requestData->authenticationSource ) + ->withAuthenticationRequestType( $requestData->authenticationRequestType ) + ->withMessageCategory( $requestData->messageCategory ) + ->withChallengeRequestIndicator( $requestData->challengeRequestIndicator ) + ->withBrowserData( $this->getBrowserData( $requestData ) ) + ->withMethodUrlCompletion( $methodUrlCompletion ) + ->execute(); + + // frictionless flow + if ($threeDSecureData->status !== "CHALLENGE_REQUIRED") { + $responseJson["result"] = $threeDSecureData->status; + $responseJson["authenticationValue"] = $threeDSecureData->authenticationValue; + $responseJson["serverTransactionId"] = $threeDSecureData->serverTransactionId; + $responseJson["messageVersion"] = $threeDSecureData->messageVersion; + $responseJson["eci"] = $threeDSecureData->eci; + + } else { //challenge flow + $responseJson["result"] = $threeDSecureData->status; + $responseJson["challengeMandated"] = $threeDSecureData->challengeMandated; + $responseJson["challenge"]["requestUrl"] = $threeDSecureData->issuerAcsUrl; + $responseJson["challenge"]["encodedChallengeRequest"] = $threeDSecureData->payerAuthenticationRequest; + } + + wp_send_json( $responseJson ); + } + + private function getBrowserData( $requestData ) { + $browserData = new BrowserData(); + $browserData->acceptHeader = isset( $_SERVER['HTTP_ACCEPT'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_ACCEPT'] ) ) : ''; + $browserData->colorDepth = $requestData->browserData->colorDepth; + $browserData->ipAddress = isset( $_SERVER['REMOTE_ADDR'] ) ? wc_clean( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; + $browserData->javaEnabled = $requestData->browserData->javaEnabled ?? false; + $browserData->javaEnabled = true; + $browserData->javaScriptEnabled = $requestData->browserData->javascriptEnabled; + $browserData->language = $requestData->browserData->language; + $browserData->screenHeight = $requestData->browserData->screenHeight; + $browserData->screenWidth = $requestData->browserData->screenWidth; + $browserData->challengWindowSize = $requestData->challengeWindow->windowSize; + $browserData->timeZone = 0; + $browserData->userAgent = $requestData->browserData->userAgent; + + return $browserData; + } + + private function mapAddress( $addressData ) { + $address = new Address(); + foreach ( $addressData as $key => $value ) { + if ( property_exists( $address, $key ) ) { + $address->{$key} = $value; + } + }; + + $address->countryCode = CountryUtils::getCountryCodeByCountry( $addressData->country ); + + return $address; + } +} \ No newline at end of file diff --git a/src/Gateways/Requests/VerifyRequest.php b/src/Gateways/Requests/VerifyRequest.php index 6fb2063..6f89714 100644 --- a/src/Gateways/Requests/VerifyRequest.php +++ b/src/Gateways/Requests/VerifyRequest.php @@ -4,6 +4,7 @@ use GlobalPayments\WooCommercePaymentGatewayProvider\Data\PaymentTokenData; use GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\AbstractGateway; +use GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\GpApiGateway; defined( 'ABSPATH' ) || exit; @@ -15,6 +16,14 @@ public function get_transaction_type() { public function get_args() { $token = ( new PaymentTokenData( $this ) )->get_token(); + if ( GpApiGateway::GATEWAY_ID === $this->gateway_id ) { + return array( + RequestArg::CARD_DATA => $token, + RequestArg::CURRENCY => null !== $this->order ? $this->order->get_currency() : get_woocommerce_currency(), + RequestArg::SERVER_TRANS_ID => $this->data[ $this->gateway_id ]['serverTransId'] ?? null, + ); + } + return array( RequestArg::CARD_DATA => $token, ); From c077afbb90d361f6ef24ac0ab15c67f1d2e85729 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 29 Mar 2021 21:45:42 +0300 Subject: [PATCH 02/38] TR-92: updates --- assets/frontend/js/globalpayments-secure-payment-fields.js | 2 +- .../Requests/ThreeDSecure/InitiateAuthenticationRequest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 5591f92..8014050 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -822,7 +822,7 @@ */ (window).globalpayments_secure_payment_fields_params, /** - * Global `globalpayments_secure_payment_fields_params` reference + * Global `globalpayments_secure_payment_threedsecure_params` reference * * @type {any} */ diff --git a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php index d331737..7121406 100644 --- a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php @@ -13,6 +13,8 @@ use GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\AbstractGateway; use GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\Requests\AbstractRequest; +defined('ABSPATH') || exit; + class InitiateAuthenticationRequest extends AbstractRequest { public function get_transaction_type() { From 96917a1a00c6f8af7d6cbd07e54219190ee60410 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Tue, 30 Mar 2021 14:58:16 +0300 Subject: [PATCH 03/38] TR-92: 3ds requests updates --- .../globalpayments-secure-payment-fields.js | 73 +++++++++------ .../ThreeDSecure/CheckEnrollmentRequest.php | 50 +++++++---- .../InitiateAuthenticationRequest.php | 90 +++++++++++-------- 3 files changed, 131 insertions(+), 82 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 8014050..945bd05 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -13,7 +13,7 @@ * * @param {object} options */ - function GlobalPaymentsWooCommerce( options, threeDSecureOptions) { + function GlobalPaymentsWooCommerce( options, threeDSecureOptions ) { /** * Card form instance @@ -28,12 +28,14 @@ * @type {string} */ this.id = options.id; + /** * Payment field options * * @type {object} */ this.fieldOptions = options.field_options; + /** * Payment gateway options * @@ -41,8 +43,20 @@ */ this.gatewayOptions = options.gateway_options; + /** + * 3DS endpoints + */ this.threedsecure = threeDSecureOptions.threedsecure; + + /** + * Order info + */ this.order = threeDSecureOptions.order; + + /** + * + * @type {null} + */ this.tokenResponse = null; this.attachEventHandlers(); @@ -71,12 +85,18 @@ // Order Pay + Add payment method if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) || $( 'form#add_payment_method' ).length > 0 ) { $( document ).ready( this.renderPaymentFields.bind( this ) ); + if ( 'globalpayments_gpapi' === this.id) { + $( document ).ready( this.threeDSSecure.bind( this ) ); + } return; } // Checkout if ( wc_checkout_params.is_checkout ) { $( document.body ).on( 'updated_checkout', this.renderPaymentFields.bind( this ) ); + if ( 'globalpayments_gpapi' === this.id) { + $( document.body ).on( 'updated_checkout', this.threeDSSecure.bind( this ) ); + } return; } }, @@ -235,18 +255,8 @@ response.details.cardSecurityCode = cvvVal; tokenResponseElement.value = JSON.stringify( response ); + that.placeOrder(); }); - - if ( 'globalpayments_gpapi' !== this.id) { - this.placeOrder(); - return; - } - - if ( wc_checkout_params.is_checkout ) { - this.validateFields(); - } - - this.threeDSSecure(); }, /** @@ -286,6 +296,9 @@ return pattern.test(zipcode); }, + /** + * 3DS Process + */ threeDSSecure: function () { const { checkVersion, @@ -293,10 +306,9 @@ ChallengeWindowSize, } = GlobalPayments.ThreeDSecure; - const checkVersionButton = document.querySelector( this.getSubmitButtonTargetSelector() ); - - if (!checkVersionButton) { - console.error('!checkVersionButton'); + const checkVersionButton = $( this.getPlaceOrderButtonSelector() ); + if ( ! checkVersionButton ) { + console.error( 'Warning! Place Order button cannot be loaded' ); return; } @@ -304,32 +316,39 @@ const start3DS = async (e) => { e.preventDefault(); + if ( wc_checkout_params.is_checkout && ! this.validateFields() ) { + return; + } + try { versionCheckData = await checkVersion(this.threedsecure.checkEnrollmentUrl, { tokenResponse: this.tokenResponse, + wcTokenId: $( 'input[name="wc-' + this.id + '-payment-token"]:checked', this.getForm() ).val(), amount: this.order.amount, currency: this.order.currency, }); - // Card holder not enrolled in 3D Secure, continue the Magento flow. + // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. if (versionCheckData.enrolled === "NOT_ENROLLED") { - this.placeOrder(); + $( this.getForm() ).submit(); return; } //Something went wrong with CheckEnrolment request on server side if (versionCheckData.error) { - this.showPaymentError( versionCheckData.reasons ); + this.showPaymentError( versionCheckData.message ); return; } - } catch (e) { - this.showPaymentError(e.reasons); + } catch ( e ) { + console.error( e.reasons ); + this.showPaymentError( e.reasons[0].message ); return; } try { authenticationData = await initiateAuthentication(this.threedsecure.initiateAuthenticationUrl, { tokenResponse: this.tokenResponse, + wcTokenId: $( 'input[name="wc-' + this.id + '-payment-token"]:checked', this.getForm() ).val(), versionCheckData: versionCheckData, challengeWindow: { windowSize: ChallengeWindowSize.Windowed500x600, @@ -343,15 +362,14 @@ case "AUTHENTICATION_SUCCESSFUL": // frictionless authentication success this.createInputElement( 'serverTransId', authenticationData.serverTransactionId ); - this.placeOrder(); + $( this.getForm() ).submit(); return; - break; case "CHALLENGE_REQUIRED": // challenge authentication success if (authenticationData.challenge.response.data.transStatus == "Y") { this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId); - this.placeOrder(); - return; + $( this.getForm() ).submit(); + return; } // challenge authentication failure this.showPaymentError('3DS Authentication failed. Please try again!'); @@ -363,7 +381,7 @@ return; } } catch (e) { - console.error(e); + console.error( e ); this.showPaymentError( e.reasons ); return; } @@ -371,8 +389,7 @@ return false; }; - checkVersionButton.addEventListener('click', start3DS); - checkVersionButton.click(); + checkVersionButton.on('click', start3DS ); }, createInputElement: function ( name, value ) { diff --git a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php index 95d4251..49dac5c 100644 --- a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php @@ -12,6 +12,8 @@ class CheckEnrollmentRequest extends AbstractRequest { const NOT_ENROLLED = 'NOT_ENROLLED'; + const NO_RESPONSE = 'NO_RESPONSE'; + public function get_transaction_type() { return AbstractGateway::TXN_TYPE_CHECK_ENROLLMENT; } @@ -21,22 +23,38 @@ public function get_args() { } public function do_request() { + $response = []; $requestData = $this->data; - $tokenResponse = json_decode($requestData->tokenResponse); - - $paymentMethod = new CreditCardData(); - $paymentMethod->token = $tokenResponse->paymentReference; - - $threeDSecureData = Secure3dService::checkEnrollment($paymentMethod) - ->withAmount($requestData->amount) - ->withCurrency($requestData->currency) - ->execute(); - $response["enrolled"] = $threeDSecureData->enrolled ?? self::NOT_ENROLLED; - $response['version'] = $threeDSecureData->getVersion(); - $response["serverTransactionId"] = $threeDSecureData->serverTransactionId ?? ''; - $response["methodUrl"] = $threeDSecureData->issuerAcsUrl ?? ''; - $response['methodData'] = $threeDSecureData->payerAuthenticationRequest ?? ''; - - wp_send_json($response); + try { + if ( isset( $requestData->tokenResponse ) ) { + $tokenResponse = json_decode( $requestData->tokenResponse ); + $token = $tokenResponse->paymentReference; + } else { + $tokenResponse = \WC_Payment_Tokens::get( $requestData->wcTokenId ); + $token = $tokenResponse->get_token(); + } + + $paymentMethod = new CreditCardData(); + $paymentMethod->token = $token; + + $threeDSecureData = Secure3dService::checkEnrollment($paymentMethod) + ->withAmount($requestData->amount) + ->withCurrency($requestData->currency) + ->execute(); + $response["enrolled"] = $threeDSecureData->enrolled ?? self::NOT_ENROLLED; + $response['version'] = $threeDSecureData->getVersion(); + $response["serverTransactionId"] = $threeDSecureData->serverTransactionId ?? ''; + $response["methodUrl"] = $threeDSecureData->issuerAcsUrl ?? ''; + $response['methodData'] = $threeDSecureData->payerAuthenticationRequest ?? ''; + } catch (\Exception $e) { + $response = [ + 'error' => true, + 'message' => $e->getMessage(), + 'enrolled' => self::NO_RESPONSE, + ]; + } + + + wp_send_json( $response ); } } \ No newline at end of file diff --git a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php index 7121406..844b8a1 100644 --- a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php @@ -26,46 +26,60 @@ public function get_args() { } public function do_request() { - $requestData = $this->data; - $tokenResponse = json_decode($requestData->tokenResponse); $responseJson = []; + $requestData = $this->data; + + try { + if ( isset( $requestData->tokenResponse ) ) { + $tokenResponse = json_decode( $requestData->tokenResponse ); + $token = $tokenResponse->paymentReference; + } else { + $tokenResponse = \WC_Payment_Tokens::get( $requestData->wcTokenId ); + $token = $tokenResponse->get_token(); + } - $paymentMethod = new CreditCardData(); - $paymentMethod->token = $tokenResponse->paymentReference; - - $threeDSecureData = new ThreeDSecure(); - $threeDSecureData->serverTransactionId = $requestData->versionCheckData->serverTransactionId; - $methodUrlCompletion = ( $requestData->versionCheckData->methodData && $requestData->versionCheckData->methodUrl ) ? - MethodUrlCompletion::YES : MethodUrlCompletion::NO; - - $threeDSecureData = Secure3dService::initiateAuthentication( $paymentMethod, $threeDSecureData ) - ->withAmount( $requestData->order->amount ) - ->withCurrency( $requestData->order->currency ) - ->withOrderCreateDate( date( 'Y-m-d H:i:s' ) ) - ->withAddress( $this->mapAddress( $requestData->order->billingAddress ), AddressType::BILLING ) - ->withAddress( $this->mapAddress( $requestData->order->shippingAddress ), AddressType::SHIPPING ) - ->withCustomerEmail( $requestData->order->customerEmail ) - ->withAuthenticationSource( $requestData->authenticationSource ) - ->withAuthenticationRequestType( $requestData->authenticationRequestType ) - ->withMessageCategory( $requestData->messageCategory ) - ->withChallengeRequestIndicator( $requestData->challengeRequestIndicator ) - ->withBrowserData( $this->getBrowserData( $requestData ) ) - ->withMethodUrlCompletion( $methodUrlCompletion ) - ->execute(); - - // frictionless flow - if ($threeDSecureData->status !== "CHALLENGE_REQUIRED") { - $responseJson["result"] = $threeDSecureData->status; - $responseJson["authenticationValue"] = $threeDSecureData->authenticationValue; - $responseJson["serverTransactionId"] = $threeDSecureData->serverTransactionId; - $responseJson["messageVersion"] = $threeDSecureData->messageVersion; - $responseJson["eci"] = $threeDSecureData->eci; - - } else { //challenge flow - $responseJson["result"] = $threeDSecureData->status; - $responseJson["challengeMandated"] = $threeDSecureData->challengeMandated; - $responseJson["challenge"]["requestUrl"] = $threeDSecureData->issuerAcsUrl; - $responseJson["challenge"]["encodedChallengeRequest"] = $threeDSecureData->payerAuthenticationRequest; + $paymentMethod = new CreditCardData(); + $paymentMethod->token = $token; + + $threeDSecureData = new ThreeDSecure(); + $threeDSecureData->serverTransactionId = $requestData->versionCheckData->serverTransactionId; + $methodUrlCompletion = ( $requestData->versionCheckData->methodData && $requestData->versionCheckData->methodUrl ) ? + MethodUrlCompletion::YES : MethodUrlCompletion::NO; + + $threeDSecureData = Secure3dService::initiateAuthentication( $paymentMethod, $threeDSecureData ) + ->withAmount( $requestData->order->amount ) + ->withCurrency( $requestData->order->currency ) + ->withOrderCreateDate( date( 'Y-m-d H:i:s' ) ) + ->withAddress( $this->mapAddress( $requestData->order->billingAddress ), AddressType::BILLING ) + ->withAddress( $this->mapAddress( $requestData->order->shippingAddress ), AddressType::SHIPPING ) + ->withCustomerEmail( $requestData->order->customerEmail ) + ->withAuthenticationSource( $requestData->authenticationSource ) + ->withAuthenticationRequestType( $requestData->authenticationRequestType ) + ->withMessageCategory( $requestData->messageCategory ) + ->withChallengeRequestIndicator( $requestData->challengeRequestIndicator ) + ->withBrowserData( $this->getBrowserData( $requestData ) ) + ->withMethodUrlCompletion( $methodUrlCompletion ) + ->execute(); + + // frictionless flow + if ($threeDSecureData->status !== "CHALLENGE_REQUIRED") { + $responseJson["result"] = $threeDSecureData->status; + $responseJson["authenticationValue"] = $threeDSecureData->authenticationValue; + $responseJson["serverTransactionId"] = $threeDSecureData->serverTransactionId; + $responseJson["messageVersion"] = $threeDSecureData->messageVersion; + $responseJson["eci"] = $threeDSecureData->eci; + + } else { //challenge flow + $responseJson["result"] = $threeDSecureData->status; + $responseJson["challengeMandated"] = $threeDSecureData->challengeMandated; + $responseJson["challenge"]["requestUrl"] = $threeDSecureData->issuerAcsUrl; + $responseJson["challenge"]["encodedChallengeRequest"] = $threeDSecureData->payerAuthenticationRequest; + } + } catch (\Exception $e) { + $responseJson = [ + 'error' => true, + 'message' => $e->getMessage(), + ]; } wp_send_json( $responseJson ); From 9885a79fcf6db1f342b1c862d01b49b35a46015d Mon Sep 17 00:00:00 2001 From: apetrovici Date: Tue, 30 Mar 2021 14:59:02 +0300 Subject: [PATCH 04/38] TR-92: config access token permissions update --- src/Gateways/Clients/SdkClient.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index c65c6a9..4c2ecb8 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -327,9 +327,7 @@ protected function configure_sdk() { $gatewayConfig->setChallengeNotificationUrl($servicesConfig['challengeNotificationUrl']); $gatewayConfig->setChannel( Channels::CardNotPresent ); if ( in_array( $this->get_arg( RequestArg::TXN_TYPE ), $this->client_transactions, true ) ) { - $accessTokenInfo = new AccessTokenInfo(); - $accessTokenInfo->setPermissions($this->access_token_permissions); - $gatewayConfig->setAccessTokenInfo($accessTokenInfo); + $gatewayConfig->setPermissions( $this->access_token_permissions ); } unset( $this->args[ RequestArg::SERVICES_CONFIG ]['gatewayProvider'] ); From 1996e0c80c1b25fa6c8c60c4a7b1f3afcdec8853 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Tue, 30 Mar 2021 14:59:36 +0300 Subject: [PATCH 05/38] TR-92: fix comma --- src/Gateways/Requests/AuthorizationRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gateways/Requests/AuthorizationRequest.php b/src/Gateways/Requests/AuthorizationRequest.php index 60a2523..092f8dc 100644 --- a/src/Gateways/Requests/AuthorizationRequest.php +++ b/src/Gateways/Requests/AuthorizationRequest.php @@ -19,7 +19,7 @@ public function get_args() { RequestArg::AMOUNT => null !== $this->order ? $this->order->get_total() : null, RequestArg::CURRENCY => null !== $this->order ? $this->order->get_currency() : null, RequestArg::CARD_DATA => $token, - RequestArg::SERVER_TRANS_ID => $this->data[ $this->gateway_id ]['serverTransId'] ?? null; + RequestArg::SERVER_TRANS_ID => $this->data[ $this->gateway_id ]['serverTransId'] ?? null, ); } } From 6a475140cbb6a3dbeaefeea34127490af72f4756 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Tue, 30 Mar 2021 17:44:40 +0300 Subject: [PATCH 06/38] TR-92: update 3ds js lib --- assets/frontend/js/globalpayments-3ds.js | 1166 +++++++++++++++++++++- 1 file changed, 1128 insertions(+), 38 deletions(-) diff --git a/assets/frontend/js/globalpayments-3ds.js b/assets/frontend/js/globalpayments-3ds.js index d638fdf..5160308 100644 --- a/assets/frontend/js/globalpayments-3ds.js +++ b/assets/frontend/js/globalpayments-3ds.js @@ -1,7 +1,7 @@ -this.GlobalPayments = this.GlobalPayments || {}; -this.GlobalPayments.ThreeDSecure = (function (exports) { - 'use strict'; - +this.GlobalPayments = this.GlobalPayments || {}; +this.GlobalPayments.ThreeDSecure = (function (exports) { + 'use strict'; + /*! ***************************************************************************** Copyright (c) Microsoft Corporation. @@ -67,8 +67,1098 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - } - + } + + function createCommonjsModule(fn, basedir, module) { + return module = { + path: basedir, + exports: {}, + require: function (path, base) { + return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); + } + }, fn(module, module.exports), module.exports; + } + + function getAugmentedNamespace(n) { + if (n.__esModule) return n; + var a = Object.defineProperty({}, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; + } + + function commonjsRequire () { + throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); + } + + self.fetch||(self.fetch=function(e,n){return n=n||{},new Promise(function(t,s){var r=new XMLHttpRequest,o=[],u=[],i={},a=function(){return {ok:2==(r.status/100|0),statusText:r.statusText,status:r.status,url:r.responseURL,text:function(){return Promise.resolve(r.responseText)},json:function(){return Promise.resolve(r.responseText).then(JSON.parse)},blob:function(){return Promise.resolve(new Blob([r.response]))},clone:a,headers:{keys:function(){return o},entries:function(){return u},get:function(e){return i[e.toLowerCase()]},has:function(e){return e.toLowerCase()in i}}}};for(var c in r.open(n.method||"get",e,!0),r.onload=function(){r.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,function(e,n,t){o.push(n=n.toLowerCase()),u.push([n,t]),i[n]=i[n]?i[n]+","+t:t;}),t(a());},r.onerror=s,r.withCredentials="include"==n.credentials,n.headers)r.setRequestHeader(c,n.headers[c]);r.send(n.body||null);})}); + + if (!Array.prototype.forEach) { + Array.prototype.forEach = function (fn) { + for (var i = 0; i < this.length; i++) { + fn(this[i], i, this); + } + }; + } + + var byteLength_1 = byteLength; + var toByteArray_1 = toByteArray; + var fromByteArray_1 = fromByteArray; + + var lookup = []; + var revLookup = []; + var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array; + + var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i]; + revLookup[code.charCodeAt(i)] = i; + } + + // Support decoding URL-safe base64 strings, as Node.js does. + // See: https://en.wikipedia.org/wiki/Base64#URL_applications + revLookup['-'.charCodeAt(0)] = 62; + revLookup['_'.charCodeAt(0)] = 63; + + function getLens (b64) { + var len = b64.length; + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('='); + if (validLen === -1) validLen = len; + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4); + + return [validLen, placeHoldersLen] + } + + // base64 is 4/3 + up to two characters of the original data + function byteLength (b64) { + var lens = getLens(b64); + var validLen = lens[0]; + var placeHoldersLen = lens[1]; + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen + } + + function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen + } + + function toByteArray (b64) { + var tmp; + var lens = getLens(b64); + var validLen = lens[0]; + var placeHoldersLen = lens[1]; + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)); + + var curByte = 0; + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen; + + var i; + for (i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)]; + arr[curByte++] = (tmp >> 16) & 0xFF; + arr[curByte++] = (tmp >> 8) & 0xFF; + arr[curByte++] = tmp & 0xFF; + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4); + arr[curByte++] = tmp & 0xFF; + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2); + arr[curByte++] = (tmp >> 8) & 0xFF; + arr[curByte++] = tmp & 0xFF; + } + + return arr + } + + function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] + } + + function encodeChunk (uint8, start, end) { + var tmp; + var output = []; + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF); + output.push(tripletToBase64(tmp)); + } + return output.join('') + } + + function fromByteArray (uint8) { + var tmp; + var len = uint8.length; + var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes + var parts = []; + var maxChunkLength = 16383; // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))); + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1]; + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ); + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1]; + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ); + } + + return parts.join('') + } + + var base64Js = { + byteLength: byteLength_1, + toByteArray: toByteArray_1, + fromByteArray: fromByteArray_1 + }; + + var base64 = createCommonjsModule(function (module, exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + exports.base64decode = exports.base64encode = void 0; + + function base64encode(text) { + var i; + var len = text.length; + var Arr = typeof Uint8Array !== "undefined" ? Uint8Array : Array; + var u8array = new Arr(len); + for (i = 0; i < len; i++) { + u8array[i] = text.charCodeAt(i); + } + return base64Js.fromByteArray(u8array); + } + exports.base64encode = base64encode; + function base64decode(text) { + var u8Array = base64Js.toByteArray(text); + var i; + var len = u8Array.length; + var bStr = ""; + for (i = 0; i < len; i++) { + bStr += String.fromCharCode(u8Array[i]); + } + return bStr; + } + exports.base64decode = base64decode; + window.btoa = window.btoa || base64encode; + window.atob = window.atob || base64decode; + + }); + + var json2 = createCommonjsModule(function (module, exports) { + /* ----------------------------------------------------------------------------- + This file is based on or incorporates material from the projects listed below + (collectively, "Third Party Code"). Microsoft is not the original author of the + Third Party Code. The original copyright notice and the license, under which + Microsoft received such Third Party Code, are set forth below. Such licenses + and notices are provided for informational purposes only. Microsoft, not the + third party, licenses the Third Party Code to you under the terms of the + Apache License, Version 2.0. See License.txt in the project root for complete + license information. Microsoft reserves all rights not expressly granted under + the Apache 2.0 License, whether by implication, estoppel or otherwise. + ----------------------------------------------------------------------------- */ + Object.defineProperty(exports, "__esModule", { value: true }); + exports.JSON = void 0; + /* + json2.js + 2011-10-19 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as "\t" or " "), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? "0" + n : n; + } + + return this.getUTCFullYear() + "-" + + f(this.getUTCMonth() + 1) + "-" + + f(this.getUTCDate()) + "T" + + f(this.getUTCHours()) + ":" + + f(this.getUTCMinutes()) + ":" + + f(this.getUTCSeconds()) + "Z"; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(["e", {pluribus: "unum"}]); + // text is "["e",{"pluribus":"unum"}]" + + text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); + // text is "[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]" + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + "Date(" + this[key] + ")" : value; + }); + // text is "["Date(---current time---)"]" + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + let a; + if (typeof value === "string") { + a = + /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse("["Date(09/09/2001)"]", function (key, value) { + let d; + if (typeof value === "string" && + value.slice(0, 5) === "Date(" && + value.slice(-1) === ")") { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + This is a reference implementation. You are free to copy, modify, or + redistribute. + */ + /*jslint evil: true, regexp: true */ + /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf + */ + // create a JSON object only if one does not already exist. We create the + // methods in a closure to avoid creating global variables. + exports.JSON = {}; + (function () { + function f(n) { + // format integers to have at least two digits. + return n < 10 ? "0" + n : n; + } + if (typeof Date.prototype.toJSON !== "function") { + Date.prototype.toJSON = function (_KEY) { + return isFinite(this.valueOf()) + ? this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + : ""; + }; + var strProto = String.prototype; + var numProto = Number.prototype; + numProto.JSON = strProto.JSON = Boolean.prototype.toJSON = function (_KEY) { + return this.valueOf(); + }; + } + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + // tslint:disable-next-line + var esc = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var gap; + var indent; + var meta = { + // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + '"': '\\"', + "\\": "\\\\", + }; + var rep; + function quote(quoteStr) { + // if the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // otherwise we must also replace the offending characters with safe escape + // sequences. + esc.lastIndex = 0; + return esc.test(quoteStr) + ? '"' + + quoteStr.replace(esc, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + + '"' + : '"' + quoteStr + '"'; + } + function str(key, holder) { + // produce a string from holder[key]. + var i; // the loop counter. + var k; // the member key. + var v; // the member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + // if the value has a toJSON method, call it to obtain a replacement value. + if (value && + typeof value === "object" && + typeof value.toJSON === "function") { + value = value.toJSON(key); + } + // if we were called with a replacer function, then call the replacer to + // obtain a replacement value. + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + // what happens next depends on the value"s type. + switch (typeof value) { + case "string": + return quote(value); + case "number": + // json numbers must be finite. Encode non-finite numbers as null. + return isFinite(value) ? String(value) : "null"; + case "boolean": + case "null": + // if the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce "null". The case is included here in + // the remote chance that this gets fixed someday. + return String(value); + // if the type is "object", we might be dealing with an object or an array or + // null. + case "object": + // due to a specification blunder in ECMAScript, typeof null is "object", + // so watch out for that case. + if (!value) { + return "null"; + } + // make an array to hold the partial: string[] results of stringifying this object value. + gap += indent; + partial = []; + // is the value an array? + if (Object.prototype.toString.apply(value, []) === "[object Array]") { + // the value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i.toString(), value) || "null"; + } + // join all of the elements together, separated with commas, and wrap them in + // brackets. + v = + partial.length === 0 + ? "[]" + : gap + ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + // if the replacer is an array, use it to select the members to be stringified. + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ": " : ":") + v); + } + } + } + } + else { + // otherwise, iterate through all of the keys in the object. + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ": " : ":") + v); + } + } + } + } + // join all of the member texts together, separated with commas, + // and wrap them in braces. + v = + partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + return undefined; + } + // if the JSON object does not yet have a stringify method, give it one. + if (typeof exports.JSON.stringify !== "function") { + exports.JSON.stringify = function (value, replacer, space) { + // the stringify method takes a value and an optional replacer, and an optional + // space parameter, and returns a JSON text. The replacer can be a function + // that can replace values, or an array of strings that will select the keys. + // a default replacer method can be provided. Use of the space parameter can + // produce text that is more easily readable. + var i; + gap = ""; + indent = ""; + // if the space parameter is a number, make an indent string containing that + // many spaces. + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + // if the space parameter is a string, it will be used as the indent string. + } + else if (typeof space === "string") { + indent = space; + } + // if there is a replacer, it must be a function or an array. + // otherwise, throw an error. + rep = replacer; + if (replacer && + typeof replacer !== "function" && + (typeof replacer !== "object" || typeof replacer.length !== "number")) { + throw new Error("JSON.stringify"); + } + // make a fake root object containing our value under the key of "". + // return the result of stringifying the value. + return str("", { "": value }); + }; + } + // if the JSON object does not yet have a parse method, give it one. + if (typeof exports.JSON.parse !== "function") { + exports.JSON.parse = function (text, reviver) { + // the parse method takes a text and an optional reviver function, and returns + // a JavaScript value if the text is a valid JSON text. + var j; + function walk(holder, key) { + // the walk method is used to recursively walk the resulting structure so + // that modifications can be made. + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + value[k] = v; + } + } + } + return reviver.call(holder, key, value); + } + // parsing happens in four stages. In the first stage, we replace certain + // unicode characters with escape sequences. JavaScript handles many characters + // incorrectly, either silently deleting them, or treating them as line endings. + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + // in the second stage, we run the text against regular expressions that look + // for non-JSON patterns. We are especially concerned with "()" and "new" + // because they can cause invocation, and "=" because it can cause mutation. + // but just to be safe, we want to reject all unexpected forms. + // we split the second stage into 4 regexp operations in order to work around + // crippling inefficiencies in IE"s and Safari"s regexp engines. First we + // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we + // replace all simple value tokens with "]" characters. Third, we delete all + // open brackets that follow a colon or comma or that begin the text. Finally, + // we look to see that the remaining characters are only whitespace or "]" or + // "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + if (/^[\],:{}\s]*$/.test(text + .replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") + .replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) { + // in the third stage we use the eval function to compile the text into a + // javascript structure. The "{" operator is subject to a syntactic ambiguity + // in JavaScript: it can begin a block or an object literal. We wrap the text + // in parens to eliminate the ambiguity. + // tslint:disable-next-line:function-constructor + j = new Function("return (" + text + ")")(); + // in the optional fourth stage, we recursively walk the new structure, passing + // each name/value pair to a reviver function for possible transformation. + return typeof reviver === "function" ? walk({ "": j }, "") : j; + } + // if the text is not JSON parseable, then a SyntaxError is thrown. + throw new SyntaxError("JSON.parse"); + }; + } + })(); + + }); + + var json = createCommonjsModule(function (module, exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + + window.JSON = window.JSON || json2.JSON; + + }); + + // ES5 15.2.3.9 + // http://es5.github.com/#x15.2.3.9 + if (!Object.freeze) { + Object.freeze = function (object) { + if (Object(object) !== object) { + throw new TypeError("Object.freeze can only be called on Objects."); + } + // this is misleading and breaks feature-detection, but + // allows "securable" code to "gracefully" degrade to working + // but insecure code. + return object; + }; + } + // detect a Rhino bug and patch it + try { + Object.freeze(function () { return undefined; }); + } + catch (exception) { + Object.freeze = (function (freezeObject) { + return function (object) { + if (typeof object === "function") { + return object; + } + else { + return freezeObject(object); + } + }; + })(Object.freeze); + } + + if (!Object.prototype.hasOwnProperty) { + Object.prototype.hasOwnProperty = function (prop) { + return typeof this[prop] !== "undefined"; + }; + } + if (!Object.getOwnPropertyNames) { + Object.getOwnPropertyNames = function (obj) { + var keys = []; + for (var key in obj) { + if (typeof obj.hasOwnProperty !== "undefined" && + obj.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys; + }; + } + + // Source: https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend + (function (arr) { + arr.forEach(function (item) { + if (item.hasOwnProperty('prepend')) { + return; + } + Object.defineProperty(item, 'prepend', { + configurable: true, + enumerable: true, + writable: true, + value: function prepend() { + var argArr = Array.prototype.slice.call(arguments); + var docFrag = document.createDocumentFragment(); + argArr.forEach(function (argItem) { + var isNode = argItem instanceof Node; + docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem))); + }); + this.insertBefore(docFrag, this.firstChild); + } + }); + }); + })([Element.prototype, Document.prototype, DocumentFragment.prototype]); + + /** + * @this {Promise} + */ + function finallyConstructor(callback) { + var constructor = this.constructor; + return this.then( + function(value) { + // @ts-ignore + return constructor.resolve(callback()).then(function() { + return value; + }); + }, + function(reason) { + // @ts-ignore + return constructor.resolve(callback()).then(function() { + // @ts-ignore + return constructor.reject(reason); + }); + } + ); + } + + function allSettled(arr) { + var P = this; + return new P(function(resolve, reject) { + if (!(arr && typeof arr.length !== 'undefined')) { + return reject( + new TypeError( + typeof arr + + ' ' + + arr + + ' is not iterable(cannot read property Symbol(Symbol.iterator))' + ) + ); + } + var args = Array.prototype.slice.call(arr); + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call( + val, + function(val) { + res(i, val); + }, + function(e) { + args[i] = { status: 'rejected', reason: e }; + if (--remaining === 0) { + resolve(args); + } + } + ); + return; + } + } + args[i] = { status: 'fulfilled', value: val }; + if (--remaining === 0) { + resolve(args); + } + } + + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + } + + // Store setTimeout reference so promise-polyfill will be unaffected by + // other code modifying setTimeout (like sinon.useFakeTimers()) + var setTimeoutFunc = setTimeout; + + function isArray(x) { + return Boolean(x && typeof x.length !== 'undefined'); + } + + function noop() {} + + // Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + }; + } + + /** + * @constructor + * @param {Function} fn + */ + function Promise$1(fn) { + if (!(this instanceof Promise$1)) + throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + /** @type {!number} */ + this._state = 0; + /** @type {!boolean} */ + this._handled = false; + /** @type {Promise|undefined} */ + this._value = undefined; + /** @type {!Array} */ + this._deferreds = []; + + doResolve(fn, this); + } + + function handle(self, deferred) { + while (self._state === 3) { + self = self._value; + } + if (self._state === 0) { + self._deferreds.push(deferred); + return; + } + self._handled = true; + Promise$1._immediateFn(function() { + var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; + if (cb === null) { + (self._state === 1 ? resolve : reject)(deferred.promise, self._value); + return; + } + var ret; + try { + ret = cb(self._value); + } catch (e) { + reject(deferred.promise, e); + return; + } + resolve(deferred.promise, ret); + }); + } + + function resolve(self, newValue) { + try { + // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === self) + throw new TypeError('A promise cannot be resolved with itself.'); + if ( + newValue && + (typeof newValue === 'object' || typeof newValue === 'function') + ) { + var then = newValue.then; + if (newValue instanceof Promise$1) { + self._state = 3; + self._value = newValue; + finale(self); + return; + } else if (typeof then === 'function') { + doResolve(bind(then, newValue), self); + return; + } + } + self._state = 1; + self._value = newValue; + finale(self); + } catch (e) { + reject(self, e); + } + } + + function reject(self, newValue) { + self._state = 2; + self._value = newValue; + finale(self); + } + + function finale(self) { + if (self._state === 2 && self._deferreds.length === 0) { + Promise$1._immediateFn(function() { + if (!self._handled) { + Promise$1._unhandledRejectionFn(self._value); + } + }); + } + + for (var i = 0, len = self._deferreds.length; i < len; i++) { + handle(self, self._deferreds[i]); + } + self._deferreds = null; + } + + /** + * @constructor + */ + function Handler(onFulfilled, onRejected, promise) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.promise = promise; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, self) { + var done = false; + try { + fn( + function(value) { + if (done) return; + done = true; + resolve(self, value); + }, + function(reason) { + if (done) return; + done = true; + reject(self, reason); + } + ); + } catch (ex) { + if (done) return; + done = true; + reject(self, ex); + } + } + + Promise$1.prototype['catch'] = function(onRejected) { + return this.then(null, onRejected); + }; + + Promise$1.prototype.then = function(onFulfilled, onRejected) { + // @ts-ignore + var prom = new this.constructor(noop); + + handle(this, new Handler(onFulfilled, onRejected, prom)); + return prom; + }; + + Promise$1.prototype['finally'] = finallyConstructor; + + Promise$1.all = function(arr) { + return new Promise$1(function(resolve, reject) { + if (!isArray(arr)) { + return reject(new TypeError('Promise.all accepts an array')); + } + + var args = Array.prototype.slice.call(arr); + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call( + val, + function(val) { + res(i, val); + }, + reject + ); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise$1.allSettled = allSettled; + + Promise$1.resolve = function(value) { + if (value && typeof value === 'object' && value.constructor === Promise$1) { + return value; + } + + return new Promise$1(function(resolve) { + resolve(value); + }); + }; + + Promise$1.reject = function(value) { + return new Promise$1(function(resolve, reject) { + reject(value); + }); + }; + + Promise$1.race = function(arr) { + return new Promise$1(function(resolve, reject) { + if (!isArray(arr)) { + return reject(new TypeError('Promise.race accepts an array')); + } + + for (var i = 0, len = arr.length; i < len; i++) { + Promise$1.resolve(arr[i]).then(resolve, reject); + } + }); + }; + + // Use polyfill for setImmediate for performance gains + Promise$1._immediateFn = + // @ts-ignore + (typeof setImmediate === 'function' && + function(fn) { + // @ts-ignore + setImmediate(fn); + }) || + function(fn) { + setTimeoutFunc(fn, 0); + }; + + Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { + if (typeof console !== 'undefined' && console) { + console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console + } + }; + + var src = /*#__PURE__*/Object.freeze({ + __proto__: null, + 'default': Promise$1 + }); + + var Promise$2 = /*@__PURE__*/getAugmentedNamespace(src); + + var promise = createCommonjsModule(function (module, exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + + window.Promise = + window.Promise || Promise$2.default || Promise$2; + + }); + + if (!String.prototype.repeat) { + String.prototype.repeat = function (length) { + var result = ""; + for (var i = 0; i < length; i++) { + result += this; + } + return result; + }; + } + + var polyfills = createCommonjsModule(function (module, exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + + + + + + + + + + + }); + (function (AuthenticationSource) { AuthenticationSource["Browser"] = "BROWSER"; AuthenticationSource["MobileSDK"] = "MOBILE_SDK"; @@ -205,8 +1295,8 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { default: return exports.MessageCategory.Payment; } - } - + } + var GPError = /** @class */ (function (_super) { __extends(GPError, _super); function GPError(reasons, message) { @@ -216,14 +1306,14 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { return _this; } return GPError; - }(Error)); - + }(Error)); + function handleNotificationMessageEvent(event, data, origin) { if (window.parent !== window) { window.parent.postMessage({ data: data, event: event }, origin || window.location.origin); } - } - + } + function makeRequest(endpoint, data) { return __awaiter(this, void 0, void 0, function () { var headers, rawResponse, _a, e_1, reasons; @@ -268,8 +1358,8 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { } }); }); - } - + } + // most of this module is pulled from the legacy Realex Payments JavaScript library var isWindowsMobileOs = /Windows Phone|IEMobile/.test(navigator.userAgent); var isAndroidOrIOs = /Android|iPad|iPhone|iPod/.test(navigator.userAgent); @@ -430,8 +1520,8 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { }, true); } }; - } - + } + function postToIframe(endpoint, fields, options) { return new Promise(function (resolve, reject) { var timeout; @@ -536,8 +1626,8 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { targetElement.appendChild(data.iframe); } return true; - } - + } + /** * Retrieves client browser runtime data. */ @@ -678,7 +1768,7 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if (!data.challengeMandated) return [3 /*break*/, 2]; + if (!(data.challengeMandated || data.status === exports.TransactionStatus.ChallengeRequired)) return [3 /*break*/, 2]; data.challenge = data.challenge || {}; if (!data.challenge.requestUrl) { throw new Error("Invalid challenge state. Missing challenge URL"); @@ -712,22 +1802,22 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { */ function handleMethodNotification(data, origin) { handleNotificationMessageEvent("methodNotification", data, origin); - } - - exports.checkVersion = checkVersion; - exports.colorDepth = colorDepth; - exports.dimensionsFromChallengeWindowSize = dimensionsFromChallengeWindowSize; - exports.getBrowserData = getBrowserData; - exports.handle3dsVersionCheck = handle3dsVersionCheck; - exports.handleChallengeNotification = handleChallengeNotification; - exports.handleInitiateAuthentication = handleInitiateAuthentication; - exports.handleMethodNotification = handleMethodNotification; - exports.initiateAuthentication = initiateAuthentication; - exports.messageCategoryFromAuthenticationRequestType = messageCategoryFromAuthenticationRequestType; - - Object.defineProperty(exports, '__esModule', { value: true }); - - return exports; - -}({})); -//# sourceMappingURL=globalpayments-3ds.js.map + } + + exports.checkVersion = checkVersion; + exports.colorDepth = colorDepth; + exports.dimensionsFromChallengeWindowSize = dimensionsFromChallengeWindowSize; + exports.getBrowserData = getBrowserData; + exports.handle3dsVersionCheck = handle3dsVersionCheck; + exports.handleChallengeNotification = handleChallengeNotification; + exports.handleInitiateAuthentication = handleInitiateAuthentication; + exports.handleMethodNotification = handleMethodNotification; + exports.initiateAuthentication = initiateAuthentication; + exports.messageCategoryFromAuthenticationRequestType = messageCategoryFromAuthenticationRequestType; + + Object.defineProperty(exports, '__esModule', { value: true }); + + return exports; + +}({})); +//# sourceMappingURL=globalpayments-3ds.js.map From e97115c2e50530b64dded08c14b547ab8754f7d9 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Tue, 30 Mar 2021 19:25:55 +0300 Subject: [PATCH 07/38] TR-92: remove unused namespace --- src/Gateways/Clients/SdkClient.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index 5259261..7745203 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -5,7 +5,6 @@ use GlobalPayments\Api\Builders\TransactionBuilder; use GlobalPayments\Api\Entities\Address; use GlobalPayments\Api\Entities\Enums\GpApi\Channels; -use GlobalPayments\Api\Entities\GpApi\AccessTokenInfo; use GlobalPayments\Api\Entities\Transaction; use GlobalPayments\Api\Entities\Enums\AddressType; use GlobalPayments\Api\Entities\Enums\CardType; From 8d94d9ad2e600b0109d27e1478114554d935ebc5 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 31 Mar 2021 15:37:30 +0300 Subject: [PATCH 08/38] TR-92: handle 3DS1 --- .../globalpayments-secure-payment-fields.js | 12 ++++++++++ src/Gateways/AbstractGateway.php | 2 +- src/Gateways/Clients/SdkClient.php | 1 + src/Gateways/GpApiGateway.php | 18 +++++++++----- .../Requests/AuthorizationRequest.php | 1 + src/Gateways/Requests/RequestArg.php | 1 + .../ThreeDSecure/CheckEnrollmentRequest.php | 24 ++++++++++++++++--- src/Gateways/Requests/VerifyRequest.php | 1 + 8 files changed, 50 insertions(+), 10 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 2833199..0d231c7 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -326,6 +326,11 @@ wcTokenId: $( 'input[name="wc-' + this.id + '-payment-token"]:checked', this.getForm() ).val(), amount: this.order.amount, currency: this.order.currency, + challengeWindow: { + windowSize: ChallengeWindowSize.Windowed500x600, + displayMode: 'lightbox', + hide: false, + }, }); // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. @@ -345,6 +350,13 @@ return; } + if ( "ONE" === versionCheckData.version ) { + this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); + this.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); + $( this.getForm() ).submit(); + return; + } + try { authenticationData = await initiateAuthentication(this.threedsecure.initiateAuthenticationUrl, { tokenResponse: this.tokenResponse, diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index 5ad9268..c5adc57 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -260,7 +260,7 @@ public function tokenization_script() { // Global Payments scripts for handling client-side tokenization wp_enqueue_script( 'globalpayments-secure-payment-fields-lib', - 'https://api2.heartlandportico.com/securesubmit.v1/token/gp-1.6.0/globalpayments' + 'https://js.globalpay.com/v1/globalpayments' . ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ) . '.js', array(), WC()->version, diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index 7745203..dd2d173 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -285,6 +285,7 @@ protected function threedsecure_is_enabled() { protected function set_threedsecure_data() { $threeDSecureData = Secure3dService::getAuthenticationData() ->withServerTransactionId( $this->get_arg( RequestArg::SERVER_TRANS_ID ) ) + ->withPayerAuthenticationResponse( $this->get_arg( RequestArg::PARES ) ) ->execute(); if ( ! in_array( $threeDSecureData->eci, ["01", "02", "05", "06"] ) ) { diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index acaa2ea..d121ca8 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -186,12 +186,18 @@ public function process_threeDSecure_challengeNotification() try { $response = new \stdClass(); - $convertedCRes = json_decode(base64_decode($_POST['cres'])); - - $response = json_encode([ - 'threeDSServerTransID' => $convertedCRes->threeDSServerTransID, - 'transStatus' => $convertedCRes->transStatus ?? '', - ]); + if ( isset( $_POST['cres'] ) ) { + $convertedCRes = json_decode( base64_decode( $_POST['cres'] ) ); + + $response = json_encode([ + 'threeDSServerTransID' => $convertedCRes->threeDSServerTransID, + 'transStatus' => $convertedCRes->transStatus ?? '', + ]); + } + + if ( isset( $_POST['PaRes'] ) ) { + $response = json_encode( [ 'PaRes' => $_POST['PaRes'] ], JSON_UNESCAPED_SLASHES ); + } $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); $script = << null !== $this->order ? $this->order->get_currency() : null, RequestArg::CARD_DATA => $token, RequestArg::SERVER_TRANS_ID => $this->data[ $this->gateway_id ]['serverTransId'] ?? null, + RequestArg::PARES => $this->data[ $this->gateway_id ]['PaRes'] ?? null, ); } } diff --git a/src/Gateways/Requests/RequestArg.php b/src/Gateways/Requests/RequestArg.php index 65c16cc..47ecbde 100644 --- a/src/Gateways/Requests/RequestArg.php +++ b/src/Gateways/Requests/RequestArg.php @@ -8,6 +8,7 @@ abstract class RequestArg { const CARD_DATA = 'CARD_DATA'; const CARD_HOLDER_NAME = 'CARD_HOLDER_NAME'; const CURRENCY = 'CURRENCY'; + const PARES = 'PARES'; const SERVER_TRANS_ID = 'SERVER_TRANS_ID'; const SERVICES_CONFIG = 'SERVICES_CONFIG'; const SHIPPING_ADDRESS = 'SHIPPING_ADDRESS'; diff --git a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php index 49dac5c..d601217 100644 --- a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php @@ -2,6 +2,7 @@ namespace GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\Requests\ThreeDSecure; +use GlobalPayments\Api\Entities\Enums\Secure3dVersion; use GlobalPayments\Api\PaymentMethods\CreditCardData; use GlobalPayments\Api\Services\Secure3dService; use GlobalPayments\WooCommercePaymentGatewayProvider\Gateways\AbstractGateway; @@ -10,9 +11,10 @@ defined('ABSPATH') || exit; class CheckEnrollmentRequest extends AbstractRequest { + const ENROLLED = 'ENROLLED'; const NOT_ENROLLED = 'NOT_ENROLLED'; - const NO_RESPONSE = 'NO_RESPONSE'; + const NO_RESPONSE = 'NO_RESPONSE'; public function get_transaction_type() { return AbstractGateway::TXN_TYPE_CHECK_ENROLLMENT; @@ -44,8 +46,24 @@ public function do_request() { $response["enrolled"] = $threeDSecureData->enrolled ?? self::NOT_ENROLLED; $response['version'] = $threeDSecureData->getVersion(); $response["serverTransactionId"] = $threeDSecureData->serverTransactionId ?? ''; - $response["methodUrl"] = $threeDSecureData->issuerAcsUrl ?? ''; - $response['methodData'] = $threeDSecureData->payerAuthenticationRequest ?? ''; + if ( self::ENROLLED !== $threeDSecureData->enrolled ) { + wp_send_json( $response ); + } + + if ( Secure3dVersion::TWO === $threeDSecureData->getVersion() ) { + $response["methodUrl"] = $threeDSecureData->issuerAcsUrl ?? ''; + $response['methodData'] = $threeDSecureData->payerAuthenticationRequest ?? ''; + + wp_send_json($response); + } + + if ( Secure3dVersion::ONE === $threeDSecureData->getVersion() ) { + $response['TermUrl'] = $threeDSecureData->challengeReturnUrl; + $response["status"] = $threeDSecureData->status; + $response["challengeMandated"] = $threeDSecureData->challengeMandated; + $response["challenge"]["requestUrl"] = $threeDSecureData->issuerAcsUrl; + $response["challenge"]["encodedChallengeRequest"] = $threeDSecureData->payerAuthenticationRequest; + } } catch (\Exception $e) { $response = [ 'error' => true, diff --git a/src/Gateways/Requests/VerifyRequest.php b/src/Gateways/Requests/VerifyRequest.php index 6f89714..37949c7 100644 --- a/src/Gateways/Requests/VerifyRequest.php +++ b/src/Gateways/Requests/VerifyRequest.php @@ -21,6 +21,7 @@ public function get_args() { RequestArg::CARD_DATA => $token, RequestArg::CURRENCY => null !== $this->order ? $this->order->get_currency() : get_woocommerce_currency(), RequestArg::SERVER_TRANS_ID => $this->data[ $this->gateway_id ]['serverTransId'] ?? null, + RequestArg::PARES => $this->data[ $this->gateway_id ]['PaRes'] ?? null, ); } From c7e34a7d781a0bfc93f7d58e937076a564170d75 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 31 Mar 2021 15:41:10 +0300 Subject: [PATCH 09/38] TR-92: update endpoints --- src/Gateways/AbstractGateway.php | 8 ++++---- src/Gateways/GpApiGateway.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index c5adc57..d849c4f 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -301,10 +301,10 @@ public function tokenization_script() { 'globalpayments_secure_payment_threedsecure_params', array( 'threedsecure' => array( - 'methodNotificationUrl' => $this->get_api_url('threedsecure_methodnotification'), - 'challengeNotificationUrl' => $this->get_api_url('threedsecure_challengenotification'), - 'checkEnrollmentUrl' => $this->get_api_url('threedsecure_checkenrollment'), - 'initiateAuthenticationUrl' => $this->get_api_url('threedsecure_initiateauthentication'), + 'methodNotificationUrl' => $this->get_api_url( 'globalpayments_threedsecure_methodnotification' ), + 'challengeNotificationUrl' => $this->get_api_url( 'globalpayments_threedsecure_challengenotification' ), + 'checkEnrollmentUrl' => $this->get_api_url( 'globalpayments_threedsecure_checkenrollment' ), + 'initiateAuthenticationUrl' => $this->get_api_url( 'globalpayments_threedsecure_initiateauthentication' ), ), 'order' => array ( 'amount' => $this->get_session_amount(), diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index d121ca8..f0f7129 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -119,10 +119,10 @@ protected function add_hooks() { * The WooCommerce API allows plugins make a callback to a special URL that will then load the specified class (if it exists) * and run an action. This is also useful for gateways that are not initialized. */ - add_action( 'woocommerce_api_threedsecure_checkenrollment', array( $this, 'process_threeDSecure_checkEnrollment' ) ); - add_action( 'woocommerce_api_threedsecure_methodnotification', array( $this, 'process_threeDSecure_methodNotification' ) ); - add_action( 'woocommerce_api_threedsecure_initiateauthentication', array( $this, 'process_threeDSecure_initiateAuthentication' ) ); - add_action( 'woocommerce_api_threedsecure_challengenotification', array( $this, 'process_threeDSecure_challengeNotification' ) ); + add_action( 'woocommerce_api_globalpayments_threedsecure_checkenrollment', array( $this, 'process_threeDSecure_checkEnrollment' ) ); + add_action( 'woocommerce_api_globalpayments_threedsecure_methodnotification', array( $this, 'process_threeDSecure_methodNotification' ) ); + add_action( 'woocommerce_api_globalpayments_threedsecure_initiateauthentication', array( $this, 'process_threeDSecure_initiateAuthentication' ) ); + add_action( 'woocommerce_api_globalpayments_threedsecure_challengenotification', array( $this, 'process_threeDSecure_challengeNotification' ) ); } public function mapResponseCodeToFriendlyMessage( $responseCode ) { From cf47953ede5d6e8ccbdc5e397423b9cb678df939 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 31 Mar 2021 16:14:18 +0300 Subject: [PATCH 10/38] TR-92: update endpoints --- src/Gateways/GpApiGateway.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index f0f7129..0444717 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -100,8 +100,8 @@ public function get_backend_gateway_options() { 'AppKey' => $this->app_key, 'developerId' => '', 'environment' => $this->is_production ? Environment::PRODUCTION : Environment::TEST, - 'methodNotificationUrl' => $this->get_api_url('threedsecure_methodnotification'), - 'challengeNotificationUrl' => $this->get_api_url('threedsecure_challengenotification'), + 'methodNotificationUrl' => $this->get_api_url('globalpayments_threedsecure_methodnotification'), + 'challengeNotificationUrl' => $this->get_api_url('globalpayments_threedsecure_challengenotification'), ); } From 54521528d280d70d7021b892871fa32e17c8e863 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 31 Mar 2021 16:17:33 +0300 Subject: [PATCH 11/38] TR-92: updated 3ds lib to handle 3DS1 --- assets/frontend/js/globalpayments-3ds.js | 1431 +++++++++--------- assets/frontend/js/globalpayments-3ds.min.js | 32 +- 2 files changed, 751 insertions(+), 712 deletions(-) diff --git a/assets/frontend/js/globalpayments-3ds.js b/assets/frontend/js/globalpayments-3ds.js index 5160308..466179d 100644 --- a/assets/frontend/js/globalpayments-3ds.js +++ b/assets/frontend/js/globalpayments-3ds.js @@ -2,71 +2,71 @@ this.GlobalPayments = this.GlobalPayments || {}; this.GlobalPayments.ThreeDSecure = (function (exports) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */ - /* global Reflect, Promise */ - - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } - - function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } - - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } } function createCommonjsModule(fn, basedir, module) { @@ -1159,649 +1159,688 @@ this.GlobalPayments.ThreeDSecure = (function (exports) { }); - (function (AuthenticationSource) { - AuthenticationSource["Browser"] = "BROWSER"; - AuthenticationSource["MobileSDK"] = "MOBILE_SDK"; - AuthenticationSource["StoredRecurring"] = "STORED_RECURRING"; - })(exports.AuthenticationSource || (exports.AuthenticationSource = {})); - (function (AuthenticationRequestType) { - AuthenticationRequestType["AddCard"] = "ADD_CARD"; - AuthenticationRequestType["CardholderVerification"] = "CARDHOLDER_VERIFICATION"; - AuthenticationRequestType["InstalmentTransaction"] = "INSTALMENT_TRANSACTION"; - AuthenticationRequestType["MaintainCard"] = "MAINTAIN_CARD"; - AuthenticationRequestType["PaymentTransaction"] = "PAYMENT_TRANSACTION"; - AuthenticationRequestType["RecurringTransaction"] = "RECURRING_TRANSACTION"; - })(exports.AuthenticationRequestType || (exports.AuthenticationRequestType = {})); - (function (ChallengeRequestIndicator) { - ChallengeRequestIndicator["ChallengeMandated"] = "CHALLENGE_MANDATED"; - ChallengeRequestIndicator["ChallengePreferred"] = "CHALLENGE_PREFERRED"; - ChallengeRequestIndicator["NoChallengeRequested"] = "NO_CHALLENGE_REQUESTED"; - ChallengeRequestIndicator["NoPreference"] = "NO_PREFERENCE"; - })(exports.ChallengeRequestIndicator || (exports.ChallengeRequestIndicator = {})); - (function (ChallengeWindowSize) { - ChallengeWindowSize["FullScreen"] = "FULL_SCREEN"; - ChallengeWindowSize["Windowed250x400"] = "WINDOWED_250X400"; - ChallengeWindowSize["Windowed390x400"] = "WINDOWED_390X400"; - ChallengeWindowSize["Windowed500x600"] = "WINDOWED_500X600"; - ChallengeWindowSize["Windowed600x400"] = "WINDOWED_600X400"; - })(exports.ChallengeWindowSize || (exports.ChallengeWindowSize = {})); - (function (MessageCategory) { - MessageCategory["NonPayment"] = "NON_PAYMENT_AUTHENTICATION"; - MessageCategory["Payment"] = "PAYMENT_AUTHENTICATION"; - })(exports.MessageCategory || (exports.MessageCategory = {})); - (function (MethodUrlCompletion) { - MethodUrlCompletion["Unavailable"] = "UNAVAILABLE"; - MethodUrlCompletion["No"] = "NO"; - MethodUrlCompletion["Yes"] = "YES"; - })(exports.MethodUrlCompletion || (exports.MethodUrlCompletion = {})); - (function (TransactionStatus) { - TransactionStatus["AuthenticationAttemptedButNotSuccessful"] = "AUTHENTICATION_ATTEMPTED_BUT_NOT_SUCCESSFUL"; - TransactionStatus["AuthenticationCouldNotBePerformed"] = "AUTHENTICATION_COULD_NOT_BE_PERFORMED"; - TransactionStatus["AuthenticationFailed"] = "AUTHENTICATION_FAILED"; - TransactionStatus["AuthenticationIssuerRejected"] = "AUTHENTICATION_ISSUER_REJECTED"; - TransactionStatus["AuthenticationSuccessful"] = "AUTHENTICATION_SUCCESSFUL"; - TransactionStatus["ChallengeRequired"] = "CHALLENGE_REQUIRED"; - })(exports.TransactionStatus || (exports.TransactionStatus = {})); - (function (TransactionStatusReason) { - TransactionStatusReason["CardAuthenticationFailed"] = "CARD_AUTHENTICATION_FAILED"; - TransactionStatusReason["UnknownDevice"] = "UNKNOWN_DEVICE"; - TransactionStatusReason["UnsupportedDevice"] = "UNSUPPORTED_DEVICE"; - TransactionStatusReason["ExceedsAuthenticationFrequencyLimit"] = "EXCEEDS_AUTHENTICATION_FREQUENCY_LIMIT"; - TransactionStatusReason["ExpiredCard"] = "EXPIRED_CARD"; - TransactionStatusReason["InvalidCardNumber"] = "INVALID_CARD_NUMBER"; - TransactionStatusReason["InvalidTransaction"] = "INVALID_TRANSACTION"; - TransactionStatusReason["NoCardRecord"] = "NO_CARD_RECORD"; - TransactionStatusReason["SecurityFailure"] = "SECURITY_FAILURE"; - TransactionStatusReason["StolenCard"] = "STOLEN_CARD"; - TransactionStatusReason["SuspectedFraud"] = "SUSPECTED_FRAUD"; - TransactionStatusReason["TransactionNotPermittedToCardholder"] = "TRANSACTION_NOT_PERMITTED_TO_CARDHOLDER"; - TransactionStatusReason["CardholderNotEnrolledInService"] = "CARDHOLDER_NOT_ENROLLED_IN_SERVICE"; - TransactionStatusReason["TransactionTimedOutAtTheAcs"] = "TRANSACTION_TIMED_OUT_AT_THE_ACS"; - TransactionStatusReason["LowConfidence"] = "LOW_CONFIDENCE"; - TransactionStatusReason["MediumConfidence"] = "MEDIUM_CONFIDENCE"; - TransactionStatusReason["HighConfidence"] = "HIGH_CONFIDENCE"; - TransactionStatusReason["VeryHighConfidence"] = "VERY_HIGH_CONFIDENCE"; - TransactionStatusReason["ExceedsAcsMaximumChallenges"] = "EXCEEDS_ACS_MAXIMUM_CHALLENGES"; - TransactionStatusReason["NonPaymentTransactionNotSupported"] = "NON_PAYMENT_TRANSACTION_NOT_SUPPORTED"; - TransactionStatusReason["ThreeriTransactionNotSupported"] = "THREERI_TRANSACTION_NOT_SUPPORTED"; - })(exports.TransactionStatusReason || (exports.TransactionStatusReason = {})); - function colorDepth(value) { - var result = ""; - switch (value) { - case 1: - return "ONE_BIT"; - case 2: - result += "TWO"; - break; - case 4: - result += "FOUR"; - break; - case 8: - result += "EIGHT"; - break; - case 15: - result += "FIFTEEN"; - break; - case 16: - result += "SIXTEEN"; - break; - case 24: - case 30: - result += "TWENTY_FOUR"; - break; - case 32: - result += "THIRTY_TWO"; - break; - case 48: - result += "FORTY_EIGHT"; - break; - default: - throw new Error("Unknown color depth '" + value + "'"); - } - return result + "_BITS"; - } - function dimensionsFromChallengeWindowSize(options) { - var height = 0; - var width = 0; - switch (options.size || options.windowSize) { - case exports.ChallengeWindowSize.Windowed250x400: - height = 250; - width = 400; - break; - case exports.ChallengeWindowSize.Windowed390x400: - height = 390; - width = 400; - break; - case exports.ChallengeWindowSize.Windowed500x600: - height = 500; - width = 600; - break; - case exports.ChallengeWindowSize.Windowed600x400: - height = 600; - width = 400; - break; - } - return { height: height, width: width }; - } - function messageCategoryFromAuthenticationRequestType(authenticationRequestType) { - switch (authenticationRequestType) { - case exports.AuthenticationRequestType.AddCard: - case exports.AuthenticationRequestType.CardholderVerification: - case exports.AuthenticationRequestType.MaintainCard: - return exports.MessageCategory.NonPayment; - case exports.AuthenticationRequestType.InstalmentTransaction: - case exports.AuthenticationRequestType.PaymentTransaction: - case exports.AuthenticationRequestType.RecurringTransaction: - default: - return exports.MessageCategory.Payment; - } + (function (AuthenticationSource) { + AuthenticationSource["Browser"] = "BROWSER"; + AuthenticationSource["MobileSDK"] = "MOBILE_SDK"; + AuthenticationSource["StoredRecurring"] = "STORED_RECURRING"; + })(exports.AuthenticationSource || (exports.AuthenticationSource = {})); + (function (AuthenticationRequestType) { + AuthenticationRequestType["AddCard"] = "ADD_CARD"; + AuthenticationRequestType["CardholderVerification"] = "CARDHOLDER_VERIFICATION"; + AuthenticationRequestType["InstalmentTransaction"] = "INSTALMENT_TRANSACTION"; + AuthenticationRequestType["MaintainCard"] = "MAINTAIN_CARD"; + AuthenticationRequestType["PaymentTransaction"] = "PAYMENT_TRANSACTION"; + AuthenticationRequestType["RecurringTransaction"] = "RECURRING_TRANSACTION"; + })(exports.AuthenticationRequestType || (exports.AuthenticationRequestType = {})); + (function (ChallengeRequestIndicator) { + ChallengeRequestIndicator["ChallengeMandated"] = "CHALLENGE_MANDATED"; + ChallengeRequestIndicator["ChallengePreferred"] = "CHALLENGE_PREFERRED"; + ChallengeRequestIndicator["NoChallengeRequested"] = "NO_CHALLENGE_REQUESTED"; + ChallengeRequestIndicator["NoPreference"] = "NO_PREFERENCE"; + })(exports.ChallengeRequestIndicator || (exports.ChallengeRequestIndicator = {})); + (function (ChallengeWindowSize) { + ChallengeWindowSize["FullScreen"] = "FULL_SCREEN"; + ChallengeWindowSize["Windowed250x400"] = "WINDOWED_250X400"; + ChallengeWindowSize["Windowed390x400"] = "WINDOWED_390X400"; + ChallengeWindowSize["Windowed500x600"] = "WINDOWED_500X600"; + ChallengeWindowSize["Windowed600x400"] = "WINDOWED_600X400"; + })(exports.ChallengeWindowSize || (exports.ChallengeWindowSize = {})); + (function (MessageCategory) { + MessageCategory["NonPayment"] = "NON_PAYMENT_AUTHENTICATION"; + MessageCategory["Payment"] = "PAYMENT_AUTHENTICATION"; + })(exports.MessageCategory || (exports.MessageCategory = {})); + (function (MethodUrlCompletion) { + MethodUrlCompletion["Unavailable"] = "UNAVAILABLE"; + MethodUrlCompletion["No"] = "NO"; + MethodUrlCompletion["Yes"] = "YES"; + })(exports.MethodUrlCompletion || (exports.MethodUrlCompletion = {})); + (function (TransactionStatus) { + TransactionStatus["AuthenticationAttemptedButNotSuccessful"] = "AUTHENTICATION_ATTEMPTED_BUT_NOT_SUCCESSFUL"; + TransactionStatus["AuthenticationCouldNotBePerformed"] = "AUTHENTICATION_COULD_NOT_BE_PERFORMED"; + TransactionStatus["AuthenticationFailed"] = "AUTHENTICATION_FAILED"; + TransactionStatus["AuthenticationIssuerRejected"] = "AUTHENTICATION_ISSUER_REJECTED"; + TransactionStatus["AuthenticationSuccessful"] = "AUTHENTICATION_SUCCESSFUL"; + TransactionStatus["ChallengeRequired"] = "CHALLENGE_REQUIRED"; + })(exports.TransactionStatus || (exports.TransactionStatus = {})); + (function (TransactionStatusReason) { + TransactionStatusReason["CardAuthenticationFailed"] = "CARD_AUTHENTICATION_FAILED"; + TransactionStatusReason["UnknownDevice"] = "UNKNOWN_DEVICE"; + TransactionStatusReason["UnsupportedDevice"] = "UNSUPPORTED_DEVICE"; + TransactionStatusReason["ExceedsAuthenticationFrequencyLimit"] = "EXCEEDS_AUTHENTICATION_FREQUENCY_LIMIT"; + TransactionStatusReason["ExpiredCard"] = "EXPIRED_CARD"; + TransactionStatusReason["InvalidCardNumber"] = "INVALID_CARD_NUMBER"; + TransactionStatusReason["InvalidTransaction"] = "INVALID_TRANSACTION"; + TransactionStatusReason["NoCardRecord"] = "NO_CARD_RECORD"; + TransactionStatusReason["SecurityFailure"] = "SECURITY_FAILURE"; + TransactionStatusReason["StolenCard"] = "STOLEN_CARD"; + TransactionStatusReason["SuspectedFraud"] = "SUSPECTED_FRAUD"; + TransactionStatusReason["TransactionNotPermittedToCardholder"] = "TRANSACTION_NOT_PERMITTED_TO_CARDHOLDER"; + TransactionStatusReason["CardholderNotEnrolledInService"] = "CARDHOLDER_NOT_ENROLLED_IN_SERVICE"; + TransactionStatusReason["TransactionTimedOutAtTheAcs"] = "TRANSACTION_TIMED_OUT_AT_THE_ACS"; + TransactionStatusReason["LowConfidence"] = "LOW_CONFIDENCE"; + TransactionStatusReason["MediumConfidence"] = "MEDIUM_CONFIDENCE"; + TransactionStatusReason["HighConfidence"] = "HIGH_CONFIDENCE"; + TransactionStatusReason["VeryHighConfidence"] = "VERY_HIGH_CONFIDENCE"; + TransactionStatusReason["ExceedsAcsMaximumChallenges"] = "EXCEEDS_ACS_MAXIMUM_CHALLENGES"; + TransactionStatusReason["NonPaymentTransactionNotSupported"] = "NON_PAYMENT_TRANSACTION_NOT_SUPPORTED"; + TransactionStatusReason["ThreeriTransactionNotSupported"] = "THREERI_TRANSACTION_NOT_SUPPORTED"; + })(exports.TransactionStatusReason || (exports.TransactionStatusReason = {})); + function colorDepth(value) { + var result = ""; + switch (value) { + case 1: + return "ONE_BIT"; + case 2: + result += "TWO"; + break; + case 4: + result += "FOUR"; + break; + case 8: + result += "EIGHT"; + break; + case 15: + result += "FIFTEEN"; + break; + case 16: + result += "SIXTEEN"; + break; + case 24: + case 30: + result += "TWENTY_FOUR"; + break; + case 32: + result += "THIRTY_TWO"; + break; + case 48: + result += "FORTY_EIGHT"; + break; + default: + throw new Error("Unknown color depth '" + value + "'"); + } + return result + "_BITS"; + } + function dimensionsFromChallengeWindowSize(options) { + var height = 0; + var width = 0; + switch (options.size || options.windowSize) { + case exports.ChallengeWindowSize.Windowed250x400: + height = 250; + width = 400; + break; + case exports.ChallengeWindowSize.Windowed390x400: + height = 390; + width = 400; + break; + case exports.ChallengeWindowSize.Windowed500x600: + height = 500; + width = 600; + break; + case exports.ChallengeWindowSize.Windowed600x400: + height = 600; + width = 400; + break; + } + return { height: height, width: width }; + } + function messageCategoryFromAuthenticationRequestType(authenticationRequestType) { + switch (authenticationRequestType) { + case exports.AuthenticationRequestType.AddCard: + case exports.AuthenticationRequestType.CardholderVerification: + case exports.AuthenticationRequestType.MaintainCard: + return exports.MessageCategory.NonPayment; + case exports.AuthenticationRequestType.InstalmentTransaction: + case exports.AuthenticationRequestType.PaymentTransaction: + case exports.AuthenticationRequestType.RecurringTransaction: + default: + return exports.MessageCategory.Payment; + } } - var GPError = /** @class */ (function (_super) { - __extends(GPError, _super); - function GPError(reasons, message) { - var _this = _super.call(this, message || "Error: see `reasons` property") || this; - _this.error = true; - _this.reasons = reasons; - return _this; - } - return GPError; + var GPError = /** @class */ (function (_super) { + __extends(GPError, _super); + function GPError(reasons, message) { + var _this = _super.call(this, message || "Error: see `reasons` property") || this; + _this.error = true; + _this.reasons = reasons; + return _this; + } + return GPError; }(Error)); - function handleNotificationMessageEvent(event, data, origin) { - if (window.parent !== window) { - window.parent.postMessage({ data: data, event: event }, origin || window.location.origin); - } + function handleNotificationMessageEvent(event, data, origin) { + if (window.parent !== window) { + window.parent.postMessage({ data: data, event: event }, origin || window.location.origin); + } } - function makeRequest(endpoint, data) { - return __awaiter(this, void 0, void 0, function () { - var headers, rawResponse, _a, e_1, reasons; - var _b; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - headers = { - "Content-Type": "application/json", - }; - _c.label = 1; - case 1: - _c.trys.push([1, 6, , 7]); - return [4 /*yield*/, fetch(endpoint, { - body: JSON.stringify(data), - credentials: "omit", - headers: typeof Headers !== "undefined" ? new Headers(headers) : headers, - method: "POST", - })]; - case 2: - rawResponse = _c.sent(); - if (!!rawResponse.ok) return [3 /*break*/, 4]; - _a = GPError.bind; - _b = { - code: rawResponse.status.toString() - }; - return [4 /*yield*/, rawResponse.text()]; - case 3: throw new (_a.apply(GPError, [void 0, [ - (_b.message = _c.sent(), - _b) - ], rawResponse.statusText]))(); - case 4: return [4 /*yield*/, rawResponse.json()]; - case 5: return [2 /*return*/, _c.sent()]; - case 6: - e_1 = _c.sent(); - reasons = [{ code: e_1.name, message: e_1.message }]; - if (e_1.reasons) { - reasons = reasons.concat(e_1.reasons); - } - throw new GPError(reasons); - case 7: return [2 /*return*/]; - } - }); - }); + function makeRequest(endpoint, data) { + return __awaiter(this, void 0, void 0, function () { + var headers, rawResponse, _a, e_1, reasons; + var _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + headers = { + "Content-Type": "application/json", + }; + _c.label = 1; + case 1: + _c.trys.push([1, 6, , 7]); + return [4 /*yield*/, fetch(endpoint, { + body: JSON.stringify(data), + credentials: "omit", + headers: typeof Headers !== "undefined" ? new Headers(headers) : headers, + method: "POST", + })]; + case 2: + rawResponse = _c.sent(); + if (!!rawResponse.ok) return [3 /*break*/, 4]; + _a = GPError.bind; + _b = { + code: rawResponse.status.toString() + }; + return [4 /*yield*/, rawResponse.text()]; + case 3: throw new (_a.apply(GPError, [void 0, [ + (_b.message = _c.sent(), + _b) + ], rawResponse.statusText]))(); + case 4: return [4 /*yield*/, rawResponse.json()]; + case 5: return [2 /*return*/, _c.sent()]; + case 6: + e_1 = _c.sent(); + reasons = [{ code: e_1.name, message: e_1.message }]; + if (e_1.reasons) { + reasons = reasons.concat(e_1.reasons); + } + throw new GPError(reasons); + case 7: return [2 /*return*/]; + } + }); + }); } - // most of this module is pulled from the legacy Realex Payments JavaScript library - 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); - // For IOs/Android and small screen devices always open in new tab/window - // TODO: Confirm/implement once sandbox support is in place - var isMobileNewTab = !isWindowsMobileOs && (isAndroidOrIOs || isMobileXS); - // Display IFrame on WIndows Phone OS mobile devices - var isMobileIFrame = isWindowsMobileOs || isMobileNewTab; - var randomId = Math.random() - .toString(16) - .substr(2, 8); - function createLightbox(iFrame, options) { - // Create the overlay - var overlayElement = createOverlay(); - // Create the spinner - var spinner = createSpinner(); - document.body.appendChild(spinner); - var _a = dimensionsFromChallengeWindowSize(options), height = _a.height, width = _a.width; - // Configure the iframe - if (height) { - iFrame.setAttribute("height", height + "px"); - } - iFrame.setAttribute("frameBorder", "0"); - if (width) { - iFrame.setAttribute("width", width + "px"); - } - iFrame.setAttribute("seamless", "seamless"); - iFrame.style.zIndex = "10001"; - iFrame.style.position = "absolute"; - iFrame.style.transition = "transform 0.5s ease-in-out"; - iFrame.style.transform = "scale(0.7)"; - iFrame.style.opacity = "0"; - overlayElement.appendChild(iFrame); - if (isMobileIFrame || options.windowSize === exports.ChallengeWindowSize.FullScreen) { - iFrame.style.top = "0px"; - iFrame.style.bottom = "0px"; - iFrame.style.left = "0px"; - iFrame.style.marginLeft = "0px;"; - iFrame.style.width = "100%"; - iFrame.style.height = "100%"; - iFrame.style.minHeight = "100%"; - iFrame.style.WebkitTransform = "translate3d(0,0,0)"; - iFrame.style.transform = "translate3d(0, 0, 0)"; - var metaTag = document.createElement("meta"); - metaTag.name = "viewport"; - metaTag.content = - "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"; - document.getElementsByTagName("head")[0].appendChild(metaTag); - } - else { - iFrame.style.top = "40px"; - iFrame.style.left = "50%"; - iFrame.style.marginLeft = "-" + width / 2 + "px"; - } - iFrame.onload = getIFrameOnloadEventHandler(iFrame, spinner, overlayElement, options); - } - function closeModal() { - Array.prototype.slice.call(document - .querySelectorAll("[target$=\"-" + randomId + "\"],[id$=\"-" + randomId + "\"]")) - .forEach(function (element) { - if (element.parentNode) { - element.parentNode.removeChild(element); - } - }); - } - function createOverlay() { - var overlay = document.createElement("div"); - overlay.setAttribute("id", "GlobalPayments-overlay-" + randomId); - overlay.style.position = "fixed"; - overlay.style.width = "100%"; - overlay.style.height = "100%"; - overlay.style.top = "0"; - overlay.style.left = "0"; - overlay.style.transition = "all 0.3s ease-in-out"; - overlay.style.zIndex = "100"; - if (isMobileIFrame) { - overlay.style.position = "absolute !important"; - overlay.style.WebkitOverflowScrolling = "touch"; - overlay.style.overflowX = "hidden"; - overlay.style.overflowY = "scroll"; - } - document.body.appendChild(overlay); - setTimeout(function () { - overlay.style.background = "rgba(0, 0, 0, 0.7)"; - }, 1); - return overlay; - } - function createCloseButton(options) { - if (document.getElementById("GlobalPayments-frame-close-" + randomId) !== null) { - return; - } - var closeButton = document.createElement("img"); - closeButton.id = "GlobalPayments-frame-close-" + randomId; - closeButton.src = - // tslint:disable-next-line:max-line-length - "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUJFRjU1MEIzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUJFRjU1MEMzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQkVGNTUwOTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQkVGNTUwQTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHco5QAAAHpSURBVHjafFRdTsJAEF42JaTKn4glGIg++qgX4AAchHAJkiZcwnAQD8AF4NFHCaC2VgWkIQQsfl/jNJUik8Duzs/XmW9mN7Xb7VRc5vP5zWKxaK5Wq8Zmu72FqobfJG0YQ9M0+/l8/qFQKDzGY1JxENd1288vLy1s786KRZXJZCLber1Wn7MZt4PLarVnWdZ9AmQ8Hncc17UvymVdBMB/MgPQm+cFFcuy6/V6lzqDf57ntWGwYdBIVx0TfkBD6I9M35iRJgfIoAVjBLDZbA4CiJ5+9AdQi/EahibqDTkQx6fRSIHcPwA8Uy9A9Gcc47Xv+w2wzhRDYzqdVihLIbsIiCvP1NNOoX/29FQx3vgOgtt4FyRdCgPRarX4+goB9vkyAMh443cOEsIAAcjncuoI4TXWMAmCIGFhCQLAdZ8jym/cRJ+Y5nC5XCYAhINKpZLgSISZgoqh5iiLQrojAFICVwGS7tCfe5DbZzkP56XS4NVxwvTI/vXVVYIDnqmnnX70ZxzjNS8THHooK5hMpxHQIREA+tEfA9djfHR3MHkdx3Hspe9r3B+VzWaj2RESyR2mlCUE4MoGQDdxiwHURq2t94+PO9bMIYyTyDNLwMoM7g8+BfKeYGniyw2MdfSehF3Qmk1IvCc/AgwAaS86Etp38bUAAAAASUVORK5CYII="; - closeButton.style.transition = "all 0.5s ease-in-out"; - closeButton.style.opacity = "0"; - closeButton.style.float = "left"; - closeButton.style.position = "absolute"; - closeButton.style.left = "50%"; - closeButton.style.zIndex = "99999999"; - closeButton.style.top = "30px"; - var width = dimensionsFromChallengeWindowSize(options).width; - if (width) { - closeButton.style.marginLeft = width / 2 - 8 /* half image width */ + "px"; - } - setTimeout(function () { - closeButton.style.opacity = "1"; - }, 500); - if (isMobileIFrame || options.windowSize === exports.ChallengeWindowSize.FullScreen) { - closeButton.style.float = "right"; - closeButton.style.top = "20px"; - closeButton.style.left = "initial"; - closeButton.style.marginLeft = "0px"; - closeButton.style.right = "20px"; - } - return closeButton; - } - function createSpinner() { - var spinner = document.createElement("img"); - spinner.setAttribute("src", - // tslint:disable-next-line:max-line-length - "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iIzAwNzBiYSIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iI2MwZGNlZSIgZmlsbC1vcGFjaXR5PSIwLjI1IiB0cmFuc2Zvcm09InJvdGF0ZSgyMjUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMjcwIDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDMxNSA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzQ1IDY0IDY0OzkwIDY0IDY0OzEzNSA2NCA2NDsxODAgNjQgNjQ7MjI1IDY0IDY0OzI3MCA2NCA2NDszMTUgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMjgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="); - spinner.setAttribute("id", "GlobalPayments-loader-" + randomId); - spinner.style.left = "50%"; - spinner.style.position = "fixed"; - spinner.style.background = "#FFFFFF"; - spinner.style.borderRadius = "50%"; - spinner.style.width = "30px"; - spinner.style.zIndex = "200"; - spinner.style.marginLeft = "-15px"; - spinner.style.top = "120px"; - return spinner; - } - function getIFrameOnloadEventHandler(iFrame, spinner, overlayElement, options) { - return function () { - iFrame.style.opacity = "1"; - iFrame.style.transform = "scale(1)"; - iFrame.style.backgroundColor = "#ffffff"; - if (spinner.parentNode) { - spinner.parentNode.removeChild(spinner); - } - var closeButton; - closeButton = createCloseButton(options); - if (closeButton) { - overlayElement.appendChild(closeButton); - closeButton.addEventListener("click", function () { - if (closeButton) { - closeModal(); - } - }, true); - } - }; + // most of this module is pulled from the legacy Realex Payments JavaScript library + 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); + // For IOs/Android and small screen devices always open in new tab/window + // TODO: Confirm/implement once sandbox support is in place + var isMobileNewTab = !isWindowsMobileOs && (isAndroidOrIOs || isMobileXS); + // Display IFrame on WIndows Phone OS mobile devices + var isMobileIFrame = isWindowsMobileOs || isMobileNewTab; + var randomId = Math.random() + .toString(16) + .substr(2, 8); + function createLightbox(iFrame, options) { + // Create the overlay + var overlayElement = createOverlay(); + // Create the spinner + var spinner = createSpinner(); + document.body.appendChild(spinner); + var _a = dimensionsFromChallengeWindowSize(options), height = _a.height, width = _a.width; + // Configure the iframe + if (height) { + iFrame.setAttribute("height", height + "px"); + } + iFrame.setAttribute("frameBorder", "0"); + if (width) { + iFrame.setAttribute("width", width + "px"); + } + iFrame.setAttribute("seamless", "seamless"); + iFrame.style.zIndex = "10001"; + iFrame.style.position = "absolute"; + iFrame.style.transition = "transform 0.5s ease-in-out"; + iFrame.style.transform = "scale(0.7)"; + iFrame.style.opacity = "0"; + overlayElement.appendChild(iFrame); + if (isMobileIFrame || options.windowSize === exports.ChallengeWindowSize.FullScreen) { + iFrame.style.top = "0px"; + iFrame.style.bottom = "0px"; + iFrame.style.left = "0px"; + iFrame.style.marginLeft = "0px;"; + iFrame.style.width = "100%"; + iFrame.style.height = "100%"; + iFrame.style.minHeight = "100%"; + iFrame.style.WebkitTransform = "translate3d(0,0,0)"; + iFrame.style.transform = "translate3d(0, 0, 0)"; + var metaTag = document.createElement("meta"); + metaTag.name = "viewport"; + metaTag.content = + "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"; + document.getElementsByTagName("head")[0].appendChild(metaTag); + } + else { + iFrame.style.top = "40px"; + iFrame.style.left = "50%"; + iFrame.style.marginLeft = "-" + width / 2 + "px"; + } + iFrame.onload = getIFrameOnloadEventHandler(iFrame, spinner, overlayElement, options); + } + function closeModal() { + Array.prototype.slice.call(document + .querySelectorAll("[target$=\"-" + randomId + "\"],[id$=\"-" + randomId + "\"]")) + .forEach(function (element) { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + }); + } + function createOverlay() { + var overlay = document.createElement("div"); + overlay.setAttribute("id", "GlobalPayments-overlay-" + randomId); + overlay.style.position = "fixed"; + overlay.style.width = "100%"; + overlay.style.height = "100%"; + overlay.style.top = "0"; + overlay.style.left = "0"; + overlay.style.transition = "all 0.3s ease-in-out"; + overlay.style.zIndex = "100"; + if (isMobileIFrame) { + overlay.style.position = "absolute !important"; + overlay.style.WebkitOverflowScrolling = "touch"; + overlay.style.overflowX = "hidden"; + overlay.style.overflowY = "scroll"; + } + document.body.appendChild(overlay); + setTimeout(function () { + overlay.style.background = "rgba(0, 0, 0, 0.7)"; + }, 1); + return overlay; + } + function createCloseButton(options) { + if (document.getElementById("GlobalPayments-frame-close-" + randomId) !== null) { + return; + } + var closeButton = document.createElement("img"); + closeButton.id = "GlobalPayments-frame-close-" + randomId; + closeButton.src = + // tslint:disable-next-line:max-line-length + "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUJFRjU1MEIzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUJFRjU1MEMzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQkVGNTUwOTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQkVGNTUwQTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHco5QAAAHpSURBVHjafFRdTsJAEF42JaTKn4glGIg++qgX4AAchHAJkiZcwnAQD8AF4NFHCaC2VgWkIQQsfl/jNJUik8Duzs/XmW9mN7Xb7VRc5vP5zWKxaK5Wq8Zmu72FqobfJG0YQ9M0+/l8/qFQKDzGY1JxENd1288vLy1s786KRZXJZCLber1Wn7MZt4PLarVnWdZ9AmQ8Hncc17UvymVdBMB/MgPQm+cFFcuy6/V6lzqDf57ntWGwYdBIVx0TfkBD6I9M35iRJgfIoAVjBLDZbA4CiJ5+9AdQi/EahibqDTkQx6fRSIHcPwA8Uy9A9Gcc47Xv+w2wzhRDYzqdVihLIbsIiCvP1NNOoX/29FQx3vgOgtt4FyRdCgPRarX4+goB9vkyAMh443cOEsIAAcjncuoI4TXWMAmCIGFhCQLAdZ8jym/cRJ+Y5nC5XCYAhINKpZLgSISZgoqh5iiLQrojAFICVwGS7tCfe5DbZzkP56XS4NVxwvTI/vXVVYIDnqmnnX70ZxzjNS8THHooK5hMpxHQIREA+tEfA9djfHR3MHkdx3Hspe9r3B+VzWaj2RESyR2mlCUE4MoGQDdxiwHURq2t94+PO9bMIYyTyDNLwMoM7g8+BfKeYGniyw2MdfSehF3Qmk1IvCc/AgwAaS86Etp38bUAAAAASUVORK5CYII="; + closeButton.style.transition = "all 0.5s ease-in-out"; + closeButton.style.opacity = "0"; + closeButton.style.float = "left"; + closeButton.style.position = "absolute"; + closeButton.style.left = "50%"; + closeButton.style.zIndex = "99999999"; + closeButton.style.top = "30px"; + var width = dimensionsFromChallengeWindowSize(options).width; + if (width) { + closeButton.style.marginLeft = width / 2 - 8 /* half image width */ + "px"; + } + setTimeout(function () { + closeButton.style.opacity = "1"; + }, 500); + if (isMobileIFrame || options.windowSize === exports.ChallengeWindowSize.FullScreen) { + closeButton.style.float = "right"; + closeButton.style.top = "20px"; + closeButton.style.left = "initial"; + closeButton.style.marginLeft = "0px"; + closeButton.style.right = "20px"; + } + return closeButton; + } + function createSpinner() { + var spinner = document.createElement("img"); + spinner.setAttribute("src", + // tslint:disable-next-line:max-line-length + "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iIzAwNzBiYSIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iI2MwZGNlZSIgZmlsbC1vcGFjaXR5PSIwLjI1IiB0cmFuc2Zvcm09InJvdGF0ZSgyMjUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMjcwIDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDMxNSA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzQ1IDY0IDY0OzkwIDY0IDY0OzEzNSA2NCA2NDsxODAgNjQgNjQ7MjI1IDY0IDY0OzI3MCA2NCA2NDszMTUgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMjgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="); + spinner.setAttribute("id", "GlobalPayments-loader-" + randomId); + spinner.style.left = "50%"; + spinner.style.position = "fixed"; + spinner.style.background = "#FFFFFF"; + spinner.style.borderRadius = "50%"; + spinner.style.width = "30px"; + spinner.style.zIndex = "200"; + spinner.style.marginLeft = "-15px"; + spinner.style.top = "120px"; + return spinner; + } + function getIFrameOnloadEventHandler(iFrame, spinner, overlayElement, options) { + return function () { + iFrame.style.opacity = "1"; + iFrame.style.transform = "scale(1)"; + iFrame.style.backgroundColor = "#ffffff"; + if (spinner.parentNode) { + spinner.parentNode.removeChild(spinner); + } + var closeButton; + closeButton = createCloseButton(options); + if (closeButton) { + overlayElement.appendChild(closeButton); + closeButton.addEventListener("click", function () { + if (closeButton) { + closeModal(); + } + }, true); + } + }; } - function postToIframe(endpoint, fields, options) { - return new Promise(function (resolve, reject) { - var timeout; - if (options.timeout) { - timeout = setTimeout(function () { - ensureIframeClosed(timeout); - reject(new Error("timeout reached")); - }, options.timeout); - } - var iframe = document.createElement("iframe"); - iframe.id = iframe.name = "GlobalPayments-3DSecure-" + randomId; - iframe.style.display = options.hide ? "none" : "inherit"; - var form = createForm(endpoint, iframe.id, fields); - switch (options.displayMode) { - case "redirect": - // TODO: Add redirect support once sandbox environment respects configured - // challengeNotificationUrl instead of hardcoded value - ensureIframeClosed(timeout); - reject(new Error("Not implemented")); - return; - case "lightbox": - createLightbox(iframe, options); - break; - case "embedded": - default: - if (!handleEmbeddedIframe(reject, { iframe: iframe, timeout: timeout }, options)) { - // rejected - return; - } - break; - } - window.addEventListener("message", getWindowMessageEventHandler(resolve, { - origin: options.origin, - timeout: timeout, - })); - document.body.appendChild(form); - form.submit(); - }); - } - function createForm(action, target, fields) { - var form = document.createElement("form"); - form.setAttribute("method", "POST"); - form.setAttribute("action", action); - form.setAttribute("target", target); - for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) { - var field = fields_1[_i]; - var input = document.createElement("input"); - input.setAttribute("type", "hidden"); - input.setAttribute("name", field.name); - input.setAttribute("value", field.value); - form.appendChild(input); - } - return form; - } - function ensureIframeClosed(timeout) { - if (timeout) { - clearTimeout(timeout); - } - try { - Array.prototype.slice.call(document - .querySelectorAll("[target$=\"-" + randomId + "\"],[id$=\"-" + randomId + "\"]")) - .forEach(function (element) { - if (element.parentNode) { - element.parentNode.removeChild(element); - } - }); - } - catch (e) { - /** */ - } - } - function getWindowMessageEventHandler(resolve, data) { - return function (e) { - var origin = data.origin || window.location.origin; - if (origin !== e.origin) { - return; - } - ensureIframeClosed(data.timeout || 0); - resolve(e.data); - }; - } - function handleEmbeddedIframe(reject, data, options) { - var targetElement; - if (options.hide) { - targetElement = document.body; - } - else if (typeof options.target === "string") { - targetElement = document.querySelector(options.target); - } - else { - targetElement = options.target; - } - if (!targetElement) { - ensureIframeClosed(data.timeout || 0); - reject(new Error("Embed target not found")); - return false; - } - var _a = dimensionsFromChallengeWindowSize(options), height = _a.height, width = _a.width; - if (data.iframe) { - data.iframe.setAttribute("height", height ? height + "px" : "100%"); - data.iframe.setAttribute("width", width ? width + "px" : "100%"); - targetElement.appendChild(data.iframe); - } - return true; + function postToIframe(endpoint, fields, options) { + return new Promise(function (resolve, reject) { + var timeout; + if (options.timeout) { + timeout = setTimeout(function () { + ensureIframeClosed(timeout); + reject(new Error("timeout reached")); + }, options.timeout); + } + var iframe = document.createElement("iframe"); + iframe.id = iframe.name = "GlobalPayments-3DSecure-" + randomId; + iframe.style.display = options.hide ? "none" : "inherit"; + var form = createForm(endpoint, iframe.id, fields); + switch (options.displayMode) { + case "redirect": + // TODO: Add redirect support once sandbox environment respects configured + // challengeNotificationUrl instead of hardcoded value + ensureIframeClosed(timeout); + reject(new Error("Not implemented")); + return; + case "lightbox": + createLightbox(iframe, options); + break; + case "embedded": + default: + if (!handleEmbeddedIframe(reject, { iframe: iframe, timeout: timeout }, options)) { + // rejected + return; + } + break; + } + window.addEventListener("message", getWindowMessageEventHandler(resolve, { + origin: options.origin, + timeout: timeout, + })); + document.body.appendChild(form); + form.submit(); + }); + } + function createForm(action, target, fields) { + var form = document.createElement("form"); + form.setAttribute("method", "POST"); + form.setAttribute("action", action); + form.setAttribute("target", target); + for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) { + var field = fields_1[_i]; + var input = document.createElement("input"); + input.setAttribute("type", "hidden"); + input.setAttribute("name", field.name); + input.setAttribute("value", field.value); + form.appendChild(input); + } + return form; + } + function ensureIframeClosed(timeout) { + if (timeout) { + clearTimeout(timeout); + } + try { + Array.prototype.slice.call(document + .querySelectorAll("[target$=\"-" + randomId + "\"],[id$=\"-" + randomId + "\"]")) + .forEach(function (element) { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + }); + } + catch (e) { + /** */ + } + } + function getWindowMessageEventHandler(resolve, data) { + return function (e) { + var origin = data.origin || window.location.origin; + if (origin !== e.origin) { + return; + } + ensureIframeClosed(data.timeout || 0); + resolve(e.data); + }; + } + function handleEmbeddedIframe(reject, data, options) { + var targetElement; + if (options.hide) { + targetElement = document.body; + } + else if (typeof options.target === "string") { + targetElement = document.querySelector(options.target); + } + else { + targetElement = options.target; + } + if (!targetElement) { + ensureIframeClosed(data.timeout || 0); + reject(new Error("Embed target not found")); + return false; + } + var _a = dimensionsFromChallengeWindowSize(options), height = _a.height, width = _a.width; + if (data.iframe) { + data.iframe.setAttribute("height", height ? height + "px" : "100%"); + data.iframe.setAttribute("width", width ? width + "px" : "100%"); + targetElement.appendChild(data.iframe); + } + return true; } - /** - * Retrieves client browser runtime data. - */ - function getBrowserData() { - var now = new Date(); - return { - colorDepth: screen && colorDepth(screen.colorDepth), - javaEnabled: navigator && navigator.javaEnabled(), - javascriptEnabled: true, - language: navigator && navigator.language, - screenHeight: screen && screen.height, - screenWidth: screen && screen.width, - time: now, - timezoneOffset: now.getTimezoneOffset() / 60, - userAgent: navigator && navigator.userAgent, - }; - } - /** - * Facilitates backend request to merchant integration to check the enrolled 3DS version for the consumer's card. - * - * @param endpoint Merchant integration endpoint responsible for performing the version check - * @param data Request data to aid in version check request - * @throws When an error occurred during the request - */ - function checkVersion(endpoint, data) { - return __awaiter(this, void 0, void 0, function () { - var response, e_1, reasons; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - data = data || {}; - _a.label = 1; - case 1: - _a.trys.push([1, 4, , 5]); - return [4 /*yield*/, makeRequest(endpoint, data)]; - case 2: - response = (_a.sent()); - return [4 /*yield*/, handle3dsVersionCheck(response, data.methodWindow)]; - case 3: return [2 /*return*/, _a.sent()]; - case 4: - e_1 = _a.sent(); - reasons = [{ code: e_1.name, message: e_1.message }]; - if (e_1.reasons) { - reasons = reasons.concat(e_1.reasons); - } - throw new GPError(reasons); - case 5: return [2 /*return*/]; - } - }); - }); - } - /** - * Facilitates backend request to merchant integration to initiate 3DS 2.0 authentication workflows with the consumer. - * - * @param endpoint Merchant integration endpoint responsible for initiating the authentication request - * @param data Request data to aid in initiating authentication - * @throws When an error occurred during the request - */ - function initiateAuthentication(endpoint, data) { - return __awaiter(this, void 0, void 0, function () { - var response, e_2, reasons; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 3, , 4]); - data.authenticationSource = - data.authenticationSource || exports.AuthenticationSource.Browser; - data.authenticationRequestType = - data.authenticationRequestType || - exports.AuthenticationRequestType.PaymentTransaction; - data.messageCategory = - data.messageCategory || - messageCategoryFromAuthenticationRequestType(data.authenticationRequestType); - data.challengeRequestIndicator = - data.challengeRequestIndicator || exports.ChallengeRequestIndicator.NoPreference; - // still needs ip address and accept header from server-side - data.browserData = data.browserData || getBrowserData(); - return [4 /*yield*/, makeRequest(endpoint, data)]; - case 1: - response = (_a.sent()); - return [4 /*yield*/, handleInitiateAuthentication(response, data.challengeWindow)]; - case 2: return [2 /*return*/, _a.sent()]; - case 3: - e_2 = _a.sent(); - reasons = [{ code: e_2.name, message: e_2.message }]; - if (e_2.reasons) { - reasons = reasons.concat(e_2.reasons); - } - throw new GPError(reasons); - case 4: return [2 /*return*/]; - } - }); - }); - } - /** - * Handles response from merchant integration endpoint for the version check request. When a card is enrolled and a - * method URL is present, a hidden iframe to the method URL will be created to handle device fingerprinting - * requirements. - * - * @param data Version check data from merchant integration endpoint - * @param options Configuration options for the method window - * @throws When a card is not enrolled - */ - function handle3dsVersionCheck(data, options) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!data.enrolled) { - throw new Error("Card not enrolled"); - } - options = options || {}; - options.hide = typeof options.hide === "undefined" ? true : options.hide; - options.timeout = - typeof options.timeout === "undefined" ? 30 * 1000 : options.timeout; - if (!data.methodUrl) return [3 /*break*/, 2]; - return [4 /*yield*/, postToIframe(data.methodUrl, [{ name: "threeDSMethodData", value: data.methodData }], options)]; - case 1: - _a.sent(); - _a.label = 2; - case 2: return [2 /*return*/, data]; - } - }); - }); - } - /** - * Handles response from merchant integration endpoint for initiating 3DS 2.0 authentication flows with consumer. If a - * challenge is mandated, an iframe will be created for the issuer's necessary challenge URL. - * - * @param data Initiate authentication data from merchant integration endpoint - * @param options Configuration options for the challenge window - * @throws When a challenge is mandated but no challenge URL was supplied - * @throws When an error occurred during the challenge request - */ - function handleInitiateAuthentication(data, options) { - return __awaiter(this, void 0, void 0, function () { - var response; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!(data.challengeMandated || data.status === exports.TransactionStatus.ChallengeRequired)) return [3 /*break*/, 2]; - data.challenge = data.challenge || {}; - if (!data.challenge.requestUrl) { - throw new Error("Invalid challenge state. Missing challenge URL"); - } - return [4 /*yield*/, postToIframe(data.challenge.requestUrl, [ - { name: "creq", value: data.challenge.encodedChallengeRequest }, - ], options)]; - case 1: - response = _a.sent(); - data.challenge.response = response; - _a.label = 2; - case 2: return [2 /*return*/, data]; - } - }); - }); - } - /** - * Assists with notifying the parent window of challenge status - * - * @param data Raw data from the challenge notification - * @param origin Target origin for the message. Default is `window.location.origin`. - */ - function handleChallengeNotification(data, origin) { - handleNotificationMessageEvent("challengeNotification", data, origin); - } - /** - * Assists with notifying the parent window of method status - * - * @param data Raw data from the method notification - * @param origin Target origin for the message. Default is `window.location.origin`. - */ - function handleMethodNotification(data, origin) { - handleNotificationMessageEvent("methodNotification", data, origin); + /** + * Retrieves client browser runtime data. + */ + function getBrowserData() { + var now = new Date(); + return { + colorDepth: screen && colorDepth(screen.colorDepth), + javaEnabled: navigator && navigator.javaEnabled(), + javascriptEnabled: true, + language: navigator && navigator.language, + screenHeight: screen && screen.height, + screenWidth: screen && screen.width, + time: now, + timezoneOffset: now.getTimezoneOffset() / 60, + userAgent: navigator && navigator.userAgent, + }; + } + /** + * Facilitates backend request to merchant integration to check the enrolled 3DS version for the consumer's card. + * + * @param endpoint Merchant integration endpoint responsible for performing the version check + * @param data Request data to aid in version check request + * @throws When an error occurred during the request + */ + function checkVersion(endpoint, data) { + return __awaiter(this, void 0, void 0, function () { + var response, e_1, reasons; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + data = data || {}; + _a.label = 1; + case 1: + _a.trys.push([1, 4, , 5]); + return [4 /*yield*/, makeRequest(endpoint, data)]; + case 2: + response = (_a.sent()); + if (response.version === "ONE") { + return [4 /*yield*/, handle3ds1VersionCheck(response, data.challengeWindow)]; + } + return [4 /*yield*/, handle3dsVersionCheck(response, data.methodWindow)]; + case 3: return [2 /*return*/, _a.sent()]; + case 4: + e_1 = _a.sent(); + reasons = [{ code: e_1.name, message: e_1.message }]; + if (e_1.reasons) { + reasons = reasons.concat(e_1.reasons); + } + throw new GPError(reasons); + case 5: return [2 /*return*/]; + } + }); + }); + } + /** + * Facilitates backend request to merchant integration to initiate 3DS 2.0 authentication workflows with the consumer. + * + * @param endpoint Merchant integration endpoint responsible for initiating the authentication request + * @param data Request data to aid in initiating authentication + * @throws When an error occurred during the request + */ + function initiateAuthentication(endpoint, data) { + return __awaiter(this, void 0, void 0, function () { + var response, e_2, reasons; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 3, , 4]); + data.authenticationSource = + data.authenticationSource || exports.AuthenticationSource.Browser; + data.authenticationRequestType = + data.authenticationRequestType || + exports.AuthenticationRequestType.PaymentTransaction; + data.messageCategory = + data.messageCategory || + messageCategoryFromAuthenticationRequestType(data.authenticationRequestType); + data.challengeRequestIndicator = + data.challengeRequestIndicator || exports.ChallengeRequestIndicator.NoPreference; + // still needs ip address and accept header from server-side + data.browserData = data.browserData || getBrowserData(); + return [4 /*yield*/, makeRequest(endpoint, data)]; + case 1: + response = (_a.sent()); + return [4 /*yield*/, handleInitiateAuthentication(response, data.challengeWindow)]; + case 2: return [2 /*return*/, _a.sent()]; + case 3: + e_2 = _a.sent(); + reasons = [{ code: e_2.name, message: e_2.message }]; + if (e_2.reasons) { + reasons = reasons.concat(e_2.reasons); + } + throw new GPError(reasons); + case 4: return [2 /*return*/]; + } + }); + }); + } + /** + * Handles response from merchant integration endpoint for the version check request. When a card is enrolled and a + * method URL is present, a hidden iframe to the method URL will be created to handle device fingerprinting + * requirements. + * + * @param data Version check data from merchant integration endpoint + * @param options Configuration options for the method window + * @throws When a card is not enrolled + */ + function handle3dsVersionCheck(data, options) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!data.enrolled) { + throw new Error("Card not enrolled"); + } + options = options || {}; + options.hide = typeof options.hide === "undefined" ? true : options.hide; + options.timeout = + typeof options.timeout === "undefined" ? 30 * 1000 : options.timeout; + if (!data.methodUrl) return [3 /*break*/, 2]; + return [4 /*yield*/, postToIframe(data.methodUrl, [{ name: "threeDSMethodData", value: data.methodData }], options)]; + case 1: + _a.sent(); + _a.label = 2; + case 2: return [2 /*return*/, data]; + } + }); + }); + } + /** + * Handles response from merchant integration endpoint for the version check request. When a card is enrolled in 3DS1, a + * challenge is mandated and an iframe will be created for the issuer's necessary challenge URL. + * + * @param data Version check data from merchant integration endpoint + * @param options Configuration options for the challenge window + * @throws When a card is not enrolled + */ + function handle3ds1VersionCheck(data, options) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(data.challengeMandated || data.status === exports.TransactionStatus.ChallengeRequired)) return [3 /*break*/, 2]; + data.challenge = data.challenge || {}; + if (!data.challenge.requestUrl) { + throw new Error("Invalid challenge state. Missing challenge URL"); + } + return [4 /*yield*/, postToIframe( + data.challenge.requestUrl, + [ + { name: "TermUrl", value: data.TermUrl }, + { name: "MD", value: data.serverTransactionId }, + { name: "PaReq", value: data.challenge.encodedChallengeRequest }, + ], + options)]; + case 1: + response = _a.sent(); + data.challenge.response = response; + _a.label = 2; + case 2: return [2 /*return*/, data]; + } + }); + }); + } + /** + * Handles response from merchant integration endpoint for initiating 3DS 2.0 authentication flows with consumer. If a + * challenge is mandated, an iframe will be created for the issuer's necessary challenge URL. + * + * @param data Initiate authentication data from merchant integration endpoint + * @param options Configuration options for the challenge window + * @throws When a challenge is mandated but no challenge URL was supplied + * @throws When an error occurred during the challenge request + */ + function handleInitiateAuthentication(data, options) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(data.challengeMandated || data.status === exports.TransactionStatus.ChallengeRequired)) return [3 /*break*/, 2]; + data.challenge = data.challenge || {}; + if (!data.challenge.requestUrl) { + throw new Error("Invalid challenge state. Missing challenge URL"); + } + return [4 /*yield*/, postToIframe(data.challenge.requestUrl, [ + { name: "creq", value: data.challenge.encodedChallengeRequest }, + ], options)]; + case 1: + response = _a.sent(); + data.challenge.response = response; + _a.label = 2; + case 2: return [2 /*return*/, data]; + } + }); + }); + } + /** + * Assists with notifying the parent window of challenge status + * + * @param data Raw data from the challenge notification + * @param origin Target origin for the message. Default is `window.location.origin`. + */ + function handleChallengeNotification(data, origin) { + handleNotificationMessageEvent("challengeNotification", data, origin); + } + /** + * Assists with notifying the parent window of method status + * + * @param data Raw data from the method notification + * @param origin Target origin for the message. Default is `window.location.origin`. + */ + function handleMethodNotification(data, origin) { + handleNotificationMessageEvent("methodNotification", data, origin); } exports.checkVersion = checkVersion; diff --git a/assets/frontend/js/globalpayments-3ds.min.js b/assets/frontend/js/globalpayments-3ds.min.js index e30f385..74d8282 100644 --- a/assets/frontend/js/globalpayments-3ds.min.js +++ b/assets/frontend/js/globalpayments-3ds.min.js @@ -1,16 +1,16 @@ -this.GlobalPayments=this.GlobalPayments||{},this.GlobalPayments.ThreeDSecure=function(e){"use strict"; -/*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */var t,n,i,a,r,o,s,c,l=function(e,t){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function u(e,t,n,i){return new(n||(n=Promise))((function(a,r){function o(e){try{c(i.next(e))}catch(e){r(e)}}function s(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?a(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,s)}c((i=i.apply(e,t||[])).next())}))}function d(e,t){var n,i,a,r,o={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return r={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(r[Symbol.iterator]=function(){return this}),r;function s(r){return function(s){return function(r){if(n)throw new TypeError("Generator is already executing.");for(;o;)try{if(n=1,i&&(a=2&r[0]?i.return:r[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,r[1])).done)return a;switch(i=0,a&&(r=[2&r[0],a.value]),r[0]){case 0:case 1:a=r;break;case 4:return o.label++,{value:r[1],done:!1};case 5:o.label++,i=r[1],r=[0];continue;case 7:r=o.ops.pop(),o.trys.pop();continue;default:if(!(a=o.trys,(a=a.length>0&&a[a.length-1])||6!==r[0]&&2!==r[0])){o=0;continue}if(3===r[0]&&(!a||r[1]>a[0]&&r[1]0?window.innerWidth:screen.width)<=360||(window.innerHeight>0?window.innerHeight:screen.height)<=360,E=M||!M&&(m||T),C=Math.random().toString(16).substr(2,8);function w(t,n){var i=function(){var e=document.createElement("div");e.setAttribute("id","GlobalPayments-overlay-"+C),e.style.position="fixed",e.style.width="100%",e.style.height="100%",e.style.top="0",e.style.left="0",e.style.transition="all 0.3s ease-in-out",e.style.zIndex="100",E&&(e.style.position="absolute !important",e.style.WebkitOverflowScrolling="touch",e.style.overflowX="hidden",e.style.overflowY="scroll");return document.body.appendChild(e),setTimeout((function(){e.style.background="rgba(0, 0, 0, 0.7)"}),1),e}(),a=function(){var e=document.createElement("img");return e.setAttribute("src","data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iIzAwNzBiYSIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iI2MwZGNlZSIgZmlsbC1vcGFjaXR5PSIwLjI1IiB0cmFuc2Zvcm09InJvdGF0ZSgyMjUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMjcwIDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDMxNSA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzQ1IDY0IDY0OzkwIDY0IDY0OzEzNSA2NCA2NDsxODAgNjQgNjQ7MjI1IDY0IDY0OzI3MCA2NCA2NDszMTUgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMjgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="),e.setAttribute("id","GlobalPayments-loader-"+C),e.style.left="50%",e.style.position="fixed",e.style.background="#FFFFFF",e.style.borderRadius="50%",e.style.width="30px",e.style.zIndex="200",e.style.marginLeft="-15px",e.style.top="120px",e}();document.body.appendChild(a);var r=h(n),o=r.height,s=r.width;if(o&&t.setAttribute("height",o+"px"),t.setAttribute("frameBorder","0"),s&&t.setAttribute("width",s+"px"),t.setAttribute("seamless","seamless"),t.style.zIndex="10001",t.style.position="absolute",t.style.transition="transform 0.5s ease-in-out",t.style.transform="scale(0.7)",t.style.opacity="0",i.appendChild(t),E||n.windowSize===e.ChallengeWindowSize.FullScreen){t.style.top="0px",t.style.bottom="0px",t.style.left="0px",t.style.marginLeft="0px;",t.style.width="100%",t.style.height="100%",t.style.minHeight="100%",t.style.WebkitTransform="translate3d(0,0,0)",t.style.transform="translate3d(0, 0, 0)";var c=document.createElement("meta");c.name="viewport",c.content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0",document.getElementsByTagName("head")[0].appendChild(c)}else t.style.top="40px",t.style.left="50%",t.style.marginLeft="-"+s/2+"px";t.onload=function(t,n,i,a){return function(){var r;t.style.opacity="1",t.style.transform="scale(1)",t.style.backgroundColor="#ffffff",n.parentNode&&n.parentNode.removeChild(n),(r=function(t){if(null!==document.getElementById("GlobalPayments-frame-close-"+C))return;var n=document.createElement("img");n.id="GlobalPayments-frame-close-"+C,n.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUJFRjU1MEIzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUJFRjU1MEMzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQkVGNTUwOTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQkVGNTUwQTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHco5QAAAHpSURBVHjafFRdTsJAEF42JaTKn4glGIg++qgX4AAchHAJkiZcwnAQD8AF4NFHCaC2VgWkIQQsfl/jNJUik8Duzs/XmW9mN7Xb7VRc5vP5zWKxaK5Wq8Zmu72FqobfJG0YQ9M0+/l8/qFQKDzGY1JxENd1288vLy1s786KRZXJZCLber1Wn7MZt4PLarVnWdZ9AmQ8Hncc17UvymVdBMB/MgPQm+cFFcuy6/V6lzqDf57ntWGwYdBIVx0TfkBD6I9M35iRJgfIoAVjBLDZbA4CiJ5+9AdQi/EahibqDTkQx6fRSIHcPwA8Uy9A9Gcc47Xv+w2wzhRDYzqdVihLIbsIiCvP1NNOoX/29FQx3vgOgtt4FyRdCgPRarX4+goB9vkyAMh443cOEsIAAcjncuoI4TXWMAmCIGFhCQLAdZ8jym/cRJ+Y5nC5XCYAhINKpZLgSISZgoqh5iiLQrojAFICVwGS7tCfe5DbZzkP56XS4NVxwvTI/vXVVYIDnqmnnX70ZxzjNS8THHooK5hMpxHQIREA+tEfA9djfHR3MHkdx3Hspe9r3B+VzWaj2RESyR2mlCUE4MoGQDdxiwHURq2t94+PO9bMIYyTyDNLwMoM7g8+BfKeYGniyw2MdfSehF3Qmk1IvCc/AgwAaS86Etp38bUAAAAASUVORK5CYII=",n.style.transition="all 0.5s ease-in-out",n.style.opacity="0",n.style.float="left",n.style.position="absolute",n.style.left="50%",n.style.zIndex="99999999",n.style.top="30px";var i=h(t).width;i&&(n.style.marginLeft=i/2-8+"px");setTimeout((function(){n.style.opacity="1"}),500),(E||t.windowSize===e.ChallengeWindowSize.FullScreen)&&(n.style.float="right",n.style.top="20px",n.style.left="initial",n.style.marginLeft="0px",n.style.right="20px");return n}(a))&&(i.appendChild(r),r.addEventListener("click",(function(){r&&Array.prototype.slice.call(document.querySelectorAll('[target$="-'+C+'"],[id$="-'+C+'"]')).forEach((function(e){e.parentNode&&e.parentNode.removeChild(e)}))}),!0))}}(t,a,i,n)}function b(e,t,n){return new Promise((function(i,a){var r;n.timeout&&(r=setTimeout((function(){p(r),a(new Error("timeout reached"))}),n.timeout));var o=document.createElement("iframe");o.id=o.name="GlobalPayments-3DSecure-"+C,o.style.display=n.hide?"none":"inherit";var s=function(e,t,n){var i=document.createElement("form");i.setAttribute("method","POST"),i.setAttribute("action",e),i.setAttribute("target",t);for(var a=0,r=n;a0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0?o-4:o;for(n=0;n>16&255,a[c++]=t>>8&255,a[c++]=255&t;2===i&&(t=l[e.charCodeAt(n)]<<2|l[e.charCodeAt(n+1)]>>4,a[c++]=255&t);1===i&&(t=l[e.charCodeAt(n)]<<10|l[e.charCodeAt(n+1)]<<4|l[e.charCodeAt(n+2)]>>2,a[c++]=t>>8&255,a[c++]=255&t);return a},u=function(e){for(var t,n=e.length,r=n%3,o=[],i=0,a=n-r;ia?a:i+16383));1===r?(t=e[n-1],o.push(s[t>>2]+s[t<<4&63]+"==")):2===r&&(t=(e[n-2]<<8)+e[n-1],o.push(s[t>>10]+s[t>>4&63]+s[t<<2&63]+"="));return o.join("")},s=[],l=[],d="undefined"!=typeof Uint8Array?Uint8Array:Array,f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",h=0,y=f.length;h0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function g(e,t,n){for(var r,o,i=[],a=t;a>18&63]+s[o>>12&63]+s[o>>6&63]+s[63&o]);return i.join("")}l["-".charCodeAt(0)]=62,l["_".charCodeAt(0)]=63;var A,I={byteLength:a,toByteArray:c,fromByteArray:u},m=(o(function(e,t){function n(e){var t,n=e.length,r=new("undefined"!=typeof Uint8Array?Uint8Array:Array)(n);for(t=0;t0?window.innerWidth:screen.width)<=360||(window.innerHeight>0?window.innerHeight:screen.height)<=360,V=k||!k&&(J||B),Q=Math.random().toString(16).substr(2,8);function q(t,n){var r=function(){var e=document.createElement("div");e.setAttribute("id","GlobalPayments-overlay-"+Q),e.style.position="fixed",e.style.width="100%",e.style.height="100%",e.style.top="0",e.style.left="0",e.style.transition="all 0.3s ease-in-out",e.style.zIndex="100",V&&(e.style.position="absolute !important",e.style.WebkitOverflowScrolling="touch",e.style.overflowX="hidden",e.style.overflowY="scroll");return document.body.appendChild(e),setTimeout(function(){e.style.background="rgba(0, 0, 0, 0.7)"},1),e}(),o=function(){var e=document.createElement("img");return e.setAttribute("src","data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iIzAwNzBiYSIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNMzguNTIgMzMuMzdMMjEuMzYgMTYuMkE2My42IDYzLjYgMCAwIDEgNTkuNS4xNnYyNC4zYTM5LjUgMzkuNSAwIDAgMC0yMC45OCA4LjkyeiIgZmlsbD0iI2MwZGNlZSIgZmlsbC1vcGFjaXR5PSIwLjI1IiB0cmFuc2Zvcm09InJvdGF0ZSgyMjUgNjQgNjQpIi8+PHBhdGggZD0iTTM4LjUyIDMzLjM3TDIxLjM2IDE2LjJBNjMuNiA2My42IDAgMCAxIDU5LjUuMTZ2MjQuM2EzOS41IDM5LjUgMCAwIDAtMjAuOTggOC45MnoiIGZpbGw9IiNjMGRjZWUiIGZpbGwtb3BhY2l0eT0iMC4yNSIgdHJhbnNmb3JtPSJyb3RhdGUoMjcwIDY0IDY0KSIvPjxwYXRoIGQ9Ik0zOC41MiAzMy4zN0wyMS4zNiAxNi4yQTYzLjYgNjMuNiAwIDAgMSA1OS41LjE2djI0LjNhMzkuNSAzOS41IDAgMCAwLTIwLjk4IDguOTJ6IiBmaWxsPSIjYzBkY2VlIiBmaWxsLW9wYWNpdHk9IjAuMjUiIHRyYW5zZm9ybT0icm90YXRlKDMxNSA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzQ1IDY0IDY0OzkwIDY0IDY0OzEzNSA2NCA2NDsxODAgNjQgNjQ7MjI1IDY0IDY0OzI3MCA2NCA2NDszMTUgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMjgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="),e.setAttribute("id","GlobalPayments-loader-"+Q),e.style.left="50%",e.style.position="fixed",e.style.background="#FFFFFF",e.style.borderRadius="50%",e.style.width="30px",e.style.zIndex="200",e.style.marginLeft="-15px",e.style.top="120px",e}();document.body.appendChild(o);var i=Z(n),a=i.height,c=i.width;if(a&&t.setAttribute("height",a+"px"),t.setAttribute("frameBorder","0"),c&&t.setAttribute("width",c+"px"),t.setAttribute("seamless","seamless"),t.style.zIndex="10001",t.style.position="absolute",t.style.transition="transform 0.5s ease-in-out",t.style.transform="scale(0.7)",t.style.opacity="0",r.appendChild(t),V||n.windowSize===e.ChallengeWindowSize.FullScreen){t.style.top="0px",t.style.bottom="0px",t.style.left="0px",t.style.marginLeft="0px;",t.style.width="100%",t.style.height="100%",t.style.minHeight="100%",t.style.WebkitTransform="translate3d(0,0,0)",t.style.transform="translate3d(0, 0, 0)";var u=document.createElement("meta");u.name="viewport",u.content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0",document.getElementsByTagName("head")[0].appendChild(u)}else t.style.top="40px",t.style.left="50%",t.style.marginLeft="-"+c/2+"px";t.onload=function(t,n,r,o){return function(){var i;t.style.opacity="1",t.style.transform="scale(1)",t.style.backgroundColor="#ffffff",n.parentNode&&n.parentNode.removeChild(n),(i=function(t){if(null!==document.getElementById("GlobalPayments-frame-close-"+Q))return;var n=document.createElement("img");n.id="GlobalPayments-frame-close-"+Q,n.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUJFRjU1MEIzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUJFRjU1MEMzMUQ3MTFFNThGQjNERjg2NEZCRjFDOTUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQkVGNTUwOTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQkVGNTUwQTMxRDcxMUU1OEZCM0RGODY0RkJGMUM5NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlHco5QAAAHpSURBVHjafFRdTsJAEF42JaTKn4glGIg++qgX4AAchHAJkiZcwnAQD8AF4NFHCaC2VgWkIQQsfl/jNJUik8Duzs/XmW9mN7Xb7VRc5vP5zWKxaK5Wq8Zmu72FqobfJG0YQ9M0+/l8/qFQKDzGY1JxENd1288vLy1s786KRZXJZCLber1Wn7MZt4PLarVnWdZ9AmQ8Hncc17UvymVdBMB/MgPQm+cFFcuy6/V6lzqDf57ntWGwYdBIVx0TfkBD6I9M35iRJgfIoAVjBLDZbA4CiJ5+9AdQi/EahibqDTkQx6fRSIHcPwA8Uy9A9Gcc47Xv+w2wzhRDYzqdVihLIbsIiCvP1NNOoX/29FQx3vgOgtt4FyRdCgPRarX4+goB9vkyAMh443cOEsIAAcjncuoI4TXWMAmCIGFhCQLAdZ8jym/cRJ+Y5nC5XCYAhINKpZLgSISZgoqh5iiLQrojAFICVwGS7tCfe5DbZzkP56XS4NVxwvTI/vXVVYIDnqmnnX70ZxzjNS8THHooK5hMpxHQIREA+tEfA9djfHR3MHkdx3Hspe9r3B+VzWaj2RESyR2mlCUE4MoGQDdxiwHURq2t94+PO9bMIYyTyDNLwMoM7g8+BfKeYGniyw2MdfSehF3Qmk1IvCc/AgwAaS86Etp38bUAAAAASUVORK5CYII=",n.style.transition="all 0.5s ease-in-out",n.style.opacity="0",n.style.float="left",n.style.position="absolute",n.style.left="50%",n.style.zIndex="99999999",n.style.top="30px";var r=Z(t).width;r&&(n.style.marginLeft=r/2-8+"px");setTimeout(function(){n.style.opacity="1"},500),(V||t.windowSize===e.ChallengeWindowSize.FullScreen)&&(n.style.float="right",n.style.top="20px",n.style.left="initial",n.style.marginLeft="0px",n.style.right="20px");return n}(o))&&(r.appendChild(i),i.addEventListener("click",function(){i&&Array.prototype.slice.call(document.querySelectorAll('[target$="-'+Q+'"],[id$="-'+Q+'"]')).forEach(function(e){e.parentNode&&e.parentNode.removeChild(e)})},!0))}}(t,o,r,n)}function X(e,t,n){return new Promise(function(r,o){var i;n.timeout&&(i=setTimeout(function(){K(i),o(new Error("timeout reached"))},n.timeout));var a=document.createElement("iframe");a.id=a.name="GlobalPayments-3DSecure-"+Q,a.style.display=n.hide?"none":"inherit";var c=function(e,t,n){var r=document.createElement("form");r.setAttribute("method","POST"),r.setAttribute("action",e),r.setAttribute("target",t);for(var o=0,i=n;o Date: Wed, 31 Mar 2021 16:21:29 +0300 Subject: [PATCH 12/38] TR-92: remove wc notices removal --- assets/frontend/js/globalpayments-secure-payment-fields.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 0d231c7..3600bcc 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -505,9 +505,6 @@ this.unblockOnError(); - // Remove notices from all sources - $( '.woocommerce-error, .woocommerce-message' ).remove(); - $form.prepend( '
' + message + '
' ); $( 'html, body' ).animate( { From 665f52424b6e0d0dacdb62e0dcc08f48bf3cdce4 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 1 Apr 2021 15:37:09 +0300 Subject: [PATCH 13/38] TR-92: gateway config updates --- src/Gateways/Clients/SdkClient.php | 30 ++++--------------- src/Gateways/GpApiGateway.php | 6 ++-- .../Requests/GetAccessTokenRequest.php | 6 +++- src/Gateways/Requests/RequestArg.php | 1 + 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index dd2d173..a5a8021 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -4,7 +4,6 @@ use GlobalPayments\Api\Builders\TransactionBuilder; use GlobalPayments\Api\Entities\Address; -use GlobalPayments\Api\Entities\Enums\GpApi\Channels; use GlobalPayments\Api\Entities\Transaction; use GlobalPayments\Api\Entities\Enums\AddressType; use GlobalPayments\Api\Entities\Enums\CardType; @@ -63,10 +62,6 @@ class SdkClient implements ClientInterface { AbstractGateway::TXN_TYPE_VOID, ); - protected $access_token_permissions = array( - 'PMT_POST_Create_Single', - ); - /** * Card data * @@ -318,40 +313,25 @@ protected function configure_sdk() { case GatewayProvider::TRANSIT: $gatewayConfig = new TransitConfig(); $gatewayConfig->acceptorConfig = new AcceptorConfig(); // defaults should work here + if ( $this->get_arg( RequestArg::TXN_TYPE ) === AbstractGateway::TXN_TYPE_CREATE_MANIFEST ) { + $gatewayConfig->deviceId = $this->args[ RequestArg::SERVICES_CONFIG ]['tsepDeviceId']; + } break; case GatewayProvider::GENIUS: $gatewayConfig = new GeniusConfig(); break; case GatewayProvider::GP_API: $gatewayConfig = new GpApiConfig(); - $servicesConfig = $this->args[ RequestArg::SERVICES_CONFIG ]; - $gatewayConfig->setAppId( $servicesConfig['AppId'] ); - $gatewayConfig->setAppKey( $servicesConfig['AppKey'] ); - $gatewayConfig->setMethodNotificationUrl($servicesConfig['methodNotificationUrl']); - $gatewayConfig->setChallengeNotificationUrl($servicesConfig['challengeNotificationUrl']); - $gatewayConfig->setChannel( Channels::CardNotPresent ); - if ( in_array( $this->get_arg( RequestArg::TXN_TYPE ), $this->client_transactions, true ) ) { - $gatewayConfig->setPermissions( $this->access_token_permissions ); + if ( $this->has_arg( RequestArg::PERMISSIONS ) ) { + $gatewayConfig->permissions = $this->get_arg( RequestArg::PERMISSIONS ); } - - unset( $this->args[ RequestArg::SERVICES_CONFIG ]['gatewayProvider'] ); - unset( $this->args[ RequestArg::SERVICES_CONFIG ]['methodNotificationUrl'] ); - unset( $this->args[ RequestArg::SERVICES_CONFIG ]['challengeNotificationUrl'] ); break; } - $config = $this->set_object_data( $gatewayConfig, $this->args[ RequestArg::SERVICES_CONFIG ] ); - if ( - $this->args['SERVICES_CONFIG']['gatewayProvider'] === GatewayProvider::TRANSIT && - $this->get_arg( RequestArg::TXN_TYPE ) === AbstractGateway::TXN_TYPE_CREATE_MANIFEST - ) { - $config->deviceId = $this->args[ RequestArg::SERVICES_CONFIG ]['tsepDeviceId']; - } - ServicesContainer::configureService( $config ); } diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index 0444717..e36f993 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -4,6 +4,7 @@ use GlobalPayments\Api\Entities\Enums\Environment; use GlobalPayments\Api\Entities\Enums\GatewayProvider; +use GlobalPayments\Api\Entities\Enums\GpApi\Channels; use GlobalPayments\WooCommercePaymentGatewayProvider\Plugin; defined( 'ABSPATH' ) || exit; @@ -96,8 +97,9 @@ public function get_frontend_gateway_options() { public function get_backend_gateway_options() { return array( - 'AppId' => $this->app_id, - 'AppKey' => $this->app_key, + 'appId' => $this->app_id, + 'appKey' => $this->app_key, + 'channel' => Channels::CardNotPresent, 'developerId' => '', 'environment' => $this->is_production ? Environment::PRODUCTION : Environment::TEST, 'methodNotificationUrl' => $this->get_api_url('globalpayments_threedsecure_methodnotification'), diff --git a/src/Gateways/Requests/GetAccessTokenRequest.php b/src/Gateways/Requests/GetAccessTokenRequest.php index 043d025..c79c7db 100644 --- a/src/Gateways/Requests/GetAccessTokenRequest.php +++ b/src/Gateways/Requests/GetAccessTokenRequest.php @@ -12,6 +12,10 @@ public function get_transaction_type() { } public function get_args() { - return array(); + return array( + RequestArg::PERMISSIONS => array( + 'PMT_POST_Create_Single', + ) + ); } } diff --git a/src/Gateways/Requests/RequestArg.php b/src/Gateways/Requests/RequestArg.php index 47ecbde..2b6a30c 100644 --- a/src/Gateways/Requests/RequestArg.php +++ b/src/Gateways/Requests/RequestArg.php @@ -9,6 +9,7 @@ abstract class RequestArg { const CARD_HOLDER_NAME = 'CARD_HOLDER_NAME'; const CURRENCY = 'CURRENCY'; const PARES = 'PARES'; + const PERMISSIONS = 'PERMISSIONS'; const SERVER_TRANS_ID = 'SERVER_TRANS_ID'; const SERVICES_CONFIG = 'SERVICES_CONFIG'; const SHIPPING_ADDRESS = 'SHIPPING_ADDRESS'; From 8a92e9b44a32946e5d9cba9b3b6d8d0c7da06301 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 1 Apr 2021 15:39:22 +0300 Subject: [PATCH 14/38] TR-92: hosted fields style changes --- .../globalpayments-secure-payment-fields.css | 6 ++ .../globalpayments-secure-payment-fields.js | 83 +++++++++---------- src/Gateways/AbstractGateway.php | 9 +- 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/assets/frontend/css/globalpayments-secure-payment-fields.css b/assets/frontend/css/globalpayments-secure-payment-fields.css index dbb420b..3593889 100644 --- a/assets/frontend/css/globalpayments-secure-payment-fields.css +++ b/assets/frontend/css/globalpayments-secure-payment-fields.css @@ -1,7 +1,13 @@ .globalpayments iframe { min-height: 3.6rem; + width: 100%; } +.globalpayments iframe[id^="secure-payment-field-submit"] { + min-height: 6.6rem; +} + + div[id^="GlobalPayments-overlay-"] { z-index: 1001 !important; } \ No newline at end of file diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 3600bcc..e9e04f2 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -269,33 +269,15 @@ || ! $( '#billing_last_name' ).val() || ! $( '#billing_address_1' ).val() || ! $( '#billing_city' ).val() - || ! $( '#billing_postcode' ).val() || ! $( '#billing_phone' ).val() || ! $( '#billing_email' ).val() ) { this.showPaymentError( 'Please fill in the required fields.' ); return false; } - if ( ! this.isValidZipCode( $( '#billing_postcode' ).val() ) ) { - this.showPaymentError( 'Billing ZIP is not a valid postcode / ZIP. ' ); - return false; - } - return true; }, - /** - * Validate zipcode - * - * @param {String} zipcode - * @return {Boolean} - */ - isValidZipCode: function ( zipcode ) { - let pattern = /^[a-zA-Z0-9-\s]{1,16}$/; - - return pattern.test(zipcode); - }, - /** * 3DS Process */ @@ -505,6 +487,9 @@ this.unblockOnError(); + // Remove notices from all sources + $( '.woocommerce-error, .woocommerce-message' ).remove(); + $form.prepend( '
' + message + '
' ); $( 'html, body' ).animate( { @@ -612,7 +597,7 @@ var imageBase = 'https://api2.heartlandportico.com/securesubmit.v1/token/gp-1.6.0/assets'; return { 'html': { - 'font-size': '62.5%' + 'font-size': '80%' }, 'body': { 'font-size': '1.4rem' @@ -648,42 +633,48 @@ 'outline': 'none' }, '#secure-payment-field[type=button]': { - 'text-align': 'center', - 'text-transform': 'none', - 'white-space': 'nowrap', - - 'background-image': 'none', - 'background': '#1979c3', - 'border': '1px solid #1979c3', - 'color': '#ffffff', - 'cursor': 'pointer', - 'display': 'inline-block', - 'font-family': '"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif', - 'font-weight': '500', - 'padding': '14px 17px', - 'font-size': '1.8rem', - 'line-height': '2.2rem', - 'box-sizing': 'border-box', - 'vertical-align': 'middle', - 'margin': '0', - 'height': 'initial', - 'width': 'initial', - 'flex': 'initial', - 'position': 'absolute', - 'right': '0' + 'white-space': 'nowrap', + 'background-image': 'none', + '-webkit-appearance': 'none', + '-moz-appearance': 'none', + 'float': 'none', + 'box-sizing': 'border-box', + 'margin-bottom': '1em', + 'background': '#000000', + 'border': 'none', + 'border-radius': '0', + 'color': '#fff', + 'cursor': 'pointer', + 'display': 'inline-block', + 'font-size': '17px', + 'font-weight': '600', + 'letter-spacing': '0.0333em', + 'line-height': '1.25', + 'margin': '0', + 'opacity': '1', + 'padding': '1.1em 1.44em', + 'text-align': 'center', + 'text-decoration': 'none', + 'text-transform': 'uppercase', + 'transition': 'opacity 0.15s linear', + 'height': 'initial', + 'width': '100%', + 'flex': 'initial', + 'position': 'relative' }, '#secure-payment-field[type=button]:focus': { 'outline': 'none', 'box-shadow': 'none', - 'background': '#006bb4', + 'background': '##000000', 'border': '1px solid #006bb4', 'color': '#ffffff' }, '#secure-payment-field[type=button]:hover': { - 'background': '#006bb4', - 'border': '1px solid #006bb4', - 'color': '#ffffff' + 'background': '##000000', + 'border': '0px', + 'color': '#ffffff', + 'text-decoration': 'underline' }, '.card-cvv': { 'background': 'transparent url(' + imageBase + '/cvv.png) no-repeat right', diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index d849c4f..b293332 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -251,9 +251,16 @@ public function tokenization_script() { parent::tokenization_script(); // Global Payments styles for client-side tokenization + $css_style = Plugin::get_url( '/assets/frontend/css/globalpayments-secure-payment-fields.css' ); + /** + * Allow iframe styling according to theme + * + * @param $css_style CSS stylesheet + */ + $css_style = apply_filters( 'globalpayments_secure_payment_fields', $css_style ); wp_enqueue_style( 'globalpayments-secure-payment-fields', - Plugin::get_url( '/assets/frontend/css/globalpayments-secure-payment-fields.css' ), + $css_style, array(), WC()->version ); From c21358e5318f7dba221ad2d74f8a2d3fdbc8ad0d Mon Sep 17 00:00:00 2001 From: apetrovici Date: Fri, 2 Apr 2021 13:13:31 +0300 Subject: [PATCH 15/38] TR-92: add namespace --- src/Gateways/Clients/SdkClient.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index a5a8021..1d0229b 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -4,6 +4,7 @@ use GlobalPayments\Api\Builders\TransactionBuilder; use GlobalPayments\Api\Entities\Address; +use GlobalPayments\Api\Entities\Exceptions\ApiException; use GlobalPayments\Api\Entities\Transaction; use GlobalPayments\Api\Entities\Enums\AddressType; use GlobalPayments\Api\Entities\Enums\CardType; From b827d5142c5457e16e2927b3ad8877509e614444 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 5 Apr 2021 08:24:34 +0300 Subject: [PATCH 16/38] TR-92: add capture action only for authorize --- src/Gateways/AbstractGateway.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index b293332..2480767 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -739,6 +739,11 @@ public function get_decline_message( string $response_code ) { */ public static function addCaptureOrderAction( $actions ) { + global $theorder; + + if ( AbstractGateway::TXN_TYPE_AUTHORIZE !== $theorder->get_meta('_globalpayments_payment_action') ) { + return $actions; + } $actions['capture_credit_card_authorization'] = 'Capture credit card authorization'; return $actions; } From 32a024f0aef8fbc82f07ca98b24e99b13bd66424 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 5 Apr 2021 11:39:23 +0300 Subject: [PATCH 17/38] TR-92: disable add payment method if admin option not set to allow --- src/Gateways/AbstractGateway.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index 2480767..231fae9 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -473,6 +473,7 @@ protected function add_hooks() { if ( is_add_payment_method_page() ) { add_action( 'wp_enqueue_scripts', array( $this, 'tokenization_script' ) ); + add_filter( 'woocommerce_available_payment_gateways', array( $this, 'woocommerce_available_payment_gateways') ); } } @@ -748,4 +749,18 @@ public static function addCaptureOrderAction( $actions ) return $actions; } + /** + * Disable adding new cards via 'My Account', if "Allow Card Saving" option not checked in admin. + * + * @param array $available_gateways + * @return array + */ + public function woocommerce_available_payment_gateways( $available_gateways ) { + if ( 'no' === $this->get_option( 'allow_card_saving' ) ) { + unset( $available_gateways[ $this->id ]); + } + + return $available_gateways; + } + } From 7533e7985d531b75cd6f9ad56ec9c6b3d02c6757 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 5 Apr 2021 16:47:12 +0300 Subject: [PATCH 18/38] TR-92: updates styles and errors --- .../globalpayments-secure-payment-fields.css | 18 +- .../globalpayments-secure-payment-fields.js | 159 +++++++----------- src/Gateways/AbstractGateway.php | 2 +- 3 files changed, 74 insertions(+), 105 deletions(-) diff --git a/assets/frontend/css/globalpayments-secure-payment-fields.css b/assets/frontend/css/globalpayments-secure-payment-fields.css index 3593889..c7d7b20 100644 --- a/assets/frontend/css/globalpayments-secure-payment-fields.css +++ b/assets/frontend/css/globalpayments-secure-payment-fields.css @@ -3,11 +3,19 @@ width: 100%; } -.globalpayments iframe[id^="secure-payment-field-submit"] { - min-height: 6.6rem; -} - - div[id^="GlobalPayments-overlay-"] { z-index: 1001 !important; +} + +.woocommerce-globalpayments-validation-error { + margin-bottom:2.617924em; + background-color:#e2401c; + margin-left:0; + border-radius:2px; + color:#fff; + clear:both; + border-left:.6180469716em solid rgba(0,0,0,.15); + padding:1em 2em 1em 3.5em; + position:relative; + list-style:none outside } \ No newline at end of file diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index e9e04f2..8224756 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -198,7 +198,6 @@ var newSavedCardSelected = 'new' === $( this.getStoredPaymentMethodsRadioSelector() + ':checked' ).val(); var shouldBeVisible = (paymentGatewaySelected && ! savedCardsAvailable) || (savedCardsAvailable && newSavedCardSelected); - if (shouldBeVisible) { // our gateway was selected $( this.getSubmitButtonTargetSelector() ).show(); @@ -271,7 +270,7 @@ || ! $( '#billing_city' ).val() || ! $( '#billing_phone' ).val() || ! $( '#billing_email' ).val() ) { - this.showPaymentError( 'Please fill in the required fields.' ); + return false; } @@ -297,8 +296,9 @@ // handle 3DS 2.0 workflow const start3DS = async (e) => { e.preventDefault(); - if ( wc_checkout_params.is_checkout && ! this.validateFields() ) { + this.showPaymentError( 'Please fill in the required fields.' ); + return; } @@ -315,13 +315,19 @@ }, }); + if ( "ONE" === versionCheckData.version ) { + this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); + this.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); + $( this.getForm() ).submit(); + return; + } + // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. if (versionCheckData.enrolled === "NOT_ENROLLED") { $( this.getForm() ).submit(); return; } - //Something went wrong with CheckEnrolment request on server side if (versionCheckData.error) { this.showPaymentError( versionCheckData.message ); return; @@ -332,13 +338,6 @@ return; } - if ( "ONE" === versionCheckData.version ) { - this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); - this.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); - $( this.getForm() ).submit(); - return; - } - try { authenticationData = await initiateAuthentication(this.threedsecure.initiateAuthenticationUrl, { tokenResponse: this.tokenResponse, @@ -351,29 +350,8 @@ order: this.order, }); - switch (authenticationData.result) { - case "SUCCESS_AUTHENTICATED": - case "AUTHENTICATION_SUCCESSFUL": - // frictionless authentication success - this.createInputElement( 'serverTransId', authenticationData.serverTransactionId ); - $( this.getForm() ).submit(); - return; - case "CHALLENGE_REQUIRED": - // challenge authentication success - if (authenticationData.challenge.response.data.transStatus == "Y") { - this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId); - $( this.getForm() ).submit(); - return; - } - // challenge authentication failure - this.showPaymentError('3DS Authentication failed. Please try again!'); - return; - case "NOT_AUTHENTICATED": - case "FAILED": - default: - this.showPaymentError('3DS Authentication failed. Please try again!'); - return; - } + this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId); + $( this.getForm() ).submit(); } catch (e) { console.error( e ); this.showPaymentError( e.reasons ); @@ -384,6 +362,12 @@ }; checkVersionButton.on('click', start3DS ); + $( document ).on("click",'img[id^="GlobalPayments-frame-close-"]', this.cancelTransaction.bind( this ) ); + + }, + + cancelTransaction: function () { + this.showPaymentError( 'Transaction canceled' ); }, createInputElement: function ( name, value ) { @@ -461,7 +445,7 @@ * @returns */ resetValidationErrors: function () { - $( '.' + this.id + ' .validation-error' ).hide(); + $( '.' + this.id + ' .woocommerce-globalpayments-validation-error' ).hide(); }, /** @@ -472,11 +456,11 @@ * @returns */ showValidationError: function (fieldType) { - $( '.' + this.id + '.' + fieldType + ' .validation-error' ).show(); + $( '.' + this.id + '.' + fieldType + ' .woocommerce-globalpayments-validation-error' ).show(); }, /** - * Shows payment error and scrolls to it + * Shows payment error and scrolls to it * * @param {string} message Error message * @@ -488,9 +472,9 @@ this.unblockOnError(); // Remove notices from all sources - $( '.woocommerce-error, .woocommerce-message' ).remove(); + $( '.woocommerce-NoticeGroup, .woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-globalpayments-checkout-error' ).remove(); - $form.prepend( '
' + message + '
' ); + $form.prepend( '
' + message + '
' ); $( 'html, body' ).animate( { scrollTop: ( $form.offset().top - 100 ) @@ -513,7 +497,6 @@ } var numberOfReasons = error.reasons.length; - for ( var i = 0; i < numberOfReasons; i++ ) { var reason = error.reasons[i]; switch ( reason.code ) { @@ -597,84 +580,62 @@ var imageBase = 'https://api2.heartlandportico.com/securesubmit.v1/token/gp-1.6.0/assets'; return { 'html': { - 'font-size': '80%' + 'font-size': '100%', + '-webkit-text-size-adjust': '100%', }, + 'body': { - 'font-size': '1.4rem' + 'font-size': '14px', }, '#secure-payment-field-wrapper': { - 'postition': 'relative' + 'position': 'relative' }, '#secure-payment-field': { - '-o-transition': 'border-color ease-in-out .15s,box-shadow ease-in-out .15s', - '-webkit-box-shadow': 'inset 0 1px 1px rgba(0,0,0,.075)', - '-webkit-transition': 'border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s', 'background-color': '#fff', - 'border': '1px solid #cecece', - 'border-radius': '2px', - 'box-shadow': 'none', - 'box-sizing': 'border-box', + 'border': '1px solid #ccc', + 'border-radius': '4px', 'display': 'block', - 'font-family': '"Roboto", sans-serif', - 'font-size': '11px', - 'font-smoothing': 'antialiased', + + 'font-size': '14px', 'height': '35px', - 'margin': '5px 0 10px 0', - 'max-width': '100%', - 'outline': '0', - 'padding': '0 10px', - 'transition': 'border-color ease-in-out .15s,box-shadow ease-in-out .15s', - 'vertical-align': 'baseline', - 'width': '100%' + 'padding': '6px 12px', + 'width': '100%', }, '#secure-payment-field:focus': { 'border': '1px solid lightblue', 'box-shadow': '0 1px 3px 0 #cecece', 'outline': 'none' }, - '#secure-payment-field[type=button]': { - 'white-space': 'nowrap', - 'background-image': 'none', - '-webkit-appearance': 'none', - '-moz-appearance': 'none', - 'float': 'none', - 'box-sizing': 'border-box', - 'margin-bottom': '1em', - 'background': '#000000', - 'border': 'none', - 'border-radius': '0', - 'color': '#fff', - 'cursor': 'pointer', - 'display': 'inline-block', - 'font-size': '17px', - 'font-weight': '600', - 'letter-spacing': '0.0333em', - 'line-height': '1.25', - 'margin': '0', - 'opacity': '1', - 'padding': '1.1em 1.44em', - 'text-align': 'center', - 'text-decoration': 'none', - 'text-transform': 'uppercase', - 'transition': 'opacity 0.15s linear', - 'height': 'initial', - 'width': '100%', - 'flex': 'initial', - 'position': 'relative' + '#secure-payment-field[type=button]': { + 'cursor': 'pointer', + 'border': '0', + 'border-radius': '0', + 'background': 'none', + 'background-color': '#333333', + 'border-color': '#333333', + 'color': '#fff', + 'padding': '.6180469716em 1.41575em', + 'text-decoration': 'none', + 'text-shadow': 'none', + 'display': 'inline-block', + 'height': 'initial', + 'width': '100%', + 'flex': 'initial', + 'position': 'relative', + 'margin': '0', + '-webkit-appearance': 'none', + 'white-space': 'pre-wrap', + 'margin-bottom': '0', + 'float': 'none', + 'font': '600 1.41575em Source Sans Pro,HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif !important' }, '#secure-payment-field[type=button]:focus': { - 'outline': 'none', - - 'box-shadow': 'none', - 'background': '##000000', - 'border': '1px solid #006bb4', - 'color': '#ffffff' + 'color': '#fff', + 'background': '#000000', }, '#secure-payment-field[type=button]:hover': { - 'background': '##000000', - 'border': '0px', - 'color': '#ffffff', - 'text-decoration': 'underline' + 'color': '#fff', + 'background': '#000000', }, '.card-cvv': { 'background': 'transparent url(' + imageBase + '/cvv.png) no-repeat right', diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index 231fae9..516929c 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -449,7 +449,7 @@ protected function secure_payment_field_html_format() { '
-
' From bf249bf59653ae288614556e755bd0c60408ea41 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 5 Apr 2021 16:50:48 +0300 Subject: [PATCH 19/38] TR-92: bug fix toggle buttons only when gateway selected --- assets/frontend/js/globalpayments-secure-payment-fields.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 8224756..0d1527e 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -194,10 +194,14 @@ */ toggleSubmitButtons: function () { var paymentGatewaySelected = $( this.getPaymentMethodRadioSelector() ).is( ':checked' ); + if ( ! paymentGatewaySelected ) { + return; + } + var savedCardsAvailable = $( this.getStoredPaymentMethodsRadioSelector() + '[value!="new"]' ).length > 0; var newSavedCardSelected = 'new' === $( this.getStoredPaymentMethodsRadioSelector() + ':checked' ).val(); - var shouldBeVisible = (paymentGatewaySelected && ! savedCardsAvailable) || (savedCardsAvailable && newSavedCardSelected); + var shouldBeVisible = ( ! savedCardsAvailable ) || ( savedCardsAvailable && newSavedCardSelected ); if (shouldBeVisible) { // our gateway was selected $( this.getSubmitButtonTargetSelector() ).show(); From 2eb350857b49e8e4302e04bfe805c5c6ff210d8b Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 5 Apr 2021 17:27:00 +0300 Subject: [PATCH 20/38] TR-92: bug fix --- .../js/globalpayments-secure-payment-fields.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 0d1527e..70eb572 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -319,15 +319,15 @@ }, }); - if ( "ONE" === versionCheckData.version ) { - this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); - this.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); + // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. + if (versionCheckData.enrolled === "NOT_ENROLLED") { $( this.getForm() ).submit(); return; } - // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. - if (versionCheckData.enrolled === "NOT_ENROLLED") { + if ( "ONE" === versionCheckData.version ) { + this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); + this.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); $( this.getForm() ).submit(); return; } @@ -544,7 +544,7 @@ alert(reason.message); break; default: - break; + this.showPaymentError( reason.message ); } } }, From 73c3fa949a000a892c9c83c15d4be5c2af39dd47 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 5 Apr 2021 18:32:15 +0300 Subject: [PATCH 21/38] TR-92: display error thrown by init auth --- assets/frontend/js/globalpayments-secure-payment-fields.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 70eb572..843bb23 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -354,6 +354,11 @@ order: this.order, }); + if ( authenticationData.error ) { + this.showPaymentError( authenticationData.message ); + return; + } + this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId); $( this.getForm() ).submit(); } catch (e) { From 1bdbd174c5e0494aeda0edf57ba58a688ead8ef6 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Tue, 6 Apr 2021 15:42:51 +0300 Subject: [PATCH 22/38] TR-92: update 3ds requests --- .../globalpayments-secure-payment-fields.js | 125 +++++++++--------- src/Gateways/GpApiGateway.php | 5 +- 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 843bb23..e9f1792 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -285,92 +285,87 @@ * 3DS Process */ threeDSSecure: function () { - const { - checkVersion, - initiateAuthentication, - ChallengeWindowSize, - } = GlobalPayments.ThreeDSecure; - - const checkVersionButton = $( this.getPlaceOrderButtonSelector() ); + var checkVersionButton = $( this.getPlaceOrderButtonSelector() ); if ( ! checkVersionButton ) { console.error( 'Warning! Place Order button cannot be loaded' ); return; } - // handle 3DS 2.0 workflow - const start3DS = async (e) => { + //handle 3DS 2.0 workflow + var start3DS = async (e) => { e.preventDefault(); if ( wc_checkout_params.is_checkout && ! this.validateFields() ) { this.showPaymentError( 'Please fill in the required fields.' ); - + e.stopPropagation(); return; } - try { - versionCheckData = await checkVersion(this.threedsecure.checkEnrollmentUrl, { - tokenResponse: this.tokenResponse, - wcTokenId: $( 'input[name="wc-' + this.id + '-payment-token"]:checked', this.getForm() ).val(), - amount: this.order.amount, - currency: this.order.currency, - challengeWindow: { - windowSize: ChallengeWindowSize.Windowed500x600, - displayMode: 'lightbox', - hide: false, - }, - }); + var _that = this; + + GlobalPayments.ThreeDSecure.checkVersion( this.threedsecure.checkEnrollmentUrl, { + tokenResponse: this.tokenResponse, + wcTokenId: $( 'input[name="wc-' + this.id + '-payment-token"]:checked', this.getForm() ).val(), + amount: this.order.amount, + currency: this.order.currency, + challengeWindow: { + windowSize: GlobalPayments.ThreeDSecure.ChallengeWindowSize.Windowed500x600, + displayMode: 'lightbox', + hide: false, + }, + }) + .then( function( versionCheckData ) { + // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. + if ( versionCheckData.enrolled === "NOT_ENROLLED" ) { + $( _that.getForm() ).submit(); + return; + } - // Card holder not enrolled in 3D Secure, continue the WooCommerce flow. - if (versionCheckData.enrolled === "NOT_ENROLLED") { - $( this.getForm() ).submit(); - return; - } + if ( "ONE" === versionCheckData.version ) { + _that.createInputElement( 'serverTransId', versionCheckData.challenge.response.data.MD ); + _that.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); + $( _that.getForm() ).submit(); + return; + } - if ( "ONE" === versionCheckData.version ) { - this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); - this.createInputElement( 'PaRes', versionCheckData.challenge.response.data.PaRes ); - $( this.getForm() ).submit(); - return; - } + if ( versionCheckData.error ) { + _that.showPaymentError( versionCheckData.message ); + return; + } - if (versionCheckData.error) { - this.showPaymentError( versionCheckData.message ); + GlobalPayments.ThreeDSecure.initiateAuthentication( _that.threedsecure.initiateAuthenticationUrl, { + tokenResponse: _that.tokenResponse, + wcTokenId: $( 'input[name="wc-' + _that.id + '-payment-token"]:checked', _that.getForm() ).val(), + versionCheckData: versionCheckData, + challengeWindow: { + windowSize: GlobalPayments.ThreeDSecure.ChallengeWindowSize.Windowed500x600, + displayMode: 'lightbox', + }, + order: _that.order, + }) + .then( function ( authenticationData ) { + if ( authenticationData.error ) { + _that.showPaymentError( authenticationData.message ); + return; + } + _that.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); + $( _that.getForm() ).submit(); + + }) + + }) + .catch( function( e ) { + console.error( e ); + _that.showPaymentError( e.reasons[0].message ); return; - } - } catch ( e ) { - console.error( e.reasons ); - this.showPaymentError( e.reasons[0].message ); - return; - } - - try { - authenticationData = await initiateAuthentication(this.threedsecure.initiateAuthenticationUrl, { - tokenResponse: this.tokenResponse, - wcTokenId: $( 'input[name="wc-' + this.id + '-payment-token"]:checked', this.getForm() ).val(), - versionCheckData: versionCheckData, - challengeWindow: { - windowSize: ChallengeWindowSize.Windowed500x600, - displayMode: 'lightbox', - }, - order: this.order, }); - if ( authenticationData.error ) { - this.showPaymentError( authenticationData.message ); - return; - } - this.createInputElement( 'serverTransId', versionCheckData.serverTransactionId); - $( this.getForm() ).submit(); - } catch (e) { - console.error( e ); - this.showPaymentError( e.reasons ); - return; - } + return false; }; - checkVersionButton.on('click', start3DS ); + checkVersionButton.off( 'click' ).on('click', start3DS ); $( document ).on("click",'img[id^="GlobalPayments-frame-close-"]', this.cancelTransaction.bind( this ) ); }, @@ -481,7 +476,7 @@ this.unblockOnError(); // Remove notices from all sources - $( '.woocommerce-NoticeGroup, .woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-globalpayments-checkout-error' ).remove(); + $( '.woocommerce-globalpayments-checkout-error' ).remove(); $form.prepend( '
' + message + '
' ); diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index e36f993..46e2556 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -198,7 +198,10 @@ public function process_threeDSecure_challengeNotification() } if ( isset( $_POST['PaRes'] ) ) { - $response = json_encode( [ 'PaRes' => $_POST['PaRes'] ], JSON_UNESCAPED_SLASHES ); + $response = json_encode( [ + 'MD' => $_POST['MD'], + 'PaRes' => $_POST['PaRes'] + ], JSON_UNESCAPED_SLASHES ); } $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); From f16251b394aa8da78ba00669227fdb250969d29a Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 7 Apr 2021 12:45:41 +0300 Subject: [PATCH 23/38] TR-92: block form on place order submit --- assets/frontend/js/globalpayments-secure-payment-fields.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index e9f1792..f3859d0 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -350,7 +350,7 @@ _that.createInputElement( 'serverTransId', versionCheckData.serverTransactionId ); $( _that.getForm() ).submit(); - }) + }); }) .catch( function( e ) { @@ -366,6 +366,8 @@ }; checkVersionButton.off( 'click' ).on('click', start3DS ); + checkVersionButton.on( 'click', this.blockOnSubmit.bind( this ) ); + $( document ).on("click",'img[id^="GlobalPayments-frame-close-"]', this.cancelTransaction.bind( this ) ); }, From b7c044d3b17ff7ae2be6b7576e1b0eb6e48aeea1 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 7 Apr 2021 21:14:15 +0300 Subject: [PATCH 24/38] TR-92: format refund amount --- src/Gateways/Requests/RefundRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gateways/Requests/RefundRequest.php b/src/Gateways/Requests/RefundRequest.php index 11f8ec4..df030df 100644 --- a/src/Gateways/Requests/RefundRequest.php +++ b/src/Gateways/Requests/RefundRequest.php @@ -15,7 +15,7 @@ public function get_transaction_type() { public function get_args() { $gateway_id = $this->order->get_transaction_id(); $description = $this->data['refund_reason']; - $refund_amount = $this->data['refund_amount']; + $refund_amount = wc_format_decimal( $this->data['refund_amount'] ); return array( RequestArg::CURRENCY => $this->order->get_currency(), From 036054f97108241d64093f6360270508c86b7068 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 7 Apr 2021 21:33:31 +0300 Subject: [PATCH 25/38] TR-92: fix bug --- .../Requests/ThreeDSecure/CheckEnrollmentRequest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php index d601217..5ba2932 100644 --- a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php @@ -28,12 +28,12 @@ public function do_request() { $response = []; $requestData = $this->data; try { - if ( isset( $requestData->tokenResponse ) ) { - $tokenResponse = json_decode( $requestData->tokenResponse ); - $token = $tokenResponse->paymentReference; - } else { + if ( 'new' !== $requestData->wcTokenId ) { $tokenResponse = \WC_Payment_Tokens::get( $requestData->wcTokenId ); $token = $tokenResponse->get_token(); + } else { + $tokenResponse = json_decode( $requestData->tokenResponse ); + $token = $tokenResponse->paymentReference; } $paymentMethod = new CreditCardData(); @@ -43,6 +43,7 @@ public function do_request() { ->withAmount($requestData->amount) ->withCurrency($requestData->currency) ->execute(); + $response["enrolled"] = $threeDSecureData->enrolled ?? self::NOT_ENROLLED; $response['version'] = $threeDSecureData->getVersion(); $response["serverTransactionId"] = $threeDSecureData->serverTransactionId ?? ''; From 76292f02a28df960cd1ef728ac20ebd2d859a409 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 7 Apr 2021 21:48:11 +0300 Subject: [PATCH 26/38] TR-92: set country on config --- src/Gateways/GpApiGateway.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index 46e2556..6c1acdc 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -100,6 +100,7 @@ public function get_backend_gateway_options() { 'appId' => $this->app_id, 'appKey' => $this->app_key, 'channel' => Channels::CardNotPresent, + 'country' => wc_get_base_location()['country'], 'developerId' => '', 'environment' => $this->is_production ? Environment::PRODUCTION : Environment::TEST, 'methodNotificationUrl' => $this->get_api_url('globalpayments_threedsecure_methodnotification'), From 359a8fcceaedfc0143fe3b5dd08d929f7f431a43 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 7 Apr 2021 22:12:50 +0300 Subject: [PATCH 27/38] TR-92: fix 3DS block on submit --- assets/frontend/js/globalpayments-secure-payment-fields.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index f3859d0..96aa463 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -293,6 +293,7 @@ //handle 3DS 2.0 workflow var start3DS = async (e) => { + this.blockOnSubmit(); e.preventDefault(); if ( wc_checkout_params.is_checkout && ! this.validateFields() ) { this.showPaymentError( 'Please fill in the required fields.' ); @@ -300,6 +301,7 @@ return; } + var _that = this; GlobalPayments.ThreeDSecure.checkVersion( this.threedsecure.checkEnrollmentUrl, { @@ -366,7 +368,6 @@ }; checkVersionButton.off( 'click' ).on('click', start3DS ); - checkVersionButton.on( 'click', this.blockOnSubmit.bind( this ) ); $( document ).on("click",'img[id^="GlobalPayments-frame-close-"]', this.cancelTransaction.bind( this ) ); From 8e7e55e6fb866968a9618fd2ecdd6e44831b27e8 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Wed, 7 Apr 2021 22:14:02 +0300 Subject: [PATCH 28/38] TR-92: bug fix for add payment method --- src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php index 5ba2932..add312b 100644 --- a/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/CheckEnrollmentRequest.php @@ -28,7 +28,7 @@ public function do_request() { $response = []; $requestData = $this->data; try { - if ( 'new' !== $requestData->wcTokenId ) { + if ( isset( $requestData->wcTokenId ) && 'new' !== $requestData->wcTokenId ) { $tokenResponse = \WC_Payment_Tokens::get( $requestData->wcTokenId ); $token = $tokenResponse->get_token(); } else { From 617832b6fd4bcaf0af3eb11cb81b614f019d0875 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 10:32:33 +0300 Subject: [PATCH 29/38] TR-92: update readme --- readme.txt | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index da1f605..51c6694 100755 --- a/readme.txt +++ b/readme.txt @@ -1,9 +1,36 @@ === Global Payments Gateway Provider for WooCommerce === -Contributors: markhagan -Tags: woocommerce, woo, commerce, global, payments, heartland, payment, systems, tsys, genius,gateway, token, tokenize, save cards -Tested up to: 5.2 +Contributors: globalpayments +Tags: woocommerce, woo, commerce, global, payments, heartland, payment, systems, tsys, genius, gpapi, gp-api, 3DS, gateway, token, tokenize, save cards +Requires at least: 5.4 +Tested up to: 5.7 +Requires PHP: 7.2 Stable tag: trunk License: MIT License URI: https://github.com/globalpayments/globalpayments-gateway-provider-for-woocommerce/blob/master/LICENSE +== Description == This extension allows WooCommerce to use the available Global Payments payment gateways. All card data is tokenized using the respective gateway's tokenization service. + += Features = +- Heartland Portico gateway +- TSYS Genius gateway +- TSYS TransIT gateway with TSEP +- Global Payments API (GP-API) gateway +- Credit Cards +- Integrates with Woocommerce +- Sale transactions (automatic capture or separate capture action later) +- Refund transactions from a previous Sale +- Verify payment method +- Stored payment methods +- 3D Secure 2 & SCA +- 3D Secure 1 + += Support = +For more information or questions, please email developers@globalpay.com . + += Developer Docs = +Discover our developer portal powered by Heartland, a Global Payments Company (https://developer.heartlandpaymentsystems.com/) or our portal for companies located outside the US (https://developer.globalpay.com/). +== Changelog == + += 1.0.0 = +* Initial release. From 977ad13246f4777add2d604b197d904e1c38dcc2 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 12:02:37 +0300 Subject: [PATCH 30/38] TR-92: update readme --- readme.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 51c6694..e94c1f9 100755 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,6 @@ Contributors: globalpayments Tags: woocommerce, woo, commerce, global, payments, heartland, payment, systems, tsys, genius, gpapi, gp-api, 3DS, gateway, token, tokenize, save cards Requires at least: 5.4 Tested up to: 5.7 -Requires PHP: 7.2 Stable tag: trunk License: MIT License URI: https://github.com/globalpayments/globalpayments-gateway-provider-for-woocommerce/blob/master/LICENSE @@ -30,6 +29,13 @@ For more information or questions, please email Date: Thu, 8 Apr 2021 12:44:21 +0300 Subject: [PATCH 31/38] TR-92: no valdation for order pay --- assets/frontend/js/globalpayments-secure-payment-fields.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 96aa463..fe267ed 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -295,13 +295,12 @@ var start3DS = async (e) => { this.blockOnSubmit(); e.preventDefault(); - if ( wc_checkout_params.is_checkout && ! this.validateFields() ) { + if ( 1 === wc_checkout_params.is_checkout && ! this.validateFields() ) { this.showPaymentError( 'Please fill in the required fields.' ); e.stopPropagation(); return; } - var _that = this; GlobalPayments.ThreeDSecure.checkVersion( this.threedsecure.checkEnrollmentUrl, { From 628ffd6bccbcabde4aa48e31d947774d9428f4ac Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 12:49:11 +0300 Subject: [PATCH 32/38] TR-92: Error: Your plugin and author URIs are the same --- globalpayments-gateway-provider-for-woocommerce.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/globalpayments-gateway-provider-for-woocommerce.php b/globalpayments-gateway-provider-for-woocommerce.php index 80c6308..9dba55e 100755 --- a/globalpayments-gateway-provider-for-woocommerce.php +++ b/globalpayments-gateway-provider-for-woocommerce.php @@ -5,9 +5,8 @@ * Description: This extension allows WooCommerce to use the available Global Payments payment gateways. All card data is tokenized using the respective gateway's tokenization service. * Version: 1.0.0 * Requires PHP: 5.5.9 - * WC tested up to: 3.8.0 + * WC tested up to: 5.0.0 * Author: Global Payments - * Author URI: https://github.com/globalpayments/globalpayments-gateway-provider-for-woocommerce */ defined( 'ABSPATH' ) || exit; From dd37ec14dd3bf87f74c3d8f7ded3a85cdc22381e Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 18:03:33 +0300 Subject: [PATCH 33/38] TR-92: bug fix uncaught exception --- src/Gateways/AbstractGateway.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index 516929c..5a8fa60 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -507,6 +507,7 @@ public function add_payment_method() { try { $response = $this->submit_request( $request ); + $is_successful = $this->handle_response( $request, $response ); } catch ( Exception $e ) { return array( 'result' => 'failure', @@ -514,8 +515,6 @@ public function add_payment_method() { ); } - $is_successful = $this->handle_response( $request, $response ); - return array( 'result' => $is_successful ? 'success' : 'failure', 'redirect' => $redirect, From c7bf12617a6918b1a32134a3778af6f0d4a4cbba Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 18:34:00 +0300 Subject: [PATCH 34/38] TR-92: bug fix --- .../ThreeDSecure/InitiateAuthenticationRequest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php index 844b8a1..b588ef7 100644 --- a/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php +++ b/src/Gateways/Requests/ThreeDSecure/InitiateAuthenticationRequest.php @@ -30,12 +30,12 @@ public function do_request() { $requestData = $this->data; try { - if ( isset( $requestData->tokenResponse ) ) { - $tokenResponse = json_decode( $requestData->tokenResponse ); - $token = $tokenResponse->paymentReference; - } else { + if ( isset( $requestData->wcTokenId ) && 'new' !== $requestData->wcTokenId ) { $tokenResponse = \WC_Payment_Tokens::get( $requestData->wcTokenId ); $token = $tokenResponse->get_token(); + } else { + $tokenResponse = json_decode( $requestData->tokenResponse ); + $token = $tokenResponse->paymentReference; } $paymentMethod = new CreditCardData(); From f393717fe695fb858a3c99f03c1dddfff6973894 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 19:08:21 +0300 Subject: [PATCH 35/38] TR-92: remove 3DS check for add payment method --- .../globalpayments-secure-payment-fields.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index fe267ed..69da72d 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -82,23 +82,29 @@ } ); - // Order Pay + Add payment method - if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) || $( 'form#add_payment_method' ).length > 0 ) { - $( document ).ready( this.renderPaymentFields.bind( this ) ); + // Checkout + if ( 1 == wc_checkout_params.is_checkout ) { + $( document.body ).on( 'updated_checkout', this.renderPaymentFields.bind( this ) ); if ( 'globalpayments_gpapi' === this.id) { - $( document ).ready( this.threeDSSecure.bind( this ) ); + $( document.body ).on( 'updated_checkout', this.threeDSSecure.bind( this ) ); } return; } - // Checkout - if ( wc_checkout_params.is_checkout ) { - $( document.body ).on( 'updated_checkout', this.renderPaymentFields.bind( this ) ); + // Order Pay + if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) { + $( document ).ready( this.renderPaymentFields.bind( this ) ); if ( 'globalpayments_gpapi' === this.id) { - $( document.body ).on( 'updated_checkout', this.threeDSSecure.bind( this ) ); + $( document ).ready( this.threeDSSecure.bind( this ) ); } return; } + + // Add payment method + if ( $( 'form#add_payment_method' ).length > 0 ) { + $( document ).ready( this.renderPaymentFields.bind( this ) ); + return; + } }, /** From 9d8030f4d77a0cf49ae035d6851a20aa92bab575 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Thu, 8 Apr 2021 19:10:05 +0300 Subject: [PATCH 36/38] TR-92: bug fix remove notices from previous calls --- assets/frontend/js/globalpayments-secure-payment-fields.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/frontend/js/globalpayments-secure-payment-fields.js b/assets/frontend/js/globalpayments-secure-payment-fields.js index 69da72d..5e4e7d7 100644 --- a/assets/frontend/js/globalpayments-secure-payment-fields.js +++ b/assets/frontend/js/globalpayments-secure-payment-fields.js @@ -484,7 +484,7 @@ this.unblockOnError(); // Remove notices from all sources - $( '.woocommerce-globalpayments-checkout-error' ).remove(); + $( '.woocommerce-NoticeGroup, .woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-globalpayments-checkout-error' ).remove(); $form.prepend( '
' + message + '
' ); From dd6951d1b54ad1f866c9a0972eae3de65b977850 Mon Sep 17 00:00:00 2001 From: apetrovici Date: Fri, 9 Apr 2021 09:19:01 +0300 Subject: [PATCH 37/38] TR-92: add WP review feedback --- globalpayments-gateway-provider-for-woocommerce.php | 2 +- readme.txt | 4 ++-- src/Gateways/GpApiGateway.php | 8 ++++---- src/Gateways/HeartlandGiftCards/HeartlandGiftGateway.php | 6 +++--- src/Gateways/Requests/AbstractRequest.php | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/globalpayments-gateway-provider-for-woocommerce.php b/globalpayments-gateway-provider-for-woocommerce.php index 9dba55e..a788416 100755 --- a/globalpayments-gateway-provider-for-woocommerce.php +++ b/globalpayments-gateway-provider-for-woocommerce.php @@ -3,7 +3,7 @@ * Plugin Name: Global Payments Gateway Provider for WooCommerce * Plugin URI: https://github.com/globalpayments/globalpayments-gateway-provider-for-woocommerce * Description: This extension allows WooCommerce to use the available Global Payments payment gateways. All card data is tokenized using the respective gateway's tokenization service. - * Version: 1.0.0 + * Version: 1.0.0-b.1 * Requires PHP: 5.5.9 * WC tested up to: 5.0.0 * Author: Global Payments diff --git a/readme.txt b/readme.txt index e94c1f9..addd729 100755 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: globalpayments Tags: woocommerce, woo, commerce, global, payments, heartland, payment, systems, tsys, genius, gpapi, gp-api, 3DS, gateway, token, tokenize, save cards Requires at least: 5.4 Tested up to: 5.7 -Stable tag: trunk +Stable tag: 1.0.0-b.1 License: MIT License URI: https://github.com/globalpayments/globalpayments-gateway-provider-for-woocommerce/blob/master/LICENSE @@ -38,5 +38,5 @@ After you have installed and configured the main WooCommerce plugin use the foll == Changelog == -= 1.0.0 = += 1.0.0-b.1 = * Initial release. diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index 6c1acdc..b8d80a9 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -159,7 +159,7 @@ public function process_threeDSecure_methodNotification() $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); - $convertedThreeDSMethodData = json_decode( base64_decode( $_POST['threeDSMethodData'] ) ); + $convertedThreeDSMethodData = wc_clean( json_decode( base64_decode( $_POST['threeDSMethodData'] ) ) ); $response = json_encode([ 'threeDSServerTransID' => $convertedThreeDSMethodData->threeDSServerTransID, ]); @@ -190,7 +190,7 @@ public function process_threeDSecure_challengeNotification() $response = new \stdClass(); if ( isset( $_POST['cres'] ) ) { - $convertedCRes = json_decode( base64_decode( $_POST['cres'] ) ); + $convertedCRes = wc_clean( json_decode( base64_decode( $_POST['cres'] ) ) ); $response = json_encode([ 'threeDSServerTransID' => $convertedCRes->threeDSServerTransID, @@ -200,8 +200,8 @@ public function process_threeDSecure_challengeNotification() if ( isset( $_POST['PaRes'] ) ) { $response = json_encode( [ - 'MD' => $_POST['MD'], - 'PaRes' => $_POST['PaRes'] + 'MD' => wc_clean( $_POST['MD'] ), + 'PaRes' => wc_clean( $_POST['PaRes'] ), ], JSON_UNESCAPED_SLASHES ); } diff --git a/src/Gateways/HeartlandGiftCards/HeartlandGiftGateway.php b/src/Gateways/HeartlandGiftCards/HeartlandGiftGateway.php index 6f93161..8ae4eb4 100644 --- a/src/Gateways/HeartlandGiftCards/HeartlandGiftGateway.php +++ b/src/Gateways/HeartlandGiftCards/HeartlandGiftGateway.php @@ -36,8 +36,8 @@ protected function configureServiceContainer() public function applyGiftCard() { $gift_card_balance = $this->giftCardBalance( - $_POST['gift_card_number'], - $_POST['gift_card_pin'] + wc_clean( $_POST['gift_card_number'] ), + wc_clean( $_POST['gift_card_pin'] ) ); if ($gift_card_balance['error']) { @@ -330,7 +330,7 @@ public function removeAllGiftCardsFromSession() public function removeGiftCard($removed_card = null) { if (isset($_POST['securesubmit_card_id']) && empty($removed_card)) { - $removed_card = $_POST['securesubmit_card_id']; + $removed_card = wc_clean( $_POST['securesubmit_card_id'] ); } $applied_cards = WC()->session->get('heartland_gift_card_applied'); diff --git a/src/Gateways/Requests/AbstractRequest.php b/src/Gateways/Requests/AbstractRequest.php index 1007d15..1b9e3fe 100644 --- a/src/Gateways/Requests/AbstractRequest.php +++ b/src/Gateways/Requests/AbstractRequest.php @@ -75,7 +75,7 @@ public function get_request_data( $key = null ) { } // WooCommerce should verify nonce during its checkout handling // phpcs:ignore WordPress.Security.NonceVerification - return $_POST; + return wc_clean( $_POST ); } if ( ! isset( $this->data[ $key ] ) ) { From c8e076e3fd6b692aaf3d298c1340bd5f7835cc2e Mon Sep 17 00:00:00 2001 From: apetrovici Date: Mon, 12 Apr 2021 15:39:08 +0300 Subject: [PATCH 38/38] TR-92: include js scripts correctly --- src/Gateways/AbstractGateway.php | 8 ++--- src/Gateways/GpApiGateway.php | 54 ++++++++++---------------------- 2 files changed, 21 insertions(+), 41 deletions(-) diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index 5a8fa60..129c032 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -308,10 +308,10 @@ public function tokenization_script() { 'globalpayments_secure_payment_threedsecure_params', array( 'threedsecure' => array( - 'methodNotificationUrl' => $this->get_api_url( 'globalpayments_threedsecure_methodnotification' ), - 'challengeNotificationUrl' => $this->get_api_url( 'globalpayments_threedsecure_challengenotification' ), - 'checkEnrollmentUrl' => $this->get_api_url( 'globalpayments_threedsecure_checkenrollment' ), - 'initiateAuthenticationUrl' => $this->get_api_url( 'globalpayments_threedsecure_initiateauthentication' ), + 'methodNotificationUrl' => WC()->api_request_url( 'globalpayments_threedsecure_methodnotification' ), + 'challengeNotificationUrl' => WC()->api_request_url( 'globalpayments_threedsecure_challengenotification' ), + 'checkEnrollmentUrl' => WC()->api_request_url( 'globalpayments_threedsecure_checkenrollment' ), + 'initiateAuthenticationUrl' => WC()->api_request_url( 'globalpayments_threedsecure_initiateauthentication' ), ), 'order' => array ( 'amount' => $this->get_session_amount(), diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index b8d80a9..c79d5f1 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -103,8 +103,8 @@ public function get_backend_gateway_options() { 'country' => wc_get_base_location()['country'], 'developerId' => '', 'environment' => $this->is_production ? Environment::PRODUCTION : Environment::TEST, - 'methodNotificationUrl' => $this->get_api_url('globalpayments_threedsecure_methodnotification'), - 'challengeNotificationUrl' => $this->get_api_url('globalpayments_threedsecure_challengenotification'), + 'methodNotificationUrl' => WC()->api_request_url('globalpayments_threedsecure_methodnotification'), + 'challengeNotificationUrl' => WC()->api_request_url('globalpayments_threedsecure_challengenotification'), ); } @@ -157,23 +157,18 @@ public function process_threeDSecure_methodNotification() return; } - $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); - $convertedThreeDSMethodData = wc_clean( json_decode( base64_decode( $_POST['threeDSMethodData'] ) ) ); $response = json_encode([ 'threeDSServerTransID' => $convertedThreeDSMethodData->threeDSServerTransID, ]); - $script = << - - - - - - -EOT; - header("Content-Type: text/html"); - echo $script; + + wp_enqueue_script( + 'globalpayments-threedsecure-lib', + Plugin::get_url( '/assets/frontend/js/globalpayments-3ds' ) + . ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ) . '.js' + ); + wp_add_inline_script( 'globalpayments-threedsecure-lib', 'GlobalPayments.ThreeDSecure.handleMethodNotification(' . $response . ');' ); + wp_print_scripts(); exit(); } @@ -204,19 +199,13 @@ public function process_threeDSecure_challengeNotification() 'PaRes' => wc_clean( $_POST['PaRes'] ), ], JSON_UNESCAPED_SLASHES ); } - - $globalpayments_threedsecure_lib = Plugin::get_url( '/assets/frontend/js/globalpayments-3ds.js' ); - $script = << - - - - - - -EOT; - header("Content-Type: text/html"); - echo $script; + wp_enqueue_script( + 'globalpayments-threedsecure-lib', + Plugin::get_url( '/assets/frontend/js/globalpayments-3ds' ) + . ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ) . '.js' + ); + wp_add_inline_script( 'globalpayments-threedsecure-lib', 'GlobalPayments.ThreeDSecure.handleChallengeNotification(' . $response . ');' ); + wp_print_scripts(); exit(); } catch (Exception $e) { @@ -258,13 +247,4 @@ protected function get_shipping_address() 'countryCode' => '', ]; } - - protected function get_api_url($path) { - $url = get_home_url( null, "wc-api/", is_ssl() ? 'https' : 'http' ); - if ( ! empty( $path ) && is_string( $path ) ) { - $url .= ltrim( $path, '/' ); - } - - return $url; - } }