/**
* @copyright Transpayrent ApS 2026
* @license Common Transpayrent API license
*
* @class
* @classdesc The Transpayrent Helper SDK for Mastercard Click-to-Pay is a thin wrapper around the
* [Mastercard Unified Checkout Solutions (UCS) JavaScript SDK]{@link https://developer.mastercard.com/}.
*
* **This helper does not orchestrate the Click-to-Pay UI flow.** Merchants remain responsible for
* recognition, OTP, card selection, fallback screens, and checkout popups. The helper provides:
*
* 1. **Button rendering** via {@link TranspayrentClickToPayHelper#displayPaymentButton}
* 2. **Mastercard passthrough methods** with identical names (`init`, `getCards`, `idLookup`, etc.)
* 3. **Transpayrent integration utilities** that reduce boilerplate around readiness checks,
* checkout result normalization, gateway handoff context, and optional session storage
*
* Click to Pay intentionally does **not** provide `displayPaymentSheet` orchestration.
*
* Recommended merchant sequence:
* 1. Load Transpayrent SDK + Mastercard UCS script
* 2. `helper.getReadiness()` and show/hide Click to Pay UI accordingly
* 3. `helper.init(merchantOwnedInitRequest)` then call Mastercard passthrough methods as needed
* 4. Call `checkoutWithCard` / `checkoutWithNewCard` — they normalize the Mastercard result and
* store the checkout handoff automatically when `checkoutActionCode` is `COMPLETE`
* 5. Apply the saved handoff when building gateway `authenticate` / `authorize` payloads via
* `helper.applyCheckoutHandoff(paymentDetails)` (or the static
* `TranspayrentClickToPayHelper.applyCheckoutHandoff(paymentDetails, handoff)`)
* 6. Continue with Transpayrent gateway `authenticate` / `authorize` or UI Helper `completePayment`
*
* @see [Transpayrent]{@link Transpayrent}
* @see {@link Transpayrent#getClickToPayHelper}
*
* @constructor
* @description Creates a new instance of the Transpayrent Click-to-Pay Helper, which uses the
* Mastercard Unified Checkout Solutions SDK internally to abstract the Click-to-Pay API.
*
* @example <caption>Recommended merchant-owned Click-to-Pay checkout handoff</caption>
* var helper = sdk.getClickToPayHelper({ debugLogging: true });
* var readiness = helper.getReadiness();
* if (!readiness.ready) {
* console.warn('Click to Pay unavailable:', readiness.issues);
* return;
* }
*
* helper.init(merchantOwnedInitRequest)
* .then(function() { return helper.checkoutWithCard(merchantOwnedCheckoutRequest); })
* .then(function(checkoutResult) {
* if (checkoutResult.checkoutActionCode !== 'COMPLETE') {
* return;
* }
* return uiHelper.completePayment(205, false);
* })
* .catch(function(error) {
* console.error(error.api, error.status, error.messages[0].code, error.messages[0].message);
* });
*
* @param {String} environment The Click-to-Pay environment, either PRODUCTION or TEST
* @param {ClickToPayConfig} config The base configuration for Click-to-Pay
* @param {ClickToPayTransaction} [transaction] Optional transaction details for Click-to-Pay
*/
function TranspayrentClickToPayHelper(environment, config, transaction) {
/**
* The Click-to-Pay environment that will be used by the helper SDK.
* Will be either PRODUCTION or TEST.
*
* @private
* @type {String}
*/
this._environment = environment;
/**
* @typedef {Object} ClickToPayCheckoutHandoff
* @property {String} checkoutActionCode
* @property {String} [merchantTransactionId]
* @property {String} [srcFlowId]
* @property {String} [checkoutResponse]
* @property {String} [recognitionToken]
* @property {Object} [checkoutResponseData]
* @property {*} [rawResult]
*/
/**
* @typedef {Object} ClickToPayCheckoutHandoffRef
* @property {String} merchantTransactionId
* @property {String} [srcFlowId]
*/
/**
* @typedef {Object} ClickToPayReadinessIssue
* @property {String} code
* @property {String} message
*/
/**
* @typedef {Object} ClickToPayReadiness
* @property {Boolean} ready
* @property {ClickToPayReadinessIssue[]} issues
*/
/**
* @typedef {Object} ClickToPayRecognitionSessionStoreOptions
* @property {Storage} [storage]
* @property {String} [recognitionTokenKeyPrefix]
* @property {String} [rememberMeKeyPrefix]
* @property {Boolean} [rememberMeDefault]
* @property {Function} [identityNormalizer]
*/
/**
* @typedef {Object} ClickToPayRecognitionSessionStore
* @property {Function} normalizeIdentity
* @property {Function} getRecognitionToken
* @property {Function} setRecognitionToken
* @property {Function} removeRecognitionToken
* @property {Function} getRememberMe
* @property {Function} setRememberMe
* @property {Function} clearIdentity
*/
/**
* Base configuration for Click-to-Pay
*
* @typedef {Object} ClickToPayConfig
* @property {Integer} merchantId Transpayrent's unique ID for the merchant
* @property {Object} clickToPay Mastercard Click-to-Pay SDK configuration
* @property {String} [email] Optional email address for identity lookup
* @property {String} [mobile] Optional mobile number for identity lookup
* @property {Boolean} [rememberDevice] Flag indicating whether to remember this device
* @property {Boolean} [debugLogging] When true, logs MC SDK request/response payloads to the browser
* console for debugging only. Do not enable in production unless
* intentionally troubleshooting a Click-to-Pay integration.
* The CVV/security code is always hidden from these logs regardless
* of this flag; every other field (including the PAN) is logged in
* full, so treat any captured output as sensitive when sharing it
* (email, tickets, etc.).
* @property {ClickToPayRecognitionSessionStoreOptions} [recognitionStorage]
* Optional recognition storage configuration. When provided, the helper
* creates a browser-local store and exposes it through
* {@link TranspayrentClickToPayHelper#recognitionStorage}.
*/
/**
* The base configuration for Click-to-Pay
*
* @private
* @type {ClickToPayConfig}
*/
this._config = config;
/**
* The details for the payment transaction using Click-to-Pay
*
* @typedef {Object} ClickToPayTransaction
* @property {Integer} amount The payment transaction amount
* @property {String} currency The payment transaction currency code
* @property {String} country The country the payment transaction takes place in
* @property {Boolean} [save] Flag indicating whether the consumer elected to save the card
*/
/**
* The details for the payment transaction using Click-to-Pay
*
* @private
* @type {ClickToPayTransaction}
*/
this._transaction = transaction;
/**
* Promise from the most recent init() call, or null when not initialized.
*
* @private
* @type {Promise|null}
*/
this._initPromise = null;
/**
* When true, logs MC SDK request/response payloads to the console.
*
* @private
* @type {Boolean}
*/
this._debugLogging = !!(config && config.debugLogging);
/**
* Last normalized checkout handoff stored automatically after a successful
* {@link TranspayrentClickToPayHelper#checkoutWithCard} or
* {@link TranspayrentClickToPayHelper#checkoutWithNewCard} call.
*
* @private
* @type {ClickToPayCheckoutHandoff|null}
*/
this._checkoutHandoff = null;
/**
* Identity-scoped recognition token / remember-me storage, or null when not configured.
*
* @private
* @type {ClickToPayRecognitionSessionStore|null}
*/
this._recognitionSessionStore = (config && config.recognitionStorage)
? TranspayrentClickToPayHelper.createRecognitionSessionStore(config.recognitionStorage)
: null;
this._bindRecognitionStorage();
}
var TRANSPAYRENT_C2P_CALL_SEQUENCE = 0;
// Matches by substring (not exact key name) so prefixed/suffixed variants like
// "cardSecurityCode" (Mastercard UCS) or "cardCvv" are caught too, not just bare "cvv"/"securityCode".
var TRANSPAYRENT_C2P_CVV_KEY_PATTERN = /cvv2?|cvc|securitycode/i;
function transpayrentC2pIsCvvKey(key) {
return TRANSPAYRENT_C2P_CVV_KEY_PATTERN.test(String(key).replace(/[^a-zA-Z0-9]/g, ''));
}
/**
* @private
*/
TranspayrentClickToPayHelper.prototype._bindRecognitionStorage = function() {
this.recognitionStorage = this._recognitionSessionStore || null;
};
/**
* @private
* @param {*} value
* @returns {Object|null}
*/
TranspayrentClickToPayHelper._asRecord = function(value) {
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
return null;
}
return value;
};
/**
* @private
* @param {*} value
* @returns {String|undefined}
*/
TranspayrentClickToPayHelper._readString = function(value) {
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
};
/**
* @private
* @param {Object|undefined} headers
* @param {String} key
* @returns {String|undefined}
*/
TranspayrentClickToPayHelper._getHeaderValue = function(headers, key) {
if (!headers) {
return undefined;
}
var normalizedKey = key.toLowerCase();
var headerKey;
for (headerKey in headers) {
if (headers.hasOwnProperty(headerKey) && headerKey.toLowerCase() === normalizedKey) {
return TranspayrentClickToPayHelper._readString(headers[headerKey]);
}
}
return undefined;
};
/**
* @private
* @param {String} api
* @param {String} code
* @param {String} message
* @param {Number} [status]
* @param {*} [rawError]
* @returns {Object}
*/
TranspayrentClickToPayHelper._createNormalizedError = function(api, code, message, status, rawError) {
return {
api: api,
status: typeof status === 'number' ? status : 400,
transaction_id: undefined,
rawError: rawError,
messages: [{ code: code, message: message, system: 'CLICK_TO_PAY' }]
};
};
/**
* @private
* @returns {Object}
*/
TranspayrentClickToPayHelper._createMemoryStorage = function() {
var values = {};
return {
getItem: function(key) {
return Object.prototype.hasOwnProperty.call(values, key) ? values[key] : null;
},
setItem: function(key, value) {
values[key] = String(value);
},
removeItem: function(key) {
delete values[key];
}
};
};
/**
* Normalizes an identity value for recognition/session storage keys.
*
* @static
* @param {String} identity
* @param {Function} [identityNormalizer]
* @returns {String}
*/
TranspayrentClickToPayHelper.normalizeIdentity = function(identity, identityNormalizer) {
if (typeof identityNormalizer === 'function') {
return identityNormalizer(identity);
}
var trimmed = typeof identity === 'string' ? identity.trim() : '';
if (!trimmed) {
return '';
}
if (trimmed.indexOf('@') >= 0) {
return trimmed.toLowerCase();
}
return trimmed.replace(/\s+/g, '');
};
/**
* Returns readiness diagnostics for the Mastercard UCS browser SDK.
*
* @public
* @returns {ClickToPayReadiness}
*
* @example
* var readiness = helper.getReadiness();
* if (!readiness.ready) {
* console.warn(readiness.issues);
* }
*/
TranspayrentClickToPayHelper.prototype.getReadiness = function() {
var issues = [];
if (typeof window === 'undefined') {
issues.push({
code: 'SDK_ENVIRONMENT_ERROR',
message: 'Mastercard UCS SDK is not available outside a browser environment.'
});
return { ready: false, issues: issues };
}
if (typeof window.MastercardCheckoutServices !== 'function' &&
typeof window.Click2Pay !== 'function') {
issues.push({
code: 'SDK_NOT_READY',
message: 'Mastercard UCS SDK globals were not found. Load the Mastercard script before calling Click-to-Pay methods.'
});
}
return {
ready: issues.length === 0,
issues: issues
};
};
/**
* Static readiness check for use before a helper instance is created.
*
* @static
* @returns {ClickToPayReadiness}
*/
TranspayrentClickToPayHelper.getReadiness = function() {
return TranspayrentClickToPayHelper.prototype.getReadiness();
};
/**
* @private
* @param {*} handoff
* @returns {Boolean}
*/
TranspayrentClickToPayHelper._isValidHandoffRef = function(handoff) {
return !!(
handoff &&
typeof handoff === 'object' &&
TranspayrentClickToPayHelper._readString(handoff.merchantTransactionId) &&
TranspayrentClickToPayHelper._readString(handoff.srcFlowId)
);
};
/**
* @private
* @returns {Object}
*/
TranspayrentClickToPayHelper._createHandoffMissingError = function() {
return TranspayrentClickToPayHelper._createNormalizedError(
'click_to_pay_checkout_handoff',
'CHECKOUT_HANDOFF_MISSING',
'Click to Pay checkout handoff is missing or incomplete. Both merchantTransactionId and srcFlowId are required.',
400
);
};
/**
* Applies a saved Click-to-Pay checkout handoff to an authenticate/authorize payload.
*
* @static
* @param {Object} paymentDetails
* @param {ClickToPayCheckoutHandoffRef} handoff
* @returns {Object}
* @throws {Object} Transpayrent-normalized error when `handoff` is missing or incomplete
*/
TranspayrentClickToPayHelper.applyCheckoutHandoff = function(paymentDetails, handoff) {
if (!paymentDetails || typeof paymentDetails !== 'object') {
throw TranspayrentClickToPayHelper._createHandoffMissingError();
}
if (!TranspayrentClickToPayHelper._isValidHandoffRef(handoff)) {
throw TranspayrentClickToPayHelper._createHandoffMissingError();
}
var merged = Object.assign({}, paymentDetails);
var handoffFields = {
merchant_transaction_id: handoff.merchantTransactionId,
src_flow_id: handoff.srcFlowId
};
if (merged.payment_details && typeof merged.payment_details === 'object') {
merged.payment_details = Object.assign({}, merged.payment_details, handoffFields);
return merged;
}
return Object.assign(merged, handoffFields);
};
/**
* Applies a saved checkout handoff to an authenticate/authorize payload.
* When `handoff` is omitted, uses the handoff stored by the last successful checkout call.
*
* @public
* @param {Object} paymentDetails
* @param {ClickToPayCheckoutHandoffRef} [handoff]
* @returns {Object}
* @throws {Object} Transpayrent-normalized error when handoff is missing or incomplete
*/
TranspayrentClickToPayHelper.prototype.applyCheckoutHandoff = function(paymentDetails, handoff) {
return TranspayrentClickToPayHelper.applyCheckoutHandoff(
paymentDetails,
handoff || this._checkoutHandoff
);
};
/**
* Creates an opt-in identity-scoped recognition token / remember-me store.
*
* @static
* @param {ClickToPayRecognitionSessionStoreOptions} [options]
* @returns {ClickToPayRecognitionSessionStore}
*/
TranspayrentClickToPayHelper.createRecognitionSessionStore = function(options) {
options = options || {};
var storage = options.storage;
if (!storage) {
storage = typeof localStorage !== 'undefined'
? localStorage
: TranspayrentClickToPayHelper._createMemoryStorage();
}
var recognitionTokenKeyPrefix = options.recognitionTokenKeyPrefix || 'transpayrent.c2p.recognitionToken:';
var rememberMeKeyPrefix = options.rememberMeKeyPrefix || 'transpayrent.c2p.rememberMe:';
var rememberMeDefault = options.rememberMeDefault !== false;
var identityNormalizer = options.identityNormalizer;
function normalize(identity) {
return TranspayrentClickToPayHelper.normalizeIdentity(identity, identityNormalizer);
}
function recognitionKey(identity) {
return recognitionTokenKeyPrefix + normalize(identity);
}
function rememberMeKey(identity) {
return rememberMeKeyPrefix + normalize(identity);
}
return {
normalizeIdentity: normalize,
getRecognitionToken: function(identity) {
if (!identity) {
return undefined;
}
var value = storage.getItem(recognitionKey(identity));
return value ? value : undefined;
},
setRecognitionToken: function(identity, recognitionToken) {
if (!identity || !recognitionToken) {
return;
}
storage.setItem(recognitionKey(identity), recognitionToken);
},
removeRecognitionToken: function(identity) {
if (!identity) {
return;
}
storage.removeItem(recognitionKey(identity));
},
getRememberMe: function(identity) {
if (!identity) {
return rememberMeDefault;
}
var key = rememberMeKey(identity);
var stored = storage.getItem(key);
if (stored === null) {
storage.setItem(key, rememberMeDefault ? 'true' : 'false');
return rememberMeDefault;
}
return stored === 'true';
},
setRememberMe: function(identity, value) {
if (!identity) {
return;
}
storage.setItem(rememberMeKey(identity), value ? 'true' : 'false');
},
clearIdentity: function(identity) {
if (!identity) {
return;
}
storage.removeItem(recognitionKey(identity));
storage.removeItem(rememberMeKey(identity));
}
};
};
/**
* @private
*
* Returns the Mastercard UCS SDK constructor function.
* Looks for either window.MastercardCheckoutServices or window.Click2Pay.
*
* @throws {Object} Transpayrent-normalized error if SDK constructor not found
* @returns {Function} The Mastercard SDK constructor
*/
TranspayrentClickToPayHelper.prototype._getConstructor = function() {
if (typeof window === 'undefined') {
throw this._normalizeError('click_to_pay_init', {
name: 'click2PayError',
reason: 'SDK_ENVIRONMENT_ERROR',
message: 'Mastercard UCS SDK is not available in this environment.'
});
}
if (typeof window.MastercardCheckoutServices === 'function') {
return window.MastercardCheckoutServices;
}
if (typeof window.Click2Pay === 'function') {
return window.Click2Pay;
}
throw this._normalizeError('click_to_pay_init', {
name: 'click2PayError',
reason: 'SDK_NOT_READY',
message: 'Mastercard UCS SDK is not ready. Verify script URL, srcDpaId, network policy, and SDK globals (MastercardCheckoutServices or Click2Pay).'
});
};
/**
* @private
*
* Returns or creates the Mastercard UCS service instance cached in window.mcCheckoutService.
*
* @returns {Object} The Mastercard UCS service instance
*/
TranspayrentClickToPayHelper.prototype._getService = function() {
if (typeof window === 'undefined') {
throw this._normalizeError('click_to_pay_init', {
name: 'click2PayError',
reason: 'SDK_ENVIRONMENT_ERROR',
message: 'Mastercard UCS SDK service is not available in this environment.'
});
}
if (!window.mcCheckoutService) {
window.mcCheckoutService = new (this._getConstructor())();
}
return window.mcCheckoutService;
};
/**
* @private
*
* Resets the Mastercard UCS service instance by creating a fresh one for init().
*
* @returns {Object} The new Mastercard UCS service instance
*/
TranspayrentClickToPayHelper.prototype._resetService = function() {
window.mcCheckoutService = new (this._getConstructor())();
return window.mcCheckoutService;
};
/**
* @private
*
* Normalizes Mastercard UCS SDK errors into Transpayrent error format.
*
* @param {String} api The API method name that failed
* @param {Error|Object} error The error thrown by the MC SDK
* @returns {Object} Transpayrent-normalized error object
*/
TranspayrentClickToPayHelper.prototype._normalizeError = function(api, error) {
if (error && typeof error === 'object' && Array.isArray(error.messages) && typeof error.api === 'string') {
return error;
}
var code = 'UNKNOWN_ERROR';
var message = 'An unexpected error occurred.';
var status = 400;
if (error && typeof error === 'object') {
if (typeof error.reason === 'string') {
code = error.reason;
} else if (typeof error.code === 'string') {
code = error.code;
} else if (Array.isArray(error.messages) && error.messages[0] && typeof error.messages[0].code === 'string') {
code = error.messages[0].code;
}
if (typeof error.message === 'string') {
message = error.message;
} else if (Array.isArray(error.messages) && error.messages[0] && typeof error.messages[0].message === 'string') {
message = error.messages[0].message;
}
if (typeof error.status === 'number') {
status = error.status;
}
if (typeof error.statusCode === 'string' && error.statusCode.toUpperCase() === 'CANCELED') {
code = 'CANCELED';
message = message || 'Click to Pay checkout was canceled.';
status = 400;
}
if (error.details && typeof error.details.status === 'number') {
status = error.details.status;
}
if (typeof error.transaction_id !== 'undefined') {
return {
api: api,
status: status,
transaction_id: error.transaction_id,
rawError: error,
messages: [{ code: code, message: message, system: 'CLICK_TO_PAY' }]
};
}
} else if (error instanceof Error) {
message = error.message;
}
return TranspayrentClickToPayHelper._createNormalizedError(api, code, message, status, error);
};
/**
* @private
*
* Sanitizes a value for request/response logging, returning a plain (deep-cloned) value rather
* than a JSON string - logged as its own console.log/console.error argument, it renders as an
* expandable, individually copyable object in the browser console instead of one long wrapped
* string. The CVV/security code is always hidden, regardless of `debugLogging`; every other field
* (including the PAN) is included in full.
*
* @param {*} value
* @returns {*}
*/
TranspayrentClickToPayHelper.prototype._safeSanitize = function(value) {
try {
var seen = typeof WeakSet === 'function' ? new WeakSet() : null;
var json = JSON.stringify(value, function(key, val) {
if (transpayrentC2pIsCvvKey(key)) {
return '[HIDDEN]';
}
if (typeof val === 'function') {
return '[Function]';
}
if (val !== null && typeof val === 'object') {
// Cross-realm safe: popup windowRef is not instanceof global Window.
if (key === 'windowRef' || val.window === val) {
return '[Window]';
}
if (typeof val.nodeType === 'number') {
return '[DOMNode]';
}
if (seen) {
if (seen.has(val)) {
return '[Circular]';
}
seen.add(val);
}
}
return val;
});
// JSON.stringify(undefined) returns undefined (e.g. calls with no request/result) - nothing
// to parse back in that case, just log the value as-is.
return json === undefined ? value : JSON.parse(json);
} catch (e) {
return '[unserializable]';
}
};
/**
* @private
*
* @param {String} method Mastercard SDK method name
* @param {Number} callId Diagnostic call sequence id
* @param {Object} [request]
*/
TranspayrentClickToPayHelper.prototype._logMcRequest = function(method, callId, request) {
if (!this._debugLogging) {
return;
}
try {
// Label/timestamp as one plain string, body as its own arg (an object, not a JSON string) so
// the browser console renders it expandable/collapsible with its own "Copy object" entry,
// instead of one giant wrapped line that has to be selected by hand.
console.log('[TranspayrentClickToPayHelper] >>> MC SDK request: ' + method + ' callId=' + callId +
' timestamp=' + new Date().toISOString(), this._safeSanitize(request));
} catch (_) {}
};
/**
* @private
*
* Wraps an async operation with Transpayrent error normalization and response logging.
*
* @param {String} api
* @param {Function} work Receives callId and returns a Promise
* @returns {Promise}
*/
TranspayrentClickToPayHelper.prototype._safeCall = function(api, work) {
var self = this;
var callId = ++TRANSPAYRENT_C2P_CALL_SEQUENCE;
return Promise.resolve().then(function() {
return work(callId);
}).then(function(result) {
if (self._debugLogging) {
try {
console.log('[TranspayrentClickToPayHelper] <<< ' + api + ' callId=' + callId +
' timestamp=' + new Date().toISOString(), self._safeSanitize(result));
} catch (_) {}
}
return result;
}).catch(function(error) {
var normalized = self._normalizeError(api, error);
if (self._debugLogging) {
try {
console.error('[TranspayrentClickToPayHelper] <<< ' + api + ' callId=' + callId +
' timestamp=' + new Date().toISOString(), self._safeSanitize(normalized));
} catch (_) {}
}
throw normalized;
});
};
/**
* @private
*
* Waits until the current init() call has settled.
*
* @returns {Promise}
*/
TranspayrentClickToPayHelper.prototype.ensureInitSettled = function() {
if (!this._initPromise) {
return Promise.reject(this._normalizeError('click_to_pay_init', {
name: 'click2PayError',
reason: 'NOT_INITIALIZED',
message: 'Click-to-Pay has not been initialized. Call init() first.'
}));
}
return this._initPromise;
};
/**
* @private
*
* Invokes a Mastercard UCS service method after init has settled.
*
* @param {String} api Transpayrent API label for logging and errors
* @param {String} method Mastercard service method name
* @param {Object} [request]
* @returns {Promise}
*/
TranspayrentClickToPayHelper.prototype._invokeMc = function(api, method, request) {
var self = this;
return self.ensureInitSettled().then(function() {
return self._safeCall(api, function(callId) {
self._logMcRequest(method, callId, request);
return self._getService()[method](request);
});
});
};
/**
* Initializes Click-to-Pay with a fresh service instance and optional configuration.
* This method must be called before any other Click-to-Pay operations.
*
* @public
*
* @param {Object} request The initialization request object (Mastercard UCS init schema)
* @returns {Promise} Promise that resolves with the initialization result
*
* @example
* helper.init({
* requestId: 'req-12345',
* consumerLanguage: 'en-US'
* }).then(function(result) {
* return helper.getCards();
* }).catch(function(error) {
* console.error('Init failed:', error.messages[0].message);
* });
*/
TranspayrentClickToPayHelper.prototype.init = function(request) {
var self = this;
if (self._initPromise) {
return self._initPromise;
}
var initPromise = self._safeCall('click_to_pay_init', function(callId) {
var service = self._resetService();
self._logMcRequest('init', callId, request);
return service.init(request);
}).catch(function(err) {
if (self._initPromise === initPromise) {
self._initPromise = null;
}
throw err;
});
self._initPromise = initPromise;
return initPromise;
};
/**
* Clears init state and saved checkout handoff so the next session starts from a clean runtime.
*
* @public
*/
TranspayrentClickToPayHelper.prototype.invalidateSession = function() {
this._initPromise = null;
this._checkoutHandoff = null;
if (typeof window !== 'undefined') {
window.mcCheckoutService = null;
}
};
/**
* Returns whether the Mastercard UCS browser SDK is available.
*
* @public
* @returns {Boolean}
*/
TranspayrentClickToPayHelper.prototype.isReady = function() {
return this.getReadiness().ready;
};
/**
* Throws a normalized Click-to-Pay error when the Mastercard UCS SDK is not ready.
*
* @public
*/
TranspayrentClickToPayHelper.prototype.assertReady = function() {
var readiness = this.getReadiness();
if (!readiness.ready) {
var issue = readiness.issues[0] || {
code: 'SDK_NOT_READY',
message: 'Mastercard UCS SDK is not ready.'
};
throw this._normalizeError('click_to_pay_readiness', {
name: 'click2PayError',
reason: issue.code,
message: issue.message
});
}
};
/**
* Replaces the helper's recognition storage implementation.
* Use {@link TranspayrentClickToPayHelper#recognitionStorage} for normal read/write access.
*
* @public
* @param {ClickToPayRecognitionSessionStore} store
*/
TranspayrentClickToPayHelper.prototype.useRecognitionSessionStore = function(store) {
this._recognitionSessionStore = store;
this._bindRecognitionStorage();
};
/**
* @private
*
* `rawResult` preserves the original, unmodified Mastercard checkout payload on the returned
* handoff so merchants can inspect fields this normalizer doesn't extract.
*
* @param {*} checkoutResult
* @returns {ClickToPayCheckoutHandoff|null}
*/
TranspayrentClickToPayHelper.prototype._normalizeCheckoutResult = function(checkoutResult) {
var payload = TranspayrentClickToPayHelper._asRecord(checkoutResult);
if (!payload) {
return null;
}
var headers = TranspayrentClickToPayHelper._asRecord(payload.headers);
return {
checkoutActionCode: TranspayrentClickToPayHelper._readString(payload.checkoutActionCode) || 'UNKNOWN',
merchantTransactionId: TranspayrentClickToPayHelper._readString(payload.merchantTransactionId) ||
TranspayrentClickToPayHelper._getHeaderValue(headers, 'merchant-transaction-id'),
srcFlowId: TranspayrentClickToPayHelper._readString(payload.srcFlowId) ||
TranspayrentClickToPayHelper._getHeaderValue(headers, 'x-src-cx-flow-id'),
checkoutResponse: TranspayrentClickToPayHelper._readString(payload.checkoutResponse),
recognitionToken: TranspayrentClickToPayHelper._readString(payload.recognitionToken),
checkoutResponseData: TranspayrentClickToPayHelper._asRecord(payload.checkoutResponseData),
rawResult: checkoutResult
};
};
/**
* @private
*
* @param {*} rawResult
* @returns {ClickToPayCheckoutHandoff|null}
*/
TranspayrentClickToPayHelper.prototype._completeCheckout = function(rawResult) {
var normalized = this._normalizeCheckoutResult(rawResult);
if (!normalized) {
throw this._normalizeError('click_to_pay_checkout', {
name: 'click2PayError',
reason: 'CHECKOUT_RESULT_INVALID',
message: 'Click to Pay checkout returned an unrecognizable result.'
});
}
if (normalized.checkoutActionCode === 'COMPLETE') {
if (!TranspayrentClickToPayHelper._isValidHandoffRef(normalized)) {
throw TranspayrentClickToPayHelper._createHandoffMissingError();
}
this._checkoutHandoff = normalized;
}
return normalized;
};
/**
* Enables or disables MC SDK request/response logging for debugging Click-to-Pay flows.
* Intended for development and integration troubleshooting only. The CVV/security code is
* always hidden from these logs regardless of this flag; every other field (including the
* PAN) is logged in full, so treat any captured output as sensitive when sharing it
* (email, tickets, etc.).
*
* @public
*
* @param {Boolean} enabled When true, logs payloads to the browser console
*
* @example
* var helper = sdk.getClickToPayHelper({ debugLogging: true });
* // or at runtime:
* helper.setDebugLogging(true);
*/
TranspayrentClickToPayHelper.prototype.setDebugLogging = function(enabled) {
this._debugLogging = !!enabled;
};
/**
* Retrieves the list of recognized cards for the current consumer.
* Should be called after init() to determine if the consumer is recognized.
*
* @public
*
* @param {Object} [request] Optional getCards request parameters
* @returns {Promise<Array>} Promise that resolves with the cards array (may be empty)
*
* @example
* helper.getCards().then(function(cards) {
* if (cards.length > 0) {
* console.log('Cards:', cards);
* }
* }).catch(function(error) {
* console.error('Get cards failed:', error.messages[0].message);
* });
*/
TranspayrentClickToPayHelper.prototype.getCards = function(request) {
return this._invokeMc('click_to_pay_get_cards', 'getCards', request).then(function(result) {
return Array.isArray(result) ? result : [];
});
};
/**
* Authenticates a Click-to-Pay consumer using device fingerprint or credentials.
*
* @public
*
* @param {Object} request The authentication request object
* @returns {Promise} Promise that resolves with authentication result
*/
TranspayrentClickToPayHelper.prototype.authenticate = function(request) {
return this._invokeMc('click_to_pay_authenticate', 'authenticate', request);
};
/**
* Performs an identity lookup for a consumer using email or phone number.
*
* @public
*
* @param {Object} request The identity lookup request
* @returns {Promise} Promise that resolves with profile existence indicator
*/
TranspayrentClickToPayHelper.prototype.idLookup = function(request) {
return this._invokeMc('click_to_pay_id_lookup', 'idLookup', request);
};
/**
* Initiates identity validation by sending an OTP to the consumer.
*
* @public
*
* @param {Object} [request] The initiate validation request
* @returns {Promise} Promise that resolves with OTP delivery confirmation
*/
TranspayrentClickToPayHelper.prototype.initiateValidation = function(request) {
return this._invokeMc('click_to_pay_initiate_validation', 'initiateValidation', request);
};
/**
* Completes identity validation by verifying the OTP entered by the consumer.
*
* @public
*
* @param {Object} request The validate request with OTP value
* @returns {Promise} Promise that resolves with validation result and cards
*/
TranspayrentClickToPayHelper.prototype.validate = function(request) {
return this._invokeMc('click_to_pay_validate', 'validate', request);
};
/**
* Encrypts a new card for storage and use with Click-to-Pay.
*
* @public
*
* @param {Object} request The encrypt card request with PAN and card details
* @returns {Promise} Promise that resolves with encrypted card data
*/
TranspayrentClickToPayHelper.prototype.encryptCard = function(request) {
return this._invokeMc('click_to_pay_encrypt_card', 'encryptCard', request);
};
/**
* Initiates checkout with a saved/recognized card.
*
* @public
*
* @param {Object} request The checkout request with selected card
* @returns {Promise<ClickToPayCheckoutHandoff>} Normalized checkout result; stores valid handoff on `COMPLETE`
*/
TranspayrentClickToPayHelper.prototype.checkoutWithCard = function(request) {
var self = this;
return this._invokeMc('click_to_pay_checkout_with_card', 'checkoutWithCard', request)
.then(function(result) {
return self._completeCheckout(result);
});
};
/**
* Initiates checkout with a newly encrypted card.
*
* @public
*
* @param {Object} request The checkout request with encrypted card data
* @returns {Promise<ClickToPayCheckoutHandoff>} Normalized checkout result; stores valid handoff on `COMPLETE`
*/
TranspayrentClickToPayHelper.prototype.checkoutWithNewCard = function(request) {
var self = this;
return this._invokeMc('click_to_pay_checkout_with_new_card', 'checkoutWithNewCard', request)
.then(function(result) {
return self._completeCheckout(result);
});
};
/**
* Signs out the consumer from their Click-to-Pay session.
*
* @public
*
* @param {Object} [request] Optional sign out parameters
* @returns {Promise} Promise that resolves when sign out completes
*/
TranspayrentClickToPayHelper.prototype.signOut = function(request) {
var self = this;
return self.ensureInitSettled().then(function() {
return self._safeCall('click_to_pay_sign_out', function(callId) {
var service = self._getService();
if (typeof service.signOut !== 'function') {
throw {
name: 'click2PayError',
reason: 'METHOD_NOT_AVAILABLE',
message: 'signOut method is not available on the Mastercard UCS SDK service.'
};
}
self._logMcRequest('signOut', callId, request);
return service.signOut(request);
});
}).then(function(result) {
self.invalidateSession();
return result;
});
};
/**
* Displays the Click-to-Pay button using the SRC UI Kit src-button web component when available.
* When `<src-button>` is unavailable, a plain HTML button is rendered as a visual/click target fallback only.
* That fallback does **not** indicate Mastercard Click-to-Pay runtime readiness; call {@link TranspayrentClickToPayHelper#getReadiness}
* before showing merchant-owned Click-to-Pay UI.
* It is **strongly recommended** to use {@link Transpayrent#displayPaymentButton}.
*
* @public
*
* @see Transpayrent#displayPaymentButton
*
* @param {*} container The container element for the button
* @param {PAYMENT_METHOD_ID[]} paymentMethodIds List of payment method IDs from the gateway
* @param {Object} [config] Button-only configuration (`buttonText`, `onClick`)
*/
TranspayrentClickToPayHelper.prototype.displayPaymentButton = function(container, paymentMethodIds, config) {
if (!container) {
return;
}
var hasSourceButton = typeof window !== 'undefined' &&
typeof window.customElements !== 'undefined' &&
window.customElements.get('src-button') !== undefined;
var button = hasSourceButton
? document.createElement('src-button')
: (function() {
var el = document.createElement('button');
el.type = 'button';
el.className = 'click-to-pay-button';
el.textContent = config && config.buttonText ? config.buttonText : 'Click to Pay';
return el;
})();
if (config && typeof config.onClick === 'function') {
button.addEventListener('click', config.onClick);
}
container.innerHTML = '';
container.appendChild(button);
};