CodersBarn.com
The ASP.NET Community Blog

Solution to ASP.NET Form - PayPal Problem

March 8, 2008 17:37 by agrace

Pondering... I recently started work on my first website to integrate with PayPal. The client needed it done relatively quickly. What started out for me as a mental picture of a products Web form with a "Buy Now" button, somehow turned into a full-blown e-commerce application complete with custom shopping cart. Talk about feature creep! And most of it was my own fault because I failed to anticipate the minimal requirements for the job. I'm still in the thick of it and have just implemented an admin back-end for the merchant to add products, complete with images, to the database. Then, I had to create an HttpHandler to stream the images...

They do say it's the simple things that get you stumped. For all of you ASP.NET developers out there who have yet to integrate a site with PayPal, just wait until you finally get to add that button to your form to pass the transaction details over to PayPal... In short, it won't work! The button HTML from the PayPal site is embedded in a form tag. You can only have one form on an ASP.NET page and ASP.NET provides its own. If you have a master page, then the form tag is in there and it is applied to every page in the site when they are merged with the master at runtime.

Thinking Irishman PayPal support does not offer a practical solution. They may try to get you to download their ASP.NET SDK which is 1.1 and uses Web Services. Most people have failed to get it to work with 2.0. Then they may tell you to put the form tag "outside" the main tag or on a separate HTML page, etc. I have seen endless hacks, most of which were too stupid to even consider; IFrames anyone?!!

I trudged through the forums and saw that ASP.NET developers have been asking how to get around this for the last three years or so. PayPal refuses to acknowledge the problem and seem more inclined to offer support for the PHP community. There is something radically wrong with this mindset from a business point of view.  Can PayPal not afford to pay some contractors to go in and develop an ASP.NET 2.0 SDK that will work with both NVP and Web Services? Nothing like speed to kill. Then PayPal had the temerity to invite me to complete a survey on how good I found their support service...

The Light Goes On So I turned to Google. I spent days concocting search strings that would bring that elusive nugget I needed to solve the problem. I thought I had found it when I came across the nested master page hack - keep the outer master page stripped of any form tag and then just use it for the page with the PayPal button. It would probably work, but if you don't get a code smell from that one, you may need to get your sinuses reamed out. The search continued. You know you're desperate when you start entering your grannie's middle name in the search query string :-O

Persistence finally paid off. I found an elegant solution on Jeremy Schneider's blog that consists of a custom HtmlForm class that can have the form tag rendering toggled on and off. The class is called GhostForm and has a property, RenderFormTag. When RenderFormTag is set to false, it doesn't render the opening or closing tags, but does render all of the contents. Reference the custom GhostForm class and in the code-behind of the form on which you are placing the button, place the following in the Page_Load to disable the master page form tag:

public partial class Products : System.Web.UI.Page
 {
     protected void Page_Load(object sender, EventArgs e)
     {
         GhostForm mainForm = new GhostForm();
         mainForm.RenderFormTag = false;
         .....     
     }
         // Send your data to PayPal :-)
     .....
 }

kick it on DotNetKicks.com   PHP, ASP, .NET, JSP Resources, Reviews


Tags: ,
Categories: ASP.NET | PayPal
Actions: E-mail | Permalink | Comments (44) | Comment RSSRSS comment feed

Comments

April 5. 2008 23:30

pingback

Pingback from blogsurfer.net

.NET on the ‘NET March 10-17: SubSonic Rocks and MVC is Hawt

blogsurfer.net

September 30. 2008 10:43

trackback

Trackback from Soft Yolk Corner

Asp.Net PayPal Button

Soft Yolk Corner

October 29. 2008 02:22

Kim

I always was pro America now never more.
Paypal robbed me 41 euros .
Now I understand all whose hate America.

Kim

October 29. 2008 10:55

agrace

Hey Kim,

I wouldn't know... I'm Irish :-|

Do you want us to pass the hat around?

Anthony Wink

agrace

November 1. 2008 15:45

