Source: libPayment.ds


/**
 * Container for a Payment Requests. Provides Payment Parameters for Redirect or Direct Link requests.
 *
 * Requirements:
 * - Payment Processor of appropriated Payment Method must point to Payment Method ID (prefixed by PAYMENT_PROCESSOR_PREFIX)
 * - Appropiated Credit Card Type must be same as according BRAND (eg. VISA, MasterCard etc.)
 * 
 * @module lib/libPayment.ds
 */

var dwutil = require('dw/util'),
	dworder = require('dw/order'),
	dwcustomer = require('dw/order'),
	dwsystem = require('dw/system'),
	dwweb = require('dw/web'),
	dwnet = require('dw/net'),
	dwcrypto = require('dw/crypto'),
	dwvalue = require('dw/value'),
	dwsvc = require('dw/svc'),
	dwobject = require('dw/object');

/**
 * @constructor  
 * 
 * @param {dw.order.LineItemCtnr} lineItemCtnr
 * @param {String} orderNo
 * @param {Number} amount
 * @param {String} paymentMethod
 * @param {dw.system.Request} request
 * @param {String} locale
 */
function PaymentCtnr(lineItemCtnr, orderNo, amount, paymentMethod, request, locale) {
	
	/************* Constructor - initialize general container members *******/
	try {
		var IngenicoConfiguration = getIngenicoConfiguration();
		
		var PAYMENT_PROCESSOR_PREFIX = "PAYMENT_";
		var DEFAULT_LANGUAGE = "fr_FR";
		var ACCEPT_URL = dwweb.URLUtils.https('PAYMENT_HOSTED_TOKENIZATION_PAGE_CREDIT_CARD-3DSAccept');
		var DECLINE_URL = dwweb.URLUtils.https('PAYMENT_HOSTED_TOKENIZATION_PAGE_CREDIT_CARD-3DSDecline');
		var EXCEPTION_URL = dwweb.URLUtils.https('PAYMENT_HOSTED_TOKENIZATION_PAGE_CREDIT_CARD-3DSExcept');
		// use same URL for back and cancel
		var CANCEL_URL = dwweb.URLUtils.https('PAYMENT_HOSTED_TOKENIZATION_PAGE_CREDIT_CARD-3DSCancel');
		var BACK_URL = dwweb.URLUtils.https('PAYMENT_HOSTED_TOKENIZATION_PAGE_CREDIT_CARD-3DSCancel');
		// Operation should be left empty otherwise back office configuration
		// will be overritten!
		var OPERATION = "";
		var TEMPLATE_URL = dwweb.URLUtils.https('PAYMENT_HOSTED_TOKENIZATION_PAGE_CREDIT_CARD-Template');
		var log = dwsystem.Logger.getLogger("payment");

		//login data
		var pspId = IngenicoConfiguration.PSPID;
		var userId = IngenicoConfiguration.apiUserID;
		var userPwd = IngenicoConfiguration.apiUserPassword;
		
		//orig
		var orig = IngenicoConfiguration.transactionTracker;

		var orderId = orderNo;
		var order = lineItemCtnr;
		var payId = null;
		var amount = !empty(amount) ? (amount*100).toFixed() : null;
		var address = null;
		var profile = null;
		var email = null;
		var fullName = null; //firstname + lastname
		var address1 = null;
		var city = null;
		var postalCode = null;
		var phoneNo = null;
		var country = null;
		var language = !empty(request) ? dwutil.Locale.getLocale(request.getLocale()).ID : null;
		var customLanguage = IngenicoConfiguration.language;

		// Fallback to Fr_fr
		language = empty(language) ? DEFAULT_LANGUAGE : language;

		if (customLanguage != null) {
			language = customLanguage ;	
		}

		var currency = null;
		var ccNumber = null;
		var ccExpiry = null;
		var ccHolder = null;
		var cardType = null;
		var paypaltxtoken=null;
		var paypalpayid=null;
		var billFirstname=null;
		var billLastname=null;
		var civility=null;

		// set DW Payment Method ID if specified - otherwise Payment Method
		// of first Payment instrument is used
		var paymentMethodID = !empty(paymentMethodID) ? paymentMethodID : null;
		var paymentMethod = null;

		// Status of Payment 0 - Incomplete or invalid ; 5 - Authorized;9 - Payment requested ...
		var status = null;

		// SHA
		var shaIn = IngenicoConfiguration.shaPasword;
		var hashMethod = hashMethod || IngenicoConfiguration.shaMethod;
		var digest = new dwcrypto.MessageDigest(hashMethod);

		// Basket or Order	
		if (!empty(lineItemCtnr)) {
			// Get Payment Instrument of specified Payment Method - otherwise first Payment Instrument is used
			var paymentInstruments = !empty(paymentMethodID) ? lineItemCtnr.getPaymentInstruments(paymentMethodID) : lineItemCtnr.getPaymentInstruments();
			if (!paymentInstruments.empty) {
				// this is only one value
				var paymentInstrument = paymentInstruments[0];
				if (!empty(paymentInstrument)) {
					var paymentTransaction = paymentInstrument.getPaymentTransaction();
					// Set Payment Method again to ensure appropiate Payment Method is used
					paymentMethodID = paymentInstrument.getPaymentMethod();
					// Retrieve Payment Method from Payment Processor
					paymentMethod = dworder.PaymentMgr.getPaymentMethod(paymentMethodID).getPaymentProcessor().getID().replace(PAYMENT_PROCESSOR_PREFIX, "");

					if (paymentMethod!='ECOMMERCE_PAYPAL') {
						cardType = paymentInstrument.getCreditCardType();

						// Used for Credit Card Direct Link
						ccNumber =  paymentInstrument.getCreditCardNumber();
						ccExpiry = formatExpiryNumbers(paymentInstrument.getCreditCardExpirationMonth(), paymentInstrument.getCreditCardExpirationYear());
						ccHolder =  paymentInstrument.getCreditCardHolder();
					} else {
						paypaltxtoken = paymentInstrument.custom.paypalToken;
						paypalpayid = paymentInstrument.custom.paypalPayerID;
					}
					if (!empty(paymentTransaction)) {
						// Retrieve Amount if not specified
						amount = empty(amount) ? (paymentTransaction.getAmount().getValue()*100).toFixed() : amount;
						if ((amount) <= 0) {
							throw Error("Invalid authorization Amount 0.00 at payment transaction; Basket total gross price: " + lineItemCtnr.getTotalGrossPrice());
						}
						payId = paymentTransaction.getTransactionID();
						status = paymentTransaction.custom.paymentStatus;
					} else {
						throw Error("No Payment Trancsaction available");
					}
				} else {
					throw Error("No Payment Instrument available");
				}
			} else {
				throw Error("No Payment Instrument available");
			}

			var currency = lineItemCtnr.getCurrencyCode(); 

			email = lineItemCtnr.getCustomerEmail();

			// Get Address
			address = lineItemCtnr.getBillingAddress();
			if (!empty(address)) {
				fullName = address.getFullName();
				address1 = address.getAddress1();
				city = address.getCity();
				postalCode = address.getPostalCode();
				phoneNo = address.getPhone();
				country = address.getCountryCode();
				billFirstname=address.getFirstName();
				billLastname=address.getLastName();
			}

			var customer = lineItemCtnr.getCustomer();
			if (!empty(customer) && customer.isAuthenticated()) {
				profile = customer.getProfile();
				if (profile.gender.value==1) {
					civility='M';
				} else {
					civility='F';
				}
			}
			
			var payIdSub = lineItemCtnr.custom.payment_payidSUB;
		}
	} catch (error) {
		throw error;
	}

	/************* General setter used for Payment Direct Link *******/

	this.setPaymentMethod = function(pm) {
		paymentMethod = pm;
	};

	/************* General getter used for Payment Redirect and Payment Direct Link *******/

	this.getPspId = function() {
		return pspId;
	};
	
	this.getOrder = function() {
		return order;
	};

	this.getUserId = function() {
		return userId;
	};

	this.getUserPwd = function() {
		return userPwd;
	};

	this.getOrderId = function() {
		return orderId;
	};

	this.getAmount = function() {
		return amount;
	};

	this.getEmail = function() {
		return email;
	};

	this.getCurrency = function() {
		return currency;
	};

	this.getLanguage = function() {
		return language;
	};

	this.getFullName = function() {
		return fullName;
	};

	this.getAddress1 = function(value) {
		return address1;
	};

	this.getCity = function() {
		return city;
	};

	this.getPostalCode = function() {
		return postalCode;
	};

	this.getPhoneNo = function() {
		return phoneNo;
	};

	this.getCountry = function() {
		return !empty(country) ? country.value : country;
	};

	this.getAcceptUrl = function() {
		return ACCEPT_URL;
	};

	this.getDeclineUrl = function() {
		return DECLINE_URL;
	};

	this.getExceptionUrl = function() {
		return EXCEPTION_URL;
	};

	this.getCancelUrl = function() {
		return CANCEL_URL;
	};

	this.getBackUrl = function() {
		return BACK_URL;
	};

	this.getOperation = function() {
		return OPERATION;
	};

	this.getTemplateUrl = function() {
		return TEMPLATE_URL;
	};

	this.getPaymentMethodId = function() {
		return paymentMethodID;
	};

	this.getCreditCardType = function() {
		return cardType;
	};

	this.getPaymentProcessorId = function() {
		return (PAYMENT_PROCESSOR_PREFIX + paymentMethod).toUpperCase();
	};

	this.getPaymentMethod = function() {
		return paymentMethod;
	};

	this.getCCHolder = function() {
		return ccHolder;
	};

	this.getCCNumber = function() {
		return ccNumber;
	};

	this.getCCExpiry = function() {
		return ccExpiry;
	};

	this.getPayId = function() {
		return payId;
	};
	
	this.getPayIdSub = function() {
		return payIdSub;
	};

	this.getStatus = function() {
		return status;
	};

	this.getPayPalTXTOKEN = function() {
		return paypaltxtoken;
	};

	this.getPayPalPayID = function() {
		return paypalpayid;
	};

	this.getBillFirstName = function() {
		return billFirstname;
	};

	this.getBillLastName = function() {
		return billLastname;
	};

	this.getCivility = function() {
		return civility;
	};
	
	this.getOrig = function() {
		return orig;
	};
	
	// Status critical issue fix
	this.checkStatus = function(statusCode, errorCode) {
		// Possible results are: FAIL, RETRY, SUCCESS
		var result = "SUCCESS";
		switch(statusCode.toString()) {
			case "0": //0 Incomplete or invalid
				if(this.retryErrorCodes(errorCode))
					result = "RETRY";
				else 
					result = "FAIL";
				break;
			
			case "1": //1 Cancelled by customer
				if(this.retryErrorCodes(errorCode))
					result = "RETRY";
				else 
					result = "FAIL";		
				break;
			
			case "2": //2 Authorisation declined
				if(this.retryErrorCodes(errorCode))
					result = "RETRY";
				else 
					result = "FAIL";	
				break;
				
			case "6": //6 Authorised and cancelled
				if(this.retryErrorCodes(errorCode))
					result = "RETRY";
				else 
					result = "FAIL";		
				break;
				
			case "7": //7 Payment deleted
				if(this.retryErrorCodes(errorCode))
					result = "RETRY";
				else 
					result = "FAIL";			
				break;				
				
			default:
				result = "SUCCESS";
				break;				
		}
		
		if(statusCode.toString().length > 1 && statusCode.toString() != "93") {
			result = "RETRY";		
		}
		
		return result;
	};
	
	// Error codes list with RETRY
	this.retryErrorCodes = function(errorCode) {
		var retryErrorCodes = {
		'0020001001': 'Authorisation failed. Please retry.',
		'0020001002': 'Authorisation failed. Please retry.',
		'0020001003': 'Authorisation failed. Please retry.',
		'0020001004': 'Authorisation failed. Please retry.',
		'0020001005': 'Authorisation failed. Please retry.',
		'0020001006': 'Authorisation failed. Please retry.',
		'0020001007': 'Authorisation failed. Please retry.',
		'0020001008': 'Authorisation failed. Please retry.',
		'0020001009': 'Authorisation failed. Please retry.',
		'0020001010' : 'Authorisation failed. Please retry.',
		'30001010' : 'A technical problem has occurred. Please contact the helpdesk.',
		'30001011' : 'A technical problem has occurred. Please contact the helpdesk.',
		'30001015' : 'There has been a connection error to the receiving bank. Please try later or choose another payment method.',
		'30001057' : 'There has been a connection error to the receiving bank. Please try later or choose another payment method.',
		'30001058' : 'There has been a connection error to the receiving bank. Please try later or choose another payment method.',
		'30001998' : 'A technical problem has occurred. Please try again.',
		'30001999' : 'There has been a connection error with the receiving bank. Please try later or choose another payment method.',
		'30611001' : 'Amount exceeds card limit',
		'30961001' : 'Processing temporarily not possible',
		'40001001': 'A technical problem has occurred. Please try again.',
		'40001002': 'A technical problem has occurred. Please try again.',
		'40001003': 'A technical problem has occurred. Please try again.',
		'40001004': 'A technical problem has occurred. Please try again.',
		'40001005': 'A technical problem has occurred. Please try again.',
		'40001006': 'A technical problem has occurred. Please try again.',
		'40001007': 'A technical problem has occurred. Please try again.',
		'40001008': 'A technical problem has occurred. Please try again.',
		'40001009' : 'A technical problem has occurred. Please try again.',
		'40001010' : 'A technical problem has occurred. Please try again.',
		'40001012' : 'There has been a connection error with the receiving bank. Please try later or choose another payment method.',
		'40001018' : 'A technical problem has occurred. Please try again.',
		'40001019' : 'Sorry, an error has occurred during processing. Please retry the transaction (using the Back button of the browser). If the problem persists, contact your merchant\'s helpdesk.',
		'40001020' : 'Sorry, an error occurred during processing. Please retry the operation (using the Back button of the browser). If the problem persists, please contact your merchant\'s helpdesk.',
		'40001134' : 'Authentication failed. Please retry or cancel.',
		'40001135' : 'Authentication temporarily unavailable. Please retry or cancel.',
		'40001136' : 'Technical problem with your browser. Please retry or cancel.',
		'40001137' : 'Your bank is temporarily unavailable. Please try again later or choose another payment method.',
		'50001174' : 'Cardholder Name is too long'		
		};
		
		return retryErrorCodes[errorCode.toString()] != null ? true : false;
	}

	/************* Get request parameters for Redirect *******/

	this.getRequestParameters = function() {
		var query = '';

		if (empty(this.getOrderId()) || empty(this.getAmount()) || empty(this.getCurrency())) {
			throw Error("No Order ID, Amount and Currency available");
		}

		// Add parameter uppercase for sort of sha sign
		var paymentRequestParameters = new dwutil.HashMap();

		// Mandatory Parameters
		paymentRequestParameters.put('PSPID', this.getPspId());
		paymentRequestParameters.put('ORDERID', this.getOrderId());
		paymentRequestParameters.put('AMOUNT', this.getAmount());
		paymentRequestParameters.put('CURRENCY', this.getCurrency());
		paymentRequestParameters.put('LANGUAGE', this.getLanguage());

		// Email
		paymentRequestParameters.put('EMAIL', this.getEmail());

		// Address
		paymentRequestParameters.put('CN', this.getFullName());
		paymentRequestParameters.put('OWNERADDRESS', this.getAddress1());
		paymentRequestParameters.put('OWNERTOWN', this.getCity());
		paymentRequestParameters.put('OWNERZIP', this.getPostalCode());
		paymentRequestParameters.put('OWNERTELNO', this.getPhoneNo());
		paymentRequestParameters.put('OWNERCTY', this.getCountry());

		// Redirect URLS
		paymentRequestParameters.put('ACCEPTURL', this.getAcceptUrl());
		paymentRequestParameters.put('DECLINEURL', this.getDeclineUrl());
		paymentRequestParameters.put('EXCEPTIONURL', this.getExceptionUrl());
		paymentRequestParameters.put('CANCELURL', this.getCancelUrl());
		paymentRequestParameters.put('BACKURL', this.getBackUrl());

		//ORIG
		paymentRequestParameters.put('ORIG', this.getOrig());

		// Payment
		paymentRequestParameters.put('PM', this.getPaymentMethod());

		// Operation
		paymentRequestParameters.put('OPERATION', this.getOperation());

		// Template
		paymentRequestParameters.put('TP', this.getTemplateUrl());

		//Additional Parameters
		paymentRequestParameters.put('PARAMPLUS', 'dw_paymentmethodid=' + this.getPaymentMethodId());

		// add SHA Sign
		paymentRequestParameters.put("SHASIGN", getSHASignParameterFromRequest(paymentRequestParameters, shaIn, digest,this.getOrderId()));

		return paymentRequestParameters;
	};

	/************* Get parameters for Direct Link Requests *******/	


	//Paypal Order Query
	this.getPayPalOrderQuery = function(timeout) {
		var query = '';

		if (empty(this.getOrderId()) || empty(this.getAmount()) || empty(this.getCurrency())) throw Error("No Order ID, Amount and Currency available");

		// Add parameter alphabetical sorted for sha sign
		var queryMap = new dwutil.HashMap();

		// required for credit card

		queryMap.put("ORDERID", this.getOrderId());
		queryMap.put("AMOUNT", this.getAmount());
		queryMap.put("CURRENCY",this.getCurrency());
		queryMap.put("LANGUAGE",this.getLanguage());

		queryMap.put("EMAIL", this.getEmail());
		queryMap.put("OWNERADDRESS", this.getAddress1());
		queryMap.put("OWNERCTY", this.getCountry());
		queryMap.put("OWNERTELNO", this.getPhoneNo());
		queryMap.put("OWNERTOWN", this.getCity());
		queryMap.put("ORIG", this.getOrig());
		queryMap.put("OWNERZIP", this.getPostalCode());

		queryMap.put("TXTOKEN", this.getPayPalTXTOKEN());
		queryMap.put("PAYID", this.getPayPalPayID());

		queryMap.put("PM", "PAYPAL");

		queryMap.put("SHASIGN", this.getSHASignParameterFromQuery(queryMap). toUpperCase());

		return getURIParametersFromMap(queryMap);
	};

	//New Order Query for Direct Debits
	this.getNewOrderQueryDirectDebits = function(operation, data, timeout, locale, customer) {
		var query = '';

		if (empty(this.getOrderId()) || empty(this.getAmount()) || empty(this.getCurrency())) {
			throw Error("No Order ID, Amount and Currency available");
		}

		// Add parameter alphabetical sorted for sha sign
		var queryMap = new dwutil.HashMap();

		// required for direct debits
		queryMap.put("ORDERID", this.getOrderId());
		queryMap.put("AMOUNT", this.getAmount());
		if (!empty(operation)) {
			queryMap.put("OPERATION", operation);
		}
		
		queryMap.put("ED", this.getCCExpiry());
		
		queryMap.put("CURRENCY", this.getCurrency());
		queryMap.put("LANGUAGE", this.getLanguage());

		// address
		queryMap.put("CN", this.getCCHolder());
		
		queryMap.put("EMAIL", this.getEmail());			
		queryMap.put("OWNERADDRESS", this.getAddress1());
		queryMap.put("OWNERCTY", this.getCountry());
		queryMap.put("OWNERTELNO", this.getPhoneNo());
		queryMap.put("OWNERTOWN", this.getCity());
		queryMap.put("ORIG", this.getOrig());
		queryMap.put("OWNERZIP", this.getPostalCode());

		queryMap.put("PM", this.getPaymentMethodId().replace(/_/g, " "));

		// direct debits fields
		if (data['iban']) {
			queryMap.put("CARDNO", data['iban']);
		} else {
			if (data['konto'] && data['blz']) {
				queryMap.put("CARDNO", data['konto'] + 'BLZ' + data['blz']);
			}
		}

		// New SEPA version
		var enableNewSEPAVersion = IngenicoConfiguration.enableNewSEPAVersion;
		if (enableNewSEPAVersion) {
			queryMap.put("MANDATEID", this.getOrderId());
			if (data['transactionDate']) {
				var sequenceType = 'OOFF';
				if (data['bic']) {
					queryMap.put("BIC", data['bic']);
				}
				queryMap.put("SIGNDATE", data['transactionDate']);
				queryMap.put("SEQUENCETYPE", sequenceType);
			}
		}
		

		// add SHA Sign
		queryMap.put("SHASIGN", this.getSHASignParameterFromQuery(queryMap).toUpperCase());

		return getURIParametersFromMap(queryMap);
	};
	
	//New Order Query for Credit Card HostedTokenization	 
	this.getNewOrderQueryCreditCardHostedTokenization = function(operation, enable3DS, timeout,locale) {
		var query = '';

		if (empty(this.getOrderId()) || empty(this.getAmount()) || empty(this.getCurrency())) {
			throw Error("No Order ID, Amount and Currency available");
		}

		// Add parameter alphabetical sorted for sha sign
		var queryMap = new dwutil.HashMap();

		// required for credit card

		queryMap.put("ORDERID", this.getOrderId());
		queryMap.put("AMOUNT", this.getAmount());
			
		if (!empty(operation)) {
			queryMap.put("OPERATION", operation);
		}
		
		queryMap.put("CURRENCY",this.getCurrency());
		queryMap.put("LANGUAGE",this.getLanguage());

		
		queryMap.put("EMAIL", this.getEmail());			
		queryMap.put("OWNERADDRESS", this.getAddress1());
		queryMap.put("OWNERCTY", this.getCountry());
		queryMap.put("OWNERTELNO", this.getPhoneNo());
		queryMap.put("OWNERTOWN", this.getCity());
		queryMap.put("ORIG", this.getOrig());
		queryMap.put("OWNERZIP", this.getPostalCode());

		queryMap.put("PM", this.getPaymentMethod());
		
		//alias
		queryMap.put("ALIAS", session.custom.alias);	
		queryMap.put("ALIASUSAGE",true);

		// 3DS
		queryMap.put("FLAG3D", enable3DS ? "Y" : "N");
		if (enable3DS) {
			queryMap.put("ACCEPTURL", this.getAcceptUrl()+"?orderNo="+this.getOrderId()+"&pmID="+this.getPaymentMethod() + "&tn=" + createShaForUrls(this.getOrder()));
			queryMap.put("DECLINEURL", this.getDeclineUrl()+"?orderNo="+this.getOrderId()+"&pmID="+this.getPaymentMethod() + "&tn=" + createShaForUrls(this.getOrder()));
			queryMap.put("EXCEPTIONURL", this.getExceptionUrl()+"?orderNo="+this.getOrderId()+"&pmID="+this.getPaymentMethod() + "&tn=" + createShaForUrls(this.getOrder()));
			queryMap.put("ORIG", this.getOrig());
			queryMap.put("CANCELURL", this.getCancelUrl()+"?orderNo="+this.getOrderId()+"&pmID="+this.getPaymentMethod() + "&tn=" + createShaForUrls(this.getOrder()));
			queryMap.put("BACKURL", this.getBackUrl()+"?orderNo="+this.getOrderId()+"&pmID="+this.getPaymentMethod() + "&tn=" + createShaForUrls(this.getOrder()));
			queryMap.put("COMPLUS", locale);
			// necessary for return from redirect
			queryMap.put("PARAMPLUS", "dw_is3ds=true&dw_paymentmethodid=" + this.getPaymentMethodId());
		}

		// Request timeout value in seconds. The value you set here must be smaller than the
		// timeout value in DW!
		// queryMap.put("RTIMEOUT", (timeout / 1000) - 1);

		// add SHA Sign
		queryMap.put("SHASIGN", this.getSHASignParameterFromQuery(queryMap));

		return getURIParametersFromMap(queryMap);
	};

	
	//Direct Query
	this.getDirectQuery = function(timeout) {
		var query = '';

		if (empty(this.getOrderId()) || empty(this.getAmount()) || empty(this.getCurrency())) throw Error("No Order ID, Amount and Currency available");

		// Add parameter alphabetical sorted for sha sign
		var queryMap = new dwutil.HashMap();

		queryMap.put("ORDERID", this.getOrderId());
		queryMap.put("PAYID", this.getPayId());
		if(this.getPayIdSub() != null){
			queryMap.put("PAYIDSUB", this.getPayIdSub());
		}
		
		// add SHA Sign
		queryMap.put("SHASIGN", this.getSHASignParameterFromQuery(queryMap).toUpperCase());

		return getURIParametersFromMap(queryMap);
	};

	//Maintainance Query
	this.getMaintenanceQuery = function(operation, timeout) {
		var query = '';

		if (empty(this.getOrderId()) && empty(this.getPayId())) throw Error("No Order ID or Pay (transaction) ID available");

		// Add parameter alphabetical sorted for sha sign
		var queryMap = new dwutil.HashMap();

		// required for maintenace request
		queryMap.put("ORDERID", this.getOrderId());
		queryMap.put("PAYID", this.getPayId());
		queryMap.put("AMOUNT", this.getAmount());
		queryMap.put("OPERATION", operation);

		// Request timeout value in seconds. The value you set here must be smaller than the
		// timeout value in DW!
		// queryMap.put("RTIMEOUT", (timeout / 1000) - 1);

		// add SHA Sign
		queryMap.put("SHASIGN", this.getSHASignParameterFromQuery(queryMap));

		return getURIParametersFromMap(queryMap);
	};

	this.getCredentialQuery = function() {
		return dwutil.StringUtils.format("PSPID={0}&PSWD={2}&USERID={1}", encodeURIComponent(this.getPspId()), encodeURIComponent(this.getUserId()), encodeURIComponent(this.getUserPwd()));
	};

	// get SHA Sign Parameter for query parameter
	this.getSHASignParameterFromQuery = function(queryMap) {
		var paramMap = queryMap.clone();

		// put credientials since they are missing in Query
		paramMap.put("PSPID", this.getPspId());
		paramMap.put("USERID", this.getUserId());
		paramMap.put("PSWD", this.getUserPwd());

		return getSHASignParameterFromRequest(paramMap, shaIn, digest, this.getOrderId());
	};

	// Perfrom the HTTP request to server and handle the response
	this.sendRequest = function(url, query, requestParameters, serviceName) {
		var log = dwsystem.Logger.getLogger("payment");  
		var service = dwsvc.ServiceRegistry.get(serviceName);
		var requestUrl = '';
		var logUrl = '';
		var start = new Date();
		
		var paramseters = requestParameters.split('&');
		requestParameters += !empty(query) ? query : '';
		requestUrl = url + "?" + requestParameters;
		var callPrams = {
			"url" : requestUrl,
			"paramseters" : paramseters
		}
		// mask PWD, PCI and Customer relevant data
		logUrl = requestUrl.replace(/([?|&](PSWD|CVC|CARDNO|CN|ED|OWNERADDRESS|OWNERTELNO|OWNERZIP|EMAIL|BIC|)=)[^&]*/g, '$1***');
		log.debug("Payment request:" + logUrl);

		try {
			var client = service.call(callPrams).object;
			
			log.debug("Payment response:" + client.text);

			//parse response
			if (client.statusCode == 200) {
				var resultXML = new XML(client.text);
				var succ = false;
				var ncError = resultXML.@["NCERROR"];
				var ncStatus = resultXML.@["NCSTATUS"];
				var status = resultXML.@["STATUS"];
				var errorMsg = dwutil.StringUtils.format("ncError: {0}, ncStatus: {1}, status: {2}, ncErrorPlus: {3}", ncError, ncStatus, status, resultXML.@["NCERRORPLUS"]);

				// ncError of '0' or '' indicates valid request and response
				if (ncError == "0" || ncError == "") {
					return resultXML;
				} else if(resultXML.@["PM"] == 'PAYPAL' && status == "0"){
					// quickfix for paypal status 0
					return resultXML;
				} else {
					var end = new Date();
					var difference = (end.getTime() - start.getTime()) * 0.001;
					var error = new Error(dwutil.StringUtils.format("Error returned by Payment. Return Code: {0}; Message: {1}; Url: {2}; PaymentError: {3}; Seconds since start: {4}", client.statusCode, client.statusMessage, logUrl, errorMsg, difference));
					error.ErrorCode = ncError;
					error.ErrorMsg = errorMsg;
					error.paymentErrorMsgPlus = resultXML.@["NCERRORPLUS"].toString();
					error.status = status;
					throw error;
				}
				
			} else {
				var end = new Date();
				var difference = (end.getTime() - start.getTime()) * 0.001;
				throw Error(dwutil.StringUtils.format("Error connecting to Payment. Return Code: {0}; Message: {1}; Url: {2}; Seconds since start: {3}", client.statusCode, client.statusMessage, logUrl, difference));
			}
		} catch (error) {
			var x = error;
			var end = new Date();
			var difference = (end.getTime() - start.getTime()) * 0.001;
			log.error("Error in connection to Payment: {0}; Return Code: {1}; Message: {2}; Seconds since start: {3}\n\tStack: {4}\tRequestUrl: {5}", error, client.statusCode, client.statusMessage, difference, error.stack, logUrl);
			throw error;
		}
	};
	
	// New Order Request for Direct Link, Credit Card Hosted Tokenization
	this.sendDirectLinkCreditCardHostedTokenizationNewOrderRequest = function(operation, enable3DS, locale, url, timeout) {
		var url = url || IngenicoConfiguration.NewOrderEndpoint;
		var timeout = timeout || IngenicoConfiguration.paymentCartridgeTimeout;
		
		var query = this.getNewOrderQueryCreditCardHostedTokenization(operation, enable3DS, timeout,locale);	

		var requestParameters = this.getCredentialQuery();
		var responseXML = new XML();

		responseXML = this.sendRequest(url, query, requestParameters, "Ingenico.sendDirectLinkCreditCardNewOrderRequest");

		var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
		var resultObj = {};
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString(),
				cvccheck : responseXML.@["CVCCHECK"].toString(),
				aavcheck : responseXML.@["AAVCHECK"].toString(),
				ip : responseXML.@["IP"].toString(),
				scoring : responseXML.@["SCORING"].toString(),
				sco_category : responseXML.@["SCO_CATEGORY"].toString()
			};
		} else {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString()
			};
		}

		return resultObj;
	};


	// New Order Request for Direct Link, Direct Debits
	this.sendDirectLinkDirectDebitsNewOrderRequest = function(operation, locale, data, url, timeout) {
		var url = url || IngenicoConfiguration.NewOrderEndpoint;
		var timeout = timeout || IngenicoConfiguration.paymentCartridgeTimeout;
		var query = this.getNewOrderQueryDirectDebits(operation, data, timeout, locale);
		var requestParameters = this.getCredentialQuery();
		var responseXML = new XML();
		
		responseXML = this.sendRequest(url, query, requestParameters, "Ingenico.sendDirectLinkDirectDebitsNewOrderRequest");
		
		var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString(),
				cvccheck : responseXML.@["CVCCHECK"].toString(),
				aavcheck : responseXML.@["AAVCHECK"].toString(),
				ip : responseXML.@["IP"].toString(),
				scoring : responseXML.@["SCORING"].toString(),
				sco_category : responseXML.@["SCO_CATEGORY"].toString()
			};
		} else {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString()
			};
		}

		return resultObj;
	};


	//Direct Link Direct Query request
	this.sendDirectQuery = function() {
		var url = url || IngenicoConfiguration.DirectQueryEndpoint;
		var timeout = timeout || IngenicoConfiguration.paymentCartridgeTimeout;
		var query = this.getDirectQuery(timeout);
		var requestParameters = this.getCredentialQuery();
		var responseXML = new XML();

		responseXML = this.sendRequest(url, query, requestParameters, "Ingenico.sendDirectQuery");
		
		var useFraudDetection = IngenicoConfiguration.enableFraudDetection;

		if (!empty(useFraudDetection) && useFraudDetection) {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString(),
				cvccheck : responseXML.@["CVCCHECK"].toString(),
				aavcheck : responseXML.@["AAVCHECK"].toString(),
				ip : responseXML.@["IP"].toString(),
				scoring : responseXML.@["SCORING"].toString(),
				sco_category : responseXML.@["SCO_CATEGORY"].toString()
			};
		} else {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString()
			};
		}

		return resultObj;
	};


	//Direct Link Direct Query request
	this.sendDirectLinkPayPal = function() {
		var url = IngenicoConfiguration.NewOrderEndpoint;
		var timeout = IngenicoConfiguration.paymentCartridgeTimeout;
		var query = this.getPayPalOrderQuery(timeout);
		var requestParameters = this.getCredentialQuery();
		var responseXML = new XML();

		responseXML = this.sendRequest(url, query, requestParameters, "Ingenico.sendDirectLinkPayPal");
		
		var useFraudDetection = IngenicoConfiguration.enableFraudDetection;

		if (!empty(useFraudDetection) && useFraudDetection) {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString(),
				cvccheck : responseXML.@["CVCCHECK"].toString(),
				aavcheck : responseXML.@["AAVCHECK"].toString(),
				ip : responseXML.@["IP"].toString(),
				scoring : responseXML.@["SCORING"].toString(),
				sco_category : responseXML.@["SCO_CATEGORY"].toString()
			};
		} else {
			resultObj = {
				status : responseXML.@["STATUS"].toString(),
				payid : responseXML.@["PAYID"].toString(),
				pm : responseXML.@["PM"].toString(),
				brand : responseXML.@["BRAND"].toString(),
				html : responseXML.HTML_ANSWER.toString()
			};
		}

		return resultObj;
	};


	//Maintainance Request
	this.sendDirectLinkMaintenanceRequest = function(operation, url, timeout) {
		var url = url || IngenicoConfiguration.MaintainanceEndpoint;
		var timeout = timeout || IngenicoConfiguration.paymentCartridgeTimeout;
		var query = this.getMaintenanceQuery(operation, timeout);
		var requestParameters = this.getCredentialQuery();
		var responseXML = new XML();

		responseXML = this.sendRequest(url, query, requestParameters, "Ingenico.sendDirectLinkMaintenanceRequest");

		var resultObj = {
			status : responseXML.@["STATUS"].toString(),
			payid : responseXML.@["PAYID"].toString(),
			pm : responseXML.@["PM"].toString()
		};

		return resultObj;
	};

}

