Using Authorize.NET DPM (Direct Post Method) from ASP.NET Web Forms

While thousands of custom websites with custom checkout procedures process credit cards, relatively few meet PCI compliance standards. Basically, when a credit card number TOUCHES your server, even if you do not store it, your system falls under PCI compliance guidelines which are pretty nasty. The easiest thing to do is never let a credit card number even pass through your server. Many folks use Authorize.NET’s SIM method, but it requires the credit card number passing through your server, setting you up for a likely PCI audit failure. Granted, credit card companies don’t run around auditing small online businesses, but I like doing things right and treating sensitive information with respect.

Authorize.NET DPM seems like a great method. However, I found the documentation at Authorize.NET confusing, and they do not provide an ASP.NET Web Forms example of DPM. So I extracted one from a recent project I worked on. Here are a few key points required to get this to work.

ASP.NET doesn’t appear to support posting a form to an alternate address, but actually, it does. Check out Button’s PostBackURL property. Using this property will allow your app to submit a form directly to Authorize.NET’s server.

The ‘x_relay_url’ that you passed to Authorize.NET to confirm the purchase is called directly from Authorize.NET’s servers to your server to inform your website of purchase success, failure, etc… It should point to your server and a special page that knows how to deal with the data being posted to it. Any output from your relay page is piped through the submission URL at Authorize.NET and displayed in the client’s browser. Basically, the URL the web browser displays is an Authorize.NET URL, but it displays any data from your replay response page. This means you get no cookies (IE no Session variables) from the client browser. This page should perform some validation, and issue very simple HTML/javascript redirect code through your SIM response page. Response.Redirect didn’t work for me.

This has been tested in Visual Studio 2010 / ASP.NET 4.0. Your mileage may vary in other environments. It is written in VB.Net, but should easily convert to C# or your CLR language of choice.

In order for application to compile, please first download Authorize.Net .NET SDKs, click through their EULA, then extract:

AuthorizeNet.dll

AuthorizeNet.Helpers.dll