Dana

I'm wondering which PayPal product were you successful in using? I have an ASP application that I have all the buyer info, product cost, shipping and taxes already calculated, and I just want PayPal to handle the credit card or PP charge processing. I naively thought it would be a simple call to PP passing these items as arguments, but no such luck. I would like to use Website Payments Standard - it seems like it would work for me, but I'm not sure... Does anyone have any suggestions, and would this "elegant solution" you discuss really work? Thanks for any help.

Dana

November 1. 2008 16:24

agrace

Hi Dana,

I simply used the Ghost Form class to get around the ASPNET/PayPal form issue. I wrote some custom code with a cart and hooked up to PayPal in accordance with the Website Payments Standard. Check my other post here:
www.codersbarn.com/.../...tton-with-ASPNET-20.aspx

Anthony Smile

agrace

December 3. 2008 06:08

scott

Jeremy's site seems to be down and I'm in need of the ghost class. Where can I find it?

scott

December 5. 2008 02:29

trackback

Trackback from Web Development Community

Solution to ASP.NET Form - PayPal Problem

Web Development Community

January 6. 2009 22:19

Peter

Keeping to the concept of the simplest possible thing (without hacks):

1. Put your PayPal HTML code into your ASPX
2. Remove PayPal's form tags
3. Change the PayPal image tag to a .NET ImageButton
4. Set its postbackurl to paypal and its image url to the relevant paypal image

Example:

<asp:ImageButton runat="server" PostBackUrl="https://www.paypal.com/cgi-bin/webscr" ImageUrl="www.paypal.com/.../btn_buynowCC_LG.gif" />

Works perfectly with Master Pages too!

Hope this helps

Peter

January 27. 2009 19:35

James Harrison

Hello,  A very good solution to this problem that gives you more ability to create a mini one page shop cart is here:

www.aspcode.net/Creating-a-simple-paypal-shop.aspx

It was a life saver for me. I found myself with exactly the same problem as you described in your opening paragraph.  There is a lot of php support for Pay Pal but none for ASP??

Thanks for posting this page.

James

James Harrison

January 27. 2009 22:49

agrace

James,

That's handy for someone to get up and running quickly, thanks for sharing. However, it presumes that JavaScript is enabled in the browser... just something to be aware of.

Rgds,
Anthony Smile

agrace

March 24. 2009 04:16

ted

Easiest implementation I've found for ASP.NET is to add an image button to the aspx page with an image url referencing the desired button, then on the click event on the code behind page, do a Response.Redirect and use the email option from the code snippets Paypal provides after the button is designed.

ted

April 5. 2009 22:34

Anthony

Peter's solution worked best. Thank you!

Anthony

April 29. 2009 10:51

Jesse

Exactly...  The GhostForm, although is neat and Im glad I learned how to do that (for other reasons) is overkill.

All you have to do is what Peter suggested, strip out the FORM elements of the Paypal code they give you,   use the PostbackUrl attribute of an ASP.NET Image Button and have it "post" to the Paypal url that used to be in the form element.

Works like a charm..  and no overriding the webform behavior.

It does mean you could be sending a bunch mroe date to the PayPal services, but in most cases that should mater, they jsut ignore what they dont care about.

Jesse

May 7. 2009 14:54

Kev

Hi Everyone,

The image button "PostBackUrl" solution looks tempting until you see that it doesn't change the ACTION of the FORM tag markup which comes from the server, but instead uses JS to modify the ACTION on the client...

onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions

I haven't looked into what happens if JS is disabled.

With .net you've gotta keep your eye on the page source.

Hope this helps.

Kev

May 7. 2009 17:23

Kev

Sorry, forgot to say that in any case, .net 3.5 allows you to change the Action attribute of the form tag.

Kev

May 7. 2009 17:49

agrace

Kev,

How can this be utilized to get around the single form tag problem?

Anthony Smile

agrace

May 7. 2009 18:14

Kev

Hi Anthony

