@paybyrd/threedsecure-service
Advanced tools
Comparing version 1.2.0 to 1.2.1
@@ -1,1 +0,1 @@ | ||
class ThreeDSecureService{constructor({threeDSecureUrl,container,maxAttempts,attemptDelay,culture,onProgressFn,onIFrameCreatedFn,onContainerCreatedFn}){this._onProgressFn=onProgressFn,this._maxAttempts=maxAttempts||50,this._attemptDelay=attemptDelay||2e3,this._threeDSecureUrl=threeDSecureUrl||"https://threedsecure.paybyrd.com",this._culture=culture||"en-US",this._container=container||document.body,this._onIFrameCreatedFn=onIFrameCreatedFn,this._onContainerCreatedFn=onContainerCreatedFn,this.IFRAME_DSMETHOD_NAME="threeDSMethodIframe",this.FORM_DSMETHOD_NAME="threeDSMethodForm",this.IFRAME_CHALLENGE_NAME="challengeIframe",this.FORM_CHALLENGE_NAME="challengeForm",this._fixContainer()}createAndExecute(initiatePayment,correlationId=crypto.randomUUID()){return this.create(initiatePayment,correlationId).then(createResponse=>this.preAuth(createResponse,correlationId)).then(preAuthResponse=>this.auth(preAuthResponse,correlationId)).then(authResponse=>this.postAuth(authResponse,correlationId)).then(postAuthResponse=>({...postAuthResponse,correlationId:correlationId})).catch(error=>Promise.reject({...error,correlationId:correlationId})).finally(this._destroy.bind(this))}create(initiatePayment,correlationId){return this._retry(this._isTransientStatusCode,()=>this._sendRequest({path:"/api/v1",method:"POST",payload:{...initiatePayment,browser:this._getBrowserData()},correlationId:correlationId}),"event:create")}preAuth(preAuthRequest,correlationId){return this._retry(this._isTransientStatusCode,()=>this._sendRequest({path:`/api/v1/${preAuthRequest.id}/preauth`,method:"POST",correlationId:correlationId}),"event:preAuth").then(preAuthResponse=>preAuthResponse.isDsMethodRequired?this._executeDsMethod(preAuthResponse).then(()=>preAuthResponse):preAuthResponse)}auth(authRequest,correlationId){return this._retry(this._isTransientStatusCode,()=>this._sendRequest({path:`/api/v1/${authRequest.id}/auth`,method:"POST",correlationId:correlationId}),"event:auth").then(authResponse=>authResponse.isChallengeRequired?("1.0.0"===authResponse.challengeVersion?this._executeChallengeV1(authResponse):this._executeChallengeV2(authResponse)).then(()=>authResponse):authResponse)}postAuth(postAuthRequest,correlationId){return this._retry(this._isTransientStatusCode,()=>this._sendRequest({path:`/api/v1/${postAuthRequest.id}/postAuth`,method:"POST",correlationId:correlationId}),"event:postAuth")}_executeDsMethod(preAuthResponse){return new Promise((resolve,reject)=>{try{this._onProgress({type:"event:dsMethod:start"});var iframeName=this.IFRAME_DSMETHOD_NAME,formName=this.FORM_DSMETHOD_NAME;this._createIFrame(iframeName,!1);const form=this._createForm(formName,preAuthResponse.dsMethodUrl,iframeName),threeDSMethodDataInput=this._createInput(form,"threeDSMethodData","hidden");var threeDSMethodData={threeDSServerTransID:preAuthResponse.processId,threeDSMethodNotificationURL:preAuthResponse.notificationUrl};threeDSMethodDataInput.value=this._convertToBase64UriJson(threeDSMethodData),form.submit(),this._onProgress({type:"event:dsMethod:success"}),resolve()}catch(error){return this._onProgress({type:"event:dsMethod:fail"}),reject({message:error.toString()})}})}_executeChallengeV1(authResponse){return new Promise((resolve,reject)=>{try{this._onProgress({type:"event:challenge:v1:start"});var iframeName=this.IFRAME_CHALLENGE_NAME,formName=this.FORM_CHALLENGE_NAME;this._createIFrame(iframeName);const form=this._createForm(formName,authResponse.challengeUrl,iframeName,"post"),paReqInput=this._createInput(form,"PaReq","hidden"),termUrlInput=this._createInput(form,"TermUrl","hidden");paReqInput.value=authResponse.preAuthRequest,termUrlInput.value=authResponse.notificationUrl,form.submit(),resolve(),this._onProgress({type:"event:challenge:v1:success"})}catch(error){return this._onProgress({type:"event:challenge:v1:fail",error:error}),reject({message:error.toString()})}})}_executeChallengeV2(authResponse){return new Promise((resolve,reject)=>{try{this._onProgress({type:"event:challenge:v2:start"});var iframeName=this.IFRAME_CHALLENGE_NAME,formName=this.FORM_CHALLENGE_NAME;this._createIFrame(iframeName);const form=this._createForm(formName,authResponse.challengeUrl,iframeName,"post"),threeDSRequestInput=(this._createInput(form,"threeDSSessionData","hidden"),this._createInput(form,"creq","hidden"));var cReq={threeDSServerTransID:authResponse.processId,acsTransID:authResponse.challengeId,messageVersion:authResponse.challengeVersion,messageType:"CReq",challengeWindowSize:"01"};threeDSRequestInput.value=this._convertToBase64UriJson(cReq),form.submit(),resolve(),this._onProgress({type:"event:challenge:v2:success"})}catch(error){return this._onProgress({type:"event:challenge:v2:fail",error:error}),reject({message:error.toString()})}})}_delay(timeout){return this._onProgress({type:"event:delay:start",data:{timeout:timeout}}),new Promise((resolve,_)=>{setTimeout(()=>{this._onProgress({type:"event:delay:end",data:{timeout:timeout}}),resolve()},timeout)})}_fixContainer(){this._onContainerCreatedFn?this._onContainerCreatedFn(this._container):this._container.setAttribute("style","position: relative; overflow: hidden;")}_createForm(name,action,target,method="POST"){const form=document.createElement("form");return form.id=name,form.name=name,form.action=action,form.target=target,form.method=method,this._container.appendChild(form),form}_createInput(form,name,type){const input=document.createElement("input");return input.id=name,input.name=name,input.type=type,form.appendChild(input),input}_createIFrame(name,visible=!0){const iframe=document.createElement("iframe");return iframe.id=name,iframe.name=name,this._onIFrameCreatedFn?(this._onIFrameCreatedFn(iframe),iframe.style.opacity=visible?1:0):iframe.setAttribute("style","border: none;position: absolute; top: 0; left: 0; bottom: 0; right: 0; width: 100%; height: 100%;opacity: "+(visible?"1":"0")),this._container.appendChild(iframe),iframe}_getBrowserData(){var colorDepth=[48,32,24,16,15,8,4,1].find(x=>x<=screen.colorDepth);return{javaEnabled:navigator.javaEnabled(),javascriptEnabled:!0,language:navigator.language,userAgent:navigator.userAgent,screenWidth:window.screen.width,screenHeight:window.screen.height,timezoneOffset:(new Date).getTimezoneOffset(),colorDepth:colorDepth,acceptHeader:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}}_retry(conditionFn,executeFn,eventType){return new Promise(async(resolve,reject)=>{let response=null,attempts=this._maxAttempts;do{this._onProgress({type:eventType+":start",data:{attempt:this._maxAttempts-attempts+1}});try{response=await executeFn()}catch(error){this._onProgress({type:eventType+":error",error:error.data?.error,data:{statusCode:error.status}}),response=error}if(200<=response.status&&response.status<300)return this._onProgress({type:eventType+":success",data:response.data.data}),void resolve(response.data.data)}while(attempts--,await this._delay(this._attemptDelay),0<attempts&&conditionFn(response));this._onProgress({type:eventType+":fail",error:response.data?.error}),reject(response.data?.error??{message:"Unhandled error"})})}_onProgress(event){try{this._onProgressFn?.call(null,event)}catch{}}_isTransientStatusCode(response){return 409===response.status||424===response.status||504===response.status}_convertToBase64UriJson(data){data=JSON.stringify(data);const base64Json=btoa(data);return base64Json.replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}_sendRequest({path,method,payload,correlationId}){const tryParse=json=>{if(""===json)return null;try{return JSON.parse(json)}catch{return null}};return new Promise((resolve,reject)=>{let xhr=new XMLHttpRequest;const url=new URL(path,this._threeDSecureUrl);xhr.open(method,url.toString());xhr.timeout=3e4,xhr.setRequestHeader("Accept","application/json"),xhr.setRequestHeader("Accept-Language",this._culture),xhr.setRequestHeader("Content-Type","application/json"),xhr.setRequestHeader("CorrelationId",correlationId),xhr.onload=()=>{(200<=xhr.status&&xhr.status<300?resolve:reject)({status:xhr.status,data:tryParse(xhr.responseText)})},xhr.onerror=()=>{reject({status:xhr.status,data:tryParse(xhr.responseText)})},xhr.send(JSON.stringify(payload))})}_destroy(){document.getElementById(this.IFRAME_DSMETHOD_NAME)?.remove(),document.getElementById(this.FORM_DSMETHOD_NAME)?.remove(),document.getElementById(this.IFRAME_CHALLENGE_NAME)?.remove(),document.getElementById(this.FORM_CHALLENGE_NAME)?.remove()}}export default ThreeDSecureService; | ||
export default class ThreeDSecureService{constructor({threeDSecureUrl,container,maxAttempts,attemptDelay,culture,onProgressFn,onIFrameCreatedFn,onContainerCreatedFn}){this._onProgressFn=onProgressFn,this._maxAttempts=maxAttempts||50,this._attemptDelay=attemptDelay||2e3,this._threeDSecureUrl=threeDSecureUrl||"https://threedsecure.paybyrd.com",this._culture=culture||"en-US",this._container=container||document.body,this._onIFrameCreatedFn=onIFrameCreatedFn,this._onContainerCreatedFn=onContainerCreatedFn,this.IFRAME_DSMETHOD_NAME="threeDSMethodIframe",this.FORM_DSMETHOD_NAME="threeDSMethodForm",this.IFRAME_CHALLENGE_NAME="challengeIframe",this.FORM_CHALLENGE_NAME="challengeForm",this._fixContainer()}createAndExecute(initiatePayment,correlationId=crypto.randomUUID()){return this.create(initiatePayment,correlationId).then(createResponse=>this.preAuth(createResponse,correlationId)).then(preAuthResponse=>this.auth(preAuthResponse,correlationId)).then(authResponse=>this.postAuth(authResponse,correlationId)).then(postAuthResponse=>({...postAuthResponse,correlationId:correlationId})).catch(error=>Promise.reject({...error,correlationId:correlationId})).finally(this._destroy.bind(this))}create(initiatePayment,correlationId){return this._retry(this._isTransientStatusCode.bind(this),()=>this._sendRequest({path:"/api/v1",method:"POST",payload:{...initiatePayment,browser:this._getBrowserData()},correlationId:correlationId}),"event:create")}preAuth(preAuthRequest,correlationId){return this._retry(this._isTransientStatusCode.bind(this),()=>this._sendRequest({path:`/api/v1/${preAuthRequest.id}/preauth`,method:"POST",correlationId:correlationId}),"event:preAuth").then(preAuthResponse=>preAuthResponse.isDsMethodRequired?this._executeDsMethod(preAuthResponse).then(()=>preAuthResponse):preAuthResponse)}auth(authRequest,correlationId){return this._retry(this._isTransientStatusCode.bind(this),()=>this._sendRequest({path:`/api/v1/${authRequest.id}/auth`,method:"POST",correlationId:correlationId}),"event:auth").then(authResponse=>authResponse.isChallengeRequired?("1.0.0"===authResponse.challengeVersion?this._executeChallengeV1(authResponse):this._executeChallengeV2(authResponse)).then(()=>authResponse):authResponse)}postAuth(postAuthRequest,correlationId){return this._retry(this._isTransientStatusCode.bind(this),()=>this._sendRequest({path:`/api/v1/${postAuthRequest.id}/postAuth`,method:"POST",correlationId:correlationId}),"event:postAuth")}_executeDsMethod(preAuthResponse){return new Promise((resolve,reject)=>{try{this._onProgress({type:"event:dsMethod:start"});var iframeName=this.IFRAME_DSMETHOD_NAME,formName=this.FORM_DSMETHOD_NAME;this._createIFrame(iframeName,!1);const form=this._createForm(formName,preAuthResponse.dsMethodUrl,iframeName),threeDSMethodDataInput=this._createInput(form,"threeDSMethodData","hidden");var threeDSMethodData={threeDSServerTransID:preAuthResponse.processId,threeDSMethodNotificationURL:preAuthResponse.notificationUrl};threeDSMethodDataInput.value=this._convertToBase64UriJson(threeDSMethodData),form.submit(),this._onProgress({type:"event:dsMethod:success"}),resolve()}catch(error){return this._onProgress({type:"event:dsMethod:fail"}),reject({message:error.toString()})}})}_executeChallengeV1(authResponse){return new Promise((resolve,reject)=>{try{this._onProgress({type:"event:challenge:v1:start"});var iframeName=this.IFRAME_CHALLENGE_NAME,formName=this.FORM_CHALLENGE_NAME;this._createIFrame(iframeName);const form=this._createForm(formName,authResponse.challengeUrl,iframeName,"post"),paReqInput=this._createInput(form,"PaReq","hidden"),termUrlInput=this._createInput(form,"TermUrl","hidden");paReqInput.value=authResponse.preAuthRequest,termUrlInput.value=authResponse.notificationUrl,form.submit(),resolve(),this._onProgress({type:"event:challenge:v1:success"})}catch(error){return this._onProgress({type:"event:challenge:v1:fail",error:error}),reject({message:error.toString()})}})}_executeChallengeV2(authResponse){return new Promise((resolve,reject)=>{try{this._onProgress({type:"event:challenge:v2:start"});var iframeName=this.IFRAME_CHALLENGE_NAME,formName=this.FORM_CHALLENGE_NAME;this._createIFrame(iframeName);const form=this._createForm(formName,authResponse.challengeUrl,iframeName,"post"),threeDSRequestInput=(this._createInput(form,"threeDSSessionData","hidden"),this._createInput(form,"creq","hidden"));var cReq={threeDSServerTransID:authResponse.processId,acsTransID:authResponse.challengeId,messageVersion:authResponse.challengeVersion,messageType:"CReq",challengeWindowSize:"01"};threeDSRequestInput.value=this._convertToBase64UriJson(cReq),form.submit(),resolve(),this._onProgress({type:"event:challenge:v2:success"})}catch(error){return this._onProgress({type:"event:challenge:v2:fail",error:error}),reject({message:error.toString()})}})}_delay(timeout){return this._onProgress({type:"event:delay:start",data:{timeout:timeout}}),new Promise((resolve,_)=>{setTimeout(()=>{this._onProgress({type:"event:delay:end",data:{timeout:timeout}}),resolve()},timeout)})}_fixContainer(){this._onContainerCreatedFn?this._onContainerCreatedFn(this._container):this._container.setAttribute("style","position: relative; overflow: hidden;")}_createForm(name,action,target,method="POST"){const form=document.createElement("form");return form.id=name,form.name=name,form.action=action,form.target=target,form.method=method,this._container.appendChild(form),form}_createInput(form,name,type){const input=document.createElement("input");return input.id=name,input.name=name,input.type=type,form.appendChild(input),input}_createIFrame(name,visible=!0){const iframe=document.createElement("iframe");return iframe.id=name,iframe.name=name,this._onIFrameCreatedFn?(this._onIFrameCreatedFn(iframe),iframe.style.opacity=visible?1:0):iframe.setAttribute("style","border: none;position: absolute; top: 0; left: 0; bottom: 0; right: 0; width: 100%; height: 100%;opacity: "+(visible?"1":"0")),this._container.appendChild(iframe),iframe}_getBrowserData(){var colorDepth=[48,32,24,16,15,8,4,1].find(x=>x<=screen.colorDepth);return{javaEnabled:navigator.javaEnabled(),javascriptEnabled:!0,language:navigator.language,userAgent:navigator.userAgent,screenWidth:window.screen.width,screenHeight:window.screen.height,timezoneOffset:(new Date).getTimezoneOffset(),colorDepth:colorDepth,acceptHeader:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}}_retry(conditionFn,executeFn,eventType){return new Promise(async(resolve,reject)=>{let response=null,attempts=this._maxAttempts;do{this._onProgress({type:eventType+":start",data:{attempt:this._maxAttempts-attempts+1}});try{response=await executeFn()}catch(error){this._onProgress({type:eventType+":error",error:error.data?.error,data:{statusCode:error.status}}),response=error}if(200<=response.status&&response.status<300)return this._onProgress({type:eventType+":success",data:response.data.data}),void resolve(response.data.data)}while(attempts--,await this._delay(this._attemptDelay),0<attempts&&conditionFn(response));this._onProgress({type:eventType+":fail",error:response.data?.error}),reject(response.data?.error??{message:"Unhandled error"})})}_onProgress(event){this._safeExecute(()=>this._onProgressFn?.call(null,event))}_isTransientStatusCode(response){return this._safeExecute(()=>500==response.status||409===response.status||424===response.status||504===response.status,!0)}_convertToBase64UriJson(data){data=JSON.stringify(data);const base64Json=btoa(data);return base64Json.replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}_sendRequest({path,method,payload,correlationId}){const tryParse=json=>""===json?null:this._safeExecute(()=>JSON.parse(json),null);return new Promise((resolve,reject)=>{let xhr=new XMLHttpRequest;const url=new URL(path,this._threeDSecureUrl);xhr.open(method,url.toString());xhr.timeout=3e4,xhr.setRequestHeader("Accept","application/json"),xhr.setRequestHeader("Accept-Language",this._culture),xhr.setRequestHeader("Content-Type","application/json"),xhr.setRequestHeader("CorrelationId",correlationId),xhr.onload=()=>{try{if(200<=xhr.status&&xhr.status<300)return void resolve({status:xhr.status,data:tryParse(xhr.responseText)});reject({status:xhr.status,data:tryParse(xhr.responseText)})}catch(error){console.log(error),reject({status:500,data:{message:error.toString()}})}},xhr.onerror=()=>{reject({status:xhr.status,data:tryParse(xhr.responseText)})};var json=this._safeExecute(()=>JSON.stringify(payload),"{}");xhr.send(json)})}_destroy(){this._safeExecute(()=>document.getElementById(this.IFRAME_DSMETHOD_NAME)?.remove()),this._safeExecute(()=>document.getElementById(this.FORM_DSMETHOD_NAME)?.remove()),this._safeExecute(()=>document.getElementById(this.IFRAME_CHALLENGE_NAME)?.remove()),this._safeExecute(()=>document.getElementById(this.FORM_CHALLENGE_NAME)?.remove())}_safeExecute(action,defaultResult){try{return action()}catch(error){return console.log(error),defaultResult}}} |
{ | ||
"name": "@paybyrd/threedsecure-service", | ||
"version": "1.2.0", | ||
"version": "1.2.1", | ||
"description": "JS service implementation to control the 3DS flow over Paybyrd API", | ||
@@ -11,3 +11,4 @@ "main": "dist/threedsecure-service.min.js", | ||
"scripts": { | ||
"prepublish": "uglifyjs src/threedsecure-service.js -c -o dist/threedsecure-service.min.js" | ||
"prepublish": "uglifyjs src/threedsecure-service.js -c -o dist/threedsecure-service.min.js", | ||
"samples": "live-server --open=./samples" | ||
}, | ||
@@ -29,4 +30,5 @@ "keywords": [ | ||
"devDependencies": { | ||
"uglify-js": "^3.16.2" | ||
"uglify-js": "^3.16.2", | ||
"live-server": "1.2.2" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
class ThreeDSecureService { | ||
export default class ThreeDSecureService { | ||
constructor({ | ||
@@ -51,3 +51,3 @@ threeDSecureUrl, | ||
return this._retry( | ||
this._isTransientStatusCode, | ||
this._isTransientStatusCode.bind(this), | ||
() => this._sendRequest({ | ||
@@ -67,3 +67,3 @@ path: '/api/v1', | ||
return this._retry( | ||
this._isTransientStatusCode, | ||
this._isTransientStatusCode.bind(this), | ||
() => this._sendRequest({ | ||
@@ -87,3 +87,3 @@ path: `/api/v1/${preAuthRequest.id}/preauth`, | ||
return this._retry( | ||
this._isTransientStatusCode, | ||
this._isTransientStatusCode.bind(this), | ||
() => this._sendRequest({ | ||
@@ -110,3 +110,3 @@ path: `/api/v1/${authRequest.id}/auth`, | ||
return this._retry( | ||
this._isTransientStatusCode, | ||
this._isTransientStatusCode.bind(this), | ||
() => this._sendRequest({ | ||
@@ -408,13 +408,7 @@ path: `/api/v1/${postAuthRequest.id}/postAuth`, | ||
_onProgress(event) { | ||
try { | ||
this._onProgressFn?.call(null, event); | ||
} catch | ||
{ | ||
// do nothing | ||
} | ||
this._safeExecute(() => this._onProgressFn?.call(null, event)); | ||
} | ||
_isTransientStatusCode(response) { | ||
return response.status === 409 || response.status === 424 || response.status === 504; | ||
return this._safeExecute(() => response.status == 500 || response.status === 409 || response.status === 424 || response.status === 504, true); | ||
} | ||
@@ -437,7 +431,3 @@ | ||
} | ||
try { | ||
return JSON.parse(json); | ||
} catch { | ||
return null; | ||
} | ||
return this._safeExecute(() => JSON.parse(json), null); | ||
} | ||
@@ -449,3 +439,2 @@ | ||
xhr.open(method, url.toString()); | ||
@@ -462,13 +451,24 @@ | ||
xhr.onload = () => { | ||
if (xhr.status >= 200 && xhr.status < 300) { | ||
resolve({ | ||
try { | ||
if (xhr.status >= 200 && xhr.status < 300) { | ||
resolve({ | ||
status: xhr.status, | ||
data: tryParse(xhr.responseText) | ||
}); | ||
return; | ||
} | ||
reject({ | ||
status: xhr.status, | ||
data: tryParse(xhr.responseText) | ||
}); | ||
return; | ||
} | ||
reject({ | ||
status: xhr.status, | ||
data: tryParse(xhr.responseText) | ||
}); | ||
catch (error) { | ||
console.log(error); | ||
reject({ | ||
status: 500, | ||
data: { | ||
message: error.toString() | ||
} | ||
}); | ||
} | ||
}; | ||
@@ -482,3 +482,5 @@ xhr.onerror = () => { | ||
xhr.send(JSON.stringify(payload)); | ||
const json = this._safeExecute(() => JSON.stringify(payload), '{}'); | ||
xhr.send(json); | ||
}); | ||
@@ -488,9 +490,16 @@ } | ||
_destroy() { | ||
document.getElementById(this.IFRAME_DSMETHOD_NAME)?.remove(); | ||
document.getElementById(this.FORM_DSMETHOD_NAME)?.remove(); | ||
document.getElementById(this.IFRAME_CHALLENGE_NAME)?.remove(); | ||
document.getElementById(this.FORM_CHALLENGE_NAME)?.remove(); | ||
this._safeExecute(() => document.getElementById(this.IFRAME_DSMETHOD_NAME)?.remove()); | ||
this._safeExecute(() => document.getElementById(this.FORM_DSMETHOD_NAME)?.remove()); | ||
this._safeExecute(() => document.getElementById(this.IFRAME_CHALLENGE_NAME)?.remove()); | ||
this._safeExecute(() => document.getElementById(this.FORM_CHALLENGE_NAME)?.remove()); | ||
} | ||
} | ||
export default ThreeDSecureService; | ||
_safeExecute(action, defaultResult) { | ||
try { | ||
return action(); | ||
} catch (error) { | ||
console.log(error); | ||
return defaultResult; | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
63525
7
437
2