And place them in the bin/ directory. Then edit the Web.config and replace the AUTHORIZE_NET variables with values from your developer account at Authorize.net. Lastly, make sure Authorize.NET can reach your relay URL (which should be “http://domain/siteroot/SIM.aspx”). This may require running your application in IIS (NOT the Visual Studio development server) and poking a hole through your firewall for testing. See notes in the code for more details, or check the Authorize.NET documentation.

The demo also has error handling and log4net to log any errors to a log.txt file in the site root.

Thanks very much. This is a great example. I can’t get it to work on my site, but that’s not your fault. When authorize.net does a post to my relay page, asp.net is responding with a 302 redirect instead of executing the relay page. I have no idea why, but it’s causing my hair to fall out.

Have you attached your debugger to IIS and put a breakpoint in SIM.aspx? Is the breakpoint even hitting on Page_Load? Are 100% sure you can hit your SIM.aspx URL (or your test site in general) from outside of your firewall?

Also, is the site URL pointing back to https or http link? I recall reading somewhere that Authorize.net may not work with a self-signed certificate on an https site… so for testing, you might need to use http, not https.

Well, I’m not sure how I would be able to debug the relay page. Running the site in the debugger (in Visual Studio 2010) attaches only to localhost. So, I couldn’t find a way to let the external request from authorize.net get to the debugging session. However, perhaps using IIS for the debugging session (rather than VS2010’s debugging web server) would allow me to do that. I didn’t get that far.

My first thoughts were to explicitly turn off the automatic form validation and authorization features so that it wouldn’t try to redirect me back to a login page. That didn’t help, but they probably weren’t turned on in the first place.

I did end up getting past the problem for now. The x_relay_url was being specified with https. It’s not a self-assigned certificate but a genuine Thawte certificate. However, the problem did go away when switching the relay URL to http instead. It’s unlcear to me as to why. However, this will get me moving along for the moment. I can revisit it later if needed. I’m thinking it must actually be something with my site. My site is what’s giving the redirect back, which I found via the server logs. So, I don’t think it actually has anything to do with https other than how my site might be processing it.

At any rate, none of this is probably generally applicable, but I thought I’d mention it “just in case”.

Thanks again for this example. I owe you at least a beer. If you’re ever in Seattle and want to collect on that, let me know!

While working on this, I recall that others have reported problems with https and SIM responses. If memory serves, SIM response on DPM is mostly identical to other Authorize.net SIm replay-response processing techniques, so you can reference this issue on their forums for their other methods. Examples:

I am so hoping this works for my project… I’m at wits end because I know this is not rocket science, nowhere all this MVC stuff is killing me. why on earth has auth.net built the bulk of their class libraries on the MVC framework?

THANK YOU for creating this in good ol’ tried-and-true web forms! I worked for too long on the MVC mess, finally got the OOB functionality, but taking it any further was a nightmare.

As for your nice creation, I did get everything in place and compiling properly, easily etc. However, once submitted the page returns to my Error.aspx page with the following message:
________________________________________________

Error Occurred

Error: Validation failed:(TESTMODE) This transaction has been approved., ResponseCode:1

This error has been recorded. We have been notified automatically.

Your credit card transaction was approved. We will examine the cause of the error immediately, and ensure that your purchase is honored or refunded. We apologize for any inconvenience. If you have any questions, please contact us at XXX.

I checke my Authorize.Net test account, and there is no record of any transactions today.

I believe I have set everything according to your instructions. I do not have, and never have had, an MD5 hash. With my MVC test site, I never needed one either. Is this necessary for the test environment? This is the only deficiency I can identify.

It’s obviously failing on the sr.Validate() call in SIM.aspx.vb. That call helps validate that the response is from Authorize.net and not someone else. To be honest, I’m no Authorize.net expert, but this seems like an important step. So… an MD5 hash would be a good idea in my humble opinion.

I went into my test account settings, generated the hash, applied it to my web.config, and still the same results. Could there be any validation issues in the hidden input type fields (first name, last name, etc.)?
-Steve

I doubt any of the fields would cause what you are seeing. Please check the code and make sure the hash is being passed into the Authorize.NET API correctly. If you still run into problems, you might have to contact Authorize.NET to help you troubleshoot the issue. I’d also search their forums. Note that the SIM API operates very similarly when it comes to validating results, so there should be lots of help out there on this topic.

i am experiencing the same result and hoping you have found a solution.

Error Occurred

Error: Validation failed:(TESTMODE) This transaction has been approved., ResponseCode:1

This error has been recorded. We have been notified automatically.

Your credit card transaction was approved. We will examine the cause of the error immediately, and ensure that your purchase is honored or refunded. We apologize for any inconvenience. If you have any questions, please contact us at XXX.

Hi Steve,
After adding MD5 has configuration to both the web.config and authorize.net testing interface, I am not longer receiving the error message, and I am being forwarded to the Thanks.aspx page, which I think it is the expected result if no errors occur on sim.aspx. This means that th sr.validation was failing because i had not configured the MD5 in either place. so, this is a required configuration to make it work.

Now, on another topic: I noticed that my transactions are not getting reflected on authorize.net sandbox or testing account at authorize.net

Adding “…/Confirm.aspx” to the value of my main URL fails because it would cause other redirects to go to ie: “http://mywebsite.com/confirm.aspx/error.aspx”. So am I to assume I should set my home page of http://mywebsite.com in IIS to http://mywebsite.com/confirm.aspx ?

Excellent post! New to Authorize.Net and your documentation for ASP.Net is far superior. What’s your take on using .Net controls/Validation controls. In an effort to not have anything postback, do I have to resort to JavaScript to check simple things like a zip code being numeric? Your example seems to capture all the “general” fields ahead of the CC fields. Is that the best approach so sensitive data doesn’t go anywhere but the Authorize.net servers?

Yes… that’s exactly why I did it. I prefer to capture as much of the general information before it goes to the server. It also allows me to use as many .NET validation controls as I want on the other pages. If I have a postback to work with, I also be assured that any server side validation will also be processed. I also can record the customer details in a database, and then fill in the authorization numbers when I get the postback from Authorize.NET. Lastly, it’s a pretty typical part of the checkout process to have a final verification page where you enter the CC number.

You probably can get away with using ASP.NET validation controls, but I personally wouldn’t do it since I don’t 100% for sure know the behavior of the validation control, and can’t guarantee it WON’T post back (with possible CC number in tow!)… If you go this route, I would test thoroughly in as many browsers as possible to ensure the built-in ASP.NET client-side validation scripts are sufficient, actually work, and most importantly, DO NOT post back for any reason. It’s risky. It would be safer to use jquery / javascript validations that never post back.

Thanks for taking the stupid out of DPM with your excellent example. I had a question now that this post is more than a year old. On page 31 of the DPM guide, there is a sentence that states “Redirects or frames in the relay script are not recommended because the information might not be transferred properly.”

Have you had any any problems redirecting from sim.aspx to your various pages? In your comments on sim.aspx.vb it appears that you are saving to your db prior to redirect, so I assume you have been fine.

Well, note that I don’t use Response.Redirect. I call my own Sub, EndWithRedirect, which pushes out some simple javascript to redirect the client browser then ending the response stream before the page even renders. I’ve had no problems with this technique, but I’d be sure to test thoroughly regardless.

I imagine calling Response.Redirect wouldn’t work because it redirects through HTTP protocol, not HTML… something that Authorize.Net isn’t going to be able to deal with. However, javascript works fine, and I’d bet a <meta http-equiv=”refresh”> in the header would probably do the trick as well. (http://en.wikipedia.org/wiki/Meta_refresh)

Thanks for this awesome post! It got me up and running in no time and the checkout process is now tightly integrated with my site and works great. There is only one thing that is confusing me in the sample project you posted.

In SIM.aspx, after you have verified that everything went fine, you pull the sr.InvoiceNumber from the SIMResponse and store it in a variable. You then have some code which is commented out which says to ‘Make sure the purchase ID is valid…. if not….’ and then you use some undefined object drPurchase to presumably determine if the purchase ID was valid or not. Can you tell me what this is all about? http://i.imgur.com/0wo8Qon.png

Thanks again. If you have PayPal, send me an email and I’ll donate a few bucks for your efforts.

sorry for the delayed response. (I was setting up a new server and didn’t get email notifications about these comments… resolved now thankfully!)

That commented out code is basically saying “verify that nPurchaseID exists in the database.” Stepping back for a sec… the way I use SIM is to gather as many purchase details as possible from the user before presenting them the page that collects credit card details and posts to Authorize.NET. I store those details in a database, and nPurchaseID is the ID of the record in the database. When the response comes back from Authorize.NET to the SIM page, that commented code (and other code that I removed for clarity) performs additional verification on the response to ensure that the nPurchaseID exists in the database, and that, say, the amount from Authorize.NET matches what the Purchase record expects. If something goes wrong, the system would send me an email, requiring further examination of the situation. If all checks out, the code might then write additional details to the purchase record in the database to indicate that the purchase was successful.

Hi andrew, this is a wonderful piece of code and I am glad I found this at the right time. I completely rely on your demo code now. All is well so far, when I hit ‘Complete the purchase’ the postbackurl is hit but no sign of SIM.aspx being called though x_relay_url is rightly set. What am I missing?

I don’t know enough about your environment to really help here. SIM.aspx must be reachable from the authorize.net servers (IE the public internet)… from the original post:

“…make sure Authorize.NET can reach your relay URL (which should be “http://domain/siteroot/SIM.aspx”). This may require running your application in IIS (NOT the Visual Studio development server) and poking a hole through your firewall for testing.”

The function AddOrderReturnOrderID was tested on localhost and is fine. But the code above produces he following message
“Error Occurred
Error: Fatal error. This error has been logged and you will be contacted shortly.
This error has been recorded. We have been notified automatically.
Your credit card transaction was approved. We will examine the cause of the error immediately, and ensure that your purchase is honored or refunded. We apologize for any inconvenience. If you have any questions, please contact us at XXX.
PurchaseID (if applicable): n/a”.

Please, If by the chance you will see my this. Do you see any problem in code lines above?

You’ve passed along the user facing error information. The error presented to the user is not intended to help you debug your program. I would review the error handling source code and rewrite it to meet your needs. Good luck…

Please advice what it could be. In your code values of Card Number and Expiration code are hard code within code behind. For example
Public ReadOnly Property CardNumber() As String
Get
If strAuthorizeNet_x_test_request.Equals(“TRUE”) Then
Return “4111111111111111”
End If
Return “”
End Get
End Property

I am trying to read those values from form controls like

Public ReadOnly Property CardNumber() As String
Get
Return cardNumberBox.Text.ToString
End Get
End Property

For some reason the value I typed is not submitted. Getting Transaction denied. Response code3.

Glad to hear you fixed the problem. Please note that you should never allow credit card numbers to post back to your site for PCI compliance reasons. This is actually why Authorize.NET DPM exists. The reason you couldn’t see it is because of the PostBackURL property on the button:

Actually I was able to solve the problem only when one more time looked at your code in the downloaded sample. All form fields for posting to Authorized.net MUST be HTML controls but not ASP.NET server controls. By mistake for Card number and Expiration Date in my code were used ASP.NET server controls. As soon as I found out this the problem was fixed.

It is true that fix came after long battle. Programming some times looks like boxing!

Not sure what you are asking. ALL user created fields in Confirm.aspx post to authorize.net and then post to SIM.aspx. You can code whatever you want in to SIM.aspx to communicate values over to Thanks.aspx.

I like to write the transaction details to a database table with an ID field, set the ID field in a Session variable prior to directing the user to Confirm.aspx. I can then reference that Session variable in Thanks.aspx since the user’s browser is communicating with that page. Note that SIM.aspx cannot access session variables for reasons mentioned in the article at the top of this page.

The base code for the presentation layer and add to the form action “https://test.authorize.net/gateway/transact.dll” sandbox URL. The SIM can be renamed to complete.aspx and all the SIM does is redirect for completed or failed orders. Using a Javascript submit button is a security problem.

SIM.aspx will work for this example. Typical programming made this more difficult than needed. The payment screen is now fully customizable using HTML form objects.

I have used your sample project in an aspx app that needed to support DPM – thank you very much. I made many changes to what you did to handle the needs of our site, but the core pages were very helpful.

However, I have what is no doubt an aspx issue that I need to resolve. Perhaps others who also have aspx sites that incorporate DPM can give me some suggestions…

I have, of course, a callback (reply) form that is launched by Authorize.Net. In our case it does nothing whatsoever except to collect the appropriate items from the Request.Form object, and pass them to another page via a response redirect that displays the transaction status.

Like all of the other pages in the site, this page is defined with a Master page for its look and feel, but the status display page is rendered without any of the master page trappings.

In comparing the page source between the status for and another page using the same Master page, there are a number of items missing in the former, as follows:

Unless there is something wrong with the client’s browser (IE javascript disabled or malfunctioning?), the credit card details will not touch your site in the example provided. The credit card will be submitted directly to Authorize.NET via HTTPS. Just because a page generated a form, doesn’t mean it has to post back to that form… it can post anywhere. See my point #1 in the article above.

HOWEVER, it if the page collecting the credit card details isn’t SSL secured, it certainly won’t inspire confidence in your users… so certainly, securing your pages would be a good idea.

In the end, however, if you don’t want to buy an SSL certificate, and you don’t care about maintaining your site’s look through the checkout process, maybe this is what you are looking for?

PS… all this said, there is a way to avoid a “javascript disabled” problem I mentioned above. You can actually create forms that post elsewhere. You can simply change the form element’s action attribute set to the postback URL. Just as everything else, you won’t get any server side validators to run since the page will never post back to your server. I just saw that CyberSource provides an example of how to do this with their Secure Acceptance – Silent Order Post method: http://apps.cybersource.com/library/documentation/dev_guides/samples/sa_sop/csharp_sop.zip

I have been trying to understand the DPM method from authorize.net But I found the information authorize.net has a little confuse, I saw your example and want to know if you can guide me through it please.

I basically need to create my own from and response, but I also need to collect at least the 4 last digits of the credit card in my database for invoices.