Ah... Now there's a problem. I guess re-writing the Action attribute only really works if you run your own shopping basket and can submit a total order to PayPal, so you only need one form.

I'll have a think, but I'm not hopeful. Sorry!

Cheers

Kev

May 7. 2009 20:51

Kev

Hi Anthony,

It's a bit round the houses, but could you put the forms in as literal text, avoiding the parser?

The whole block of form text could perhaps be loaded in from an external file as a further step.

A quick test like the following made two extra form tags in the browser source....

Literal l2 = new Literal();
l2.Text = "<form></form>";

Literal l3 = new Literal();
l3.Text = "<form></form>";
            
body.Controls.Add(l2);
body.Controls.Add(l3);

If you're loading an external file obviously only 1 literal control is needed.

I guess you could also have a public variable in code-behind and use <%= TheFormText %> in the markup, setting TheFormText from a loaded text file.

Since the aspx page isn't posted back to the server that rendered it, the extra form tags shouldn't cause upset.

Regards

Kev

May 7. 2009 20:54

Kev

Forgot to say that "body" is the body tag made into a server control...

<body id="body" runat="server">

Kev

May 7. 2009 20:58

agrace

Kev,

I guess there's only one way to find out if it works, but it does seem like a lot of hassle. I have heard mention already of the new form action attribute in 3.5 but I haven't seen anyone taking advantage of it to get around the PayPal form tag problem. I still think the GhostForm is the easiest way to go...

Thinking out loud: does MVC suffer the same form tag limitation?

Anthony Smile

agrace

May 8. 2009 02:40

agrace

If anyone has some spare time and don't mind getting their hands dirty, this may offer a solution:

www.nikhilk.net/...Mapping-Form-URL-Rewriting.aspx

I've said it before and I'll say it again, Jeremy's solution is the simplest and cleanest approach to take to get around the PayPal form tag problem....

agrace

May 8. 2009 15:51

Kev

Hi there,

You can set set things up to re-write the form action by overiding Render. It's usually used in conjunction with URL-rewwriting so that the form action matches the re-written URL, allowing postbacks to work ok. I think it was mentioned in a ScottGu article about URL rewriting, weblogs.asp.net/.../...ewriting-with-asp-net.aspx.