/************* Helper Functions *******/

/**
 * Get SHASign used for request	
 * 
 * @param {dw.util.HashMap} requestParameters
 * @param {String} shaIn
 * @param {dw.crypto.MessageDigest} digest
 * @param {String} orderID
 * @param {Object} comparator
 * @returns {dw.crypto.MessageDigest} digest
 */
function getSHASignParameterFromRequest(requestParameters, shaIn, digest, orderID, comparator) {
	// Add parameter alphabetical sorted for sha sign
	
	if(comparator) {
		var paramSortedMap =  new dwutil.SortedMap(comparator);
	} else {
		var paramSortedMap =  new dwutil.SortedMap();
	}
	
	paramSortedMap.putAll(requestParameters);

	// build sha sign
	var paramKeySet = paramSortedMap.keySet();
	var paramIterator = paramKeySet.iterator();
	var paramValue = '';
	var paramName = '';
	var shaSign = '';
	var shaSignLog = '';
	while ( paramIterator.hasNext() ) {
		paramName = paramIterator.next().toString();
		paramValue = paramSortedMap.get(paramName);
		if (!empty(paramValue)) {
			//var parambytes=new dwutil.Bytes(paramValue.toString());
			//parambytes.toString("windows-1252")
			shaSign += paramName.toUpperCase() + '=' + paramValue.toString() + shaIn;
		}
	}

	if (!empty(shaIn) && !empty(shaSign)) {
		return digest.digest(shaSign);
	}
	return '';
}

/**
 * Get SHASign used for request	
 * 
 * @param {dw.util.HashMap} requestParameters
 * @param {String} shaIn
 * @param {dw.crypto.MessageDigest} digest
 * @param {String} orderID
 * @param {Object} comparator
 * @returns {dw.crypto.MessageDigest} digest
 */
