Skip to content

Instantly share code, notes, and snippets.

@ccjmne
Last active July 19, 2023 20:35
Show Gist options
  • Save ccjmne/a25fe512be9c3f5633a05992d65576fc to your computer and use it in GitHub Desktop.
Save ccjmne/a25fe512be9c3f5633a05992d65576fc to your computer and use it in GitHub Desktop.
EVO Mock Redirect TamperMonkey Script for Unite
(()=>{"use strict";const e={ECB:0,CBC:1},a={PKCS5:0,ONE_AND_ZEROS:1,LAST_BYTE:2,NULL:3,SPACES:4},c={STRING:0,UINT8_ARRAY:1};function d(e){return e>>>0}function f(e,a){return d(e^a)}function b(e,a){return d(e+a|0)}function t(e,a,c,f){return d(e<<24|a<<16|c<<8|f)}function r(e){return[e>>>24&255,e>>>16&255,e>>>8&255,255&e]}function n(e){return"string"==typeof e}function s(e){return"object"==typeof e&&"byteLength"in e}function o(e){return n(e)||s(e)}function i(e,a){let c=!1;return Object.keys(e).forEach((d=>{e[d]===a&&(c=!0)})),c}function l(e){if(n(e))return(new TextEncoder).encode(e);if(s(e))return new Uint8Array(e);throw new Error("Unsupported type")}const u="243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89452821e638d01377be5466cf34e90c6cc0ac29b7c97c50dd3f84d5b5b54709179216d5d98979fb1bd1310ba698dfb5ac2ffd72dbd01adfb7b8e1afed6a267e96ba7c9045f12c7f9924a19947b3916cf70801f2e2858efc16636920d871574e69a458fea3f4933d7e0d95748f728eb658718bcd5882154aee7b54a41dc25a59b59c30d5392af26013c5d1b023286085f0ca417918b8db38ef8e79dcb0603a180e6c9e0e8bb01e8a3ed71577c1bd314b2778af2fda55605c60e65525f3aa55ab945748986263e8144055ca396a2aab10b6b4cc5c341141e8cea15486af7c72e993b3ee1411636fbc2a2ba9c55d741831f6ce5c3e169b87931eafd6ba336c24cf5c7a325381289586773b8f48986b4bb9afc4bfe81b6628219361d809ccfb21a991487cac605dec8032ef845d5de98575b1dc262302eb651b8823893e81d396acc50f6d6ff383f442392e0b4482a484200469c8f04a9e1f9b5e21c66842f6e96c9a670c9c61abd388f06a51a0d2d8542f68960fa728ab5133a36eef0b6c137a3be4ba3bf0507efb2a98a1f1651d39af017666ca593e82430e888cee8619456f9fb47d84a5c33b8b5ebee06f75d885c12073401a449f56c16aa64ed3aa62363f77061bfedf72429b023d37d0d724d00a1248db0fead349f1c09b075372c980991b7b25d479d8f6e8def7e3fe501ab6794c3b976ce0bd04c006bac1a94fb6409f60c45e5c9ec2196a246368fb6faf3e6c53b51339b2eb3b52ec6f6dfc511f9b30952ccc814544af5ebd09bee3d004de334afd660f2807192e4bb3c0cba85745c8740fd20b5f39b9d3fbdb5579c0bd1a60320ad6a100c6402c7279679f25fefb1fa3cc8ea5e9f8db3222f83c7516dffd616b152f501ec8ad0552ab323db5fafd23876053317b483e00df829e5c57bbca6f8ca01a87562edf1769dbd542a8f6287effc3ac6732c68c4f5573695b27b0bbca58c8e1ffa35db8f011a010fa3d98fd2183b84afcb56c2dd1d35b9a53e479b6f84565d28e49bc4bfb9790e1ddf2daa4cb7e3362fb1341cee4c6e8ef20cada36774c01d07e9efe2bf11fb495dbda4dae909198eaad8e716b93d5a0d08ed1d0afc725e08e3c5b2f8e7594b78ff6e2fbf2122b648888b812900df01c4fad5ea0688fc31cd1cff191b3a8c1ad2f2f2218be0e1777ea752dfe8b021fa1e5a0cc0fb56f74e818acf3d6ce89e299b4a84fe0fd13e0b77cc43b81d2ada8d9165fa2668095770593cc7314211a1477e6ad206577b5fa86c75442f5fb9d35cfebcdaf0c7b3e89a0d6411bd3ae1e7e4900250e2d2071b35e226800bb57b8e0af2464369bf009b91e5563911d59dfa6aa78c14389d95a537f207d5ba202e5b9c5832603766295cfa911c819684e734a41b3472dca7b14a94a1b5100529a532915d60f573fbc9bc6e42b60a47681e6740008ba6fb5571be91ff296ec6b2a0dd915b6636521e7b9f9b6ff34052ec585566453b02d5da99f8fa108ba47996e85076a4b7a70e9b5b32944db75092ec4192623ad6ea6b049a7df7d9cee60b88fedb266ecaa8c71699a17ff5664526cc2b19ee1193602a575094c29a0591340e4183a3e3f54989a5b429d656b8fe4d699f73fd6a1d29c07efe830f54d2d38e6f0255dc14cdd20868470eb266382e9c6021ecc5e09686b3f3ebaefc93c9718146b6a70a1687f358452a0e286b79c5305aa5007373e07841c7fdeae5c8e7d44ec5716f2b8b03ada37f0500c0df01c1f040200b3ffae0cf51a3cb574b225837a58dc0921bdd19113f97ca92ff69432477322f547013ae5e58137c2dadcc8b576349af3dda7a94461460fd0030eecc8c73ea4751e41e238cd993bea0e2f3280bba1183eb3314e548b384f6db9086f420d03f60a04bf2cb8129024977c795679b072bcaf89afde9a771fd9930810b38bae12dccf3f2e5512721f2e6b7124501adde69f84cd877a5847187408da17bc9f9abce94b7d8cec7aec3adb851dfa63094366c464c3d2ef1c18473215d908dd433b3724c2ba1612a14d432a65c45150940002133ae4dd71dff89e10314e5581ac77d65f11199b043556f1d7a3c76b3c11183b5924a509f28fe6ed97f1fbfa9ebabf2c1e153c6e86e34570eae96fb1860e5e0a5a3e2ab3771fe71c4e3d06fa2965dcb999e71d0f803e89d65266c8252e4cc9789c10b36ac6150eba94e2ea78a5fc3c531e0a2df4f2f74ea7361d2b3d1939260f19c279605223a708f71312b6ebadfe6eeac31f66e3bc4595a67bc883b17f37d1018cff28c332ddefbe6c5aa56558218568ab9802eecea50fdb2f953b2aef7dad5b6e2f841521b62829076170ecdd4775619f151013cca830eb61bd960334fe1eaa0363cfb5735c904c70a239d59e9e0bcbaade14eecc86bc60622ca79cab5cabb2f3846e648b1eaf19bdf0caa02369b9655abb5040685a323c2ab4b3319ee9d5c021b8f79b540b19875fa09995f7997e623d7da8f837889a97e32d7711ed935f166812810e358829c7e61fd696dedfa17858ba9957f584a51b2272639b83c3ff1ac24696cdb30aeb532e30548fd948e46dbc312858ebf2ef34c6ffeafe28ed61ee7c3c735d4a14d9e864b7e342105d14203e13e045eee2b6a3aaabeadb6c4f15facb4fd0c742f442ef6abbb5654f3b1d41cd2105d81e799e86854dc7e44b476a3d816250cf62a1f25b8d2646fc8883a0c1c7b6a37f1524c369cb749247848a0b5692b285095bbf00ad19489d1462b17423820e0058428d2a0c55f5ea1dadf43e233f70613372f0928d937e41d65fecf16c223bdb7cde3759cbee74604085f2a7ce77326ea607808419f8509ee8efd85561d99735a969a7aac50c06c25a04abfc800bcadc9e447a2ec3453484fdd567050e1e9ec9db73dbd3105588cd675fda79e3674340c5c43465713e38d83d28f89ef16dff20153e21e78fb03d4ae6e39f2bdb83adf7e93d5a68948140f7f64c261c94692934411520f77602d4f7bcf46b2ed4a20068d40824713320f46a43b7d4b7500061af1e39f62e9724454614214f74bf8b88404d95fc1d96b591af70f4ddd366a02f45bfbc09ec03bd97857fac6dd031cb850496eb27b355fd3941da2547e6abca0a9a28507825530429f40a2c86dae9b66dfb68dc1462d7486900680ec0a427a18dee4f3ffea2e887ad8cb58ce0067af4d6b6aace1e7cd3375fecce78a399406b2a4220fe9e35d9f385b9ee39d7ab3b124e8b1dc9faf74b6d185626a36631eae397b23a6efa74dd5b43326841e7f7ca7820fbfb0af54ed8feb397454056acba48952755533a3a20838d87fe6ba9b7d096954b55a867bca1159a58cca9296399e1db33a62a4a563f3125f95ef47e1c9029317cfdf8e80204272f7080bb155c05282ce395c11548e4c66d2248c1133fc70f86dc07f9c9ee41041f0f404779a45d886e17325f51ebd59bc0d1f2bcc18f41113564257b7834602a9c60dff8e8a31f636c1b0e12b4c202e1329eaf664fd1cad181156b2395e0333e92e13b240b62eebeb92285b2a20ee6ba0d99de720c8c2da2f728d012784595b794fd647d0862e7ccf5f05449a36f877d48fac39dfd27f33e8d1e0a476341992eff743a6f6eabf4f8fd37a812dc60a1ebddf8991be14cdb6e6b0dc67b55106d672c372765d43bdcd0e804f1290dc7cc00ffa3b5390f92690fed0b667b9ffbcedb7d9ca091cf0bd9155ea3bb132f88515bad247b9479bf763bd6eb37392eb3cc1159798026e297f42e312d6842ada7c66a2b3b12754ccc782ef11c6a124237b79251e706a1bbe64bfb63501a6b101811caedfa3d25bdd8e2e1c3c9444216590a121386d90cec6ed5abea2a64af674eda86a85fbebfe98864e4c3fe9dbc8057f0f7c08660787bf86003604dd1fd8346f6381fb07745ae04d736fccc83426b33f01eab71b08041873c005e5f77a057bebde8ae2455464299bf582e614e58f48ff2ddfda2f474ef388789bdc25366f9c3c8b38e74b475f25546fcd9b97aeb26618b1ddf84846a0e79915f95e2466e598e20b457708cd55591c902de4cb90bace1bb8205d011a862487574a99eb77f19b6e0a9dc09662d09a1c4324633e85a1f0209f0be8c4a99a0251d6efe101ab93d1d0ba5a4dfa186f20f2868f169dcb7da83573906fea1e2ce9b4fcd7f5250115e01a70683faa002b5c40de6d0279af88c27773f8641c3604c0661a806b5f0177a28c0f586e0006058aa30dc7d6211e69ed72338ea6353c2dd94c2c21634bbcbee5690bcb6deebfc7da1ce591d766f05e4094b7c018839720a3d7c927c2486e3725f724d9db91ac15bb4d39eb8fced54557808fca5b5d83d7cd34dad0fc41e50ef5eb161e6f8a28514d96c51133c6fd5c7e756e14ec4362abfceddc6c837d79a323492638212670efa8e406000e03a39ce37d3faf5cfabc277375ac52d1b5cb0679e4fa33742d382274099bc9bbed5118e9dbf0f7315d62d1c7ec700c47bb78c1b6b21a19045b26eb1be6a366eb45748ab2fbc946e79c6a376d26549c2c8530ff8ee468dde7dd5730a1d4cd04dc62939bbdba9ba4650ac9526e8be5ee304a1fad5f06a2d519a63ef8ce29a86ee22c089c2b843242ef6a51e03aa9cf2d0a483c061ba9be96a4d8fe51550ba645bd62826a2f9a73a3ae14ba99586ef5562e9c72fefd3f752f7da3f046f6977fa0a5980e4a91587b086019b09e6ad3b3ee593e990fd5a9e34d7972cf0b7d9022b8b5196d5ac3a017da67dd1cf3ed67c7d2d281f9f25cfadf2b89b5ad6b4725a88f54ce029ac71e019a5e647b0acfded93fa9be8d3c48d283b57ccf8d5662979132e28785f0191ed756055f7960e44e3d35e8c15056dd488f46dba03a161250564f0bdc3eb9e153c9057a297271aeca93a072a1b3f6d9b1e6321f5f59c66fb26dcf3197533d928b155fdf5035634828aba3cbb28517711c20ad9f8abcc5167ccad925f4de817513830dc8e379d58629320f991ea7a90c2fb3e7bce5121ce64774fbe32a8b6e37ec3293d4648de53696413e680a2ae0810dd6db22469852dfd09072166b39a460a6445c0dd586cdecf1c20c8ae5bbef7dd1b588d40ccd2017f6bb4e3bbdda26a7e3a59ff453e350a44bcb4cdd572eacea8fa6484bb8d6612aebf3c6f47d29be463542f5d9eaec2771bf64e6370740e0d8de75b1357f8721671af537d5d4040cb084eb4e2cc34d2466a0115af84e1b0042895983a1d06b89fb4ce6ea0486f3f3b823520ab82011a1d4b277227f8611560b1e7933fdcbb3a792b344525bda08839e151ce794b2f32c9b7a01fbac9e01cc87ebcc7d1f6cf0111c3a1e8aac71a908749d44fbd9ad0dadecbd50ada380339c32ac69136678df9317ce0b12b4ff79e59b743f5bb3af2d519ff27d9459cbf97222c15e6fc2a0f91fc719b941525fae59361ceb69cebc2a8645912baa8d1b6c1075ee3056a0c10d25065cb03a442e0ec6e0e1698db3b4c98a0be3278e9649f1f9532e0d392dfd3a0342b8971f21e1b0a74414ba3348cc5be7120c37632d8df359f8d9b992f2ee60b6f470fe3f11de54cda541edad891ce6279cfcd3e7e6f1618b166fd2c1d05848fd2c5f6fb2299f523f357a632762393a8353156cccd02acf081625a75ebb56e16369788d273ccde96629281b949d04c50901b71c65614e6c6c7bd327a140a45e1d006c3f27b9ac9aa53fd62a80f00bb25bfe235bdd2f671126905b2040222b6cbcf7ccd769c2b53113ec01640e3d338abbd602547adf0ba38209cf746ce7677afa1c52075606085cbfe4e8ae88dd87aaaf9b04cf9aa7e1948c25c02fb8a8c01c36ae4d6ebe1f990d4f869a65cdea03f09252dc208e69fb74e6132ce77e25b578fdfe33ac372e6";class h{static get MODE(){return e}static get PADDING(){return a}static get TYPE(){return c}constructor(c,d=e.ECB,b=a.PKCS5){if(!o(c))throw new Error("Key should be a string or an ArrayBuffer / Buffer");if(c.length<1||c.byteLength<1)throw new Error("Key should not be empty");if(!i(e,d))throw new Error("Unsupported mode");if(!i(a,b))throw new Error("Unsupported padding");this.mode=d,this.padding=b,this.iv=null;const r=function(){const e={p:[],s:[[],[],[],[]]};let a=0;for(let c=0;c<18;c++){const c=u.substring(a,a+8);a+=8,e.p.push(Number(`0x${c}`))}for(let c=0;c<4;c++)for(let d=0;d<256;d++){const d=u.substring(a,a+8);a+=8,e.s[c].push(Number(`0x${d}`))}return e}();this.p=r.p,this.s=r.s,c=function(e){if(e.length>=72)return e;const a=[];for(;a.length<72;)for(let c=0;c<e.length;c++)a.push(e[c]);return new Uint8Array(a)}(l(c));for(let e=0,a=0;e<18;e++,a+=4){const d=t(c[a],c[a+1],c[a+2],c[a+3]);this.p[e]=f(this.p[e],d)}let n=0,s=0;for(let e=0;e<18;e+=2)[n,s]=this._encryptBlock(n,s),this.p[e]=n,this.p[e+1]=s;for(let e=0;e<4;e++)for(let a=0;a<256;a+=2)[n,s]=this._encryptBlock(n,s),this.s[e][a]=n,this.s[e][a+1]=s}setIv(e){if(!o(e))throw new Error("IV should be a string or an ArrayBuffer / Buffer");if(8!==(e=l(e)).length)throw new Error("IV should be 8 byte length");this.iv=e}encode(c){if(!o(c))throw new Error("Encode data should be a string or an ArrayBuffer / Buffer");if(this.mode!==e.ECB&&!this.iv)throw new Error("IV is not set");return c=function(e,c){const d=8-e.length%8;if(8===d&&e.length>0&&c!==a.PKCS5)return e;const f=new Uint8Array(e.length+d),b=[];let t=d,r=0;switch(c){case a.PKCS5:r=d;break;case a.ONE_AND_ZEROS:b.push(128),t--;break;case a.SPACES:r=32}for(;t>0;){if(c===a.LAST_BYTE&&1===t){b.push(d);break}b.push(r),t--}return f.set(e),f.set(b,e.length),f}(l(c),this.padding),this.mode===e.ECB?this._encodeECB(c):this.mode===e.CBC?this._encodeCBC(c):void 0}decode(d,f=c.STRING){if(!o(d))throw new Error("Decode data should be a string or an ArrayBuffer / Buffer");if(this.mode!==e.ECB&&!this.iv)throw new Error("IV is not set");if((d=l(d)).length%8!=0)throw new Error("Decoded data should be multiple of 8 bytes");switch(this.mode){case e.ECB:d=this._decodeECB(d);break;case e.CBC:d=this._decodeCBC(d)}switch(d=function(e,c){let d=0;switch(c){case a.LAST_BYTE:case a.PKCS5:{const a=e[e.length-1];a<=8&&(d=a);break}case a.ONE_AND_ZEROS:{let a=1;for(;a<=8;){const c=e[e.length-a];if(128===c){d=a;break}if(0!==c)break;a++}break}case a.NULL:case a.SPACES:{const f=c===a.SPACES?32:0;let b=1;for(;b<=8;){if(e[e.length-b]!==f){d=b-1;break}b++}break}}return e.subarray(0,e.length-d)}(d,this.padding),f){case c.UINT8_ARRAY:return d;case c.STRING:return(new TextDecoder).decode(d);default:throw new Error("Unsupported return type")}}_encryptBlock(e,a){for(let c=0;c<16;c++)e=f(e,this.p[c]),a=f(a,this._f(e)),[e,a]=[a,e];return[e,a]=[a,e],a=f(a,this.p[16]),[e=f(e,this.p[17]),a]}_decryptBlock(e,a){for(let c=17;c>1;c--)e=f(e,this.p[c]),a=f(a,this._f(e)),[e,a]=[a,e];return[e,a]=[a,e],a=f(a,this.p[1]),[e=f(e,this.p[0]),a]}_f(e){const a=e>>>24&255,c=e>>>16&255,d=e>>>8&255,t=255&e;let r=b(this.s[0][a],this.s[1][c]);return r=f(r,this.s[2][d]),b(r,this.s[3][t])}_encodeECB(e){const a=new Uint8Array(e.length);for(let c=0;c<e.length;c+=8){let d=t(e[c],e[c+1],e[c+2],e[c+3]),f=t(e[c+4],e[c+5],e[c+6],e[c+7]);[d,f]=this._encryptBlock(d,f),a.set(r(d),c),a.set(r(f),c+4)}return a}_encodeCBC(e){const a=new Uint8Array(e.length);let c=t(this.iv[0],this.iv[1],this.iv[2],this.iv[3]),d=t(this.iv[4],this.iv[5],this.iv[6],this.iv[7]);for(let b=0;b<e.length;b+=8){let n=t(e[b],e[b+1],e[b+2],e[b+3]),s=t(e[b+4],e[b+5],e[b+6],e[b+7]);[n,s]=[f(c,n),f(d,s)],[n,s]=this._encryptBlock(n,s),[c,d]=[n,s],a.set(r(n),b),a.set(r(s),b+4)}return a}_decodeECB(e){const a=new Uint8Array(e.length);for(let c=0;c<e.length;c+=8){let d=t(e[c],e[c+1],e[c+2],e[c+3]),f=t(e[c+4],e[c+5],e[c+6],e[c+7]);[d,f]=this._decryptBlock(d,f),a.set(r(d),c),a.set(r(f),c+4)}return a}_decodeCBC(e){const a=new Uint8Array(e.length);let c,d,b=t(this.iv[0],this.iv[1],this.iv[2],this.iv[3]),n=t(this.iv[4],this.iv[5],this.iv[6],this.iv[7]);for(let s=0;s<e.length;s+=8){let o=t(e[s],e[s+1],e[s+2],e[s+3]),i=t(e[s+4],e[s+5],e[s+6],e[s+7]);[c,d]=[o,i],[o,i]=this._decryptBlock(o,i),[o,i]=[f(b,o),f(n,i)],[b,n]=[c,d],a.set(r(o),s),a.set(r(i),s+4)}return a}}class p{}p.transId="TransID",p.refNr="RefNr",p.userData="UserData",p.mac="MAC",p.amount="Amount",p.currency="Currency",p.orderDesc="OrderDesc",p.urlSuccess="URLSuccess",p.urlFailure="URLFailure",p.response="Response",p.urlNotify="URLNotify",p.capture="Capture",p.mid="mid",p.payId="PayID",p.xid="XID",p.type="Type",p.pcnr="PCNr",p.ccEpiry="CCExpiry",p.ccBrand="CCBrand",p.status="Status",p.description="Description",p.code="Code";class y{}function g(e,a,c,d){var f;const b=new URLSearchParams(e),t=[...function(e,a,c){var d,f,b,t;return[p.amount,p.currency,p.response,p.orderDesc,p.urlFailure,p.urlSuccess,p.urlNotify,p.capture].forEach((a=>e.delete(a))),e.set(p.mid,null!==(d=a.get("MerchantID"))&&void 0!==d?d:"mid_not_exist"),e.set(p.pcnr,"0022589500163111"),e.set(p.payId,"cef0006a2d54467d84174b08704e6314"),e.set(p.xid,"69f842ba459c48c188f25c2245d33585"),e.set(p.type,"SSL"),e.set(p.ccEpiry,"202912"),e.set(p.ccBrand,"VISA"),e.set(p.status,null!==(f=c.get(p.status))&&void 0!==f?f:"failure"),e.set(p.description,null!==(b=c.get(p.description))&&void 0!==b?b:"description"),e.set(p.code,null!==(t=c.get(p.code))&&void 0!==t?t:"321321"),e}((n=null!==(f=b.get("Data"))&&void 0!==f?f:"no_data",r=new h(a,h.MODE.ECB,h.PADDING.SPACES).decode(new Uint8Array(function(e){var a,c;return null!==(c=null===(a=e.match(/.{2}/g))||void 0===a?void 0:a.map((e=>parseInt(e,16))))&&void 0!==c?c:[]}(n)).buffer,h.TYPE.STRING),new Map(r.split("&").map((e=>e.split("="))))),b,c).entries()].map((([e,a])=>`${e}=${a}`)).join("&");var r,n;const s=(o=t,i=new h(a,h.MODE.ECB,h.PADDING.SPACES).encode(o),[...i].map((e=>Number(e).toString(16).toUpperCase().padStart(2,"0"))).join(""));var o,i;return`http://dev-stage.mercateo.${d}/basket/deucsredirect?Len=${t.length}&Data=${s}`}y.gb="T...",y.de="B...",function(){var e,a;const c=document.createElement("label");c.textContent="Hostname:",c.style.textAlign="end",c.setAttribute("for","hostname");const d=document.createElement("input");d.type="text",d.id="hostname",d.name="hostname",d.required=!0,d.value=null!==(e=localStorage.getItem("hostnameInput"))&&void 0!==e?e:"local.mercateo.co.uk:8081",d.addEventListener("change",(()=>localStorage.setItem("hostnameInput",d.value)));const f=document.createElement("label");f.textContent="Blowfish Key:",f.style.textAlign="end",f.setAttribute("for","blowfishKey");const b=document.createElement("input");b.type="text",b.id="blowfishKey",b.name="blowfishKey",b.required=!0,b.value=null!==(a=localStorage.getItem("blowfishInput"))&&void 0!==a?a:"",b.addEventListener("change",(()=>localStorage.setItem("blowfishInput",b.value)));const t=document.createElement("button");t.type="button",t.textContent="Redirect to Failure",t.onclick=()=>window.location.href=function(e,a,c){const d=new Map;return d.set(p.status,"FAILED"),d.set(p.description,"FAILED"),d.set(p.code,"21200200"),g(e,a,d,"co.uk")}(window.location.search.substring(1),b.value).replace(/(?<=https?:\/\/)[^/]+/,d.value);const r=document.createElement("button");r.type="button",r.textContent="Redirect to Success",r.onclick=()=>window.location.href=function(e,a,c){const d=new Map;return d.set(p.status,"AUTHORIZED"),d.set(p.description,"success"),d.set(p.code,"00000000"),g(e,a,d,"co.uk")}(window.location.search.substring(1),b.value).replace(/(?<=https?:\/\/)[^/]+/,d.value);const n=document.createElement("div");n.style.gridColumn="1 / 3",n.style.display="grid",n.style.gap="10px",n.style.gridTemplate="auto / 1fr 1fr",n.appendChild(t),n.appendChild(r);const s=document.createElement("form");s.id="redirectForm",s.style.display="grid",s.style.background="whitesmoke",s.style.color="grey",s.style.border="1px solid lightgrey",s.style.gridTemplate="1fr 1fr 1fr / auto 1fr",s.style.gap="10px",s.style.padding="10px",s.style.borderRadius="3px",s.style.boxShadow="rgba(0, 0, 0, 0.35) 0px 5px 15px",s.appendChild(c),s.appendChild(d),s.appendChild(f),s.appendChild(b),s.appendChild(n);const o=document.createElement("div");o.style.position="fixed",o.style.top="10px",o.style.left="50%",o.style.transform="translate(-50%) perspective(0)",o.style.zIndex="9999";const i=o.attachShadow({mode:"closed"}),l=document.createElement("style");l.textContent="\n :host * {\n font-family: sans-serif\n font-size: 1rem\n white-space: nowrap\n }",i.appendChild(l),i.appendChild(s),document.body.appendChild(o)}()})();
import { Blowfish } from 'egoroof-blowfish'
class ParamName {
static transId: string = 'TransID'
static refNr: string = 'RefNr'
static userData: string = 'UserData'
static mac: string = 'MAC'
static amount: string = 'Amount'
static currency: string = 'Currency'
static orderDesc: string = 'OrderDesc'
static urlSuccess: string = 'URLSuccess'
static urlFailure: string = 'URLFailure'
static response: string = 'Response'
static urlNotify: string = 'URLNotify'
static capture: string = 'Capture'
static mid: string = 'mid'
static payId: string = 'PayID'
static xid: string = 'XID'
static type: string = 'Type'
static pcnr: string = 'PCNr'
static ccEpiry: string = 'CCExpiry'
static ccBrand: string = 'CCBrand'
static status: string = 'Status'
static description: string = 'Description'
static code: string = 'Code'
}
export class GeoAreaBlowFishKey {
static gb: string = 'T...'
static de: string = 'B...'
}
export function redirectToSuccess(queryString: string, geoAreaKey: GeoAreaBlowFishKey, devStageDomain: string) {
const responseMapData = new Map<string, string>()
responseMapData.set(ParamName.status, 'AUTHORIZED')
responseMapData.set(ParamName.description, 'success')
responseMapData.set(ParamName.code, '00000000')
return getRedirectResult(queryString, geoAreaKey, responseMapData, devStageDomain)
}
export function redirectToFailure(queryString: string, geoAreaKey: GeoAreaBlowFishKey, devStageDomain: string) {
const responseMapData = new Map<string, string>()
responseMapData.set(ParamName.status, 'FAILED')
responseMapData.set(ParamName.description, 'FAILED')
responseMapData.set(ParamName.code, '21200200')
return getRedirectResult(queryString, geoAreaKey, responseMapData, devStageDomain)
}
function getRedirectResult(
queryString: string,
geoAreaKey: GeoAreaBlowFishKey,
responseMapData: Map<string, string>,
devStageDomain: string,
) {
const urlParams = new URLSearchParams(queryString)
const dataToEncrypt = getDataToEncrypt(manipulateData(
getDataAsMap(decodeData(urlParams.get('Data') ?? 'no_data', geoAreaKey)), urlParams,
responseMapData
))
const encryptedData = convertDecToHex(encryptData(dataToEncrypt, geoAreaKey))
return `http://dev-stage.mercateo.${devStageDomain}/basket/deucsredirect?Len=${dataToEncrypt.length}&Data=${encryptedData}`
}
function getDataToEncrypt(manipulatedData: Map<string, string>) {
return [...manipulatedData.entries()].map(([key, value]) => `${key}=${value}`).join('&')
}
function encryptData(encodeStr: string, geoArea: GeoAreaBlowFishKey) {
return new Blowfish(geoArea, Blowfish.MODE.ECB, Blowfish.PADDING.SPACES).encode(encodeStr)
}
function manipulateData(
mappedData: Map<string, string>,
urlParams: URLSearchParams,
responseParams: Map<string, string>,
) {
[
ParamName.amount,
ParamName.currency,
ParamName.response,
ParamName.orderDesc,
ParamName.urlFailure,
ParamName.urlSuccess,
ParamName.urlNotify,
ParamName.capture,
].forEach(entry => mappedData.delete(entry))
mappedData.set(ParamName.mid, urlParams.get('MerchantID') ?? 'mid_not_exist')
mappedData.set(ParamName.pcnr, '0022589500163111')
mappedData.set(ParamName.payId, 'cef0006a2d54467d84174b08704e6314')
mappedData.set(ParamName.xid, '69f842ba459c48c188f25c2245d33585')
mappedData.set(ParamName.type, 'SSL')
mappedData.set(ParamName.ccEpiry, '202912')
mappedData.set(ParamName.ccBrand, 'VISA')
mappedData.set(ParamName.status, responseParams.get(ParamName.status) ?? 'failure')
mappedData.set(ParamName.description, responseParams.get(ParamName.description) ?? 'description')
mappedData.set(ParamName.code, responseParams.get(ParamName.code) ?? '321321')
return mappedData
}
function decodeData(hexData: string, geoArea: GeoAreaBlowFishKey) {
return new Blowfish(geoArea, Blowfish.MODE.ECB, Blowfish.PADDING.SPACES)
.decode(new Uint8Array(convertHexToDec(hexData)).buffer as Uint8Array, Blowfish.TYPE.STRING)
}
function getDataAsMap(decodedData: string) {
return new Map(decodedData.split('&').map(param => param.split('=') as [string, string]))
}
function convertHexToDec(hex: string) {
return hex.match(/.{2}/g)?.map(chunk => parseInt(chunk, 16)) ?? []
}
function convertDecToHex(dec: Uint8Array) {
return [...dec].map(num => Number(num).toString(16).toUpperCase().padStart(2, '0')).join('')
}
(function setupRedirectForm() {
const hostnameLabel = document.createElement('label')
hostnameLabel.textContent = 'Hostname:'
hostnameLabel.style.textAlign = 'end'
hostnameLabel.setAttribute('for', 'hostname')
const hostnameInput = document.createElement('input')
hostnameInput.type = 'text'
hostnameInput.id = 'hostname'
hostnameInput.name = 'hostname'
hostnameInput.required = true
hostnameInput.value = localStorage.getItem('hostnameInput') ?? 'local.mercateo.co.uk:8081'
hostnameInput.addEventListener('change', () => localStorage.setItem('hostnameInput', hostnameInput.value))
const blowfishLabel = document.createElement('label')
blowfishLabel.textContent = 'Blowfish Key:'
blowfishLabel.style.textAlign = 'end'
blowfishLabel.setAttribute('for', 'blowfishKey')
const blowfishInput = document.createElement('input')
blowfishInput.type = 'text'
blowfishInput.id = 'blowfishKey'
blowfishInput.name = 'blowfishKey'
blowfishInput.required = true
blowfishInput.value = localStorage.getItem('blowfishInput') ?? ''
blowfishInput.addEventListener('change', () => localStorage.setItem('blowfishInput', blowfishInput.value))
const failureButton = document.createElement('button')
failureButton.type = 'button'
failureButton.textContent = 'Redirect to Failure'
failureButton.onclick = () => window.location.href = redirectToFailure(window.location.search.substring(1), blowfishInput.value, 'co.uk')
.replace(/(?<=https?:\/\/)[^/]+/, hostnameInput.value)
const successButton = document.createElement('button')
successButton.type = 'button'
successButton.textContent = 'Redirect to Success'
successButton.onclick = () => window.location.href = redirectToSuccess(window.location.search.substring(1), blowfishInput.value, 'co.uk')
.replace(/(?<=https?:\/\/)[^/]+/, hostnameInput.value)
const buttons = document.createElement('div')
buttons.style.gridColumn = '1 / 3'
buttons.style.display = 'grid'
buttons.style.gap = '10px'
buttons.style.gridTemplate = 'auto / 1fr 1fr'
buttons.appendChild(failureButton)
buttons.appendChild(successButton)
const form = document.createElement('form')
form.id = 'redirectForm'
form.style.display = 'grid'
form.style.background = 'whitesmoke'
form.style.color = 'grey'
form.style.border = '1px solid lightgrey'
form.style.gridTemplate = '1fr 1fr 1fr / auto 1fr'
form.style.gap = '10px'
form.style.padding = '10px'
form.style.borderRadius = '3px'
form.style.boxShadow = 'rgba(0, 0, 0, 0.35) 0px 5px 15px'
form.appendChild(hostnameLabel)
form.appendChild(hostnameInput)
form.appendChild(blowfishLabel)
form.appendChild(blowfishInput)
form.appendChild(buttons)
const shadowContainer = document.createElement('div')
shadowContainer.style.position = 'fixed'
shadowContainer.style.top = '10px'
shadowContainer.style.left = '50%'
shadowContainer.style.transform = 'translate(-50%) perspective(0)'
shadowContainer.style.zIndex = '9999'
const shadowRoot = shadowContainer.attachShadow({ mode: 'closed' })
const style = document.createElement('style')
style.textContent = `
:host * {
font-family: sans-serif
font-size: 1rem
white-space: nowrap
}`
shadowRoot.appendChild(style)
shadowRoot.appendChild(form)
document.body.appendChild(shadowContainer)
})()
{
"name": "nodejs-typescript",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"start": "webpack watch --mode development",
"build": "webpack --mode production"
},
"author": "",
"license": "ISC",
"dependencies": {
"egoroof-blowfish": "^4.0.1",
"esbuild": "^0.18.14",
"node-fetch": "^3.3.1",
"ts-loader": "^9.4.4",
"webpack": "^5.88.2"
},
"devDependencies": {
"@types/node": "^20.4.2",
"webpack-cli": "^5.1.4"
}
}
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext", "DOM"],
"types": ["node", "@types/node"],
"strict": true,
"moduleResolution": "Node"
},
"include": ["*.ts"]
}
module.exports = (_, argv) => ({
devtool: argv.mode !== 'production' && 'inline-source-map',
entry: './index.ts',
module: {
rules: [{
test: /\.ts$/,
use: {
loader: 'ts-loader',
options: {
transpileOnly: true,
}
},
exclude: /node_modules/,
}]
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment