/**
* @copyright Transpayrent ApS 2022
* @license Common Transpayrent API license
* @version 1.2.0
*
* @class
* @classdesc The Transpayrent UI Helper SDK simplifies completion of the entire payment process into the following 3 steps:
* 1) Override UI Helper SDK methods to return the correct request for the specified payment method:
* * [Create Payment Transaction]{@link TranspayrentUIHelper#getCreateTransactionRequest}
* * [Initialize Payment]{@link TranspayrentUIHelper#getInitializePaymentRequest}
* * [Authenticate Consumer]{@link TranspayrentUIHelper#getAuthenticateConsumerRequest}
* * [Authorize Payment]{@link TranspayrentUIHelper#getAuthorizePaymentRequest}
* * [Save]{@link TranspayrentUIHelper#getSaveRequest}
* 2) Construct requests for each operation: createTransaction, initialize, authenticate, authorize and save
* 3) Call the [Complete the Payment using the Transpayrent UI Helper SDK]{@link TranspayrentUIHelper#completePayment}
*
* @see [TranspayrentUIHelper]{@link TranspayrentUIHelper}
* @see {@link TranspayrentUIHelper#getCreateTransactionRequest}
* @see {@link TranspayrentUIHelper#getInitializePaymentRequest}
* @see {@link TranspayrentUIHelper#getAuthenticateConsumerRequest}
* @see {@link TranspayrentUIHelper#getAuthenticateConsumerRequest}
* @see {@link TranspayrentUIHelper#getAuthorizePaymentRequest}
* @see {@link TranspayrentUIHelper#getSaveRequest}
* @see {@link TranspayrentUIHelper#displayStatusMessage}
* @see {@link TranspayrentUIHelper#displayAuthenticationFailure}
* @see {@link TranspayrentUIHelper#displayPaymentConfirmation}
*
* @constructor
* @description Creates a new instance of the Transpayrent UI Helper SDK, which uses the Transpayrent SDK internally to handle the secure communication with Transpayrent's Payment Gateway.
* @example <caption>Instantiate the Transpayrent SDK and Transpayrent UI Helper SDK</caption>
* <script src="https://storage.googleapis.com/static.[ENVIRONMENT].transpayrent.cloud/v1/swagger-client.js"></script>
* <script src="https://storage.googleapis.com/static.[ENVIRONMENT].transpayrent.cloud/v1/transpayrent.js"></script>
* <script>
* // Override UI Helper SDK methods to return the correct request for the specified payment method
* TranspayrentUIHelper.prototype.getCreateTransactionRequest = function (paymentMethodId) {
* return createTransactionRequests[paymentMethodId];
* }
* TranspayrentUIHelper.prototype.getInitializePaymentRequest = function (paymentMethodId) {
* return initializePaymentRequests[paymentMethodId];
* }
* TranspayrentUIHelper.prototype.getAuthenticateConsumerRequest = function (paymentMethodId) {
* return authenticateConsumerRequests[paymentMethodId];
* }
* TranspayrentUIHelper.prototype.getAuthorizePaymentRequest = function (paymentMethodId) {
* return authorizePaymentRequests[paymentMethodId];
* }
* TranspayrentUIHelper.prototype.getSaveRequest = function (paymentMethodId) {
* return saveRequests[paymentMethodId];
* }
*
* // SDK configuration
* const transpayrentConfig = {
* merchantId: [UNIQUE MERCHANT ID ASSIGNED BY TRANSPAYRENT],
* sessionId: [ID IN THE RESPONSE FROM "Create Payment Session"],
* accessToken: '[x-transpayrent-access-token HTTP HEADER IN THE RESPONSE FROM "Create Payment Session"]'
* };
* const url = 'https://generator.[ENVIRONMENT].transpayrent.cloud/v1/'+ transpayrentConfig.merchantId +'/system/PAYMENT_GATEWAY/sdk/CLIENT';
*
* // Instantiate SDK and UI Helper SDK
* const sdk = new Transpayrent(url, transpayrentConfig);
* const helper = new TranspayrentUIHelper(sdk, document.getElementById('container'), document.getElementById('loader') );
*
* // Construct requests for each operation: createTransaction, initialize, authenticate, authorize and save
* var card = { card_number: 4111111111111111, // Successful Authorization: Manual Challenge with browser fingerprint
* expiry_month: 9,
* expiry_year: 22,
* cvv: 987,
* card_holder_name: 'John Doe',
* save: true };
* card.payment_method_id = sdk.getPaymentMethodId(card.card_number);
* var address = { street : 'Arne Jacobsens Allé 7',
* appartment : '5. sal',
* city : 'Copenhagen S',
* postal_code : '2300',
* state : '82',
* country: 208 }; // Denmark
* var phone = { international_dialing_code: 45, // Denmark
* phone_number: 89671108 };
* var email = 'hello@transpayrent.dk';
*
* var createTransactionRequests = new Array();
* // Card based payment
* createTransactionRequests[card.payment_method_id] = { correlation_id : 'TP-'+ transpayrentConfig.sessionId,
* amount: { currency : 208, // DKK
* value : card.save ? 0 : 100 } };
* // Transpayrent - Consumer Wallet
* createTransactionRequests[201] = { correlation_id : 'TP-'+ transpayrentConfig.sessionId,
* amount: { currency : 208, // DKK
* value : 100 } };
* // MobilePay Online
* createTransactionRequests[202] = { correlation_id : 'TP-'+ transpayrentConfig.sessionId,
* amount: { currency : 208, // DKK
* value : 100 } };
*
* var initializePaymentRequests = new Array();
* // Card based payment
* initializePaymentRequests[card.payment_method_id] = { payment_method_id : card.payment_method_id }
* // Consumer Wallet
* initializePaymentRequests[201] = { payment_method_id : 201 }
* // MobilePay Online
* initializePaymentRequests[202] = { payment_method_id : 202,
* mobile : phone,
* save : false };
*
* var authenticateConsumerRequests = new Array();
* // Card based payment
* authenticateConsumerRequests[card.payment_method_id] = { payment_details : card,
* billing_address : address,
* shipping_address : address,
* contact : { mobile : phone,
* work : phone,
* home : phone,
* email : email } };
*
* var authorizePaymentRequests = new Array();
* // Card based payment
* authorizePaymentRequests[card.payment_method_id] = { payment_details : card };
* // Consumer Wallet
* authorizePaymentRequests[201] = { payment_details : { payment_method_id : 201,
* valuable_id : [VALUEABLE ID IN THE RESPONSE FROM "Create Payment Session"],
* access_token : '[MERCHANT'S SINGLE SIGN-ON TOKEN FOR AUTHORIZING A PAYMENT WITH THE CONSUMER'S WALLET]' } };
* // MobilePay Online
* authorizePaymentRequests[202] = { payment_details : { payment_method_id : 202 } };
*
* var saveRequest = { payment_details : card,
* consumer_id : '[MERCHANT'S CONSUMER ID FROM PAYMENT SESSION]',
* name : "My VISA Card" }
*
* // Complete the payment using the UI Helper SDK
* var paymentMethodId = 201; // Consumer Wallet
* helper.completePayment(paymentMethodId, card.save);
* </script>
*
* @param {Transpayrent} sdk The instance of the Transpayrent SDK, which will be used by the UI Helper
* @param {Element} container The container element in which the constructed iframe for initializing a payment with a 3rd party wallet such as MobilePay or authenticating the consumer using 3D Secure will be displayed. Defaults to `document.body`.
* @param {Element} loader The loading indicator element
*/
function TranspayrentUIHelper(sdk, container, loader) {
/**
* The instance of the Transpayrent SDK, which will be used by the UI Helper
*
* @private
*
* @type {Transpayrent}
*/
this._sdk = sdk;
/**
* The container element in which the constructed iframe for initializing a payment with a 3rd party wallet such as MobilePay or authenticating the consumer using 3D Secure will be displayed. Defaults to `document.body`.
*
* @private
*
* @type {Element}
*/
this._container = container;
/**
* The loading indicator element
*
* @private
*
* @type {Element}
*/
this._loader = loader;
/* ========== COMMON METHODS START ========== */
/**
* A representation of the configuration for completing the consumer's payment
*
* @property {boolean} disable_authentication Boolean flag specifying whether Strong Consumer Authentication (SCA) should be disabled.
* Explicitly disabling SCA is useful for Mail Order / Telephone Order (MOTO) transactions.
*
* @typedef {Object} Configuration
*/
/**
* Completes the consumer's payment by executing the following steps:
* 1) Create a payment transaction with the specified payment method
* 2) Initialize the payment for the specified payment method
* 3) Complete Strong Consumer Authentication (SCA) using 3D Secure if applicable
* 4) Authorize the payment for the payment transaction using the specified payment method
*
* @see {@link Transpayrent#createTransaction}
* @see {@link Transpayrent#initialize}
* @see {@link Transpayrent#authenticate}
* @see {@link Transpayrent#authorize}
* @see {@link Transpayrent#save}
* @see {@link TranspayrentUIHelper#displayStatusMessage}
* @see {@link TranspayrentUIHelper#displayAuthenticationFailure}
* @see {@link TranspayrentUIHelper#displayPaymentConfirmation}
* @see {@link TranspayrentUIHelper#getCreateTransactionRequest}
* @see {@link TranspayrentUIHelper#getInitializePaymentRequest}
* @see {@link TranspayrentUIHelper#getAuthenticateConsumerRequest}
* @see {@link TranspayrentUIHelper#getAuthorizePaymentRequest}
* @see {@link TranspayrentUIHelper#getSaveRequest}
* @see {@link TranspayrentUIHelper#getIFrameConfig}
*
* @param {PAYMENT_METHOD_ID} paymentMethodId The payment method selected by the consumer
* @param {boolean} save Boolean flag indicating whether the payment details for the specified payment method will be saved
* @param {Configuration} config Configuration for completing the consumer's payment
*/
TranspayrentUIHelper.prototype.completePayment = function (paymentMethodId, save, config) {
if (save == null) {
save = this.getInitializePaymentRequest(paymentMethodId).save;
}
config = Object.assign({ disable_authentication : false },
config);
if (this._container) {
this._container.style.visibility = 'visible';
}
var request = this.getCreateTransactionRequest(paymentMethodId);
var token = null;
const amount = request.amount;
this._sdk.createTransaction(paymentMethodId, request)
.then(
transaction => {
// Creation of Payment Transaction failed - Display status message
if (transaction.status) {
this.displayStatusMessage(transaction);
return Promise.reject(null);
}
else {
var cfg = this.getIFrameConfig('payment-initialization');
if (cfg.container) {
this._sdk.clearContainer(cfg.container);
}
return this._sdk.initialize(transaction.id, this.getInitializePaymentRequest(paymentMethodId), cfg);
}
})
.then(
initialization => {
// Initialization of Payment Transaction failed - Display status message
if (initialization.status) {
this.displayStatusMessage(initialization);
return Promise.reject(null);
}
else {
// Token returned by a 3rd party wallet such as Apple Pay or Google Pay
if (initialization.hasOwnProperty('token') ) {
token = initialization.token;
}
// Redirect or App-Switch
if (initialization.connection && initialization.connection.action <= 2) {
window.location.href = initialization.connection.url;
}
// Payment Transaction is exempt from Strong Consumer Authentication (SCA) or SCA disabled
else if (this._sdk.isSCAExempt(paymentMethodId, amount, save) || config.disable_authentication) {
return Promise.resolve(initialization);
}
else {
request = this.getAuthenticateConsumerRequest(paymentMethodId);
// Perform Strong Consumer Authentication (SCA)
if (request) {
// Use token returned by a 3rd party wallet such as Google Pay for strong consumer authentication
if (token) {
request.payment_details.token = token;
}
var cfg = this.getIFrameConfig('consumer-authentication');
if (cfg.container) {
this._sdk.clearContainer(cfg.container);
}
return this._sdk.authenticate(initialization.transaction_id, request, cfg);
}
// Strong Consumer Authentication (SCA) not available for Payment Method
else {
return Promise.resolve(initialization);
}
}
}
})
.then(
authentication => {
// Consumer Authentication failed
if (authentication.status) {
// Consumer Authentication failure
if (authentication.status == 511) {
this.displayAuthenticationFailure(authentication);
}
// API request failed - Display status message
else {
this.displayStatusMessage(authentication);
}
return Promise.reject(null);
}
// Payment Transaction is exempt from Strong Consumer Authentication (SCA) or SCA disabled
else if (this._sdk.isSCAExempt(paymentMethodId, amount, save) || config.disable_authentication) {
request = this.getAuthorizePaymentRequest(paymentMethodId);
// Use token returned by a 3rd party wallet such as Apple Pay or Google Pay for payment authorization
if (token) {
if (request.hasOwnProperty('payment_details') ) {
request.payment_details.token = token;
}
else {
request.token = token;
}
}
return this._sdk.authorize(authentication.transaction_id, request);
}
// Consumer Authentication successfully completed: Save Payment Card
else if (save) {
request = this.getSaveRequest(paymentMethodId);
// Use token returned by a 3rd party wallet such as Apple Pay or Google Pay for payment authorization
if (token) {
request.payment_details.token = token;
}
request.payment_details.cryptogram = authentication.cryptogram;
return this._sdk.save(authentication.transaction_id, request);
}
// Consumer Authentication successfully completed: Authorize Payment
else {
request = this.getAuthorizePaymentRequest(paymentMethodId);
if (request.hasOwnProperty('payment_details') ) {
// Use token returned by a 3rd party wallet such as Apple Pay or Google Pay for payment authorization
if (token) {
request.payment_details.token = token;
}
request.payment_details.cryptogram = authentication.cryptogram;
}
else {
// Use token returned by a 3rd party wallet such as Apple Pay or Google Pay for payment authorization
if (token) {
request.token = token;
}
request.cryptogram = authentication.cryptogram;
}
return this._sdk.authorize(authentication.transaction_id, request);
}
})
.then(
authorization => {
// Payment Authorization failed - Display status message
if (authorization.status) {
// "Additional authentication" required
if (authorization.messages[0].code == 640416) {
request = this.getAuthenticateConsumerRequest(paymentMethodId);
// Perform Strong Consumer Authentication (SCA)
if (request) {
// Use token returned by a 3rd party wallet such as Google Pay for strong consumer authentication
if (token) {
request.payment_details.token = token;
}
var cfg = this.getIFrameConfig('consumer-authentication');
if (cfg.container) {
this._sdk.clearContainer(cfg.container);
}
this._sdk.authenticate(authorization.transaction_id, request, cfg)
.then(
authentication => {
// Consumer Authentication failed
if (authentication.status) {
// Consumer Authentication failure
if (authentication.status == 511) {
this.displayAuthenticationFailure(authentication);
}
// API request failed - Display status message
else {
this.displayStatusMessage(authentication);
}
return Promise.reject(null);
}
else {
request = this.getAuthorizePaymentRequest(paymentMethodId);
if (request.hasOwnProperty('payment_details') ) {
// Use token returned by a 3rd party wallet such as Apple Pay or Google Pay for payment authorization
if (token) {
request.payment_details.token = token;
}
request.payment_details.cryptogram = authentication.cryptogram;
}
else {
// Use token returned by a 3rd party wallet such as Apple Pay or Google Pay for payment authorization
if (token) {
request.token = token;
}
request.cryptogram = authentication.cryptogram;
}
this._sdk.authorize(authentication.transaction_id, request)
.then(
reauth => {
// Payment Authorization failed - Display status message
if (reauth.status) {
this.displayStatusMessage(reauth);
}
// Payment Authorization completed - Display success message
else {
this.displayPaymentConfirmation(reauth);
}
}
)
.catch(reason => {
// Low level error - Display error message
if (reason) {
console.error(`Internal error: ${reason} ${reason.stack}`);
}
if (this._container) {
this._container.style.visibility = 'hidden';
}
});
}
}
)
.catch(reason => {
// Low level error - Display error message
if (reason) {
console.error(`Internal error: ${reason} ${reason.stack}`);
}
if (this._container) {
this._container.style.visibility = 'hidden';
}
});
}
else {
this.displayStatusMessage(authorization);
}
}
else {
this.displayStatusMessage(authorization);
}
}
// Payment Authorization completed - Display success message
else {
this.displayPaymentConfirmation(authorization);
}
})
.catch(reason => {
// Low level error - Display error message
if (reason) {
console.error(`Internal error: ${reason} ${reason.stack}`);
}
if (this._container) {
this._container.style.visibility = 'hidden';
}
});
}
/* ========== COMMON METHODS END ========== */
}
/* ========== CALLBACK METHODS START ========== */
/**
* Displays the details of a failed API request.
* It is strongly recommended to override this method as the default implementation will simply display an alert with the localized message.
*
* @example <caption>Override displayStatusMessage</caption>
* TranspayrentUIHelper.prototype.displayStatusMessage = function (result) {
* this._sdk.getLocalizedMessage(`API: ${result.api} failed with HTTP Status Code: ${result.status} and error: ${result.messages[0].message} (${result.messages[0].code})`)
* .then(text => {
* if (this._container) {
* this._container.style.visibility = 'hidden';
* }
* alert(text);
* });
* }
*
* @see {@link Transpayrent#getLocalizedMessage}
*
* @param {Status} result The status object with a list of status message(s) returned by the Payment Gateway
*/
TranspayrentUIHelper.prototype.displayStatusMessage = function (result) {
this._sdk.getLocalizedMessage(`API: ${result.api} failed with HTTP Status Code: ${result.status} and error: ${result.messages[0].message} (${result.messages[0].code})`)
.then(text => {
if (this._container) {
this._container.style.visibility = 'hidden';
}
alert(text);
});
}
/**
* Displays the details of a failure during Strong Consumer Authentication (SCA).
* It is strongly recommended to override this method as the default implementation will simply display an alert with the localized message.
*
* @example <caption>Override displayAuthenticationFailure</caption>
* TranspayrentUIHelper.prototype.displayAuthenticationFailure = function (result) {
* this._sdk.getLocalizedStatusMessage(result.status_code)
* .then(text => {
* if (this._container) {
* this._container.style.visibility = 'hidden';
* }
* var message = document.createElement('h1');
* message.className = 'failure';
* message.innerHTML = '<img id="loader" src="/failure.png" width="30" height="30" alt="- completed -" />';
* message.innerHTML += ` ${text} (${result.status_code})`;
* document.body.appendChild(message);
* });
* }
*
* @see {@link Transpayrent#getLocalizedStatusMessage}
*
* @param {AuthenticationFailureResult} result The result of a failed authentication of the consumer for a payment transaction using Strong Consumer Authentication (SCA)
*/
TranspayrentUIHelper.prototype.displayAuthenticationFailure = function (result) {
this._sdk.getLocalizedStatusMessage(result.status_code)
.then(text => {
if (this._container) {
this._container.style.visibility = 'hidden';
}
alert(`${text} (${result.status_code})`);
});
}
/**
* Displays the details of the successfully completed payment transaction.
* It is strongly recommended to override this method as the default implementation will simply display an alert with the localized message.
*
* @example <caption>Override displayPaymentConfirmation</caption>
* TranspayrentUIHelper.prototype.displayPaymentConfirmation = function (result) {
* this._sdk.getLocalizedMessage(`Payment transaction: ${authorization.transaction_id} successfully authorized`)
* .then(text => {
* if (this._container) {
* this._container.style.visibility = 'hidden';
* }
* var message = document.createElement('h1');
* message.className = 'success';
* message.innerHTML = '<img id="loader" src="/success.png" width="30" height="30" alt="- completed -" />';
* message.innerHTML += ` ${text}`;
* document.body.appendChild(message);
* });
* }
*
* @see {@link Transpayrent#getLocalizedMessage}
*
* @param {AuthorizePaymentResponse} authorization The details for the successful authorization of a payment transaction
*/
TranspayrentUIHelper.prototype.displayPaymentConfirmation = function (authorization) {
this._sdk.getLocalizedMessage(`Payment transaction: ${authorization.transaction_id} successfully authorized`)
.then(text => {
if (this._container) {
this._container.style.visibility = 'hidden';
}
alert(text);
});
}
/**
* Returns a representation of the consumer's payment transaction.
*
* @see {@link Transpayrent#createTransaction}
*
* @param {PAYMENT_METHOD_ID} paymentMethodId The payment method selected by the consumer
* @returns {CreatePaymentTransactionRequest} A representation of the consumer's payment transaction.
*/
TranspayrentUIHelper.prototype.getCreateTransactionRequest = function (paymentMethodId) {
console.error(`Method: getCreateTransactionRequest not overriden`);
return null;
}
/**
* Returns a representation of the initialization details provided by the consumer the selected payment method
*
* @see {@link Transpayrent#initialize}
*
* @param {PAYMENT_METHOD_ID} paymentMethodId The payment method selected by the consumer
* @returns {InitializationDetails} A representation of the initialization details provided by the consumer the selected payment method
*/
TranspayrentUIHelper.prototype.getInitializePaymentRequest = function (paymentMethodId) {
console.error(`Method: getInitializePaymentRequest not overriden`);
return null;
}
/**
* Returns a representation of the entered payment details required for strong authentication of a payment transaction using 3D Secure or equivalent.
*
* @see {@link Transpayrent#authenticate}
*
* @param {PAYMENT_METHOD_ID} paymentMethodId The payment method selected by the consumer
* @returns {AuthenticateConsumerRequest} A representation of the entered payment details required for strong authentication of a payment transaction using 3D Secure or equivalent.
* Please note that the `browser` property will be constructed automatically.
*/
TranspayrentUIHelper.prototype.getAuthenticateConsumerRequest = function (paymentMethodId) {
console.error(`Method: getAuthenticateConsumerRequest not overriden`);
return null;
}
/**
* Returns a representation of a payment details (card details, wallet details, voucher details etc.), which will be used to authorize the payment for the payment transaction
*
* @see {@link Transpayrent#authorize}
*
* @param {PAYMENT_METHOD_ID} paymentMethodId The payment method selected by the consumer
* @returns {PaymentDetails} A representation of a payment details (card details, wallet details, voucher details etc.), which will be used to authorize the payment for the payment transaction
*/
TranspayrentUIHelper.prototype.getAuthorizePaymentRequest = function (paymentMethodId) {
console.error(`Method: getAuthorizePaymentRequest not overriden`);
return null;
}
/**
* Returns the payment details for the payment card which will be securely stored in Transpayrent's Secure Valut and added to the consumer's wallet
*
* @see {@link Transpayrent#save}
*
* @param {PAYMENT_METHOD_ID} paymentMethodId The payment method selected by the consumer
* @returns {SaveConsumerValuableRequest} The payment details for the payment card which will be securely stored in Transpayrent's Secure Valut and added to the consumer's wallet
*/
TranspayrentUIHelper.prototype.getSaveRequest = function (paymentMethodId) {
console.error(`Method: getSaveRequest not overriden`);
return null;
}
/**
* Returns the configuration for the iframe element which is used when initializing the payment through a 3rd party wallet such as MobilePay
* or displaying the authentication challenge during a 3D Secure flow.
* The default implementation will apply a CSS class with the name of the action: `consumer-authentication` or `payment-initialization` to the constructed iframe.
*
* @see {@link Transpayrent#displayChallenge}
* @see {@link Transpayrent#displayPaymentInitialization}
*
* @param {String} action The action for which the iframe will be constructed, may be one of:
* - `consumer-authentication`
* - `payment-initialization`
* @returns {IFrameConfig} The configuration for the iframe element which is used when initializing the payment through a 3rd party wallet such as MobilePay or displaying the authentication challenge
*/
TranspayrentUIHelper.prototype.getIFrameConfig = function (action) {
const loader = this._loader;
var config = { container : this._container,
css: action,
callback : function (event, iframe) {
switch (event) {
case 'payment-initialization-initiated':
case 'authentication-challenge-initiated':
if (loader) {
loader.style.visibility = 'hidden';
}
break;
case 'payment-initialization-completed':
case 'authentication-challenge-completed':
if (loader) {
loader.style.visibility = 'inherit';
}
break;
default:
console.warn(`Unsupported event: ${event}`);
break;
}
} };
return config;
}
/* ========== CALLBACK METHODS END ========== */