function getSHASignParameterFromRequestUtf(requestParameters, shaIn, digest, orderID, comparator) {
	// Add parameter alphabetical sorted for sha sign
	if(comparator) {
		var paramSortedMap =  new dwutil.SortedMap(comparator);
	} else {
		var paramSortedMap =  new dwutil.SortedMap();
	}
		
	paramSortedMap.putAll(requestParameters);

	// build sha sign
	var paramKeySet = paramSortedMap.keySet();
	var paramIterator = paramKeySet.iterator();
	var paramValue = '';
	var paramName = '';
	var shaSign = '';
	while ( paramIterator.hasNext() ) {
		paramName = paramIterator.next().toString();
		paramValue = paramSortedMap.get(paramName);
		if (!empty(paramValue)) {
			shaSign += paramName.toUpperCase() + '=' + paramValue.toString() + shaIn;
		}
	}

	if (!empty(shaIn) && !empty(shaSign)) {
		return digest.digest(shaSign);
	}
	return '';
}
/**
 * format expiry YYMM
 * 
 * @param {Number} month
 * @param {number} year
 * @returns {String | null}
 */
function formatExpiryNumbers(month, year) {
	if (!empty(month) && !empty(year)) {
		return dwutil.StringUtils.formatNumber(month, "00") + year.toString().substring(year.toString().length - 2);
	}
	return null;
}

/**
 * get URI Parameter from map
 * 
 * @param {dw.util.HashMap} paramMap
 * @returns {String} query
 */
function getURIParametersFromMap(paramMap) {
	var paramKeySet = paramMap.keySet();
	var paramIterator = paramKeySet.iterator();
	var paramValue = '';
	var paramName = '';
	var query = '';
	while ( paramIterator.hasNext() ) {
		paramName = paramIterator.next().toString();
		paramValue = paramMap.get(paramName);
		//paramValue=removeFrenchChar(paramValue.toString());
		if (!empty(paramValue)) {
			query += "&" + paramName + '=' + encodeURIComponent(paramValue.toString());
		}
	}
	return query;
}

/**
 * generate SHA for Ecommerce
 * 
 * @param {dw.order.Order} order
 * @param {Number} gender
 * @param {String} locale
 * @returns {dw.crypto.MessageDigest} shaCode 
 */
function generateSHAEcommerce(order, gender, locale){
	var paymentRequestParameters = new dwutil.HashMap();
	var IngenicoConfiguration = getIngenicoConfiguration();

	var cartAmountDec = new dwutil.Decimal(parseFloat(order.totalGrossPrice.value));
	cartAmountDec = cartAmountDec.round(2);
	cartAmountDec = cartAmountDec.multiply(100);
	var cartAmount = cartAmountDec.get().toString();

	var civility='';

	if(!empty(gender)){
		if(gender==1){
			civility='M';
 		}else{
			civility='F';
		}
	}

	var cn = order.getBillingAddress().getLastName();
	var billtofirstname = order.getBillingAddress().getFirstName();
	var billtolastname = order.getBillingAddress().getLastName();
	var emailaddress = order.getCustomerEmail();
	
	var currency = order.getCurrencyCode(); 
	
	//get language from SitePreferences , else get the default one
	var lang='';
	if (locale=='default') {
		lang = 'fr_FR';
	} else {
		lang = locale + "_" + locale.toUpperCase();
	}

	var customLanguage = IngenicoConfiguration.language;	

	if (customLanguage != null) {
		lang = customLanguage;
	}

	var orderid = order.orderNo;
	var owneraddress = order.getBillingAddress().getAddress1();
	var ownercity = order.getBillingAddress().getCountryCode().value;
	var ownertel = order.getBillingAddress().getPhone();
	var ownertown = order.getBillingAddress().getCity();
	var ownerzip = order.getBillingAddress().getPostalCode();
	var pspid = IngenicoConfiguration.PSPID;
	var ecommaccepturl = dwweb.URLUtils.https('PAYMENT_ECOMMERCE-EcommAccept','orderNo',order.orderNo,'pmID',order.getPaymentInstruments()[0].paymentMethod, 'tn', createShaForUrls(order));
	var ecommdeclineurl = dwweb.URLUtils.https('PAYMENT_ECOMMERCE-EcommDecline','orderNo',order.orderNo,'pmID',order.getPaymentInstruments()[0].paymentMethod, 'tn', createShaForUrls(order));
	var ecommdcancel = dwweb.URLUtils.https('PAYMENT_ECOMMERCE-EcommCancel','orderNo',order.orderNo,'pmID',order.getPaymentInstruments()[0].paymentMethod, 'tn', createShaForUrls(order));
	
	var useStaticTemplate = IngenicoConfiguration.enableStaticTemplate;
	var useDynamicTemplate = IngenicoConfiguration.enableDynamicTemplate;
	var auth2Steps = IngenicoConfiguration.enableTwoStepsAuthorizationEcommerce;

	paymentRequestParameters.put('AMOUNT', cartAmount);

	if (!empty(gender)) {
		paymentRequestParameters.put('CIVILITY', civility);
	}

	paymentRequestParameters.put('CN',cn);
	paymentRequestParameters.put('CURRENCY',currency);
	paymentRequestParameters.put('ECOM_BILLTO_POSTAL_NAME_FIRST',billtofirstname);
	paymentRequestParameters.put('ECOM_BILLTO_POSTAL_NAME_LAST',billtolastname);
	paymentRequestParameters.put('EMAIL',emailaddress);
	paymentRequestParameters.put('LANGUAGE',lang);
	paymentRequestParameters.put('ORDERID', orderid);
	paymentRequestParameters.put('OWNERADDRESS',owneraddress );
	paymentRequestParameters.put('OWNERCTY', ownercity);
	paymentRequestParameters.put('OWNERTELNO', ownertel);
	paymentRequestParameters.put('OWNERTOWN', ownertown);
	paymentRequestParameters.put('ORIG', IngenicoConfiguration.transactionTracker);
	paymentRequestParameters.put('OWNERZIP', ownerzip);
	paymentRequestParameters.put('PSPID',pspid);
	paymentRequestParameters.put('ACCEPTURL',ecommaccepturl);
	paymentRequestParameters.put('DECLINEURL',ecommdeclineurl);
	paymentRequestParameters.put('CANCELURL',ecommdcancel);
	paymentRequestParameters.put('COMPLUS',locale);
	
	if (!empty(auth2Steps) && auth2Steps) {
		paymentRequestParameters.put('OPERATION',"RES");
	}else{
		paymentRequestParameters.put('OPERATION',"SAL");	
	}
	
	//parameters for Static Template
	if (!empty(useStaticTemplate) && useStaticTemplate) {
		if (!empty(IngenicoConfiguration.paymentTemplateTitle)) {
			paymentRequestParameters.put("TITLE",IngenicoConfiguration.paymentTemplateTitle);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateBGColor)) {
			paymentRequestParameters.put("BGCOLOR",IngenicoConfiguration.paymentTemplateBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTxtColor)) {
			paymentRequestParameters.put("TXTCOLOR",IngenicoConfiguration.paymentTemplateTxtColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblBGColor)) {
			paymentRequestParameters.put("TBLBGCOLOR",IngenicoConfiguration.paymentTemplateTblBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblTxtColor)) {
			paymentRequestParameters.put("TBLTXTCOLOR",IngenicoConfiguration.paymentTemplateTblTxtColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateButtonBGColor)) {
			paymentRequestParameters.put("BUTTONBGCOLOR",IngenicoConfiguration.paymentTemplateButtonBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateButtonTxtColor)) {
			paymentRequestParameters.put("BUTTONTXTCOLOR",IngenicoConfiguration.paymentTemplateButtonTxtColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateFontType)) {
			paymentRequestParameters.put("FONTTYPE",IngenicoConfiguration.paymentTemplateFontType);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateLogo)) {
			paymentRequestParameters.put("LOGO",IngenicoConfiguration.paymentTemplateLogo);
		}
	}

	// parameter for Dynamic Template
	if (!empty(useDynamicTemplate) && useDynamicTemplate) {
		paymentRequestParameters.put("TP",IngenicoConfiguration.paymentDynamicTemplateURL);
	}


	var PM = dworder.PaymentMgr.getPaymentMethod(order.getPaymentInstruments()[0].paymentMethod) ;

	//get Payment Method from custom attribute ,else get from Payment Instrument
	if (!empty(PM.custom.paymentEcommerceCreditCardPM)) {
		paymentRequestParameters.put('PM', PM.custom.paymentEcommerceCreditCardPM);
	} else {
		paymentRequestParameters.put('PM', order.getPaymentInstruments()[0].paymentMethod);
	}
	
	paymentRequestParameters.put('BRAND', order.getPaymentInstruments()[0].paymentMethod);

	var hashMethod = IngenicoConfiguration.shaMethod;
	var digest = new dwcrypto.MessageDigest(hashMethod);

	var shaCode = getSHASignParameterFromRequestUtf(paymentRequestParameters,IngenicoConfiguration.shaPasword,digest,orderid);
	return shaCode;
}

/**
 * generate SHA for MasterPass Ecommerce
 * @param {dw.order.Order} order
 * @param {String} locale
 * @returns {dw.crypto.MessageDigest} shaCode 
 */

function generateSHAMasterPassEcommerce(order, locale) {
	var paymentRequestParameters = new dwutil.HashMap();
	var IngenicoConfiguration = getIngenicoConfiguration();

	var cartAmountDec = new dwutil.Decimal(parseFloat(order.totalGrossPrice.value));
	cartAmountDec = cartAmountDec.round(2);
	cartAmountDec = cartAmountDec.multiply(100);
	var cartAmount = cartAmountDec.get().toString();
	var cn = order.getBillingAddress().getLastName();
	var emailaddress = order.getCustomerEmail();
	
	var currency = order.getCurrencyCode(); 
	
	//get language from SitePreferences , else get the default one
	var lang='';
	if (locale=='default') {
		lang = 'fr_FR';
	} else {
		lang = locale + "_" + locale.toUpperCase();
	}

	var customLanguage = IngenicoConfiguration.language;	

	if (customLanguage != null) {
		lang = customLanguage;
	}

	var orderid = order.orderNo;
	var owneraddress = order.getBillingAddress().getAddress1();
	var ownercity = order.getBillingAddress().getCountryCode().value;
	var ownertel = order.getBillingAddress().getPhone();
	var ownertown = order.getBillingAddress().getCity();
	var ownerzip = order.getBillingAddress().getPostalCode();
	var pspid = IngenicoConfiguration.PSPID;

	paymentRequestParameters.put('AMOUNT', cartAmount);
	paymentRequestParameters.put('CN',cn);
	paymentRequestParameters.put('CURRENCY',currency);
	paymentRequestParameters.put('EMAIL',emailaddress);
	paymentRequestParameters.put('LANGUAGE',lang);
	paymentRequestParameters.put('ORDERID', orderid);
	paymentRequestParameters.put('OWNERADDRESS',owneraddress );
	paymentRequestParameters.put('OWNERCTY', ownercity);
	paymentRequestParameters.put('OWNERTELNO', ownertel);
	paymentRequestParameters.put('OWNERTOWN', ownertown);
	paymentRequestParameters.put('OWNERZIP', ownerzip);
	paymentRequestParameters.put('PSPID',pspid);
	
	
	var ecommaccepturl = dwweb.URLUtils.https('MP_ECM-EcommAccept','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
	var ecommdeclineurl = dwweb.URLUtils.https('MP_ECM-EcommDecline','orderNo', order.orderNo, 'tn' , createShaForUrls(order)); 
	var ecommdcancel = dwweb.URLUtils.https('MP_ECM-EcommCancel','orderNo', order.orderNo, 'tn', createShaForUrls(order));
	var ecommdexeptionurl = dwweb.URLUtils.https('MP_ECM-EcommException','orderNo', order.orderNo, 'tn' , createShaForUrls(order));	
	paymentRequestParameters.put('ACCEPTURL',ecommaccepturl);
	paymentRequestParameters.put('DECLINEURL',ecommdeclineurl);
	paymentRequestParameters.put('CANCELURL',ecommdcancel);
	paymentRequestParameters.put('EXCEPTIONURL',ecommdexeptionurl);
	
	var useStaticTemplate = IngenicoConfiguration.enableStaticTemplate;
	
	//parameters for Static Template
	if (!empty(useStaticTemplate) && useStaticTemplate) {
		if (!empty(IngenicoConfiguration.paymentTemplateTitle)) {
			paymentRequestParameters.put("TITLE",IngenicoConfiguration.paymentTemplateTitle);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateBGColor)) {
			paymentRequestParameters.put("BGCOLOR",IngenicoConfiguration.paymentTemplateBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTxtColor)) {
			paymentRequestParameters.put("TXTCOLOR",IngenicoConfiguration.paymentTemplateTxtColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblBGColor)) {
			paymentRequestParameters.put("TBLBGCOLOR",IngenicoConfiguration.paymentTemplateTblBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblTxtColor)) {
			paymentRequestParameters.put("TBLTXTCOLOR",IngenicoConfiguration.paymentTemplateTblTxtColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateButtonBGColor)){
			paymentRequestParameters.put("BUTTONBGCOLOR",IngenicoConfiguration.paymentTemplateButtonBGColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateButtonTxtColor)){
			paymentRequestParameters.put("BUTTONTXTCOLOR",IngenicoConfiguration.paymentTemplateButtonTxtColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateFontType)){
			paymentRequestParameters.put("FONTTYPE",IngenicoConfiguration.paymentTemplateFontType);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateLogo)){
			paymentRequestParameters.put("LOGO",IngenicoConfiguration.paymentTemplateLogo);
		}
	}

	var paymentMethodName = order.getPaymentInstruments()[0].paymentMethod;
	var paymentMethodNameArray = paymentMethodName.split(' ');
	
	if (paymentMethodNameArray.length > 1 ) {
		paymentMethodName = paymentMethodNameArray[0];
	}
	

	paymentRequestParameters.put('PM', paymentMethodName);
	paymentRequestParameters.put('BRAND', paymentMethodName);
	
	var hashMethod = IngenicoConfiguration.shaMethod;
	var digest = new dwcrypto.MessageDigest(hashMethod);

	var shaCode = getSHASignParameterFromRequestUtf(paymentRequestParameters, IngenicoConfiguration.shaPasword, digest, orderid);
	return shaCode;
}

/**
 * generate SHA for Ecommerce Paypal
 * 
 * @param {dw.order.Order} order
 * @param {Number} gender
 * @param {String} locale
 * @returns {dw.crypto.MessageDigest} shaCode 
 */

function generateSHAEcommercePaypal(order, gender, locale) {

	var paymentRequestParameters = new dwutil.HashMap();
	var IngenicoConfiguration = getIngenicoConfiguration();

	var cartAmountDec = new dwutil.Decimal(parseFloat(order.totalGrossPrice.value));
	cartAmountDec = cartAmountDec.round(2);
	cartAmountDec = cartAmountDec.multiply(100);
	var cartAmount = cartAmountDec.get().toString();

	var civility='';
	if (!empty(gender)) { 
		if (gender==1) {
			civility='M';
		} else {
			civility='F';
		}
	}

	var cn = order.getBillingAddress().getLastName();
	var billtofirstname = order.getBillingAddress().getFirstName();
	var billtolastname = order.getBillingAddress().getLastName();
	var emailaddress = order.getCustomerEmail();

	var currency = order.getCurrencyCode(); 

	//get language from SitePreferences , else get the default one		
	var lang = '';
	if (locale == 'default'){
		lang = 'fr_FR';
	} else {
		lang = locale + "_" + locale.toUpperCase();
	}

	var customLanguage = IngenicoConfiguration.language;	

	if (customLanguage != null) {
		lang = customLanguage;
	}

	var orderid = order.orderNo;
	var owneraddress = order.getBillingAddress().getAddress1();
	var ownercity = order.getBillingAddress().getCountryCode().value;
	var ownertel = order.getBillingAddress().getPhone();
	var ownertown = order.getBillingAddress().getCity();
	var ownerzip = order.getBillingAddress().getPostalCode();
	
	var shiptofirstname = order.getDefaultShipment().getShippingAddress().getFirstName();
	var shiptolastname = order.getDefaultShipment().getShippingAddress().getLastName();
	var owneraddressShip = order.getDefaultShipment().getShippingAddress().getAddress1();
	var ownercityShip = order.getDefaultShipment().getShippingAddress().getCountryCode().value;
	var ownertownShip = order.getDefaultShipment().getShippingAddress().getCity();
	var ownerzipShip = order.getDefaultShipment().getShippingAddress().getPostalCode();	
	
	var pspid = IngenicoConfiguration.PSPID;
	var ecommaccepturl = dwweb.URLUtils.https('ECM_PPAL-EcommAccept','orderNo',order.orderNo,'pmID',order.getPaymentInstruments()[0].paymentMethod,'tn',createShaForUrls(order));
	var ecommdeclineurl = dwweb.URLUtils.https('ECM_PPAL-EcommDecline','orderNo',order.orderNo,'pmID',order.getPaymentInstruments()[0].paymentMethod,'tn',createShaForUrls(order)); 
	var ecommdcancel = dwweb.URLUtils.https('ECM_PPAL-EcommCancel','orderNo',order.orderNo,'pmID',order.getPaymentInstruments()[0].paymentMethod,'tn',createShaForUrls(order));
	
	var useStaticTemplate = IngenicoConfiguration.enableStaticTemplate;
	var auth2Steps = IngenicoConfiguration.enableTwoStepsAuthorizationEcommerce;

	paymentRequestParameters.put('AMOUNT',cartAmount);
	
	paymentRequestParameters.put("ALIAS",session.custom.alias);
	paymentRequestParameters.put("ALIASUSAGE",' ');
	if (!empty(gender)) {
		paymentRequestParameters.put('CIVILITY', civility);
	}

	paymentRequestParameters.put('CN',cn);
	paymentRequestParameters.put('CURRENCY',currency);
	paymentRequestParameters.put('ECOM_BILLTO_POSTAL_NAME_FIRST',billtofirstname);
	paymentRequestParameters.put('ECOM_BILLTO_POSTAL_NAME_LAST',billtolastname);
	paymentRequestParameters.put('EMAIL',emailaddress);
	paymentRequestParameters.put('LANGUAGE',lang);
	paymentRequestParameters.put('ORDERID', orderid);
	paymentRequestParameters.put('OWNERADDRESS',owneraddress );
	paymentRequestParameters.put('OWNERCTY', ownercity);
	paymentRequestParameters.put('OWNERTELNO', ownertel);
	paymentRequestParameters.put('OWNERTOWN', ownertown);
	paymentRequestParameters.put('ORIG', IngenicoConfiguration.transactionTracker);	
	paymentRequestParameters.put('OWNERZIP', ownerzip);
	paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_NAME_FIRST',shiptofirstname);
	paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_NAME_LAST',shiptolastname);
	paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_STREET_LINE1',owneraddressShip);
	paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_CITY',ownertownShip);
	paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_POSTALCODE',ownerzipShip);
	paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_COUNTRYCODE',ownercityShip);
	paymentRequestParameters.put('PSPID',pspid);
	paymentRequestParameters.put('ACCEPTURL',ecommaccepturl);
	paymentRequestParameters.put('DECLINEURL',ecommdeclineurl);
	paymentRequestParameters.put('CANCELURL',ecommdcancel);
	paymentRequestParameters.put('COMPLUS',locale);
	paymentRequestParameters.put('TXTOKEN',"INIT");
	
	if (!empty(auth2Steps) && auth2Steps) {
		paymentRequestParameters.put('OPERATION',"RES");
	}else{
		paymentRequestParameters.put('OPERATION',"SAL");	
	}

	if (!empty(order.defaultShipment.shippingAddress.stateCode)) {
		paymentRequestParameters.put('ECOM_SHIPTO_POSTAL_STATE',order.defaultShipment.shippingAddress.stateCode);
	}

	//parameters for Static Template
	if (!empty(useStaticTemplate) && useStaticTemplate) {
		if (!empty(IngenicoConfiguration.paymentTemplateTitle)) {
			paymentRequestParameters.put("TITLE",IngenicoConfiguration.paymentTemplateTitle);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateBGColor)) {
			paymentRequestParameters.put("BGCOLOR",IngenicoConfiguration.paymentTemplateBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTxtColor)) {
			paymentRequestParameters.put("TXTCOLOR",IngenicoConfiguration.paymentTemplateTxtColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblBGColor)) {
			paymentRequestParameters.put("TBLBGCOLOR",IngenicoConfiguration.paymentTemplateTblBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblTxtColor)) {
			paymentRequestParameters.put("TBLTXTCOLOR",IngenicoConfiguration.paymentTemplateTblTxtColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateButtonBGColor)){
			paymentRequestParameters.put("BUTTONBGCOLOR",IngenicoConfiguration.paymentTemplateButtonBGColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateButtonTxtColor)){
			paymentRequestParameters.put("BUTTONTXTCOLOR",IngenicoConfiguration.paymentTemplateButtonTxtColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateFontType)){
			paymentRequestParameters.put("FONTTYPE",IngenicoConfiguration.paymentTemplateFontType);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateLogo)){
			paymentRequestParameters.put("LOGO",IngenicoConfiguration.paymentTemplateLogo);
		}
	}

	paymentRequestParameters.put('PM', order.getPaymentInstruments()[0].paymentMethod);
	paymentRequestParameters.put('BRAND', order.getPaymentInstruments()[0].paymentMethod);

	var hashMethod = IngenicoConfiguration.shaMethod;
	var digest = new dwcrypto.MessageDigest(hashMethod);

	var shaCode = getSHASignParameterFromRequestUtf(paymentRequestParameters,IngenicoConfiguration.shaPasword,digest,orderid);
	return shaCode;
}

