I have been having a hell of a time sorting out PayPal’s documentation, as all of it applies to ASP but not MVC (including their otherwise-handy Integration Wizard). I have seen oft-reference guide by Rick Strahl, but it is also for ASP, and I have no experience with Webforms to translate into MVC.
I am stuck on one part, and have a security concern about another.
First: how do you actually submit the request to the paypal api? The documentation tells you to use a form with your password in it.
<form method=post action=https://api-3t.sandbox.paypal.com/nvp>
<input type=hidden name=USER value=API_username>
<input type=hidden name=PWD value=API_password>
<input type=hidden name=SIGNATURE value=API_signature>
<input type=hidden name=VERSION value=XX.0>
<input type=hidden name=PAYMENTREQUEST_0_PAYMENTACTION
value=Sale>
<input name=PAYMENTREQUEST_0_AMT value=19.95>
<input type=hidden name=RETURNURL
value=https://www.YourReturnURL.com>
<input type=hidden name=CANCELURL
value=https://www.YourCancelURL.com>
<input type=submit name=METHOD value=SetExpressCheckout>
</form>
Surely this form isn’t going into the View where anyone with the sense to check your source could steal your login info? I would assume this needs to be done from the controller, but I don’t know how to create do this from the controller. HttpWebRequest and WebClient look promising, but I don’t know how to actually add a form to them.
Second: even if I did make this form and api call from inside the controller where the user can’t see it, anyone with access to the source code (like the web host, or other developers) would be able to see the password. This doesn’t seem like good security. What’s the practice here? How can this be made secure?
EDIT
For the people who come looking, this is how I eventually submitted the initial request (condensed the code into one block for readability)
public static string GetResponse(RequestContext context, decimal price)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api-3t.sandbox.paypal.com/nvp");
//HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api-3t.sandbox.paypal.com/nvp");
request.Method = "POST";
UrlHelper url = new UrlHelper(context);
string urlBase = string.Format("{0}://{1}", context.HttpContext.Request.Url.Scheme, context.HttpContext.Request.Url.Authority);
string formContent = "USER=" + System.Configuration.ConfigurationManager.AppSettings["paypalUser"] +
"&PWD=" + System.Configuration.ConfigurationManager.AppSettings["paypalPassword"] +
"&SIGNATURE=" + System.Configuration.ConfigurationManager.AppSettings["paypalSignature"] +
"&VERSION=84.0" +
"&PAYMENTREQUEST_0_PAYMENTACTION=Sale" +
"&PAYMENTREQUEST_0_AMT=" + String.Format("{0:0.00}", price) +
"&RETURNURL=" + urlBase + url.Action("Confirm", "Checkout") +
"&CANCELURL=" + urlBase + url.Action("Canceled", "Checkout") +
"&METHOD=SetExpressCheckout";
byte[] byteArray = Encoding.UTF8.GetBytes(formContent);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
WebResponse response = request.GetResponse();
dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = HttpUtility.UrlDecode(reader.ReadToEnd());
reader.Close();
dataStream.Close();
response.Close();
return responseFromServer;
}
AFAIK, Paypal also provides a webservice… instead of just posting data.
You can make a POST request from your controller, allowing to hide the sensitive data from users (all those hidden values).
Here you can see an example of posting your data from code: http://msdn.microsoft.com/en-us/library/debx8sh9.aspx
About your second concern, you can have those parameters that are sensitive encripted in a web.config, and that way only on runtime you have those parameters readable.
PayPal also provides a Sandbox, for you to test your integration… so at that moment you could have this values without encripting. Once you move your app to production, replace the test parameters with your encripted production credentials.