<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Accept Hosted onReceiveCommunications function causes error on specific response in Integration and Testing</title>
    <link>https://community.developer.cybersource.com/t5/Integration-and-Testing/Accept-Hosted-onReceiveCommunications-function-causes-error-on/m-p/75000#M46699</link>
    <description>&lt;P&gt;I tested a hundred times and everything worked, then went live and my very first customer encountered an error that my team could not replicate - until we dug deep into what was happening.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The onReceiveCommunication function, as listed on the API Accept Hosted how-to's page calls for this following:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;AuthorizeNetPopup.onReceiveCommunication = function (querystr) {
	var params = parseQueryString(querystr);
	switch (params["action"]) {
		case "successfulSave":
			AuthorizeNetPopup.closePopup();
			break;
		case "cancel":
			AuthorizeNetPopup.closePopup();
			break;
		case "transactResponse":
			var response = params["response"];
			document.getElementById("token").value = response;
			AuthorizeNetPopup.closePopup();&lt;BR /&gt;                        // pass response to backend to record transaction&lt;BR /&gt;                        var res = JSON.parse(params["response"]) ; // THIS BREAKS because "response" is not a proper json string due below issue&lt;BR /&gt;                        httpReceipt(res) ; // pass JSON to backend to record transaction in DB
			break;
		case "resizeWindow":
			var w = parseInt(params["width"]);
			var h = parseInt(params["height"]);
			var ifrm = document.getElementById("iframeAuthorizeNet");
			ifrm.style.width = w.toString() + "px";
			ifrm.style.height = h.toString() + "px";
			centerPopup();
			break;
	} 

       function parseQueryString(str) {
        var vars = [];
        var arr = str.split('&amp;amp;');
        var pair;
        for (var i = 0; i &amp;lt; arr.length; i++) {
          pair = arr[i].split('=');
          vars.push(pair[0]);
          vars[pair[0]] = unescape(pair[1]);
        }
        return vars;
      }&lt;/PRE&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;However, the call to `parseQueryString` errors out IF any of the data being sent back (from Authorize) contains an ampersand (&amp;amp;) back in the "response" parameter.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;This is happening specifically because Authroize's response is both a standard URL with one of the parameter values being a JSON string.&amp;nbsp; &lt;STRONG&gt;IE: action=transactResponse&amp;amp;response={ JSON string here }&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If any value in the JSON string has a "&amp;amp;" in it, then the parseQueryString function will split the JSON string in two.&amp;nbsp; It so happens that my clients company name is "Blah Bar &amp;amp; Grill" - and Authorize was sending back the response as:&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;action=transactResponse&amp;amp;response={ ....{ .... "company" : "Blah Bar &amp;amp; Grill", "address": "123 Main Street" .... }, .....}