/**
 * generate SHA for Ecommerce Klarna
 * @param {Object} klData
 * @returns {dw.crypto.MessageDigest} shaCode
 */

function generateSHAKlarnaEcommerce(klData) {

	var paymentRequestParameters = new dwutil.HashMap();
	var IngenicoConfiguration = getIngenicoConfiguration();
	
	// Standard Ogone fields 
	paymentRequestParameters.put('PSPID', IngenicoConfiguration.PSPID);
	paymentRequestParameters.put('ORDERID', klData.orderid);	
	paymentRequestParameters.put('AMOUNT', klData.cartAmount);
	paymentRequestParameters.put('CURRENCY', klData.currency);
	paymentRequestParameters.put('LANGUAGE', klData.lang);
	paymentRequestParameters.put('OPERATION', klData.operation);
	paymentRequestParameters.put('PM', klData.paymentMethodName);
	paymentRequestParameters.put('BRAND', klData.paymentMethodName);

	// Invoicing and delivery data 
	paymentRequestParameters.put('ECOM_BILLTO_POSTAL_NAME_FIRST', klData.billtofirstname);
	paymentRequestParameters.put('ECOM_BILLTO_POSTAL_NAME_LAST', klData.billtolastname);
	paymentRequestParameters.put('OWNERADDRESS', klData.owneraddress);	
	paymentRequestParameters.put('OWNERZIP', klData.ownerzip);
	paymentRequestParameters.put('EMAIL', klData.emailaddress);	
	paymentRequestParameters.put('OWNERCTY', klData.ownercty);
	paymentRequestParameters.put('OWNERTOWN', klData.ownertown);	
	paymentRequestParameters.put('OWNERTELNO', klData.ownertel);
	
	// Additional invoicing and delivery data 
	if (!empty(klData.cuid)) {
		paymentRequestParameters.put('CUID', klData.cuid);
	}	
	if (!empty(klData.civility)) {
		paymentRequestParameters.put('ECOM_CONSUMER_GENDER', klData.civility);
	}
	if (!empty(klData.dateOfBirth)) {
		paymentRequestParameters.put('ECOM_SHIPTO_DOB', klData.dateOfBirth);
	}
	if (!empty(klData.houseNumber)) {
		paymentRequestParameters.put('ECOM_BILLTO_POSTAL_STREET_NUMBER', klData.houseNumber);
	}
	
	if (!empty(klData.shipmentMeth)) {
		paymentRequestParameters.put('ORDERSHIPMETH', klData.shipmentMeth);
	}
	paymentRequestParameters.put('ORDERSHIPCOST', klData.ordershipcost);	

	// URLS
	paymentRequestParameters.put('ACCEPTURL', klData.ecommaccepturl);
	paymentRequestParameters.put('DECLINEURL', klData.ecommdeclineurl);
	paymentRequestParameters.put('CANCELURL', klData.ecommdcancelurl);

	// OrderItem 	
	var loopstate = 1;
	for each (var item in klData.productLineItems) {	
		paymentRequestParameters.put('ITEMID' + loopstate, item.get('ITEMID' + loopstate));
		paymentRequestParameters.put('ITEMNAME' + loopstate, item.get('ITEMNAME' + loopstate));
		paymentRequestParameters.put('ITEMPRICE' + loopstate, item.get('ITEMPRICE' + loopstate));
		paymentRequestParameters.put('ITEMQUANT' + loopstate, item.get('ITEMQUANT' + loopstate));
		paymentRequestParameters.put('ITEMVATCODE' + loopstate,item.get('ITEMVATCODE' + loopstate));
		paymentRequestParameters.put('TAXINCLUDED'  + loopstate, item.get('TAXINCLUDED' + loopstate));
		
		loopstate++;
	}
	
	// Templating data	
	var useStaticTemplate = IngenicoConfiguration.enableStaticTemplate;

	//parameters for Static Template
	if (!empty(useStaticTemplate) && useStaticTemplate) {
		if (!empty(IngenicoConfiguration.paymentTemplateTitle)) {
			paymentRequestParameters.put("TITLE",IngenicoConfiguration.paymentTemplateTitle);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateBGColor)) {
			paymentRequestParameters.put("BGCOLOR",IngenicoConfiguration.paymentTemplateBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTxtColor)) {
			paymentRequestParameters.put("TXTCOLOR",IngenicoConfiguration.paymentTemplateTxtColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblBGColor)) {
			paymentRequestParameters.put("TBLBGCOLOR",IngenicoConfiguration.paymentTemplateTblBGColor);
		}
		if (!empty(IngenicoConfiguration.paymentTemplateTblTxtColor)) {
			paymentRequestParameters.put("TBLTXTCOLOR",IngenicoConfiguration.paymentTemplateTblTxtColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateButtonBGColor)){
			paymentRequestParameters.put("BUTTONBGCOLOR",IngenicoConfiguration.paymentTemplateButtonBGColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateButtonTxtColor)){
			paymentRequestParameters.put("BUTTONTXTCOLOR",IngenicoConfiguration.paymentTemplateButtonTxtColor);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateFontType)){
			paymentRequestParameters.put("FONTTYPE",IngenicoConfiguration.paymentTemplateFontType);
		}
		if(!empty(IngenicoConfiguration.paymentTemplateLogo)){
			paymentRequestParameters.put("LOGO",IngenicoConfiguration.paymentTemplateLogo);
		}
	}


	var hashMethod = IngenicoConfiguration.shaMethod;
	var digest = new dwcrypto.MessageDigest(hashMethod);
	var shaPasword = IngenicoConfiguration.shaPasword;

	var comparator = function (a, b) {
		
		var aNumber = a.split(/(\d+)/)[1];
		var bNumber = b.split(/(\d+)/)[1];
		var aString = a.replace(aNumber,'');
		var bString = b.replace(bNumber,'');
		
		if(aNumber && bNumber) {
			if (aString < bString) { 
			 	return -1;
			} else if (aString > bString) {
			  	return 1;
			} else {
				  return aNumber - bNumber;
			}
		} else {		
			if (a < b) { 
			 	return -1;
			} else if (a > b) {
			  	return 1;
			} else {
			  return 0;
			}
		}
		
	};
	
	var shaCode = getSHASignParameterFromRequest(paymentRequestParameters, shaPasword, digest, klData.orderid, comparator);
	return shaCode;
}

/**
 * convert array elements to string
 * 
 * @param {dw.util.ArrayList} arr
 * @returns {dw.util.ArrayList} result_arr
 */
