this code was give to me by the vendor, & when using it, it comes back from the server with an error saying it does not match what was sent?? any help would be greatly appreciated. This is the complete sample code:

This may sound trivial, but have you requested a sandbox account and have replaced the dummy values in the above script with those you are provided, i.e. source key.

The payment provider will rebuild the hash on their end after retrieving the secret pin for the supplied source key and extracting the seed from the hash parameter etc, then test that these hashes (encrypted strings) are equal to each other.

Hi, Yes I have account & key & Pin and I put it together with my values from the cart. I also put the cart in debug mode so I can see the return, that is how i know it is the hash!

I am thinking that there is something in the way the hash is put together that maybe wrong? my other subs handling other vendors works fine, in fact this one works fine when the hash is not used (turned off) in the sand.box? I even tried using the MD5 hash that I use in the other subs & got the same result?

I have looked but can't find the error, maybe I am missing something or just getting stupid in my old age?:)

It appears the code is supplied by the vendor ( http://wiki.usaepay.com/developer/perl ). In the past I have found Perl versions supplied by payment providers to be inadequate, I suppose they attempt to support multiple languages but their developers aren't necessarily savvy in them all, as you stated "The code looks like it was written by a php coder with limited experience in perl". Perhaps it would be worth the OP's time to try using the Business::OnlinePayment::USAePay module if they have no luck from contacting usaepay.

As far as I know, the payment provider does not have to decrypt the hash on their end and using a hashing algorithm that cannot easily be decrypted would be most secure in keeping the private key a secret. In this case it appears the pin is the private key which the payment provider can obtain from their database via the source key. All data other than the pin is posted unencrypted via the UM* parameters, therefore available without decryption. They must do something very roughly along the lines of:

I called there so called tech support & she keeps referring me back to the sample:( so I told her I need to speak to a tech because something is not working right!

I think it is because there script is PHP & mine is Perl and it is handling the hash differently: ie mine is sending it from Perl & theirs is constructing the hash in PHP & trying to match mine in Perl?

The code Fishmonger provided showed a more modern approach to the techniques used in the sample script, effectively they do the same thing therefore wasn't meant to be a fix.

Quote

I called there so called tech support & she keeps referring me back to the sample:( so I told her I need to speak to a tech because something is not working right!

Asking to talk to a tech was the right thing to do in my view. I have suffered from similar issues in the past and they were able to tell me in exact detail why the hashes didn't match (in my case it was because of carriage returns in line endings i.e. \r\n is not equal to \n).

Quote

I think it is because there script is PHP & mine is Perl and it is handling the hash differently: ie mine is sending it from Perl & theirs is constructing the hash in PHP & trying to match mine in Perl?

Regardless of the scripting languages you use to construct the hash and they use to reconstruct the hash this "shouldn't" be a concern. They will rebuild the strings in a similar manner and use the same hashing algorithm etc.

Quote

The only problem with that is they check the Hash on there end and only say whether it is valid or not! They should send their hash so I can match it on my end!

This would not be a good idea in terms of security. If they sent the hash back to an attacker, the attacker could write a script which speedily iterates through a list of pins until the hashes match, and therefore have discovered your secret pin.

In your position I would do the following: - Double check that the information such as source key you are providing are indeed correct against your sandbox account. If for example you are reading these in from a file then make sure the line endings have been removed etc. - Attempt to talk to tech as you have already tried doing, who can perhaps decipher exactly why yours and their hashes don't match. - Try using a different approach, I pointed to an sdk module for usaepay in my previous post, it appears to use a similar technique to the third provided by usaepay on their Perl page.

Inevitably it is difficult to provide definitive help without being able to test your exact code, with your exact credentials. Could you possibly provide the exact error message being returned.

Alright, that does indeed confirm that it is reporting the hashes do not match as per http://wiki.usaepay.com/developer/errorcodes. Are you hardcoding the pin, command, amount, invoice for testing purposes as they have done in the sample script, or are you fetching them from elsewhere, could you provide the exact code you are using ( obviously anonymise any private information you don't want us to know ).

Had a look over your code and it appears mostly fine except for some outdated approaches. I just wanted to point out a couple of potential issues:

- As per the sample code, for now generate your seed using rand not time. I'm concerned that there is a possibility that the payment provider will validate this in some manner and may not consider the seed generated by time as acceptable. You never know.

- I noticed you are sending a content type of application/x-www-form-urlencoded but it doesn't appear as far as I can tell that you have comprehensively url encoded all the values in your post data. You can use the URI::Escape module to do this or supply the data as a hash to URI::Query and call the stringify method on the object etc etc etc. There is a possibility that special characters in your values are breaking your post data.

- Not use any globally scoped variables and pass any lexically scoped variables derived from outside the subroutine to the subroutine. I try to keep subroutines completely self contained except for rare cases.

- Build the request parameters into a hash of array data structure then convert this to a URI escaped query string last thing ( preferably via URI::Query ).

- Check the response was successful before continuing via LWP::UserAgent's is_success method as per the modules synopsis.

- Use CGI to parse response data. There are more modern approaches that aren't as low level as CGI such as Plack, but I'm familiar with CGI and have spent years developing my own set of CGI wrapper modules that take care of the many issues CGI has.

- Keep HTML seperate from code using a template module such as Template.

- Not necessarily call &usaepay_ord from &usaepay_cc, instead return the necessary variables back to the caller and call it from there.

- Not call subroutines using the & prefix unless I desire the side effects.

I was about to post a detailed recommendation on how I'd recode your sub but, while I was driving home from work, Chris beat be to it.

I will add one thing, your code indentation/formatting is inconsistent which makes it more difficult to read and maintain. One of the first things I'd do would be to use Perl::Tidy to clean it up so that I can more easily see what it does and what needs to be changed.

Chris already mentioned using a hash structure to build your query string and here's part of the reason why. When I saw your if/else block that assigns $postdata, I found it difficult to see the differences in the assignment. When I copied those 2 300+ character strings were exactly the same, which means using an if/else block for that assignment doesn't make any sense.

As a direct consequence of what Fishmonger pointed out, I had missed that you are also posting the parameter UMcommand_type, whereas I believe you meant to use UMcommand as per the sample. Inevitably, since the command is part of the hash, and you don't provide this value, the payment provider will not be able to generate a matching hash. Fixing this and URI encoding the values to be on the safe side will hopefully fix your issue.