9 Replies Latest reply on May 31, 2006 8:34 AM by Steve Sommers

    Return querystring from Payment Processor

    Level 7
      Hi, I'm struggling with something that doesn't make any senses. Hopefully,
      someone has seen something similar before.

      I'm decoding a return value from a payment processor (Protx). The value is
      returned as an encoded value in a querystring. Here's a sample (I've
      manually broke it, but it is actually one long string):

      test.cfm?crypt=AxcOGx89dnl9cDcHUhM+HBQGGw4DInZlQzUHFkAULRo8DxZPKzs/XlkkDQBWA2s7IgIBHAst
      P19ZOEIlVgkvACI3FywFKi4LA2dUQwJBHT8DNxcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVT
      XStcfQ4DBWVWDhUzMy4lFwchBXN8BQJvQjJeCD4BJF5aQVN3bXdgBSclAVoKIxxDIi4+DQMQdzIAAVYUO
      D01EBoDHnMGd2IVLDZ3QRsAIxcsAA4rGVNFIwgHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptcV8wEDJ
      aA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0LexgoKQoxADUBV1w1W35yBH4dUikBMA==

      It is decoded with this call:

      Decoded = SimpleXor(base64Decode(strCrypt),EncryptionPassword);

      If I MANUALLY copy and paste the crypt string, i.e. strCrypt =
      "AxcOGx89dnl9cDcHUhM+HBQGGw4 etc, etc ";

      and then run the Decoded function call above, the decode works correctly,
      returning:

      Status=OK&StatusDetail=Successfully Authorised
      Transaction&VendorTxCode=51001&VPSTxId={4011AA34-0DF9-4346-BCB3-FE02D63E5332}&TxAuthNo=73 49&Amount=5.99&AVSCV2=ALL
      MATCH&AddressResult=MATCHED&PostCodeResult=MATCHED&CV2Result=MATCHED&GiftAid=0&3DSecureSt atus=OK&CAVV=MNLZ9VKZQ43Z1092HK6Z2W

      Of course, that isn't possible. If I use the obvious: strCrypt = URL.crypt;

      the Decoded string becomes:

      Status=OK&StatusDetail=Successfully Authorised
      Transaction&VendorTxCode=51001&VPSTxId={4011AA34-0DF9-4346-BCB3-FE02D63E5332}&TxAuthNo=73 49&Amount=5.99&AVSCV2=ALL
      MAi?+?V1?=h+V??2?F???{??Kl??;?"?J�????'?H(zc?=???{?m?�;?To????A?5?+?'?k?u{??\<?Q&??mA-?#b /??F`??{?g?
      #

      I've tried lots of different combinations to get the string decoded
      properly, but nothing works. What changes between the actual querystring as
      viewed in the address bar of the browser and CF grabbing it? Can I do
      anything to stop CF changing it? This is a standard querystring returned to
      thousands of systems, so it has to be something in CF. The code used is
      provided by Protx as standard.

      Any help much appreciated.


        • 1. Re: Return querystring from Payment Processor
          MikerRoo Level 1
          You didn't say what CF version you were using.

          Anyway, use the URLDecode function. You may also need to specify the correct charset.
          • 2. Return querystring from Payment Processor
            Steve Sommers Level 4
            My guess is that the + in the string is causing the problem. URL decoding changes the + to a space and I believe CF does the URL decoding for you automatically (depending on the version you are running). You may be able to simply replace all the spaces with +'s since spaces are not valid B64 encode characters. You may also have similar issues with the trailing = signs.

            To confirm this, place a <!-- #string# --> in your code and view the page source (simply displaying it on the screen may hide some of the text, viewing in notepad is more reliable for this stuff). Do this for the URL parameter test and the cut & paste test and compare.

            Hope this helps.

            On a side note, XOR encryption/decryption is a VERY weak form of encryption and will not comply with any of the card association regs. Hopefully something stronger is being used in the production environment.
            • 3. Re: Return querystring from Payment Processor
              Level 7

              "MikerRoo" <webforumsuser@macromedia.com> wrote in message
              news:e5i720$a0r$1@forums.macromedia.com...
              > You didn't say what CF version you were using.
              >
              > Anyway, use the URLDecode function. You may also need to specify the
              > correct charset.
              >

              MX7 (7,0,1,116466)

              This happens both on my development server and at the host (CrystalTech).

              I've tried the URLDecode function, with UTF-8 and ISO-8859-1; neither makes
              any difference, regrettably.

              Any other ideas?
              Cheers,


              • 4. Re: Return querystring from Payment Processor
                Level 7

                "Steve Sommers" <steve@shift4.com> wrote in message
                news:e5i79q$a8d$1@forums.macromedia.com...
                > My guess is that the + in the string is causing the problem. URL decoding
                > changes the + to a space and I beleive CF does the URL decoding for you
                > automatically (depending on the version you are running). You may be able
                > to
                > simply replace all the spaces with +'s sing spaces are not valid B64
                > encode
                > characters. You may also have similar issues with the trailing = signs.
                >
                > To confirm this, place a <!-- #string# --> in your code and view the page
                > source (simply displaying it on the screen may hide some of the text,
                > viewing
                > in notepade is more reliable for this stuff). Do this for the URL
                > parameter
                > test and the cut & paste test and compare.
                >
                > Hope this helps.
                >
                > On a side note, XOR encryption/decryption is a VERY weak form of
                > encryption
                > and will not comply with any of the card association regs. Hopefully
                > something
                > stronger is being used in the production environment.
                >

                Thanks for the reply.

                This is the base64 function:

                function base64Decode(scrambled) {
                // Initialise output variable
                output = "";

                // Fix plus to space conversion issue
                scrambled = Replace(scrambled," ","+");

                // Do decoding
                output = toString(toBinary(scrambled));

                // Return the result
                return output;

                }

                I've already got a test page outputting various options:

                <cfscript>
                crypt1 =
                "AxcOGx89dnl9cDcHUhM+HBQGGw4DInZlQzUHFkAULRo8DxZPKzs/XlkkDQBWA2s7IgIBHAstP19ZOEIlVgkvACI3 FywFKi4LA2dUQwJBHT8DNxcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVTXStcfQ4DBWVWDhUzMy4lFwchBX N8BQJvQjJeCD4BJF5aQVN3bXdgBSclAVoKIxxDIi4+DQMQdzIAAVYUOD01EBoDHnMGd2IVLDZ3QRsAIxcsAA4rGVNF IwgHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptcV8wEDJaA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0LexgoKQ oxADUBV1w1W35yBH4dUikBMA==";
                crypt2 = "AxcOGx89dnl9cDcHUhM
                HBQGGw4DInZlQzUHFkAULRo8DxZPKzs/XlkkDQBWA2s7IgIBHAstP19ZOEIlVgkvACI3FywFKi4LA2dUQwJBHT8DN xcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVTXStcfQ4DBWVWDhUzMy4lFwchBXN8BQJvQjJeCD4BJF5aQVN 3bXdgBSclAVoKIxxDIi4
                DQMQdzIAAVYUOD01EBoDHnMGd2IVLDZ3QRsAIxcsAA4rGVNFIwgHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptc V8wEDJaA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0LexgoKQoxADUBV1w1W35yBH4dUikBMA==";
                crypt3 = URLDecode(URL.crypt,"ISO-8859-1");
                crypt4 = CGI.QUERY_STRING;
                crypt5 = URL.crypt;

                // ** Decode the crypt field that was sent back to us **
                Decoded1 = SimpleXor(base64Decode(crypt1),EncryptionPassword);
                Decoded2 = SimpleXor(base64Decode(crypt2),EncryptionPassword);
                Decoded3 = SimpleXor(base64Decode(crypt3),EncryptionPassword);
                Decoded4 = SimpleXor(base64Decode(crypt4),EncryptionPassword);
                Decoded5 = SimpleXor(base64Decode(URL.crypt),EncryptionPassword);
                </cfscript>

                <body>
                <h3>Crypt1</h3>
                <p><cfoutput>#Decoded1#</cfoutput></p>

                <h3>Crypt2</h3>
                <p><cfoutput>#Decoded2#</cfoutput></p>

                <h3>Crypt3</h3>
                <p><cfoutput>#Decoded3#</cfoutput></p>

                <h3>CGI</h3>
                <p><cfoutput>#CGI.QUERY_STRING#</cfoutput></p>

                <h3>Crypt4</h3>
                <p><cfoutput>#Decoded4#</cfoutput></p>

                <h3>Crypt5</h3>
                <p><cfoutput>#Decoded5#</cfoutput></p>

                > You may also have similar issues with the trailing = signs.

                So you think I should do a replace on = as well?

                Any other ideas?

                > On a side note, XOR encryption/decryption is a VERY weak form of
                > encryption
                > and will not comply with any of the card association regs. Hopefully
                > something
                > stronger is being used in the production environment.

                This is actually the only method available with the payment processor.
                However, the credit card details are not obtained until the user is on the
                Protx site and are never passed in the URL variables.


                • 5. Re: Return querystring from Payment Processor
                  Steve Sommers Level 4
                  Assuming that crypt1 and crypt2 are your cut & paste and URL examples -- then simply replacing the spaces with + signs should work: crypt2=Replace(crypt2," ","+","ALL")
                  • 6. Re: Return querystring from Payment Processor
                    MikerRoo Level 1
                    You do not want to strip + or / -- those are vital base64 characters.

                    Also, the sample URL "test.cfm?crypt=AxcOGx89dnl9cDcHUhM+HBQGGw4DInZlQzUHFkAULRo8DxZPKzs/XlkkDQBWA2s7IgIBHAst
                    P19ZOEIlVgkvACI3FywFKi4LA2dUQwJBHT8DNxcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVT
                    XStcfQ4DBWVWDhUzMy4lFwchBXN8BQJvQjJeCD4BJF5aQVN3bXdgBSclAVoKIxxDIi4+DQMQdzIAAVYUO
                    D01EBoDHnMGd2IVLDZ3QRsAIxcsAA4rGVNFIwgHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptcV8wEDJ
                    aA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0LexgoKQoxADUBV1w1W35yBH4dUikBMA=="
                    is not valid.

                    Are you taking the Protx return value and then calling a template with it?
                    If so, you need to use URLEncodedFormat() like so:
                    <CFSET decodeURL = "test.cfm?crypt=" & URLEncodedFormat (ProtxReturnValue)>

                    With CF7 you do not need to URL decode in test.cfm.

                    Finally, SimpleXor and base64Decode are not coldfusion functions. You would use BitXor and BinaryDecode.
                    • 7. Re: Return querystring from Payment Processor
                      Level 7
                      "MikerRoo" <webforumsuser@macromedia.com> wrote in message
                      news:e5iocd$7$1@forums.macromedia.com...
                      > You do not want to strip + or / -- those are vital base64 characters.

                      OK.

                      >
                      > Also, the sample URL
                      > "test.cfm?crypt=AxcOGx89dnl9cDcHUhM+HBQGGw4DInZlQzUHFkAULRo8DxZPKzs/XlkkDQBWA2s7
                      > IgIBHAst
                      >
                      > P19ZOEIlVgkvACI3FywFKi4LA2dUQwJBHT8DNxcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVT
                      >
                      > XStcfQ4DBWVWDhUzMy4lFwchBXN8BQJvQjJeCD4BJF5aQVN3bXdgBSclAVoKIxxDIi4+DQMQdzIAAVYU
                      > O
                      >
                      > D01EBoDHnMGd2IVLDZ3QRsAIxcsAA4rGVNFIwgHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptcV8wE
                      > DJ
                      > aA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0LexgoKQoxADUBV1w1W35yBH4dUikBMA=="
                      > is not valid.

                      No, that's because I put in some hard returns; this is the working URL.crypt
                      (if I copy if from the address bar):
                      AxcOGx89dnl9cDcHUhM+HBQGGw4DInZlQzUHFkAULRo8DxZPKzs/XlkkDQBWA2s7IgIBHAstP19ZOEIlVgkvACI3Fy wFKi4LA2dUQwJBHT8DNxcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVTXStcfQ4DBWVWDhUzMy4lFwchBXN8 BQJvQjJeCD4BJF5aQVN3bXdgBSclAVoKIxxDIi4+DQMQdzIAAVYUOD01EBoDHnMGd2IVLDZ3QRsAIxcsAA4rGVNFIw gHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptcV8wEDJaA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0LexgoKQox ADUBV1w1W35yBH4dUikBMA==

                      The crypt string above is being returned to my page by Protx. I have no
                      control over it; they provide functions to decode the string into valid
                      fields. If I manually paste in the above string, it works. If I use
                      URL.crypt, it doesn't. The problem therefore is that something is happening,
                      presumably only with CFMX7, to the qs before it is passed to the routine.

                      > Are you taking the Protx return value and then calling a template with it?
                      > If so, you need to use URLEncodedFormat() like so:
                      > <b><CFSET decodeURL = "test.cfm?crypt=" & URLEncodedFormat
                      > (ProtxReturnValue)></b>

                      That fails with an error: The parameter 1 of function ToBinary, which is now
                      "AxcOGx89dnl9cDcHUhM%20HBQGGw4DInZlQzUHFkAULRo8DxZPKzs%2FXlkkDQBWA2s7IgIBHAstP19ZOEIlVgkv ACI3FywFKi4LA2dUQwJBHT8DNxcmDnMwAgZnVTJyVH9CYCcpVkd6eAIAeyYwcVRmKRVTXStcfQ4DBWVWDhUzMy4lFw chBXN8BQJvQjJeCD4BJF5aQVN3bXdgBSclAVoKIxxDIi4%20DQMQdzIAAVYUOD01EBoDHnMGd2IVLDZ3QRsAIxcsAA 4rGVNFIwgHDioKOxMrKitMDR0EZDMXBl8TdiIRNywnLwptcV8wEDJaA3ZfdlArPA8tPkRTBRASRxI4Uh8oSSwrGB0L exgoKQoxADUBV1w1W35yBH4dUikBMA%3D%3D"
                      must be a Base-64 encoded string.

                      > Finally, SimpleXor and base64Decode are not coldfusion functions. You
                      > would
                      > use BitXor and BinaryDecode.

                      No, they are functions provided by Protx. I've tried the native functions,
                      but they fail (BitXor in particular as that needs two integer values; the
                      password is a string).

                      Here's the simpleXor function:

                      function simpleXor(InString, Key) {

                      // Initialise key array
                      KeyList = ArrayNew(1);
                      // Initialise output variable
                      output = "";

                      // Convert $Key into array of ASCII values
                      // ColdFusion arrays are indexed at 1, not 0. Smart!
                      for(i = 1; i LTE Len(Key); i=i+1){
                      KeyList = Asc(Mid(Key, i, 1));
                      }

                      for(i = 0; i LTE Len(InString)-1; i=i+1){
                      // Get ASCII code from string, get ASCII code from key (loop through with
                      MOD), XOR the two
                      result = (bitXor((Asc(Mid(InString, i+1, 1))), (KeyList[(i MOD
                      Len(Key))+1])));

                      // Horrible hack to avoid problem with chr(0)
                      // If result is 0...
                      if (result eq 0){
                      // ...add the null character to output
                      output = output & urldecode("%00");
                      } else {
                      // ...add the specified character to output
                      output = output & Chr(result);
                      }

                      }

                      // Return the result
                      return output;

                      }

                      For completeness, here's the base64Decode function also:

                      function base64Decode(scrambled) {
                      // Initialise output variable
                      output = "";

                      // Fix plus to space conversion issue
                      scrambled = Replace(scrambled," ","+");

                      // Do decoding
                      output = toString(toBinary(scrambled));
                      // output = ToString(BinaryDecode(scrambled,"Base64"));

                      // Return the result
                      return output;

                      }


                      • 8. Re: Return querystring from Payment Processor
                        Level 7

                        "Steve Sommers" <steve@shift4.com> wrote in message
                        news:e5imfv$r9f$1@forums.macromedia.com...
                        > Assuming that crypt1 and crypt2 are your cut & paste and URL examples --
                        > then simply replacing the spaces with + signs should work:
                        > crypt2=Replace(crypt2," ","+","ALL")
                        >

                        Got that in the base64Decode function:

                        function base64Decode(scrambled) {
                        // Initialise output variable
                        output = "";

                        // Fix plus to space conversion issue
                        scrambled = Replace(scrambled," ","+");

                        // Do decoding
                        output = toString(toBinary(scrambled));
                        // output = ToString(BinaryDecode(scrambled,"Base64"));

                        // Return the result
                        return output;

                        }


                        ... but, my Replace function call was missing a crucial "ALL" !!! With that
                        in place, it works perfectly. Thanks for that; can't believe I missed it,
                        but something so simple could bugger it up so completely!

                        Thanks again <g>.


                        • 9. Re: Return querystring from Payment Processor
                          Steve Sommers Level 4
                          quote:

                          Originally posted by: Newsgroup User

                          ... but, my Replace function call was missing a crucial "ALL" !!! With that
                          in place, it works perfectly. Thanks for that; can't believe I missed it,
                          but something so simple could bugger it up so completely!

                          Thanks again <g>.

                          Yeah, that bites me now and then as well as many others who have posted issues on this forum. I really think defaulting the scope to "ONE" was a design flaw since almost every language other than ColdFusion only has an "ALL" version of the replace function.

                          Glad it worked.