I was tasked to use CyberSource as our payment gateway for a project. Unfortunately the CyberSource SDK works only for .NET Framework and not .NET Core. Else this project would have just been a breeze. So because the SDK is out of the picture, I have to tap into the APIs directly. The hardest part is to create the request header in a way that would be authorized by CyberSource. During this process, I found the documentation to be a bit spotty and inconsistent with the on-site API tests. Plus, the documentation examples were geared in Java. Here are the additional resources I used.
- Documentation Source 1: Go to page 19. Samples were provided in Java.
- Documentation Source 2: This one is better, and also included the “official” hashing functions I use in my sample code.
- On-Site API Demo
- My Github Sample Project
- CyberSource C# SDK: To be use as reference.
- Business Center Login (dev): Where to assign your keys and then to check for transactions.
So before you do anything you’ll need to have your merchant Id, key Id, and secret key. Register for one here and once I had my account credentials, the first thing I need to do is create my shared API keys (HTTP Signature method). Instructions for this can be read here. From there, I tested out the keys at their on-site demo. I’ll be focus on the “Process A Payment” portion. I just want to see if my get’s an actual response.
I have posted my sample code on Github, so we’ll be using that as our example. What I notice right off the bat is that I could never get my hash to match the demo, even when I use the official hash generated functions provided by CyberSource in documentation 2. That threw me off for awhile.
Once I minified the body, grab the hash, and in my sample code used a minified body, then the hash from my code started to match the one in the demo. I used this online minifier. I suspect the training line breaks, indentation, and spacing in my code differs from how the online demo does it. In any regard it doesn’t matter, I just wanted to verify that I could somehow match the hash in some fashion just to ensure my Digest and Signature functions are working correctly. In Github, you’ll see my minified body here, and you would put this file in the project’s bin folder. I chose to do it this way, at least for proof-of-concept, so that it’ll be easy for me to control the JSON body, and now have to worry parsing out the JSON. For production, you wouldn’t do it this way. Again, this was just to verified the hash functions against the demo, you don’t need to minify the JSON body.
What you’ll notice is that the documentation states that the following for the signature parameter.
Signature: keyid="6d75ffad-ed36-4a6d-85af-5609185494f4", algorithm="HmacSHA256", headers="host date (request-target) v-c-merchant-id", signature="eQkzhzu8UHEQmpBTVMibdNpPw1aLunmY41ckyLKoOjs="
But you’ll notice that the request variable in the demo looks like
Signature: keyid="6d75ffad-ed36-4a6d-85af-5609185494f4", algorithm="HmacSHA256", headers="host (request-target) v-c-merchant-id", signature="eQkzhzu8UHEQmpBTVMibdNpPw1aLunmY41ckyLKoOjs="
So I went with the live version without “date”. It was the only way for me to get my hash in my sample code to match the online demo. The other key detail which was apparent in documentation 1 is the Signature header variable name-value pair for request-target was specified as :
(request-target): post /pts/v2/payments
when it should be
(request-target): post /pts/v2/payments/
So that threw me off for awhile before I caught that small detail. And finally the small detail in the Signature header variable name-value are separated by a trailing space and a new line in documentation 2. There should not be a trailing space.
So, hopefully this tutorial was helpful for anyone out there. It took me some time to figure out all these nuinaces since its hard to troubleshoot this project since the key-pieces are encrypted and hashed, and due to security, the web API response, if you don’t have everything perfect will just return a 401 Unauthorized response without any message whatsoever. But now that you are able to get the Payment API working, you now have the correct Header in your request to pass the authentication and can tweak it a bit and apply it to all the other API requests and verbs.