Unless you do need to submit multiple forms (ie. you don't have a shopping basket on your site and you are using PayPal ready-made buttons), then I'm not sure that solutions which use client code to change the form action are necessarily a good idea as it seems to put the post destination in the hands of the client. I know someone could do it anyway, but the JS solution seems to highlight the opportunity, perhaps in an XSS attack? I'm not asserting this, there just seems something niggling about it.

So that's why I thought the 3.5 form action property setter seems clean and controlled for single form submissions.

Thinking about it again, the idea of inserting the PayPal multiple forms from a single text file via a literal control doesn't seem much effort at all. Each time you wanted to change the PayPal buttons you just paste in the new text into your text file. 1 literal control, 1 text file. Fortunately for me though, I don't need to do this as I only need 1 form tag, .net 3.5 and form1.Action = "destination.aspx".

I suppose it's horses for courses Smile
Cheers,

Kev

May 16. 2009 00:15

Jeremy Schneider

Ha ha... I was just surfing around and took a look at these comments.  I can't believe there's still this much interest over two years later.  Thanks for sticking up for my solution, Anthony Smile

Jeremy Schneider

June 5. 2009 12:22

Richard

Wow i'm so glad I found this page. I had a similar problem with SagePay/Protx. I used Peter's solution mainly because its simple, it works and I code in VB.Net (shock horror!). But I will look at implementing the Ghostform idea in VB.net when I get more time.

Big thanks to Anthony, Jeremy S and Peter...

Richard

June 18. 2009 02:29

RoGca

We are looking for a solution to go around the problem with two forms in a asp.net page and we found here. It is simple and it's working:

www.chyke.com/.../...on-HTML-Form-Post-Issues.aspx

RoGca

August 9. 2009 12:27

mayank goel

hii every one..
i hav a links in a array of string type and names in also in an array of string types.....and i want to make them downloading link with download dialog.....and link name will be from string array and link url take from other arry...
pls u can also mail me at mayank17goel@gmail.com...

mayank goel

August 14. 2009 23:35

Learn Master Guitar

Thank you for sharing this fine piece. Very inspiring! (as always, btw)

Learn Master Guitar

August 23. 2009 12:33

asp.net

This post was a bit of a life saver for me, i have been struggling for about a week to find a good way to implement paypal.

asp.net

September 10. 2009 18:11

Karl

This, like all others, there are a hundred different approaches. Here's my solution, which I think is among the simplest I've seen.

1. Put your PayPal form tag (minus the submit image) outside the main asp form on the master page and give the form an ID.
2. Code a small javascript function to submit the form.
3. From your button onclick event, call the javascript function.

Here's an example.

1. Below the closing form tag on the master page, code ...
<form id="PPLBasic" action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="9999999"/>
</form>

2. Javascript:
        function PayPalSub() {
            document.getElementById("PPLBasic").submit();
        }
3. Submit Button:
<input type="image" src="www.paypal.com/.../btn_subscribe_SM.gif" border="0"
onclick = "PayPalSub2();return false"
alt="PayPal - The safer, easier way to pay online!"/>

Good Luck!
Karl

Karl

September 10. 2009 18:15

Karl

Hmmm...Item 3 above should say onclick = "PayPalSub()", not PayPalSub2();

Karl

September 20. 2009 10:47

comas

Hi.
Will all these solutions work when i get the paypal button with encryption enabled?
Can i just add it to a ASP.NET project?
Thanks

comas

September 20. 2009 11:14

comas

Hi.
Will this work for encrypted paypal buttons?

comas

October 25. 2009 02:11

Mike

Put this above your button code that Paypal gives you.

<form>
</form>

Mike

Mike

October 25. 2009 13:12

agrace

@Mike: and how does that work? Any side effects??

agrace

December 14. 2009 21:02

Eric

Thanks to everyone, this is a great post.  I ended up using Peter's method.  

One extra piece that I wanted was to pre-populate the "amount" field in the PayPal HTML form based on some server-side calculations.  I couldn't do this with normal ASP.NET server-side script since "amount" was an HTML input field, not an asp:TextBox.  So I had to add this line in my PageLoad:

ClientScript.RegisterStartupScript(this.GetType(), "someKey", "document.getElementById('amount').value = '" + balance.ToString("C") + "';", true);

where balance was the variable of type double that I had calculated.

Eric

March 6. 2010 00:51

Mark

James solution is definitely best - simplest is always best.

Even simpler than James solution though is when you create a button in PayPal it gives you two tabs - click the "Email" tab and it provides you with a direct link to pay, rather than form posting to a link - copy the link. Create a custom button or grab the button source from the html in the first tab, create a simple Image Button with the source of your button and the postback URL to the link that you copied from the "Email" tab and set it to open in a new window.

Poof, problem solved. You don't need to use forms at all. All of the variables (email, item name, etc) are all in the querystring of the URL.

btw, just a note about the use of javascript in James' solution: those who don't have javascript enabled.. it's 2010, you can't be having much an experience on the internet these days with js turned off.

Mark

May 27. 2010 14:57

James

I Have to update this post with a solution that is now my go to solution for Pay Pal and ASP.NET.  It involves using remote post. Here is an example Flash online store I created which uses it on the server side: www.manor12.com  .

The first half of the code comes from this post (Thanks to Jigar Desai ):
www.c-sharpcorner.com/.../ASP.NetPostURL.aspx

The second half is my own Pay Pal adoption. What happens is basically you create a RemotePost object, add your variables to it, and call the function Post();  RemotePost sends it to paypal

I hope this is helpful to some developers out there.

James


-----------------------------------------------------------------
public class RemotePost
    {
        private System.Collections.Specialized.NameValueCollection Inputs = new System.Collections.Specialized.NameValueCollection();

       public string Url = "http://www.paypal.com/cgi-bin/webscr";
      
        public string Method = "post";
        public string FormName = "_xclick";
        public void Add(string name, string value)
        {
            Inputs.Add(name, value);
        }
        public void Post()
        {
            System.Web.HttpContext.Current.Response.Clear();
            System.Web.HttpContext.Current.Response.Write("<html><head>");
            System.Web.HttpContext.Current.Response.Write(string.Format("</head><body onload=\"document.{0}.submit()\">", FormName));
            System.Web.HttpContext.Current.Response.Write(string.Format("<form name=\"{0}\" method=\"{1}\" action=\"{2}\" >", FormName, Method, Url));
            for (int i = 0; i < Inputs.Keys.Count; i++)
            {
                System.Web.HttpContext.Current.Response.Write(string.Format("<input name=\"{0}\" type=\"hidden\" value=\"{1}\">", Inputs.Keys[i], Inputs[Inputs.Keys[i]]));
            }
            System.Web.HttpContext.Current.Response.Write("</form>");
            System.Web.HttpContext.Current.Response.Write("</body></html>");
            System.Web.HttpContext.Current.Response.End();
        }
    }



RemotePost myremotepost = new RemotePost();  //create RemotePost object

       //add pay pal variables
        myremotepost.Add("business", "PAYPAL@EMAIL.COM");
        myremotepost.Add("site_url", "http://www.YOURDOAMIN.com");
        myremotepost.Add("success_url", "http://www.YOURDOAMIN.com");
        myremotepost.Add("cancel_url", "http://www.YOURDOAMIN.com");
        myremotepost.Add("notify_url", "http://www.YOURDOAMIN.com");
        myremotepost.Add("return_method", "1");
        myremotepost.Add("currency_code", "CAD");
        //myremotepost.Add("lc", "US");
        myremotepost.Add("post_method", "fso");
        myremotepost.Add("no_shipping", "0");
       myremotepost.Add("curl_location", "/usr/local/bin/curl");
       myremotepost.Add("bn", "toolkit-php");
        myremotepost.Add("cmd", "_xclick");
        myremotepost.Add("country_code", "CA");
       myremotepost.Add("comment_header", "Comments");
       myremotepost.Add("continue_button_text", "Continue >>");
       myremotepost.Add("background_color","");
        myremotepost.Add("display_shipping_address", "");
       myremotepost.Add("display_comment", "0");

  //add transaction variables
       myremotepost.Add("first_name", FIRST); //fill in caps with your own strings
       myremotepost.Add("last_name", LAST);
       myremotepost.Add("address1", ADDR1);
       myremotepost.Add("address2", ADDR2);
       myremotepost.Add("city", CITY);
       myremotepost.Add("state", STATE);
       myremotepost.Add("zip", CODE);
       myremotepost.Add("email", EMAIL);
       myremotepost.Add("phone1", PHONE);
       myremotepost.Add("item_name", NAME_OF_ITEM);

       myremotepost.Add("amount", AMOUNT_OF_TRANSACTION);

       myremotepost.Post();  //this function posts to paypal
--------------------------------------------------------------

James

June 7. 2010 08:01

Simon

I agree with Mark above about James' approach - it gets the job done without too many complications.  (Thanks for the updates James).

Simon

June 7. 2010 08:51

Gary

James ... Thanks, you saved me a world of stress !! Smile

Gary

July 1. 2010 23:43

James

Karl's solution did it for me.

Just had to figure out on my own; put the submit button in the first form.

James

July 31. 2010 05:50

HostMonster Review

Cheers for this article, It's great to see another BlogEngine.NET user. Most users these days tend to use Wordpress, but I think BlogEngine is the better system to use.

HostMonster Review

July 31. 2010 07:21

cigarette

A site with news really exciting, congratulations to the author!

cigarette

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading