1/** 2 * Converge CC request 3 * 4 * @typedef {Object} ConvergeRequest 5 * @property {string} ssl_transaction_type 6 * @property {string} ssl_txn_auth_token 7 * @property {string} ssl_card_number 8 * @property {string} ssl_exp_date 9 * @property {string} ssl_cvv2cvc2 10 * @property {number} ssl_cvv2cvc2_indicator 11 * @property {string} ssl_add_token 12 * @property {string} ssl_get_token 13 * @property {number} ssl_invoice_number 14 * @property {number} ssl_merchant_txn_id 15 * @property {string} ssl_cardholder_ip 16 * @property {string} ssl_vendor_id 17 * @property {string} ssl_email 18 * @property {number} ssl_customer_id 19 * @property {number} ssl_amount 20 * @property {string} ssl_avs_address 21 * @property {string} ssl_city 22 * @property {string} ssl_state 23 * @property {string} ssl_avs_zip 24 * @property {string} ssl_description 25 * @property {string} ssl_first_name 26 * @property {string} ssl_last_name 27 * @property {string} cardholder_name 28 * @property {string} [threeds_purchase_currency] ISO 4217 numeric (e.g. '124' CAD, '840' USD) for the 3DS prompt 29 * @property {string} [threeds_bill_country] ISO 3166 numeric (e.g. '124' CA, '840' US) for the 3DS prompt 30 */ 31 32/** 33 * Converge pay callbacks 34 * 35 * @typedef {Object} ConvergePayCallbacks 36 * @property {function} onApproval 37 * @property {function} onDeclined 38 * @property {function} onError 39 */ 40 41/** 42 * @enum {string} 43 */ 44const ThreeDSTransactionStatus = { 45 AUTHENTICATION_SUCCESSFUL: 'Y', 46 NOT_AUTHENTICATED: 'N', 47 UNABLE_TO_AUTHENTICATE: 'U', 48 AUTHENTICATION_REJECTED: 'R', 49 AUTHENTICATION_GENERATED: 'A', 50 CHALLENGE_REQUIRED: 'C', 51}; 52 53/** 54 * 3D Secure Response 55 * 56 * @typedef {Object} ThreeDSResponse 57 * @property {ThreeDSTransactionStatus} transStatus 58 * @property {string} authenticationValue 59 * @property {string} messageType 60 * @property {string} messageVersion 61 * @property {string} threeDSServerTransID 62 * @property {string} dsTransID 63 * @property {string} acsTransID 64 * @property {string} acsReferenceNumber 65 * @property {string} acsOperatorID 66 * @property {string} dsReferenceNumber 67 * @property {string} authenticationType 68 * @property {string} acsChallengeMandated 69 * @property {string} acsURL 70 * @property {string} eci 71 * @property {boolean} authenticated 72 * @property {string} message 73 * @property {string} messageId 74 */ 75 76/** 77 * @typedef {Object} ThreeDSTransactionError 78 * @property {string} errorCode 79 * @property {string} errorComponent 80 * @property {string} errorDescription 81 * @property {string} errorDetail 82 * @property {string} messageType 83 * @property {string} messageVersion 84 * @property {string} threeDSServerTransID 85 */ 86 87/** 88 * @typedef {Error & { error: ThreeDSTransactionError }} ThreeDSError 89 */ 90 91/** 92 * @typedef {function} ConvergeCallback 93 * @param {threeDSecureEventName} eventName 94 * @param {object} event 95 */ 96 97const outgoingMessageType = { 98 ERROR: 'ERROR', 99 THREEDS_RESULT: 'THREEDS_RESULT', 100 THREEDS_ERROR: 'THREEDS_ERROR', 101 THREEDS_AUTHENTICATED: 'THREEDS_AUTHENTICATED', 102 THREEDS_UNAUTHENTICATED: 'THREEDS_UNAUTHENTICATED', 103 THREEDS_CHALLENGE_START: 'THREEDS_CHALLENGE_START', 104 THREEDS_CHALLENGE_END: 'THREEDS_CHALLENGE_END', 105 CONVERGE_APPROVAL: 'CONVERGE_APPROVAL', 106 CONVERGE_DECLINED: 'CONVERGE_DECLINED', 107 CONVERGE_ERROR: 'CONVERGE_ERROR', 108 CONVERGE_UNKNOWN: 'CONVERGE_UNKNOWN', 109}; 110 111window.RR = { 112 checkout: (function () { 113 const loadScript = (src) => 114 new Promise((resolve, reject) => { 115 const script = document.createElement('script'); 116 script.onload = resolve; 117 script.onerror = reject; 118 script.src = src; 119 document.head.appendChild(script); 120 }); 121 122 const loadCheckoutJS = async () => { 123 if (window.ConvergeEmbeddedPayment) return; 124 125 try { 126 await loadScript(window.CHECKOUT_JS_URL); 127 } catch (e) { 128 return Promise.reject(); 129 } 130 }; 131 132 /** 133 * @enum {string} 134 */ 135 const threeDSecureEventName = { 136 'THREEDS/LOOKUP_START': 'THREEDS/LOOKUP_START', 137 'THREEDS/LOOKUP_END': 'THREEDS/LOOKUP_END', 138 'THREEDS/DEVICE_FINGERPRINT_START': 'THREEDS/DEVICE_FINGERPRINT_START', 139 'THREEDS/DEVICE_FINGERPRINT_END': 'THREEDS/DEVICE_FINGERPRINT_END', 140 'THREEDS/AUTHENTICATION_START': 'THREEDS/AUTHENTICATION_START', 141 'THREEDS/AUTHENTICATION_END': 'THREEDS/AUTHENTICATION_END', 142 'THREEDS/CHALLENGE_START': 'THREEDS/CHALLENGE_START', 143 'THREEDS/CHALLENGE_END': 'THREEDS/CHALLENGE_END', 144 'THREEDS/RESULT_START': 'THREEDS/RESULT_START', 145 'THREEDS/RESULT_END': 'THREEDS/RESULT_END', 146 }; 147 148 const create3DSecureFactory = () => { 149 let sdk; 150 151 return (token) => { 152 if (sdk) { 153 return sdk; 154 } 155 156 const baseUrl = window.THREEDS_BASE_URL; 157 sdk = new window.Elavon3DSWebSDK({ baseUrl, token }); 158 159 return sdk; 160 }; 161 }; 162 163 const register3DSecureCallbacks = (callback) => { 164 const events = Object.keys(threeDSecureEventName); 165 const callbacks = events.reduce((callbackMap, eventName) => { 166 callbackMap[eventName] = (e) => callback(eventName, e); 167 return callbackMap; 168 }, {}); 169
170 events.forEach((eventName) => { 171 window.addEventListener(eventName, callbacks[eventName]); 172 }); 173 174 return () => { 175 events.forEach((eventName) => { 176 window.removeEventListener(eventName, callbacks[eventName]); 177 }); 178 }; 179 }; 180 181 const get3DSecureSdk = create3DSecureFactory(); 182 183 /** 184 * Request CCSALE via Converge 185 * 186 * @param {...ConvergeRequest} payload - Converge CC request 187 * @param {...ConvergePayCallbacks} callbacks 188 */ 189 const pay = (payload, callbacks) => { 190 try { 191 window.ConvergeEmbeddedPayment.pay(payload, callbacks); 192 } catch (e) { 193 return events.onError.fire(-1, 'Unable to process card payment'); 194 } 195 }; 196 197 /** 198 * @param {function} onChallengeStart 199 * @param {function} onChallengeEnd 200 * @returns {ConvergeCallback} 201 */ 202 const createConvergeCallback = (onChallengeStart, onChallengeEnd) => { 203 return (eventName) => { 204 switch (eventName) { 205 case threeDSecureEventName['THREEDS/CHALLENGE_START']: 206 return onChallengeStart(); 207 case threeDSecureEventName['THREEDS/CHALLENGE_END']: 208 return onChallengeEnd(); 209 } 210 }; 211 }; 212 213 const reverseDatestring = (dateString) => 214 `${dateString.substring(2, 4)}${dateString.substring(0, 2)}`; 215 216 /** 217 * Request CCSALE via Converge 218 * 219 * @param {...PaymentRequestPayload} request 220 * @param {function} createCallback 221 * @returns {Promise<{ data: null; error: ThreeDSError } | { data: ThreeDSResponse; error: null }>} 222 */ 223 const do3DSecure = async (request, createCallback) => { 224 let unregister = Function.prototype; 225 226 try { 227 const payload = request.data; 228 const token = request.transactionToken3DSecure; 229 const onChallengeStart = createCallback( 230 outgoingMessageType.THREEDS_CHALLENGE_START 231 ); 232 const onChallengeEnd = createCallback(outgoingMessageType.THREEDS_CHALLENGE_END); 233 const callback = createConvergeCallback(onChallengeStart, onChallengeEnd); 234 const sdk = get3DSecureSdk(token); 235 236 unregister = register3DSecureCallbacks(callback); 237 238 const res = await sdk.web3dsFlow({ 239 // NOTE: This maps to the cartId 240 messageId: payload.ssl_merchant_txn_id, 241 purchaseAmount: `${Math.round(payload.ssl_amount * 100)}`, 242 acctNumber: payload.ssl_card_number, 243 cardExpiryDate: `${reverseDatestring(payload.ssl_exp_date)}`, 244 cardholderName: payload.cardholder_name, 245 billAddrLine1: payload.ssl_avs_address, 246 billAddrCity: payload.ssl_city, 247 billAddrState: payload.ssl_state, 248 billAddrCountry: payload.threeds_bill_country || '840', 249 billAddrPostCode: payload.ssl_avs_zip, 250 purchaseCurrency: payload.threeds_purchase_currency || '840', 251 purchaseExponent: '2', 252 messageCategory: '01', 253 transType: '01', 254 threeDSRequestorAuthenticationInd: '01', 255 threeDSServerTransID: '', 256 clientStartProtocolVersion: '2.1.0', 257 clientEndProtocolVersion: '2.2.0', 258 email: payload.ssl_email, 259 }); 260 261 unregister(); 262 return { data: res, error: null }; 263 } catch (e) { 264 unregister(); 265 266 return { data: null, error: e }; 267 } 268 }; 269 270 /** 271 * According to Converge, we need to translate the eci value from 3ds to an eci index that Converge uses. 272 * https://developer.elavon.com/#/api/eb6e9106-0172-4305-bc5a-b3ebe832f823.rcosoomi/documents?converge-integration-guide/book/processing_options/3d_secure_2.html 273 * 274 * @param {string} eci The eci value from the 3ds response 275 * @returns {string} Converge's equivalent eci index 276 */ 277 const getEFSEci = (eci) => { 278 if (eci === '02' || eci === '05') { 279 return '5'; 280 } else if (eci === '01' || eci === '06') { 281 return '6'; 282 } else { 283 return '7'; 284 } 285 }; 286 287 /** 288 * Request CCSALE via Converge 289 * 290 * @param {...PaymentRequestPayload} request 291 * @param {function} createCallback 292 */ 293 const payWithCreditCard = async (request, createCallback) => { 294 try { 295 await loadCheckoutJS(); 296 } catch (e) { 297 console.warn(-1, 'Unable to load Checkout JS'); 298 return -1; 299 } 300 301 let payload = request.data; 302 if (request.transactionToken3DSecure) { 303 // https://glscrip.atlassian.net/wiki/spaces/GLSC/pages/839417857/3D+Secure 304 const threeDSecureResponse = await do3DSecure(request, createCallback); 305 const onThreeDSecureResult = createCallback(outgoingMessageType.THREEDS_RESULT); 306 const onThreeDSecureError = createCallback(outgoingMessageType.THREEDS_ERROR); 307 const onThreeDSecureUnauthenticated = createCallback( 308 outgoingMessageType.THREEDS_UNAUTHENTICATED 309 ); 310 const onThreeDSecureAuthenticated = createCallback( 311 outgoingMessageType.THREEDS_AUTHENTICATED 312 ); 313 314 onThreeDSecureResult(threeDSecureResponse.data || threeDSecureResponse.error.error); 315 316 if (threeDSecureResponse.error) { 317 if (String(threeDSecureResponse.error.error.errorCode) !== '305') { 318 return onThreeDSecureError(threeDSecureResponse.error.message); 319 } 320 payload = { 321 ...payload, 322 ssl_eci_ind: '7', 323 ssl_dir_server_tran_id: 'GLS', 324 ssl_program_protocol: '2', 325 }; 326 } else if (threeDSecureResponse.data) { 327 const { authenticated, transStatus } = threeDSecureResponse.data; 328 if ( 329 !authenticated && 330 transStatus !== ThreeDSTransactionStatus.AUTHENTICATION_GENERATED 331 ) { 332 return onThreeDSecureUnauthenticated('3D Secure - Unauthenticated'); 333 } 334 335 onThreeDSecureAuthenticated(threeDSecureResponse.data.threeDSServerTransID); 336 337 payload = { 338 ...payload, 339 ssl_eci_ind: getEFSEci(threeDSecureResponse.data.eci), 340 ssl_3dsecure_value: threeDSecureResponse.data.authenticationValue, 341 ssl_dir_server_tran_id: threeDSecureResponse.data.dsTransID, 342 ssl_program_protocol: '2', 343 }; 344 } 345 } 346 347 return pay(payload, { 348 onError: createCallback(outgoingMessageType.CONVERGE_ERROR), 349 onApproval: createCallback(outgoingMessageType.CONVERGE_APPROVAL), 350 onDeclined: createCallback(outgoingMessageType.CONVERGE_DECLINED), 351 onCancelled: createCallback(outgoingMessageType.CONVERGE_UNKNOWN), 352 onDCCDecision: createCallback(outgoingMessageType.CONVERGE_UNKNOWN), 353 onThreeDSecure: createCallback(outgoingMessageType.CONVERGE_UNKNOWN), 354 }); 355 }; 356 357 return { 358 pay, 359 payWithCreditCard, 360 do3DSecure, 361 }; 362 })(), 363};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.