Which would then be split into the following parameters into the array "vars" as:
[ 
     [ "action" : "transactResponse"],
     [ "response" : "{ ....{ .... "company" : "Blah Bar " ]
     [ " Grill", "address": "123 Main Street" .... }, .....} ]
]&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;So, anyone trying to use the full "response" value - and convert it back to a proper JSON, it would fail:&amp;nbsp; &lt;STRONG&gt;&amp;nbsp;var res = JSON.parse(params["response"] ;&amp;nbsp;&lt;/STRONG&gt;&amp;nbsp;because after splitting on the "&amp;amp;", params{[response'] was not a proper json string anymore.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Thus any "&amp;amp;" inside the JSON string would be split further and further.&amp;nbsp;&lt;FONT color="#FF0000"&gt;&lt;STRONG&gt; Authorize needs to send a fully encoded URI/URIComponent repsonse back OR send back a proper JSON.&amp;nbsp; But mixing the json string into a URL type response is bad form that leads to this kind of problem.&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;To fix the issue, I had to rewrite the above Authorize.net provided functions to the following - its not the best, or cleanest, but at the moment it seems to do the trick.&amp;nbsp; It also accounts for additional URL params specificed after the "response={JSON string}" if they were ever to exist:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;    AuthorizeNetPopup.onReceiveCommunication = function (str) {
      var params = parseResponse(str) ;
      switch (params.action) {
        case "successfulSave":
          AuthorizeNetPopup.closePopup();
          break;
        case "cancel":
          AuthorizeNetPopup.closePopup();
          break;
        case "transactResponse":
          &lt;FONT color="#FF0000"&gt;&lt;STRONG&gt;httpReceipt(params.response) ;&lt;/STRONG&gt;&lt;/FONT&gt;  // my code to send the JSON to my backend to record the transaction data into my DB.
          AuthorizeNetPopup.closePopup();
          break;
        case "resizeWindow":
          var w = parseInt(params.width) ;
          var h = parseInt(params.height) ;
          var ifrm = document.getElementById("iframeAuthorizeNet");
          ifrm.style.width = w.toString() + "px";
          ifrm.style.height = h.toString() + "px";
          centerPopup();
          break;
      }
    };

      function parseResponse(str) {
        var resInfo = [], res = {} ;
        if (str.match("&amp;amp;response=")) {
          var params = str.split("&amp;amp;response=") ; // leading params &amp;amp; response obj
          if (params[1].match("}&amp;amp;")) {
            resInfo = params[1].split("}&amp;amp;") ;  // splits off anything at end of obj
            res.response = JSON.parse(resInfo[0] + "}") ;  // add } to complete obj string again
          } else {
            res.response = JSON.parse(params[1]) ;
          }
          if (resInfo[1]) {
            params = params[0]+ "&amp;amp;" +resInfo[1] ;  // join url params back together  
          } else {
            params = params[0] ;
          }
        }  else {
          params = str ;
        }
        params = new URLSearchParams(encodeURI(params)) ;  //encode then parse
        params.forEach(function(value, key) {
          res[key] = value ;
        }) ;

        return res ;
      } &lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;I bring this to everyones attention because both of the original functions come straight from Authorize.nets API Accept Hosted page documentation.&amp;nbsp; It means Authorize.nets own functions, can't handle Authorize.nets own transaction response if the there is an additional "&amp;amp;" anywhere inside the "response={ JSON STRING}" paraemter.&amp;nbsp;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;</description>
    <pubDate>Thu, 11 Feb 2021 19:40:13 GMT</pubDate>
    <dc:creator>rolinger</dc:creator>
    <dc:date>2021-02-11T19:40:13Z</dc:date>
    <item>
      <title>Accept Hosted onReceiveCommunications function causes error on specific response</title>
      <link>https://community.developer.cybersource.com/t5/Integration-and-Testing/Accept-Hosted-onReceiveCommunications-function-causes-error-on/m-p/75000#M46699</link>
      <description>&lt;P&gt;I tested a hundred times and everything worked, then went live and my very first customer encountered an error that my team could not replicate - until we dug deep into what was happening.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The onReceiveCommunication function, as listed on the API Accept Hosted how-to's page calls for this following:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;AuthorizeNetPopup.onReceiveCommunication = function (querystr) {
	var params = parseQueryString(querystr);
	switch (params["action"]) {
		case "successfulSave":
			AuthorizeNetPopup.closePopup();
			break;
		case "cancel":
			AuthorizeNetPopup.closePopup();
			break;
		case "transactResponse":
			var response = params["response"];
			document.getElementById("token").value = response;
			AuthorizeNetPopup.closePopup();&lt;BR /&gt;                        // pass response to backend to record transaction&lt;BR /&gt;                        var res = JSON.parse(params["response"]) ; // THIS BREAKS because "response" is not a proper json string due below issue&lt;BR /&gt;                        httpReceipt(res) ; // pass JSON to backend to record transaction in DB
			break;
		case "resizeWindow":
			var w = parseInt(params["width"]);
			var h = parseInt(params["height"]);
			var ifrm = document.getElementById("iframeAuthorizeNet");
			ifrm.style.width = w.toString() + "px";
			ifrm.style.height = h.toString() + "px";
			centerPopup();
			break;
	} 

       function parseQueryString(str) {
        var vars = [];
        var arr = str.split('&amp;amp;');
        var pair;
        for (var i = 0; i &amp;lt; arr.length; i++) {
          pair = arr[i].split('=');
          vars.push(pair[0]);
          vars[pair[0]] = unescape(pair[1]);
        }
        return vars;
      }&lt;/PRE&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;However, the call to `parseQueryString` errors out IF any of the data being sent back (from Authorize) contains an ampersand (&amp;amp;) back in the "response" parameter.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;This is happening specifically because Authroize's response is both a standard URL with one of the parameter values being a JSON string.&amp;nbsp; &lt;STRONG&gt;IE: action=transactResponse&amp;amp;response={ JSON string here }&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If any value in the JSON string has a "&amp;amp;" in it, then the parseQueryString function will split the JSON string in two.&amp;nbsp; It so happens that my clients company name is "Blah Bar &amp;amp; Grill" - and Authorize was sending back the response as:&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;action=transactResponse&amp;amp;response={ ....{ .... "company" : "Blah Bar &amp;amp; Grill", "address": "123 Main Street" .... }, .....}


Which would then be split into the following parameters into the array "vars" as:
[ 
     [ "action" : "transactResponse"],
     [ "response" : "{ ....{ .... "company" : "Blah Bar " ]
     [ " Grill", "address": "123 Main Street" .... }, .....} ]
]&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;So, anyone trying to use the full "response" value - and convert it back to a proper JSON, it would fail:&amp;nbsp; &lt;STRONG&gt;&amp;nbsp;var res = JSON.parse(params["response"] ;&amp;nbsp;&lt;/STRONG&gt;&amp;nbsp;because after splitting on the "&amp;amp;", params{[response'] was not a proper json string anymore.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Thus any "&amp;amp;" inside the JSON string would be split further and further.&amp;nbsp;&lt;FONT color="#FF0000"&gt;&lt;STRONG&gt; Authorize needs to send a fully encoded URI/URIComponent repsonse back OR send back a proper JSON.&amp;nbsp; But mixing the json string into a URL type response is bad form that leads to this kind of problem.&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;To fix the issue, I had to rewrite the above Authorize.net provided functions to the following - its not the best, or cleanest, but at the moment it seems to do the trick.&amp;nbsp; It also accounts for additional URL params specificed after the "response={JSON string}" if they were ever to exist:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;    AuthorizeNetPopup.onReceiveCommunication = function (str) {
      var params = parseResponse(str) ;
      switch (params.action) {
        case "successfulSave":
          AuthorizeNetPopup.closePopup();
          break;
        case "cancel":
          AuthorizeNetPopup.closePopup();
          break;
        case "transactResponse":
          &lt;FONT color="#FF0000"&gt;&lt;STRONG&gt;httpReceipt(params.response) ;&lt;/STRONG&gt;&lt;/FONT&gt;  // my code to send the JSON to my backend to record the transaction data into my DB.
          AuthorizeNetPopup.closePopup();
          break;
        case "resizeWindow":
          var w = parseInt(params.width) ;
          var h = parseInt(params.height) ;
          var ifrm = document.getElementById("iframeAuthorizeNet");
          ifrm.style.width = w.toString() + "px";
          ifrm.style.height = h.toString() + "px";
          centerPopup();
          break;
      }
    };

      function parseResponse(str) {
        var resInfo = [], res = {} ;
        if (str.match("&amp;amp;response=")) {
          var params = str.split("&amp;amp;response=") ; // leading params &amp;amp; response obj
          if (params[1].match("}&amp;amp;")) {
            resInfo = params[1].split("}&amp;amp;") ;  // splits off anything at end of obj
            res.response = JSON.parse(resInfo[0] + "}") ;  // add } to complete obj string again
          } else {
            res.response = JSON.parse(params[1]) ;
          }
          if (resInfo[1]) {
            params = params[0]+ "&amp;amp;" +resInfo[1] ;  // join url params back together  
          } else {
            params = params[0] ;
          }
        }  else {
          params = str ;
        }
        params = new URLSearchParams(encodeURI(params)) ;  //encode then parse
        params.forEach(function(value, key) {
          res[key] = value ;
        }) ;

        return res ;
      } &lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;I bring this to everyones attention because both of the original functions come straight from Authorize.nets API Accept Hosted page documentation.&amp;nbsp; It means Authorize.nets own functions, can't handle Authorize.nets own transaction response if the there is an additional "&amp;amp;" anywhere inside the "response={ JSON STRING}" paraemter.&amp;nbsp;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;</description>
      <pubDate>Thu, 11 Feb 2021 19:40:13 GMT</pubDate>
      <guid>https://community.developer.cybersource.com/t5/Integration-and-Testing/Accept-Hosted-onReceiveCommunications-function-causes-error-on/m-p/75000#M46699</guid>
      <dc:creator>rolinger</dc:creator>
      <dc:date>2021-02-11T19:40:13Z</dc:date>
    </item>
  </channel>
</rss>