function toArrayOfStrings(arr) {
	var result_arr = new dwutil.ArrayList();
	for( var i=0; i 24534
	var cartAmountDec = new dwutil.Decimal(parseFloat(order.totalGrossPrice.value));
	cartAmountDec = cartAmountDec.round(2);
	cartAmountDec = cartAmountDec.multiply(100);
	result.cartAmount = cartAmountDec.get().toString();

	// get the language from custom preference
	// if not available there then use the locale
	var customLanguage = IngenicoConfiguration.language;	
	if(customLanguage == null) {
		if(locale == 'default'){
			result.lang='fr_FR';
		}
		else{
			result.lang = locale + "_" + locale.toUpperCase();
		}
	}else{
		result.lang = customLanguage;	
	}
	
	result.ecommaccepturl = dwweb.URLUtils.https('MP_ECM-EcommAccept','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
	result.ecommdeclineurl = dwweb.URLUtils.https('MP_ECM-EcommDecline','orderNo', order.orderNo, 'tn' , createShaForUrls(order)); 
	result.ecommdexeptionurl = dwweb.URLUtils.https('MP_ECM-EcommException','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
	result.ecommdcancelurl = dwweb.URLUtils.https('MP_ECM-EcommCancel','orderNo', order.orderNo, 'tn' , createShaForUrls(order));

	result.useStaticTemplate = IngenicoConfiguration.enableStaticTemplate;
	result.paymentMethodName = "MasterPass";
	
	// TODO: remove this from here and use the same aproach as in Klarna
	if (!empty(customer.profile)) {
		result.shaCode = generateSHAMasterPassEcommerce(order, locale); 
	} else {
		result.shaCode = generateSHAMasterPassEcommerce(order, locale);
	}
	
	return result;
}

/**
 * Returns an object containg all the information required for the request to Klarna, like accept and decline url, cart amount, etc 
 *  
 * @param {String} paymentMethod
 * @param {dw.order.Order} order
 * @param {dw.customer.Customer} customer
 * @param {String} locale
 * @param {Boolean} useAlias
 * @returns {Object} result
 */
function KlarnaEcommerceData(paymentMethod, order, customer, locale, useAlias) {
    var result = {};
	var IngenicoConfiguration = getIngenicoConfiguration();
	    
	result.formAction = IngenicoConfiguration.EcommerceEndpointISO;    
	result.operation = 'RES';	 
	
    // get the language from custom preference
	// if not available there then use the locale
	var customLanguage = IngenicoConfiguration.language;	
	if(customLanguage == null) {
		if(locale == 'default'){
			result.lang='en_US';
		}
		else{
			result.lang = locale + "_" + locale.toUpperCase();
		}
	}else{
		result.lang = customLanguage;	
	}
   	 
   	if (paymentMethod == 'Installment') {
   		result.ecommaccepturl = dwweb.URLUtils.https('KL_IN_ECM-EcommAccept','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
	    result.ecommdeclineurl = dwweb.URLUtils.https('KL_IN_ECM-EcommDecline','orderNo', order.orderNo, 'tn' , createShaForUrls(order)); 
	    result.ecommdcancelurl = dwweb.URLUtils.https('KL_IN_ECM-EcommCancel','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
   	} else {
	    result.ecommaccepturl = dwweb.URLUtils.https('KL_ECM-EcommAccept','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
	    result.ecommdeclineurl = dwweb.URLUtils.https('KL_ECM-EcommDecline','orderNo', order.orderNo, 'tn' , createShaForUrls(order)); 
	    result.ecommdcancelurl = dwweb.URLUtils.https('KL_ECM-EcommCancel','orderNo', order.orderNo, 'tn' , createShaForUrls(order));
   	}
    
    if (order.getBillingAddress().custom.gender == 1) {
    	result.civility = 'M';
    } else {
    	result.civility = 'F';
    }
    
    result.billtofirstname = order.getBillingAddress().getFirstName();
    result.billtolastname = order.getBillingAddress().getLastName();
    result.emailaddress = order.getCustomerEmail();
    result.orderid = order.orderNo;
    result.currency = order.getCurrencyCode();
    result.owneraddress = order.getBillingAddress().getAddress1();
    result.ownercty = order.getBillingAddress().getCountryCode().value.toString().toUpperCase();
    result.ownertel = order.getBillingAddress().getPhone();
    result.ownertown = order.getBillingAddress().getCity();
    result.ownerzip = order.getBillingAddress().getPostalCode();
    
    result.paymentMethodName = paymentMethod+ " " + result.ownercty;
    
    var ordershipcost = new dwutil.Decimal(parseFloat(order.getAdjustedShippingTotalGrossPrice().value));
	ordershipcost = ordershipcost.round(2);
	ordershipcost = ordershipcost.multiply(100);			
	
	result.ordershipcost = ordershipcost.get().toString();
	
	var shipmentMeth = null;
	var shipments = order.shipments;
	if(!empty(shipments) && shipments.length>0 && !empty(shipments[0].shippingMethod)) {
		shipmentMeth = shipments[0].shippingMethod.displayName;
	} 	
	result.shipmentMeth = shipmentMeth;
	
	var dob = order.getBillingAddress().custom.dateOfBirth;
	if(!empty(dob)) {
		var monthOfBirth = "0" + (dob.getMonth() + 1);
		var dayOfBirth = "0" + dob.getDate();
		result.dateOfBirth = dayOfBirth.slice(-2) + '/' +  monthOfBirth.slice(-2)  + '/' + dob.getFullYear();
	} else {
		result.dateOfBirth = null;
	}

	result.cuid = order.getBillingAddress().custom.cuid;
	result.houseNumber = order.getBillingAddress().custom.houseNumber;	
	var productLineItems =  order.getAllProductLineItems();
	var giftCertificateLineItems =  order.getGiftCertificateLineItems();
	
	result.productLineItems = new dwutil.ArrayList();
	
	var loopstate = 1;
	var itemsAmout = 0;
	for each (var item in productLineItems) {	
		if (empty(item.getProductID())) {
			continue;
		}
		var productItem = new dwutil.HashMap();
		productItem.put('ITEMID' + loopstate, item.getProductID().substring(0,15));
		productItem.put('ITEMNAME' + loopstate, item.getProductName().substring(0,35) + ","+item.getQuantity().getValue().toString());
		productItem.put('ITEMPRICE' + loopstate, item.getAdjustedGrossPrice().value.toFixed(2).toString());
		productItem.put('ITEMQUANT' + loopstate, "1");
		productItem.put('ITEMVATCODE' + loopstate, item.getTaxRate()*100 + '%');
		productItem.put('TAXINCLUDED' + loopstate, "1");
		result.productLineItems.add1(productItem);
		
		//calculate product items cost, according to calculated price
		itemsAmout = itemsAmout + item.getAdjustedGrossPrice().value;
		loopstate++;
	}
	
	for each (var item in giftCertificateLineItems) {	
		var productItem = new dwutil.HashMap();
		productItem.put('ITEMID' + loopstate, item.lineItemText.substring(0,15));
		productItem.put('ITEMNAME' + loopstate, (item.lineItemText + item.price.toString()).substring(0,40));
		productItem.put('ITEMPRICE' + loopstate, item.grossPrice.value.toFixed(2).toString());
		productItem.put('ITEMQUANT' + loopstate, "1");
		productItem.put('ITEMVATCODE' + loopstate, item.getTaxRate()*100 + '%');
		productItem.put('TAXINCLUDED' + loopstate, "1");
		result.productLineItems.add1(productItem);
		
		//calculate product items cost, according to calculated price
		itemsAmout = itemsAmout + item.grossPrice.value;
		loopstate++;
	}
	
	// Set order discount line items
	itemsAmout = addPriceAdjustments(order.getPriceAdjustments(), loopstate, order.orderNo, result, itemsAmout);
	
	var itemsAmoutDec = new dwutil.Decimal(parseFloat(itemsAmout));
	itemsAmoutDec = itemsAmoutDec.round(2);
	itemsAmoutDec = itemsAmoutDec.multiply(100);
	itemsAmoutDec = itemsAmoutDec.add(ordershipcost);

	//var itemsAmoutDec = new Decimal(parseFloat(itemsAmout * 100 + ordershipcost)).round(2);
    result.cartAmount = itemsAmoutDec.get().toString();
    
	result.shaCode = generateSHAKlarnaEcommerce(result);
	
    return result;
}

/**
 * add Price adjustments to Klarna object
 *
 * @param {dw.util.Collection} adjustments Collection of adjustment objects
 * @param {Number} loopstate Next item index
 * @param {String} oid String with the option product ID associated with the discount
 * @param {Object} result Object with Klarna data
 * @param {Number} itemsAmout calculated product items cost
 */
function addPriceAdjustments (adjustments, loopstate, oid, result, itemsAmout) {
	if (!empty(adjustments) && adjustments.length > 0) {
		for each (var adjustment in adjustments) {
		
			var adjustment_unit_price = (adjustment.grossPrice.available ? adjustment.grossPrice.value : adjustment.netPrice.value);
			var adjustment_vat_code = parseInt(adjustment.taxRate*100) + "%";
			var promoName = !empty(adjustment.promotion) && !empty(adjustment.promotion.name) ? adjustment.promotion.name : "discount";
			var promoId = adjustment.promotionID;
			// Include option ID with promotion ID if available
			if (!empty(oid)) {
				promoId = promoId + "_" + oid;
			}
			
			var productItem = new dwutil.HashMap();
			productItem.put('ITEMID' + loopstate, promoId.substring(0,15));
			productItem.put('ITEMNAME' + loopstate, promoName);
			productItem.put('ITEMPRICE' + loopstate, adjustment_unit_price.toFixed(2).toString());
			productItem.put('ITEMQUANT' + loopstate, "1");
			productItem.put('ITEMVATCODE' + loopstate, adjustment_vat_code);
			productItem.put('TAXINCLUDED' + loopstate, "1");
			result.productLineItems.add1(productItem);

			//calculate product items cost, according to calculated price
			itemsAmout = itemsAmout + adjustment_unit_price;
			loopstate++;
		}
	}
	
	return itemsAmout;
}

/**
* Creates the SHA token used to verify the authenticity of the request in the return urls.
* This token is calculated and added to the return urls and then it is read and compared with a different token calculated internally from the same parameters.
* Because Ingenico has a limitation of max 200 characters in the return urls, we take only the first 10 digits from the sha token 
* (also for this reason, the pipeline names and payment method names were shortened) 
* 
* @param {dw.order.Order} order
* @returns {String} shaCode
*/
function createShaForUrls(order) {
	var IngenicoConfiguration = getIngenicoConfiguration();
	
	if (empty(order)) {
		throw Error("No Order created");
	}
	
	// Add parameter alphabetical sorted for sha sign
	var queryMap = new dwutil.HashMap();
	queryMap.put("POSTAL", order.getBillingAddress().getPostalCode());			
	queryMap.put("PHONE", order.getBillingAddress().getPhone());
	queryMap.put("EMAIL", order.getCustomerEmail());
	
	var hashMethod = IngenicoConfiguration.shaMethod;
	var shaPasword = IngenicoConfiguration.shaPasword;
	var digest = new dwcrypto.MessageDigest(hashMethod);
	
	var shaCode = getSHASignParameterFromRequest(queryMap, shaPasword, digest, order.orderNo);
	return shaCode.substring(0,10);
}

/**
* Get the configuration information from Ingenico custom object
* @returns {Object} IngenicoConfiguration
*/
function getIngenicoConfiguration(){

	var co = dwobject.CustomObjectMgr.getCustomObject('IngenicoConfiguration','IngenicoConfiguration');
	var IngenicoConfiguration = {};
	
	/*
	* transactionTracker will be updated with every change to the cartridge (release)
	* by the cartridge developer
	* format is IGDW folowed by the date in the format YYMMDD
	*/
	IngenicoConfiguration['transactionTracker'] = 'IGDW161123';
	
	IngenicoConfiguration['enableIngenico'] = !!co.custom.enableIngenico.getValue();
	IngenicoConfiguration['PSPID'] = co.custom.PSPID;
	IngenicoConfiguration['apiUserID'] = co.custom.apiUserID;
	IngenicoConfiguration['apiUserPassword'] = co.custom.apiUserPassword;
	IngenicoConfiguration['shaMethod'] = co.custom.shaMethod.getValue();
	IngenicoConfiguration['shaPasword'] = co.custom.shaPasword;
	IngenicoConfiguration['shaOutPassword'] = co.custom.shaOutPassword;
	IngenicoConfiguration['paymentCartridgeTimeout'] = co.custom.paymentCartridgeTimeout;
	IngenicoConfiguration['MaintainanceEndpoint'] = co.custom.MaintainanceEndpoint.getValue();
	IngenicoConfiguration['NewOrderEndpoint'] = co.custom.NewOrderEndpoint.getValue();
	IngenicoConfiguration['DirectQueryEndpoint'] = co.custom.DirectQueryEndpoint.getValue();
	IngenicoConfiguration['EcommerceEndpoint'] = co.custom.EcommerceEndpoint.getValue();
	IngenicoConfiguration['EcommerceEndpointISO'] = co.custom.EcommerceEndpointISO.getValue();
	IngenicoConfiguration['HostedTokenizationPageEndpoint'] = co.custom.HostedTokenizationPageEndpoint.getValue();
	IngenicoConfiguration['enable3ds'] = !!co.custom.enable3ds.getValue();
	IngenicoConfiguration['creditCardType'] = co.custom.creditCardType;
	IngenicoConfiguration['enableNewSEPAVersion'] = !!co.custom.enableNewSEPAVersion.getValue();
	IngenicoConfiguration['HostedTokenizationAllowSaveAlias'] = !!co.custom.HostedTokenizationAllowSaveAlias.getValue();
	IngenicoConfiguration['enableFraudDetection'] = !!co.custom.enableFraudDetection.getValue();
	IngenicoConfiguration['enableTwoStepsAuthorizationDirectLink'] = !!co.custom.enableTwoStepsAuthorizationDirectLink.getValue();
	IngenicoConfiguration['enableTwoStepsAuthorizationEcommerce'] = !!co.custom.enableTwoStepsAuthorizationEcommerce.getValue();
	IngenicoConfiguration['refundPaymentMethodsDirectLink'] = co.custom.refundPaymentMethodsDirectLink;
	IngenicoConfiguration['refundPaymentMethodsEcommerce'] = co.custom.refundPaymentMethodsEcommerce;
	IngenicoConfiguration['maxCaptureAttempts'] = co.custom.maxCaptureAttempts;
	IngenicoConfiguration['daysForCapture'] = co.custom.daysForCapture;
	IngenicoConfiguration['language'] = co.custom.language.getValue();	
	IngenicoConfiguration['enableDynamicTemplate'] = co.custom.enableDynamicTemplate.getValue();
	IngenicoConfiguration['paymentDynamicTemplateURL'] = co.custom.paymentDynamicTemplateURL;
	IngenicoConfiguration['enableStaticTemplate'] = co.custom.enableStaticTemplate.getValue();
	IngenicoConfiguration['paymentTemplateBGColor'] = co.custom.paymentTemplateBGColor;
	IngenicoConfiguration['paymentTemplateButtonTxtColor'] = co.custom.paymentTemplateButtonTxtColor;
	IngenicoConfiguration['paymentTemplateTitle'] = co.custom.paymentTemplateTitle;
	IngenicoConfiguration['paymentTemplateFontType'] = co.custom.paymentTemplateFontType;
	IngenicoConfiguration['paymentTemplateTblBGColor'] = co.custom.paymentTemplateTblBGColor;
	IngenicoConfiguration['paymentTemplateTblTxtColor'] = co.custom.paymentTemplateTblTxtColor;
	IngenicoConfiguration['paymentTemplateLogo'] = co.custom.paymentTemplateLogo;
	IngenicoConfiguration['paymentTemplateTxtColor'] = co.custom.paymentTemplateTxtColor;
	IngenicoConfiguration['paymentTemplateButtonBGColor'] = co.custom.paymentTemplateButtonBGColor;
	
	return IngenicoConfiguration;
}

/**
 * Get Payment Processor for Payment Method
 * @param {String} paymentMethodID
 * @returns {String}
 */
function checkEcommercePaymentMethods(paymentMethodID) {
	var paymentMethod = dworder.PaymentMgr.getPaymentMethod(paymentMethodID) ;
	var paymentProcessor = paymentMethod.getPaymentProcessor() ;
	return paymentProcessor.ID ;
}

/**
 * Decline order call
 * @param {dw.order.Order} Order
 */
function ecommDecline(Order) {
	var service = dwsvc.ServiceRegistry.get('Ingenico.ecommDecline');	
	var url = dwweb.URLUtils.https('PAYMENT_ECOMMERCE-MarkOrdersFailed','orderNo',Order.orderNo,'pc',Order.getBillingAddress().getPostalCode(),'pn',Order.getBillingAddress().getPhone(),'ce',Order.getCustomerEmail());
	var callPrams = {
		"url" : requestUrl
	}	
	service.call(callPrams).object;
}

/**
 * Creates a payment instrument specific for the given payment type (payment method)
 * for the given basket. If any error occurs the pipelet returns PIPELET_ERROR with
 * no payment instrument being created. If the creation succeeded the script returns 
 * the newly created payment instrument.
 *
 * If only one payment instrument is allowed, any existing payment instrument may be
 * removed by using the RemoveExisting input parameter and passing a Boolean true.
 * 
 * @param {dw.order.LineItemCtnr} lineItemCtnr
 * @param {String} paymentType
 * @returns {dw.order.PaymentInstrument} 
 */
function EcommerceCreatePaymentInstrument(lineItemCtnr, paymentType){
	
	// remove existing payment instruments from the basket
	lineItemCtnr.removeAllPaymentInstruments() ;
	
	// calculate the amount to be reflected by this payment instrument
	var amount = calculateNonGiftCertificateAmount(lineItemCtnr);
	
	// create a payment instrument
	var paymentInstr = lineItemCtnr.createPaymentInstrument(paymentType, amount);
		
	if(session.forms.billing.paymentMethods.saveAlias.value){
		session.custom.saveAlias = true;
	} else {
		session.custom.saveAlias = false;
	}
	
	if(!empty(request.httpParameterMap.savedAliases.stringValue)){
		session.custom.alias = request.httpParameterMap.savedAliases.stringValue;
	} else {
		session.custom.alias = dwutil.UUIDUtils.createUUID().toUpperCase();
	}	
	
	session.custom.paymentMethode = 'PAYPAL';
	
	return paymentInstr;
}

/**
 * Verify status for Order
 * 
 * @param {dw.order.Order} order
 * @returns {String} status
 */
function EcommerceVerifyStatus(order) {
	var log = dwsystem.Logger.getLogger("payment");  
	var resultObj = null;
	var status = '';
	var start = new Date();
	
	if (empty(order)) {
		log.error('EcommerceVerifyStatus function in libPayment.ds: Missing required input parameter(s) Order.');
		return '';
	}
	
	var paymentInstruments = order.getPaymentInstruments();
	if (!paymentInstruments.empty) {
		var paymentInstrument = paymentInstruments[0];
		if (!empty(paymentInstrument)) {
			var brand = paymentInstrument.getPaymentMethod();
		}
	}
		
	try {
		var paymentCtnr = new PaymentCtnr(order, order.orderNo, null, null, null, null);	
		resultObj = paymentCtnr.sendDirectQuery();		
	} catch (error) {
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;
		
		if(error.status == "0" ){
			log.error("EcommerceVerifyStatus function in libPayment.ds: Payment Direct Link Authorization Request invalid or incomplete; OrderNo: {1}", order.orderNo);
		} else if ( error.status == "2" ) {
			log.error("EcommerceVerifyStatus function in libPayment.ds: Payment Direct Link Authorization Request Authorisation refused; OrderNo: {1}", order.orderNo);
		} else if ( error.status == "51" ) {
			log.error("EcommerceVerifyStatus function in libPayment.ds: Payment Direct Link Authorisation Request Authorisation waiting; OrderNo: {1}", order.orderNo);
		} else if ( error.status == "52" ) {
			log.error("EcommerceVerifyStatus function in libPayment.ds: Payment Direct Link Authorisation Request Authorisation not known; OrderNo: {1}", order.orderNo);
		}
		return '';
	}
	
	if (!empty(resultObj)) {
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;	
	}
	
	if(status == "95" || status == "9"){
		order.setPaymentStatus(dworder.Order.PAYMENT_STATUS_PAID);
		paymentInstrument.paymentTransaction.custom.paymentStatus = status;
		paymentInstrument.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstrument.paymentTransaction.custom.paymentStatus);
	}
	
	return status;
}

/**
 *  Get Hosted Tokenization URL
 *  
 *  @returns {String} url
 */
function GetHostedTokenizationURL() {
	var IngenicoConfiguration = getIngenicoConfiguration(); 
	
	var callURL = IngenicoConfiguration.HostedTokenizationPageEndpoint;
	var pspid = IngenicoConfiguration.PSPID;
	var shaIn = IngenicoConfiguration.shaPasword;
	var hashMethod = IngenicoConfiguration.shaMethod;
	var digest = new dwcrypto.MessageDigest(hashMethod);
		
	var requestParameters = new dwutil.HashMap();
	var sortedRequestParameters = new dwutil.SortedMap();
	var hashString = '';
	var url = '';
	
	var httpParameterMap = request.httpParameterMap;
	var locale = request.locale;
	
	session.custom.hostedTokenizationSucces = false;
	
	if(httpParameterMap.failed.booleanValue){
		var paymentMethode = session.custom.paymentMethode;
		var saveAlias = session.custom.saveAlias;
		var useAlias = session.custom.alias;
	} else {
		var paymentMethode = httpParameterMap.paymentMethode.value;
		var saveAlias = httpParameterMap.saveAlias.booleanValue;
		var useAlias = httpParameterMap.useAlias.value;
	}	
	
	requestParameters.put('Account.PSPID', pspid);
	
	if(!empty(paymentMethode) && paymentMethode == 'CreditCard'){
		requestParameters.put('Card.PaymentMethod','CreditCard');
		session.custom.paymentMethode = 'CreditCard';
	} else if (!empty(paymentMethode)){
		requestParameters.put('Card.Brand', paymentMethode.replace(/_/g,''));
		session.custom.paymentMethode = paymentMethode;
	} else {
		session.custom.hostedTokenizationSucces = true;
		return '';
	}
	
	session.custom.paymentMethode = paymentMethode;
	
	if(!empty(locale) && locale !='default'){
		requestParameters.put('Layout.Language', locale);
	}
	
	if(saveAlias){
		session.custom.saveAlias = true;
		requestParameters.put('Alias.StorePermanently', 'Y');
	} else {
		session.custom.saveAlias = false;
		requestParameters.put('Alias.StorePermanently', 'N');
	}
	if(!empty(useAlias)){		
		session.custom.alias = useAlias;
		requestParameters.put('Alias.AliasId',useAlias);
	} else {
		var alias = dwutil.UUIDUtils.createUUID().toUpperCase();
		session.custom.alias = alias;
		requestParameters.put('Alias.AliasId',alias);
	}
	
	requestParameters.put('Parameters.AcceptUrl', dwweb.URLUtils.https('Ingenico-HostedTokenizationPage').toString());
	requestParameters.put('Parameters.ExceptionUrl', dwweb.URLUtils.https('Ingenico-HostedTokenizationPage', 'failed', 'true').toString());
	
	sortedRequestParameters.putAll(requestParameters);
	
	var paramKeySet : Set = sortedRequestParameters.keySet();
	var paramIterator : Iterator = paramKeySet.iterator();
	
	var paramValue = '';
	var paramName : String = '';
	
	url += callURL + "?";
	
	while ( paramIterator.hasNext() ) {
		
		paramName = paramIterator.next().toString();
		paramValue = sortedRequestParameters.get(paramName);
		
		if (!empty(paramValue)) {
			hashString += paramName.toUpperCase() + '=' + paramValue.toString() + shaIn;
			url += paramName + '=' + paramValue.toString() + '&';
		}
	}
	
	if(!empty(shaIn) && !empty(hashString)){
		url += 'ShaSignature.ShaSign=' + digest.digest(hashString);
	} else {
		url = url.substring(0,url.lastIndexOf('&'));
	}
	
	return url;
}

/**
 * Sends Payment Direct Link Authorization.
 * @param {dw.order.Order} order
 * @param {String} locale
 * @param {String} payMethID
 * @returns {object} ret
 * @returns {String} ret.Status
 * @returns {String} ret.PaymentMethodID
 * @returns {String} ret.ErrorStatus
 * @returns {String} ret.PaymentStatus
 * @returns {String} ret.ResultStatusCheck
 * @returns {Boolean} ret.success
 */
function GetPaymentDirectLinkOrderData(order, locale, payMethID) {
	var log = dwsystem.Logger.getLogger("payment");
	var ret = {
		'Status' : null,
		'PaymentMethodID' : null,
		'ErrorStatus' : null,
		'PaymentStatus' : null,
		'ResultStatusCheck' : null,
		'success' : false		
	}
	
	if (empty(order)) {
		log.error('GetPaymentDirectLinkOrderData function in libPayment.ds: Missing required input parameter(s) Order.');
		return ret;
	}
	
	var resultObj = null;
	var status = "";
	var PaymentMethod = "";
	var brand = payMethID;
	var payID = "";
	var start = new Date();
	
	var paymentInstruments = order.getPaymentInstruments();
	var amount = calculateNonGiftCertificateAmount(order);
	
	if( paymentInstruments == null || paymentInstruments.isEmpty() ) {
		var paymentInstr = order.createPaymentInstrument(payMethID, amount);
	} else {
		var paymentInstr = paymentInstruments[0];
	}
	var paymentprocc = dworder.PaymentMgr.getPaymentMethod(payMethID).getPaymentProcessor(); 
	paymentInstr.paymentTransaction.setPaymentProcessor(paymentprocc);
	
	try {
		var paymentCtnr = new PaymentCtnr(order, order.orderNo, null, null, null, locale);
		status = paymentCtnr.getStatus();
		resultObj = paymentCtnr.sendDirectQuery();
	} catch (error) {
		dwsystem.Logger.error(error.toString());
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;
		paymentInstr.paymentTransaction.custom.paymentStatus = error.status;
		paymentInstr.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstr.paymentTransaction.custom.paymentStatus);
	
		ret.ResultStatusCheck = paymentCtnr.checkStatus(status, error.ErrorCode);
		ret.ErrorStatus=error.status.toString();
	
		return ret;
	}
	
	if (!empty(resultObj)) {
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;
		PaymentMethod = resultObj.hasOwnProperty("pm") ? resultObj.pm : '';
		payID = resultObj.hasOwnProperty("payID") ? resultObj.payID : '';
		brand = resultObj.hasOwnProperty("brand") ? resultObj.brand : '';
	
		order.custom.payment_PAYID = payID;
	
		if (!empty(brand)) {
			PaymentMethod += " - " + brand;
		}
	
		// set used payment method to ensure used payment method ID could be returned
		if (!empty(PaymentMethod)) paymentCtnr.setPaymentMethod(PaymentMethod);
	}
	
	ret.Status = status;
	ret.PaymentMethodID = paymentCtnr.getPaymentMethodId();
	
	paymentInstr.paymentTransaction.setTransactionID(payID.toString());
	paymentInstr.paymentTransaction.custom.paymentStatus=status;
	paymentInstr.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstr.paymentTransaction.custom.paymentStatus);
	paymentInstr.custom.paymentMethod=PaymentMethod;
	
	// check payment status
	ret.ResultStatusCheck = paymentCtnr.checkStatus(status, 0);
	ret.ErrorStatus = status;
	
	if (status == "41") {
		order.custom.payment_pending = true;
	} else if (status == "9") {
		order.custom.payment_needCapture = false;
		order.custom.payment_authorizationStatus = 'REQUESTED';
		paymentInstr.paymentTransaction.custom.paymentStatus = status;
		paymentInstr.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstr.paymentTransaction.custom.paymentStatus);
		order.setPaymentStatus(dworder.Order.PAYMENT_STATUS_PAID);
	}

	ret.PaymentStatus = status ;
	if(!(ret.ResultStatusCheck == "FAIL")) {
		ret.success = true;
	}
	return ret;
}

/**
 * Sends Payment Ecommerce Authorization.
 * 
 * @param {dw.order.Order} order
 * @param {String} locale
 * @param {String} payMethID
 * @returns {object} ret
 * @returns {String} ret.Status
 * @returns {String} ret.PaymentMethodID
 * @returns {String} ret.ErrorStatus
 * @returns {String} ret.PaymentStatus
 * @returns {String} ret.ResultStatusCheck
 * @returns {Boolean} ret.success
 */
function GetPaymentEcommOrderData(order, locale, payMethID) {
	var log = dwsystem.Logger.getLogger("payment");
	var ret = {
		'Status' : null,
		'PaymentMethodID' : null,
		'ErrorStatus' : null,
		'PaymentStatus' : null,
		'ResultStatusCheck' : null,
		'success' : false		
	}	
	var IngenicoConfiguration = getIngenicoConfiguration();

	
	if (empty(order)) {
		log.error('GetPaymentEcommOrderData function in libPayment.ds: Missing required input parameter(s) Order.');
		return ret;
	}
	
	var resultObj = null;
	var status = "";
	var PaymentMethod = "";
	var brand = payMethID;
	var payID = "";
	var ip = "";
	
	
	var start = new Date();
		
	try {	
		
		var paymentCtnr = new PaymentCtnr(order, order.orderNo, null, null, null, locale);
		status = paymentCtnr.getStatus();
	
		resultObj = paymentCtnr.sendDirectQuery();		
	}
	catch (error) {
		var x = error;
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;

		ret.ResultStatusCheck = paymentCtnr.checkStatus(error.status, error.ErrorCode);
		
		ret.ErrorStatus=error.status.toString();
		return ret;
	}
	
	var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
	
	if (!empty(resultObj)) {
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;
		PaymentMethod = resultObj.hasOwnProperty("pm") ? resultObj.pm : '';
		payID = resultObj.hasOwnProperty("payid") ? resultObj.payid : '';
		brand = resultObj.hasOwnProperty("brand") ? resultObj.brand : '';
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			order.custom.payment_ip = resultObj.hasOwnProperty("ip") ? resultObj.ip : '';
			order.custom.payment_aavCheck = resultObj.hasOwnProperty("aavcheck") ? resultObj.aavcheck : '';
			order.custom.payment_cvcCheck = resultObj.hasOwnProperty("cvccheck") ? resultObj.cvccheck : '';
			order.custom.payment_scoring = resultObj.hasOwnProperty("scoring") ? resultObj.scoring : '';
			order.custom.payment_scoCategory = resultObj.hasOwnProperty("sco_category") ? resultObj.sco_category : '';	
		}
		
		order.custom.payment_PAYID = payID;
	
		if(!empty(brand)){
			PaymentMethod+=" - "+brand;
		}
		
		// set used payment method to ensure used payment method ID could be returned
		if (!empty(PaymentMethod)) paymentCtnr.setPaymentMethod(PaymentMethod);
	}
	
	
	//set paymentinstrument data
	var ECOMM_PAYMENT = payMethID;
	
	if( order == null){
		return ret;
	}	
	
	ret.Status = status;
	ret.PaymentMethodID = paymentCtnr.getPaymentMethodId();
	
	var paymentInstruments = order.getPaymentInstruments();
	var amount = calculateNonGiftCertificateAmount(order);
	
	if(paymentInstruments == null || paymentInstruments.isEmpty()) {
		var paymentInstr = order.createPaymentInstrument(payMethID, amount);
	}
	else{
		var paymentInstr = paymentInstruments[0];
	}
	
	var paymentprocc = dworder.PaymentMgr.getPaymentMethod(ret.PaymentMethodID).getPaymentProcessor(); 
	
	paymentInstr.getPaymentTransaction().setPaymentProcessor(paymentprocc);
	
	paymentInstr.getPaymentTransaction().setTransactionID(payID.toString());
	paymentInstr.paymentTransaction.custom.paymentStatus = status;
	paymentInstr.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstr.paymentTransaction.custom.paymentStatus);
	paymentInstr.custom.paymentMethod=PaymentMethod;
	
	if (status == "5") {
		// Update the order for background step 2 reservation request
		order.custom.payment_isAuthorized = true;
		order.custom.payment_needCapture = true;
		order.custom.payment_captureAttempts = 0;
		order.custom.payment_authorizationStatus = 'AUTHORISED';
		order.custom.payment_data = JSON.stringify({
		paymentMethodID: ret.PaymentMethodID,
		locale: locale
		});
	}else{
		order.custom.payment_isAuthorized = false;
		order.custom.payment_needCapture = false;
		order.custom.payment_captureAttempts = 0;
	
		//# Result Status Check - possible values: FAIL, RETRY, SUCCESS
		ret.ResultStatusCheck = paymentCtnr.checkStatus(status, 0);
		if(ret.ResultStatusCheck == "FAIL") {
			return ret;
		}
			
		if(status != "9" && status != "41"){
			ret.ErrorStatus = status;
		}else{
			if(status == "41"){
				order.custom.payment_pending = true ;
			} 
		}
	}
	
	ret.PaymentStatus = status;
	ret.success = true;
	return ret;
}

/**
 * Sends PayPal Ecommerce Informations.
 * 
 * @param {dw.order.Order} order
 * @param {String} locale
 * @param {dw.web.HttpParameterMap} paramMap
 * @returns {object} ret
 * @returns {String} ret.ErrorStatus
 * @returns {Boolean} ret.success
 */
function SendPayPalInfo(order, locale, paramMap) {
	var log = dwsystem.Logger.getLogger("payment");  
	var IngenicoConfiguration = getIngenicoConfiguration();
	var ret = {
			'ErrorStatus' : null,
			'success' : false
	}
	var paymentCtnr = null;
	
	if (empty(order)) {
		log.error('SendPayPalInfo function in libPayment.ds: Missing required input parameter(s) Order.');
		return ret;
	}
	
	
	var paymentInstruments = order.getPaymentInstruments();
	var amount = calculateNonGiftCertificateAmount(order);
	
	if( paymentInstruments == null || paymentInstruments.isEmpty() ) {
		var paymentInstr = order.createPaymentInstrument("PAYPAL", amount);
	}
	else{
		var paymentInstr = paymentInstruments[0];
	}
	
	var paymentprocc = dworder.PaymentMgr.getPaymentMethod("PAYPAL").getPaymentProcessor(); 
	paymentInstr.getPaymentTransaction().setPaymentProcessor(paymentprocc);
	
	paymentInstr.custom.paypalToken = paramMap.TXTOKEN.value;
	paymentInstr.custom.paypalPayerID = paramMap.PAYID.value;
	paymentInstr.custom.paypalpayeremail = paramMap.PayerEmail.value;
	
	
	var start = new Date();
	
	try {	
		paymentCtnr = new PaymentCtnr(order, order.orderNo, null, "PAYPAL", null , locale);
		
		var status = paymentCtnr.getStatus();
	
		resultObj = paymentCtnr.sendDirectLinkPayPal();		
	}
	catch (error) {
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;
		ret.ErrorStatus = error.status.toString();
		if(error.status == "0" ){
			log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorization Request invalid or incomplete; OrderNo: {1}", order.orderNo);
			
		} else if ( error.status == "2" ) {
			log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorization Request Authorisation refused; OrderNo: {1}", order.orderNo);
			
		} else if ( error.status == "52" ) {
			log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorisation Request Authorisation not known; OrderNo: {1}", order.orderNo);
			
		}
		
		return ret;
	}
	
	var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
	
	if ( !empty(resultObj) ) {
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;
		PaymentMethod = resultObj.hasOwnProperty("pm") ? resultObj.pm : '';
		payID = resultObj.hasOwnProperty("payid") ? resultObj.payid : '';
		brand = resultObj.hasOwnProperty("brand") ? resultObj.brand : '';
		html =  resultObj.hasOwnProperty("html") ? resultObj.html : '';
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			order.custom.payment_ip = resultObj.hasOwnProperty("ip") ? resultObj.ip : '';
			order.custom.payment_aavCheck = resultObj.hasOwnProperty("aavcheck") ? resultObj.aavcheck : '';
			order.custom.payment_cvcCheck = resultObj.hasOwnProperty("cvccheck") ? resultObj.cvccheck : '';
			order.custom.payment_scoring = resultObj.hasOwnProperty("scoring") ? resultObj.scoring : '';
			order.custom.payment_scoCategory = resultObj.hasOwnProperty("sco_category") ? resultObj.sco_category : '';	
		}
		
		if(!empty(brand)){
			PaymentMethod+=" - "+brand;
		}
		
		// set used payment method to ensure used payment method ID could be returned
		if (!empty(PaymentMethod)) paymentCtnr.setPaymentMethod(PaymentMethod);
	}
	
	order.custom.payment_PAYID = payID;
	
	paymentInstr.getPaymentTransaction().setTransactionID(payID.toString());
	paymentInstr.paymentTransaction.custom.paymentStatus = status;
	paymentInstr.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstr.paymentTransaction.custom.paymentStatus);
	paymentInstr.custom.paymentMethod = PaymentMethod;
	
	var paymentInstrument : OrderPaymentInstrument = order.getPaymentInstruments()[0];
	var paymentMethodID = paymentInstrument.getPaymentMethod();
	
	if (status == "5") {
		// Update the order for background step 2 reservation request
		order.custom.payment_isAuthorized = true;
		order.custom.payment_needCapture = true;
		order.custom.payment_captureAttempts = 0;
		order.custom.payment_authorizationStatus = 'AUTHORISED';
		order.custom.payment_data = JSON.stringify({
		paymentMethodID: paymentMethodID,
		locale: locale
		});
	}else{
		order.custom.payment_isAuthorized = false;
		order.custom.payment_needCapture = false;
		order.custom.payment_captureAttempts = 0;
	
		if(status != "9"){
			ret.ErrorStatus = status.toString();
			if(status == "0" ){
				// quickfix paypal status 0
				order.custom.payment_authorizationStatus = 'NO_AUTHORISED';
				order.custom.payment_data = JSON.stringify({
					paymentMethodID: paymentMethodID,
					locale: locale
				});
				ret.success = true;
				return ret;
				/* temporarily disabled error on status 0
				log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorization Request invalid or incomplete; OrderNo: {1}", order.orderNo);
				return ret;
				*/
			} else if ( status == "46" ) {				
				log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorization Request returned with enrolled and empty HTML. Status: {0}; OrderNo: {1}", status, order.orderNo);
				
				return ret;					
			} else if ( status == "2" ) {
				log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorization Request Authorisation refused; OrderNo: {1}", order.orderNo);
				
				return ret;
			} else if ( status == "52" ) {
				log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorisation Request Authorisation not known; OrderNo: {1}", order.orderNo);
				
				return ret;
			} else if ( status == "51" ) {
				log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorisation Request Authorisation waiting; OrderNo: {1}", order.orderNo);
				
				return ret;
			} else {
				log.error("SendPayPalInfo function in libPayment.ds: PayPal Authorization Request Authorisation refused; OrderNo: {1}", order.orderNo);
				
				return ret;
			}
		}
	}
	
	ret.success = true;
	return ret;
}

/**
 * Save Paypal alias information
 */
function savePaypalAlias() {
	if(session.custom.saveAlias){
		var savedAliases = session.customer.profile.custom.hostedTokenizationPaymentMethods;
		var newPaypalAlias = true;
		var savedAliasesObj = JSON.parse(savedAliases);
		if(empty(savedAliasesObj)){
			savedAliasesObj = [];
		}
		
		for each(var alias in savedAliasesObj){
			if(alias[0]=='PAYPAL'){
				alias[1] = session.custom.alias;
				newPaypalAlias = false;
			}
		}
		
		if(newPaypalAlias){
			savedAliasesObj.push(['PAYPAL',session.custom.alias]);
		}
		session.customer.profile.custom.hostedTokenizationPaymentMethods = JSON.stringify(savedAliasesObj);
		
		session.custom.paymentMethode = null;
		session.custom.saveAlias = null;
		session.custom.alias = null;
	}
}

/**
 *  Set Klarna addition fiels
 *  
 *  @param {String} PaymentMethod
 *  @param {String} Country
 *  @param {dw.customer.Customer} CurrentCustomer
 *  @param {dw.order.Basket} Basket
 */
function setKlarnaFields(PaymentMethod, Country, CurrentCustomer, Basket) {
	var templateName = 'klarnaecommerce' + Country;
	var CurrentForms = session.forms;
	var CurrentHttpParameterMap = request.httpParameterMap;
	
	if (PaymentMethod == "Klarna Installment E-commerce") {
		templateName = 'klarnaecommerceinstallment' + Country;
	}
	if (!empty(CurrentForms.billing.paymentMethods[templateName].dateOfBirth) && CurrentHttpParameterMap[CurrentForms.billing.paymentMethods[templateName].dateOfBirth.htmlName].stringValue) {
		Basket.billingAddress.custom.dateOfBirth = new Date(CurrentHttpParameterMap[CurrentForms.billing.paymentMethods[templateName].dateOfBirth.htmlName].stringValue.replace(/-/g,'/'));
	}
	
	if (!empty(CurrentForms.billing.paymentMethods[templateName].cuid)) {
		Basket.billingAddress.custom.cuid = CurrentHttpParameterMap[CurrentForms.billing.paymentMethods[templateName].cuid.htmlName].stringValue;
	}
	if (!empty(CurrentForms.billing.paymentMethods[templateName].houseNumber)) {
		Basket.billingAddress.custom.houseNumber = CurrentHttpParameterMap[CurrentForms.billing.paymentMethods[templateName].houseNumber.htmlName].stringValue;
	}
	
	if (!empty(CurrentForms.billing.paymentMethods[templateName].gender)) { 
		Basket.billingAddress.custom.gender = parseInt(CurrentHttpParameterMap[CurrentForms.billing.paymentMethods[templateName].gender.htmlName].value);
	}
}

/**
 * Validate the Direct Debits forms data fields.
 * The script get the PaymentForms object at input, containing all forms data (Direct Debits DE, AT and NL)
 * , validate current form and add output results: - the status of the validation, - the paymentType and - the data of the current form validated.    
 *
 * @param {dw.web.FormGroup} form
 * @param {dw.customer.Customer} customer
 * @returns {Object} ret
 * @returns {dw.system.Status} ret.DirectDebitsStatus
 * @returns {String} ret.PaymentType
 * @returns {dw.web.FormGroup} ret.DirectDebitsForm
 * @returns {Boolean} ret.success
 */
function VerifyPaymentDirectDebits(form, customer) {
	var IngenicoConfiguration = getIngenicoConfiguration();
	var ret = {
			'DirectDebitsStatus' : null,
			'PaymentType' : null,
			'DirectDebitsForm' : null,
			'success' : false			
	}
	var paymentType : String = forms.selectedPaymentMethodID.htmlValue;
	var newSEPA = IngenicoConfiguration.enableNewSEPAVersion;
	
	if (empty(paymentType)) {
		Logger.error('VerifyPaymentDirectDebits function in libPayment.ds : Empty paymentType');
		return ret;
	}

	var mapForms = {
		'DIRECT_DEBITS_DE' : forms.directdebitsde,
		'DIRECT_DEBITS_NL' : forms.directdebitsnl,
		'DIRECT_DEBITS_AT' : forms.directdebitsat
	};

	var form = mapForms[paymentType];
	var status = new dwsystem.Status();

	// Validate Direct Debits DE form:
	if (paymentType == 'DIRECT_DEBITS_DE') {
		// Check to be completed the Konto and Blz fields, if Iban is empty (CARDNO = Konto + Blz) or Iban completed (CARDNO = Iban).
		// Not allowed IBAN with Konto or Blz empty, or all three empty.
		var ibanRequired = newSEPA && empty(form.iban.htmlValue);
		var ibanNullOrRequired = !newSEPA || ibanRequired;
		if (ibanRequired && empty(form.konto.htmlValue) && empty(form.blz.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'IBAN-MISSING',
				Resource.msg('forms.directdebitsde.iban.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else if (ibanNullOrRequired && empty(form.konto.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'KONTO-MISSING',
				Resource.msg('forms.directdebitsde.konto.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else if (ibanNullOrRequired && empty(form.blz.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'BLZ-MISSING',
				Resource.msg('forms.directdebitsde.blz.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else {
			// Check the fields data lengths: iban=22 digits, konto=up to 10, blz=8 digits 
			if (newSEPA && !empty(form.iban.htmlValue)) {
				if (form.iban.htmlValue.replace(/\s/g, '').length != 22) {
					var statusItem = new dwsystem.StatusItem(
						Status.ERROR,
						'IBAN-INVALID',
						Resource.msg('forms.directdebitsde.iban.invalid', 'forms', null),
						{});
					status.addItem(statusItem);
				}
			}
			if (!empty(form.konto.htmlValue)) {
				if (form.konto.htmlValue.replace(/\s/g, '').length > 10) {
					var statusItem = new dwsystem.StatusItem(
						Status.ERROR,
						'KONTO-INVALID',
						Resource.msg('forms.directdebitsde.konto.invalid', 'forms', null),
						{});
					status.addItem(statusItem);
				}
			}
			if (!empty(form.blz.htmlValue)) {
				if (form.blz.htmlValue.replace(/\s/g, '').length != 8) {
					var statusItem = new dwsystem.StatusItem(
						Status.ERROR,
						'BLZ-INVALID',
						Resource.msg('forms.directdebitsde.blz.invalid', 'forms', null),
						{});
					status.addItem(statusItem);
				}
			}
		}
		// - Expiration Date fields
		if (empty(form.month.htmlValue) || empty(form.year.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'ED-MISSING',
				Resource.msg('forms.directdebitsnl.ed.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else {
			var ccExpYear = new Number(form.year.selectedOption.value);
			var ccExpMonth = new Number(form.month.selectedOption.value) - 1; // January should be 0

			var today = new Date();
			var expDate = new Date(ccExpYear, ccExpMonth, 1);

			if (expDate < today) {
				var statusItem = new dwsystem.StatusItem(
					Status.ERROR,
					'ED-INVALID',
					Resource.msg('forms.directdebitsnl.ed.invalid', 'forms', null),
					{});
				status.addItem(statusItem);
			}
		}
	// Validate Direct Debits AT form:
	} else if (paymentType == 'DIRECT_DEBITS_AT') {
		// - konto field (lengths for Austria: konto=up to 11 digits, blz=5 digits)
		if (empty(form.konto.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'KONTO-MISSING',
				Resource.msg('forms.directdebitsat.konto.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else if (form.konto.htmlValue.replace(/\s/g, '').length > 11) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'KONTO-INVALID',
				Resource.msg('forms.directdebitsat.konto.invalid', 'forms', null),
				{});
			status.addItem(statusItem);
		}
		// - blz field
		if (empty(form.blz.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'BLZ-MISSING',
				Resource.msg('forms.directdebitsat.blz.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else if (form.blz.htmlValue.replace(/\s/g, '').length != 5) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'BLZ-INVALID',
				Resource.msg('forms.directdebitsat.blz.invalid', 'forms', null),
				{});
			status.addItem(statusItem);
		}
		// - Expiration Date fields
		if (empty(form.month.htmlValue) || empty(form.year.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'ED-MISSING',
				Resource.msg('forms.directdebitsnl.ed.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		} else {
			var ccExpYear = new Number(form.year.selectedOption.value);
			var ccExpMonth = new Number(form.month.selectedOption.value) - 1; // January should be 0

			var today = new Date();
			var expDate = new Date(ccExpYear, ccExpMonth, 1);

			if (expDate < today) {
				var statusItem = new dwsystem.StatusItem(
					Status.ERROR,
					'ED-INVALID',
					Resource.msg('forms.directdebitsnl.ed.invalid', 'forms', null),
					{});
				status.addItem(statusItem);
			}
		}
	// Validate Direct Debits NL form:
	} else if (paymentType == 'DIRECT_DEBITS_NL') {
		// - IBAN field (lengths for Netherland: Iban=18 digits, bic=up to 11 digits)
		if (empty(form.iban.htmlValue)) {
			var statusItem = new dwsystem.StatusItem(
				Status.ERROR,
				'IBAN-MISSING',
				Resource.msg('forms.directdebitsnl.iban.missing', 'forms', null),
				{});
			status.addItem(statusItem);
		}
		// - BIC field if SEPA enabled
		if (newSEPA && !empty(form.bic.htmlValue)) {
			if (form.bic.htmlValue.replace(/^\s+|\s+$/gm,'').length > 11) {
				var statusItem = new dwsystem.StatusItem(
					Status.ERROR,
					'BIC-INVALID',
					Resource.msg('forms.directdebitsnl.bic.invalid', 'forms', null),
					{});
				status.addItem(statusItem);
			}
		}
	}

	// For all Direct Debits forms 
	// - Cardholder Name field
	if (empty(form.cn.htmlValue)) {
		var statusItem = new dwsystem.StatusItem(
			Status.ERROR,
			'CN-MISSING',
			Resource.msg('forms.directdebitsnl.cn.missing', 'forms', null),
			{});
		status.addItem(statusItem);
	}

	ret.DirectDebitsStatus = status;
	ret.DirectDebitsForm = form;
	ret.PaymentType = paymentType;
	
	if (status.error) {
		return ret;
	}
	
	
	var checkValues = form.cn.htmlValue;
	if (paymentType != 'DIRECT_DEBITS_NL') {
		checkValues += "," + form.month.selectedOption.value + "," + form.year.selectedOption.value;
	} 
	
	ret.success = true;
	return ret;
}

/**
 * Invalidates the Direct Debits form element in case specified status is ERROR.
 * If status is undefined or form is invalid the pipelet returns PIPELET_ERROR. 
 * 
 * @param {dw.system.Status} status
 * @param {dw.web.FormGroup} directDebitsForm
 * @returns {Boolean}
 */
function InvalidatePaymentDirectDebitsFormElements(status, directDebitsForm) {
	
	// verify that we have a status object and a valid credit card form
	if (status == null || !directDebitsForm.valid) {
		return false;
	}
	
	// we are fine, if status is OK
	if (status.status == dwsystem.Status.OK) {
		return true;
	}
	
	// invalidate the payment card form elements
	var items = status.items.iterator();
	while (items.hasNext()) {
		var item = items.next();
	
		switch (item.code) {
			case 'IBAN-MISSING':
				directDebitsForm.iban.invalidateFormElement(item.message);
				continue;
	
			case 'IBAN-INVALID':
				directDebitsForm.iban.invalidateFormElement(item.message);
				continue;
	
			case 'KONTO-MISSING':
				directDebitsForm.konto.invalidateFormElement(item.message);
				continue;
	
			case 'KONTO-INVALID':
				directDebitsForm.konto.invalidateFormElement(item.message);
				continue;
	
			case 'BLZ-MISSING':
				directDebitsForm.blz.invalidateFormElement(item.message);
				continue;
	
			case 'BLZ-INVALID':
				directDebitsForm.blz.invalidateFormElement(item.message);
				continue;
	
			case 'BIC-MISSING':
				directDebitsForm.bic.invalidateFormElement(item.message);
				continue;
	
			case 'BIC-INVALID':
				directDebitsForm.bic.invalidateFormElement(item.message);
				continue;
	
			case 'CN-MISSING':
				directDebitsForm.cn.invalidateFormElement(item.message);
				continue;
	
			case 'ED-MISSING':
				directDebitsForm.month.invalidateFormElement(item.message);
				continue;
	
			case 'ED-INVALID':
				directDebitsForm.month.invalidateFormElement(item.message);
				continue;
	
		}
	}
	
	return true;
}

/**
 * Sends Authorization to payment through DirectLink for Direct Debits AT, DE and NL 
 * 
 * @param {dw.web.FormGroup} forms
 * @param {dw.order.Order} order
 * @param {String} locale
 * @returns {String} ret.Status
 * @returns {String} ret.StatusDescription
 * @returns {String} ret.PaymentMethod
 * @returns {String} ret.PayID
 * @returns {String} ret.Brand
 * @returns {String} ret.PaymentMethodIDOut
 * @returns {String} ret.ErrorString
 * @returns {String} ret.ResultStatusCheck
 * @returns {Boolean} ret.succes
 */
function SendDirectDebitsAuthorization(forms, order, locale) {
	var log : Logger = dwsystem.Logger.getLogger("payment");
	var IngenicoConfiguration = getIngenicoConfiguration();
	var ret = {
			'Status' : null,
			'StatusDescription' : null,
			'PaymentMethod' : null,
			'PayID' : null,
			'Brand' : null,
			'PaymentMethodIDOut' : null,
			'ErrorString' : null,
			'ResultStatusCheck' : null,
			'success' : false,
	}
	
	var paymentType = forms.selectedPaymentMethodID.htmlValue;
	
	var mapForms = {
		'DIRECT_DEBITS_DE' : forms.directdebitsde,
		'DIRECT_DEBITS_NL' : forms.directdebitsnl,
		'DIRECT_DEBITS_AT' : forms.directdebitsat
	};
	
	var form = mapForms[paymentType];
	var paymentMethodId = paymentType;

	var paymentMgr = null;
	
	if (empty(order)) {
		log.error('SendDirectDebitsAuthorization function in libPayment.ds: Missing required input parameter(s) Order.');
		return ret;
	}
	
	var resultObj = null;
	var status = "";
	var PaymentMethod = "";
	var payID = "";
	var brand = "";
	
	var start = new Date();
	var enableTwoSteps = IngenicoConfiguration.enableTwoStepsAuthorizationDirectLink;
	var enableNewSEPAVersion = IngenicoConfiguration.enableNewSEPAVersion;
	var operation = (paymentType == 'DIRECT_DEBITS_NL') ? 'VEN' : enableTwoSteps ? 'RES' : 'VEN';
	
	try {
		paymentObj = new PaymentCtnr(order, order.orderNo, null, paymentMethodId, null, locale);
	
		status = paymentObj.getStatus();
	
		var data = {};
		if (paymentType == 'DIRECT_DEBITS_DE') {
			data = {
				iban: form.iban.htmlValue,
				konto: form.konto.htmlValue,
				blz: form.blz.htmlValue
			};
		} else if (paymentType == 'DIRECT_DEBITS_NL') {
			data = {
				iban: form.iban.htmlValue
			};
			// if SEPA enabled, display BIC optional field for NL 
			if (enableNewSEPAVersion) {
				var transactionDate = order.creationDate;
				var getTDYear = transactionDate.getFullYear().toString();
				var getTDMonth = (transactionDate.getMonth() + 1) < 10 ? '0' + (transactionDate.getMonth() + 1).toString() : (transactionDate.getMonth() + 1).toString();
				var getTDDate = transactionDate.getDate() < 10 ? '0' + transactionDate.getDate().toString() : transactionDate.getDate().toString();
				
				if (form.bic.htmlValue) {
					data.bic = form.bic.htmlValue;
				}
				data.transactionDate = getTDYear + getTDMonth + getTDDate;
			}
		} else if (paymentType == 'DIRECT_DEBITS_AT') {
			data = {
				konto: form.konto.htmlValue,
				blz: form.blz.htmlValue
			};
		}
		
		
		// make the payment request and save the response to 'resultObj' 
	
		resultObj = paymentObj.sendDirectLinkDirectDebitsNewOrderRequest(operation, locale, data);
	
	
	} catch (error) {
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;
		if(!empty(error.status) && !empty(error.ErrorCode)){
			ret.ResultStatusCheck = paymentObj.checkStatus(error.status, error.ErrorCode);
		} else {
			ret.ResultStatusCheck = '';
		}
		return ret;
	}
	
	var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
	
	if ( !empty(resultObj) ) {
		// get resultObj data, store them to output 
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;
		PaymentMethod = resultObj.hasOwnProperty("pm") ? resultObj.pm : '';
		payID = resultObj.hasOwnProperty("payid") ? resultObj.payid : '';
		brand = resultObj.hasOwnProperty("brand") ? resultObj.brand : '';
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			order.custom.payment_ip = resultObj.hasOwnProperty("ip") ? resultObj.ip : '';
			order.custom.payment_aavCheck = resultObj.hasOwnProperty("aavcheck") ? resultObj.aavcheck : '';
			order.custom.payment_cvcCheck = resultObj.hasOwnProperty("cvccheck") ? resultObj.cvccheck : '';
			order.custom.payment_scoring = resultObj.hasOwnProperty("scoring") ? resultObj.scoring : '';
			order.custom.payment_scoCategory = resultObj.hasOwnProperty("sco_category") ? resultObj.sco_category : '';	
		}
		
		if(!empty(brand)){
			PaymentMethod+=" - "+brand;
		}
	
		// set used payment method to ensure used payment method ID could be returned
		if (!empty(PaymentMethod)) paymentObj.setPaymentMethod(PaymentMethod);
	}
	
	order.custom.payment_PAYID = payID;
	
	ret.Status = status;
	ret.StatusDescription = getPaymentStatusDescription(status);
	ret.PaymentMethod = PaymentMethod;
	ret.PayID = payID;
	ret.Brand = brand;
	ret.PaymentMethodIDOut = paymentMethodId;
	
	// status=5, the authorisation has been accepted
	if (status == "5") {
		// Update the order for background step 2 reservation request
		order.custom.payment_isAuthorized = true;
		order.custom.payment_needCapture = true;
		order.custom.payment_authorizationStatus = 'AUTHORISED';
		order.custom.payment_captureAttempts = 0;
		order.custom.payment_data = JSON.stringify({
			paymentMethodID: paymentMethodId,
			locale: locale
		});
	} else {
		order.custom.payment_isAuthorized = false;
		order.custom.payment_needCapture = false;
		order.custom.payment_captureAttempts = 0;
	
		ret.ResultStatusCheck = paymentObj.checkStatus(status, 0);
		
		if(status != "9"){
			// status=46, waiting for identification.
			if ( status == "46" ) {
				if (enableTwoSteps) {
					order.custom.payment_data = JSON.stringify({
						paymentMethodID: PaymentMethodId,
						locale: locale
					});
				}
			// status=51, the authorisation will be processed offline.
			} else if ( status == "51" ) {
				order.getCustom()["payment_authWaiting"] = true;
				
				log.error("SendDirectDebitsAuthorization function in libPayment.ds: Direct Link Authorisation Request Authorisation waiting; OrderNo: {0}", order.orderNo);
				ret.ErrorString = Resource.msg('billing.paymentstatus51', locale + '/checkout', null);
			}
	
		}
		// if status=9, the payment has been accepted.		
		
		if (status != "51") {
			order.setPaymentStatus(dworder.Order.PAYMENT_STATUS_PAID);
		}		
		
	}
	if(ret.ResultStatusCheck == "FAIL") {
		return ret;
	}
	ret.success = true;
	return ret;
}

/**
 *	Checks wether 3DS is necessary. When payment_last3DSauthorization on OrderPaymentInstrument exists and is before now
 *	3DS check isn't necessary anymore. Only one 3DS Check per Customer and Credit Card is required.
 * @param {dw.order.Order} order
 * @return {Boolean}
 */
function Is3DSEnabled(order) {
	
	var log = dwsystem.Logger.getLogger("payment");
	var IngenicoConfiguration = getIngenicoConfiguration();
	
	var secureCards = IngenicoConfiguration.creditCardType
		.toLocaleString()
		.split(',');//array of objects(LIST)
	secureCards = toArrayOfStrings(secureCards);//array of strings
	
	// if 'enable3ds' attribute is enabled, first get data for the current order
	if (IngenicoConfiguration.enable3ds) {
		var orderPaymentInstrument = order.currentOrder.getPaymentInstruments()[0];
		// if current card type is one from the list in secureCards, check if exists payment_last3DSauthorization
		var cardType = orderPaymentInstrument.creditCardType;
		if(empty(cardType)){
			cardType = session.custom.paymentMethode;
		}
		if(!empty(cardType) && secureCards.contains(cardType.toUpperCase())){
			if ( !empty(orderPaymentInstrument.custom.payment_last3DSauthorization) ) {
				var last3DSauthorization = new dwutils.Calendar(orderPaymentInstrument.custom.payment_last3DSauthorization); 
				if ( last3DSauthorization.before(new dwutils.Calendar()) ) {
					return true;
				}
			}else{
				// if not, create payment_last3DSauthorization = current date, enable 3DS
				var last3DSauthorization = new Date(); 
				orderPaymentInstrument.custom.payment_last3DSauthorization=last3DSauthorization;
				return true;
			}
		}
	}
	
	return false;
}

/**
 * Get Payment request Parameters
 * 
 * @param {dw.order.Order} Order
 * @param {dw.system.Request} Request
 * @param {String} PaymentMethodID
 * @returns {Object} ret
 * @returns {dw.util.HashMap} ret.PaymentRequestParameters
 * @returns {Boolean} ret.success
 */
function GetPaymentRequestParameters(Order, Request, PaymentMethodID) {
	var log = dwsystem.Logger.getLogger("payment");
	var ret = {
			'PaymentRequestParameters' : null,
			'success' : false
	}
	
	try {
		if (empty(Order)) throw Error("No Order provided");
		if (empty(Request)) throw Error("No CurrentRequest provided");
		if (empty(Order.orderNo)) throw Error("No OrderNo provided");
		
		var paymentCtnr = new PaymentCtnr(Order, Order.orderNo, null, PaymentMethodID, Request, Request.locale);
		
		ret.PaymentRequestParameters = paymentCtnr.getRequestParameters();
		ret.success = true;
	}
	catch (error) {
		log.error("GetPaymentRequestParameters function in libPayment.ds: An error occured during Payment Request Parameters build: " + error);
		return ret;
	}
	
	return ret;
}

/**
 * Change payment status
 *
 * @param {dw.order.Order} order
 * @param {dw.web.HttpParameterMap} paramMap
 * @param {String} locale
 * @returns {Object} ret
 * @returns {String} ret.ResultStatusCheck
 * @returns {Boolean} ret.success
 */
function PaymentChangeStatus(order, paramMap, locale) {
	var ret = {
			'ResultStatusCheck' : null,
			'success' : false
	}
	
	var IngenicoConfiguration = getIngenicoConfiguration();
	
	var status = paramMap.STATUS.value;

	var paymentInstrument = order.getPaymentInstruments()[0];
	var paymentMethodID = paymentInstrument.getPaymentMethod();	
	
	var resultObj = null;
	var status = "";
	var PaymentMethod = "";
	
	var payID="";
	var ip = "";
	var start : Date = new Date();
	
	try {	
		var paymentCtnr = new PaymentCtnr(order, order.orderNo, null, null, null, locale);	
		status = paymentCtnr.getStatus();
	
		resultObj = paymentCtnr.sendDirectQuery();		
	}
	catch (error) {
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;
		
		ret.ResultStatusCheck = paymentCtnr.checkStatus(error.status, error.ErrorCode);
				
		return ret;
	}
	
	var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
	if ( !empty(resultObj) ) {
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;
		PaymentMethod = resultObj.hasOwnProperty("pm") ? resultObj.pm : '';
		payID = resultObj.hasOwnProperty("payid") ? resultObj.payid : '';
		brand = resultObj.hasOwnProperty("brand") ? resultObj.brand : '';
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			order.custom.payment_ip = resultObj.hasOwnProperty("ip") ? resultObj.ip : '';
			order.custom.payment_aavCheck = resultObj.hasOwnProperty("aavcheck") ? resultObj.aavcheck : '';
			order.custom.payment_cvcCheck = resultObj.hasOwnProperty("cvccheck") ? resultObj.cvccheck : '';
			order.custom.payment_scoring = resultObj.hasOwnProperty("scoring") ? resultObj.scoring : '';
			order.custom.payment_scoCategory = resultObj.hasOwnProperty("sco_category") ? resultObj.sco_category : '';	
		}
		
		order.custom.payment_PAYID = payID;
	
		if(!empty(brand)){
			PaymentMethod+=" - "+brand;
		}
		
		// set used payment method to ensure used payment method ID could be returned
		if (!empty(PaymentMethod)) paymentCtnr.setPaymentMethod(PaymentMethod);
	}
	

	if( order == null){
		return ret;
	}
	
	var paymentInstruments = order.getPaymentInstruments();
	var amount = calculateNonGiftCertificateAmount(order);
   
    if( paymentInstruments == null || paymentInstruments.isEmpty() ) {
    	var paymentInstr = order.createPaymentInstrument(PaymentMethod, amount);
    }
    else{
    	var paymentInstr = paymentInstruments[0];
    }
	
	var paymentprocc : dworder.PaymentProcessor = dworder.PaymentMgr.getPaymentMethod(paymentMethodID).getPaymentProcessor(); 
	
    paymentInstr.getPaymentTransaction().setPaymentProcessor(paymentprocc);
  
    paymentInstr.getPaymentTransaction().setTransactionID(payID.toString());
    paymentInstr.paymentTransaction.custom.paymentStatus = status;
    paymentInstr.paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentInstr.paymentTransaction.custom.paymentStatus);
    paymentInstr.custom.paymentMethod=PaymentMethod;

	if (status == "5") {
		// Update the order for background step 2 reservation request
		order.custom.payment_isAuthorized = true;
		order.custom.payment_needCapture = true;
		order.custom.payment_captureAttempts = 0;
		order.custom.payment_authorizationStatus = 'AUTHORISED';
		order.custom.payment_data = JSON.stringify({
			paymentMethodID: paymentMethodID,
			locale: locale
		});
	} else {
		order.custom.payment_isAuthorized = false;
		order.custom.payment_needCapture = false;
		order.custom.payment_captureAttempts = 0;

		if (status == '8') {
			order.custom.payment_refundStatus = 'REFUNDED';
		} else if (status == '82') {
			order.custom.payment_refundStatus = 'UNCERTAIN';
		} else if (status == '83') {
			order.custom.payment_refundStatus = 'REFUSED';
		} else if (status == '84') {
			order.custom.payment_refundStatus = 'DECLINED_BY_ACQUIRER';
		} else if (status == '85') {
			order.custom.payment_refundStatus = 'HANDLED_BY_MERCHANT';
		}

		var paymentInstrument : OrderPaymentInstrument = order.getPaymentInstruments()[0];
		var paymentTransaction = paymentInstrument.paymentTransaction;

		if (status == '9') {
			order.custom.payment_needCapture = false;
			order.custom.payment_captureAttempts += 1;
			order.custom.payment_authorizationStatus = 'REQUESTED';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
			order.setPaymentStatus(dworder.Order.PAYMENT_STATUS_PAID);
		} else if (status == '91') {
			order.custom.payment_authorizationStatus = 'PENDING';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);	
		} else if (status == '92') {
			order.custom.payment_authorizationStatus = 'UNCERTAIN';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		} else if (status == '93') {
			order.custom.payment_authorizationStatus = 'REFUSED';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		} else if (status == '94') {
			order.custom.payment_authorizationStatus = 'DECLINED_BY_ACQUIRER';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		} else if (status == '95') {
			order.custom.payment_authorizationStatus = 'HANDLED_BY_MERCHANT';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		} else if (status == '6') {
			order.custom.payment_authorizationStatus = 'CANCELLED';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		} else if (status == '62') {
			order.custom.payment_authorizationStatus = 'UNCERTAIN';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		} else if (status == '63') {
			order.custom.payment_authorizationStatus = 'REFUSED';
			paymentTransaction.custom.paymentStatus=status;
			paymentTransaction.custom.paymentStatusDescription = getPaymentStatusDescription(paymentTransaction.custom.paymentStatus);
		}
	}
	
	// add check status method
	// output the result and do fail/success/retry	
	ret.ResultStatusCheck = paymentCtnr.checkStatus(status, 0);
	ret.success = true;
	return ret;
}

/**
 * Sets two custom attributes for orders, when TwoStep is enabled
 * 
 * @param {dw.web.HttpParameterMap} params
 * @returns {Boolean}
 */
function SetAuthorizedPayment(params) {
	var log = dwsystem.Logger.getLogger("payment");
	var IngenicoConfiguration = getIngenicoConfiguration();
	
	var orderNo = params['orderNo'].stringValue;
	var enableTwoSteps = IngenicoConfiguration.enableTwoStepsAuthorizationDirectLink;
	var order = dworder.OrderMgr.getOrder(orderNo);

	var shaCode = createShaForUrls(order);
	if(shaCode != params.tn.toString()){
		log.error('Order change not allowed, missing parameters');
		return false;
	}
	
	try {
		if (enableTwoSteps) {
			// Update the order for background step 2 reservation request
			order.custom.payment_isAuthorized = true;
			order.custom.payment_needCapture = true;
			order.custom.payment_captureAttempts = 0;
			order.custom.payment_authorizationStatus = 'AUTHORISED';
		} else {
			order.setPaymentStatus(dworder.Order.PAYMENT_STATUS_PAID);
		}		
	} catch(error) {
		log.error("SetAuthorizedPayment function in libPayment.ds: {1}", error);
	}

	return true;
}

/**
 * Validates Payment response and checks if SHAOut and parameters are valid
 * 
 * @param {dw.web.HttpParameterMap} paramMap
 * @returns {Boolean}
 */
function paymentSHAValidation(paramMap) {
	
    var log = dwsystem.Logger.getLogger("payment");
	var IngenicoConfiguration = getIngenicoConfiguration();
	
    var SHAOut = IngenicoConfiguration.shaOutPassword;
    if (empty(SHAOut)) {
        return false;
    }

    if (empty(paramMap)) {
        log.error('paymentSHAValidation function in libPayment.ds: Missing required input parameter(s).');
        return false;
    }
    if (paramMap.getParameterCount() <= 0) {
        log.error('paymentSHAValidation function in libPayment.ds: Missing Payment HTTP parameter(s).');
        return false;
    }
	
	var shaSignFromRequest;
    var hashMethod = IngenicoConfiguration.shaMethod;
    var digest = new dwcrypto.MessageDigest(hashMethod);
    var paramSortedMap = new dwutil.SortedMap();
    var paramIterator = paramMap.getParameterNames().iterator();

    while (paramIterator.hasNext()) {
        var paramName = paramIterator.next().toString();
		
		// exclude parameters sent from DW		
        if (paramName.indexOf('dw_') == 0 || paramName == 'tn' || paramName == 'orderNo' || paramName == 'pmID') {
            continue;
        }
        if (paramName == 'SHASIGN') {
            shaSignFromRequest = paramMap[paramName].value;
            continue;
        }
        // paramters without value are not included in sha calculation
		if (empty(paramMap[paramName].value)){
			continue;
		}		
        paramSortedMap.put(paramName.toUpperCase(), paramMap[paramName].value);
    }

    if (empty(shaSignFromRequest)) {
        return false;
    }

    var paramKeySet = paramSortedMap.keySet();
    var paramIterator = paramKeySet.iterator();
    var paramString = '';
    while (paramIterator.hasNext()) {
        var paramName = paramIterator.next().toString();
        var paramValue = paramSortedMap.get(paramName);
        paramString += paramName + '=' + paramValue.toString() + SHAOut;
    }

    var shaSignCalculated = digest.digest(paramString).toUpperCase();
    if (shaSignCalculated !== shaSignFromRequest) {
        log.error('paymentSHAValidation function in libPayment.ds: Payment returned parameters do not match the returned hash.');
        return false;
    }

    return true;
}

/**
 * Sends Authorization to payment through DirectLink for Credit Cards.
 * 
 * @param {dw.order.Order} order
 * @param {Boolean} enable3DS
 * @param {String} PaymentMethodID
 * @param {String} locale
 * @param {dw.customer.Customer} Customer
 * @returns {String} ret.Status
 * @returns {String} ret.PaymentMethod
 * @returns {String} ret.PayID
 * @returns {String} ret.Brand
 * @returns {Boolean} ret.Enrolled
 * @returns {String} ret.HTML
 * @returns {String} ret.PaymentMethodIDOut
 * @returns {String} ret.ErrorString
 * @returns {String} ret.ErrorString
 * @returns {Boolean} ret.succes
 */
function SendHostedTokenizationCreditCardAuthorization(order, enable3DS, PaymentMethodID, locale, Customer) {
	var log = dwsystem.Logger.getLogger("payment");  
	var IngenicoConfiguration = getIngenicoConfiguration();
	var ret = {
			'Status' : null,
			'PaymentMethod' : null,
			'PayID' : null,
			'Brand' : null,
			'Enrolled' : null,
			'HTML' : null,
			'PaymentMethodIDOut' : null,
			'ErrorString' : null,
			'ResultStatusCheck' : null,
			'succes' : false
	}
	var paymentMgr = null;
	var enable3DS = !empty(enable3DS) ? enable3DS : true;
	
	if (empty(order)) {
		log.error('SendHostedTokenizationCreditCardAuthorization function in libPayment.ds: Missing required input parameter(s) Order.');
		return ret;
	}
	
	var resultObj = null;
	var status = "";
	var PaymentMethod = "";
	var payID = "";
	var brand = "";
	var html = "";
	var ip = "";
	
	var start : Date = new Date();
	var enableTwoSteps = IngenicoConfiguration.enableTwoStepsAuthorizationDirectLink;
	var operation = enableTwoSteps ? 'RES' : 'SAL';
	
	try {
		var paymentObj = new PaymentCtnr(order, order.orderNo, null, PaymentMethodID,null,locale);
		
		status = paymentObj.getStatus();
		
		resultObj = paymentObj.sendDirectLinkCreditCardHostedTokenizationNewOrderRequest(operation, enable3DS, locale, null, null);
	
	}
	catch (error) 
	{
		var x = error;
		var end = new Date();
		var diff = (end.getTime() - start.getTime()) * 0.001;
		if(!empty(error.status) && !empty(error.ErrorCode)){
			ret.ResultStatusCheck = paymentObj.checkStatus(error.status, error.ErrorCode);
			ret.ErrorString=error.status.toString();
		} else {
			ret.ResultStatusCheck = '';
			ret.ErrorString = '';
		}
		return ret;
	}
	
	var useFraudDetection = IngenicoConfiguration.enableFraudDetection;
	
	if ( !empty(resultObj) ) {
		status = resultObj.hasOwnProperty("status") ? resultObj.status : status;
		PaymentMethod = resultObj.hasOwnProperty("pm") ? resultObj.pm : '';
		payID = resultObj.hasOwnProperty("payid") ? resultObj.payid : '';
		brand = resultObj.hasOwnProperty("brand") ? resultObj.brand : '';
		html = resultObj.hasOwnProperty("html") ? resultObj.html : '';
		
		if (!empty(useFraudDetection) && useFraudDetection) {
			order.custom.payment_ip = resultObj.hasOwnProperty("ip") ? resultObj.ip : '';
			order.custom.payment_aavCheck = resultObj.hasOwnProperty("aavcheck") ? resultObj.aavcheck : '';
			order.custom.payment_cvcCheck = resultObj.hasOwnProperty("cvccheck") ? resultObj.cvccheck : '';
			order.custom.payment_scoring = resultObj.hasOwnProperty("scoring") ? resultObj.scoring : '';
			order.custom.payment_scoCategory = resultObj.hasOwnProperty("sco_category") ? resultObj.sco_category : '';	
		}
	
		if (!empty(brand)) {
			PaymentMethod += " - " + brand;
		}
	
		// set used payment method to ensure used payment method ID could be returned
		if (!empty(PaymentMethod)) paymentObj.setPaymentMethod(PaymentMethod);
	}
	
	order.custom.payment_PAYID = payID;
	
	ret.Status = status;
	ret.PaymentMethod = PaymentMethod;
	ret.PayID = payID;
	ret.Brand = brand;
	ret.Enrolled = false;
	ret.HTML = html;
	ret.PaymentMethodIDOut = PaymentMethodID;
	
	// status=5, the authorisation has been accepted
	if (status == "5") {
		// Update the order for background step 2 reservation request
		order.custom.payment_isAuthorized = true;
		order.custom.payment_needCapture = true;
		order.custom.payment_captureAttempts = 0;
		order.custom.payment_authorizationStatus = 'AUTHORISED';
		order.custom.payment_data = JSON.stringify({
			paymentMethodID: PaymentMethodID,
			locale: locale
		});
		
		if(session.custom.saveAlias){
			var savedAliases = Customer.profile.custom.hostedTokenizationPaymentMethods;
			var savedAliasesObj = JSON.parse(savedAliases);
			if(empty(savedAliasesObj)){
				savedAliasesObj = [];
			}
			savedAliasesObj.push([brand,session.custom.alias]);
			Customer.profile.custom.hostedTokenizationPaymentMethods = JSON.stringify(savedAliasesObj);
		}
		
		session.custom.paymentMethode = null;
		session.custom.saveAlias = null;
		session.custom.alias = null;
		
	} else {
		order.custom.payment_isAuthorized = false;
		order.custom.payment_needCapture = false;
		order.custom.payment_captureAttempts = 0;
	
		if(status != "9"){
			// status=46, waiting for identification.
			if ( status == "46" ) {
				if (enableTwoSteps) {
					order.custom.payment_data = JSON.stringify({
						paymentMethodID: PaymentMethodID,
						locale: locale
					});
				}
				ret.Enrolled = true;
				
			// status=51, the authorisation will be processed offline.
			} else if ( status == "51" ) {
				ret.Enrolled = true;
				order.getCustom()["payment_authWaiting"] = true;
				
				log.error("SendHostedTokenizationCreditCardAuthorization function in libPayment.ds: Direct Link Authorisation Request Authorisation waiting; OrderNo: {0}", order.orderNo);
				ret.ErrorString = Resource.msg('billing.paymentstatus51', locale + '/checkout', null);
			}	
		}
		
		ret.ResultStatusCheck = paymentObj.checkStatus(status, 0);
		if(ret.ResultStatusCheck == "FAIL") {
			return ret;	
		}
		// status=9, the payment has been accepted
		
		// set order status as paid 
		if(!ret.Enrolled ){
			order.setPaymentStatus(dworder.Order.PAYMENT_STATUS_PAID);
		}
	}
	ret.success = true;
	return ret;

}

module.exports = {
	PaymentCtnr: PaymentCtnr,
    getSHASignParameterFromRequest: getSHASignParameterFromRequest,
    getSHASignParameterFromRequestUtf: getSHASignParameterFromRequestUtf,
    formatExpiryNumbers: formatExpiryNumbers,
    getURIParametersFromMap: getURIParametersFromMap,
    generateSHAEcommerce: generateSHAEcommerce,
    generateSHAMasterPassEcommerce: generateSHAMasterPassEcommerce,
    generateSHAEcommercePaypal: generateSHAEcommercePaypal,
    generateSHAKlarnaEcommerce: generateSHAKlarnaEcommerce,
    toArrayOfStrings: toArrayOfStrings,
    createDeviceFingerprintSessionID: createDeviceFingerprintSessionID,
    getPaymentStatusDescription: getPaymentStatusDescription,
    calculateNonGiftCertificateAmount: calculateNonGiftCertificateAmount,
    addPriceAdjustments: addPriceAdjustments,
    createShaForUrls: createShaForUrls,
    getIngenicoConfiguration: getIngenicoConfiguration,
    checkEcommercePaymentMethods: checkEcommercePaymentMethods,
    ecommDecline: ecommDecline,
    EcommerceCreatePaymentInstrument: EcommerceCreatePaymentInstrument,
    EcommerceVerifyStatus: EcommerceVerifyStatus,
    GetHostedTokenizationURL: GetHostedTokenizationURL,
    GetPaymentDirectLinkOrderData: GetPaymentDirectLinkOrderData,
    GetPaymentEcommOrderData: GetPaymentEcommOrderData,
    SendPayPalInfo: SendPayPalInfo,
    savePaypalAlias: savePaypalAlias,
    setKlarnaFields: setKlarnaFields,
    KlarnaEcommerceData: KlarnaEcommerceData,
    MasterpassEcommerceData: MasterpassEcommerceData,
    VerifyPaymentDirectDebits: VerifyPaymentDirectDebits,
    InvalidatePaymentDirectDebitsFormElements: InvalidatePaymentDirectDebitsFormElements,
    SendDirectDebitsAuthorization: SendDirectDebitsAuthorization,
    Is3DSEnabled: Is3DSEnabled,
    GetPaymentRequestParameters: GetPaymentRequestParameters,
    PaymentChangeStatus: PaymentChangeStatus,
    SetAuthorizedPayment: SetAuthorizedPayment,
    paymentSHAValidation: paymentSHAValidation,
    SendHostedTokenizationCreditCardAuthorization: SendHostedTokenizationCreditCardAuthorization
};