Validating payment amounts with WorldPay - c#

We are using WorldPay to process payments for a tiered membership system, for which the payment amount varies dependent upon the membership tier selected.
The payment is passed to WorldPay via a form post from a number of hidden fields, including:
<input type="hidden" name="amount" value="295.00" />
Essentially, the form is submitted via POST to WorldPay and the user follows a number of steps to process their payment. Once complete, the user is redirected to a specified confirmation page.
This appears to be the typical manner in which WorldPay accepts payments. There's an obvious issue here, in that the value of the hidden field could easily be tampered with by anyone with a basic knowledge of HTML. The form is posted directly to WorldPay, so we have no PostBack in which to validate the amount against the membership tier.
We have the option to validate the payment amount when a payment notification is returned to us from WorldPay by routing the callback through a handler before the confirmation page; however, I would like to avoid the situation where user submits a tampered form, pays the incorrect amount and receives no membership, then has to contact the company to have their money returned.
How might we validate that the amount being submitted is correct before processing payment?
Update
It has occurred to me that we have an additional problem whereby, even if we validate the form post server-side, there is nothing stopping a malicious user from spoofing the form post direct to WorldPay.

It is a vulnerability indeed, it can be solved easily using a signature. Check out this link:
http://culttt.com/2012/07/25/integrating-worldpay-into-a-database-driven-website/
This method should be better promoted on the help page, too bad.

One solution I can think of is this, capture the form tag's submit:
<form id="myForm" onsubmit="return validatePayment();">
and then create that JavaScript file that looks like this:
var isValidAmount = false;
function validatePayment() {
if (isValidAmount) { return true; }
// in here you want to issue an AJAX call back to your server
// with the appropriate information ... I would recommend using
// jQuery and so it might look something like this:
$.ajax( {
type: "POST",
url: url,
data: { amount: $("#amount").val(), someotherfield: somevalue },
success: function(data, textStatus, jqXHR) {
// set the flag so that it can succeed the next time through
isValidAmount = true;
// resubmit the form ... it will reenter this function but leave
// immediately returning true so the submit will actually occur
$("myForm").submit();
},
});
// this will keep the form from actually submitting the first time
return false;
}

Related

PayPal: After Check whether subscriber is paid for subscription

I'm creating website using reactjs for frontend and for backend I'm using c# asp .net mvc. I have already integrated paypal subscription in whichevery month fixed amount deducted form his account(which I have already done) by following this link https://medium.com/analytics-vidhya/paypal-subscription-in-react-1121c39b26be. and here is my code.
window.paypal.Buttons({
style: {
shape: 'rect',
color: 'gold',
layout: 'vertical',
label: 'subscribe'
},
createSubscription: function(data, actions) {
return actions.subscription.create({
'plan_id': 'my-plan-id'
});
},
onApprove: function(data, actions) {
alert(data.subscriptionID);
console.log(data)
},
onError: (err) => {
console.log(err)
}
}).render(paypal.current)
but my requirement is when user login first time user must provide his paypal account details and after providing paypal account info check user has sufficient amount in his account proceed to dashboard. But my problem is how to handle if user has no balance in his account or unpaid and in this case after login user redirected to specific page not dashboard.
In short: how to check user is paid or unpaid if paid proceed to dashboard else redirect to specific page.
how to check user is paid or unpaid if paid proceed to dashboard else redirect to specific page.
The answer to this is to consult your database, which should have this information stored (whether a payment has been made, or whether the subscription is current) and allow you to determine what to do based on the information you have.
So your real question must be how to receive notifications from PayPal that a subscription payment has been made. For that, I will refer you to the answer in here: How do you know if a user has paid for a subscription
As for how to match subscription payments to users -- when a subscription is created, you can store its ID associated with your user, which is easiest to do if you activate the subscription from the server as discussed above. You can also pass a custom_id field during subscription creation, which can contain your own user ID for reconciliation.

Secure way to Delete a record in ASP.Net MVC

I want to delete a product from my ASP.Net MVC 5 website. I want to know if adding [AntiForgeryToken] and [Authorize] is enough to secure the Delete operation?
View
<p>Delete: #Model.Name</p>
#using (Html.BeginForm("Delete", "ProductController", FormMethod.Post, new { ProductId = Model.ProductId }))
{
#Html.AntiForgeryToken()
<button type="submit">Delete</button>
}
Controller
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Delete(long ProductId)
{
/* Do I need to check if the logged in User has permission to delete the product?
var product = ProductRepository.Get(Id);
if (product.Creator == User.Identity.GetUserId<long>())
{
ProductRepository.Delete(ProductId);
}
*/
// or, can I avoid the trip to DB and just delete the record?
ProductRepository.Delete(ProductId);
}
Scenario: A hacker registers on my website and create a valid account. Now the hacker views his own product and obviously he has an AntiForgeryToken. Can he now just change the ProductId in the browser and Post a request to delete someone else's Product?
Short answer. That is not enough.
Antiforgery tokens just say that the person making the original page request is the person making the update.
The base authorize attribute just verifies that the user is logged in.
What you are looking for is data security. There's an example of this on microsoft's own site.
What you've stated in your last paragraph, a hacker can sign up for an account create their own list of products and given what you show them in the url could guess legitimate other records to edit
Say you have a url
https://example.com/product/edit/13
what is preventing the user/hacker from guessing at
https://example.com/product/edit/12
or
https://example.com/product/edit/14
Without security at the data level that says what records a user can or can't access/update, you run into a situation where a malicious user could see or edit all kinds of information.
This is the exact scenario that FISERV found to expose other client information
from the article
Hermansen had signed up to get email alerts any time a new transaction
posted to his account, and he noticed the site assigned his alert a
specific “event number.” Working on a hunch that these event numbers
might be assigned sequentially and that other records might be
available if requested directly, Hermansen requested the same page
again but first edited the site’s code in his browser so that his
event number was decremented by one digit.

c# mvc: RedirectToAction() and browser navigation buttons

In my application, i am storing an object into session which is passed to a web service to return data to display in a table. If the session exists, then it will not ask the user to input fresh data. However, if a user selects a link called "New List", then the session data will be cleared and the user prompted to enter new data.
In my code, i have an anchor defined like so:
New List
Which will trigger this Controller Action:
public ActionResult NewList()
{
Session["new_list"] = "y";
return RedirectToAction("List");
}
And then continue to execute this action:
public ActionResult List()
{
if ((string)Session["new_list"] == "y")
{
//clear session variables, load fresh data from API
}else{
//display blank table. Ask user to input data to retrieve a list
}
....
}
Now, the issue i have is when a user navigates away from the list page, and then navigates back with the browser's back button, it is still calling newlist. In the history of the browser, instead of storing List it is storing newlist which is causing the session variable to clear. What can i do to stop this from happening or is there a different mechanism to use in c# mvc that can help me achieve the desired effect.
Your main problem here is that the NewList action uses GET when it should really be a POST.
A GET request is never supposed to alter the state of a resource, but simply return the current state of the resource; while a POST request allows for the altering of a resource.
Because you allow the NewList action to be called with a GET request, the user's browser assumes (quite rightly on its part) that nothing bad/undesired will happen if it simply repeats the request in the future, e.g. when a user uses the back button.
If instead a POST request is issued, a user browser will never re-issue the request without the user confirming they actually intended to re-issue it.
The solution to your problem then is modify this to the standard PRG pattern: POST/Redirect/GET; that is, send a POST request to perform the state change, redirect the user browser to another page, and GET the result page. In this scheme, pressing the back-button would effectively "skip" over the state change action and go the previous page the user was on.
To accomplish this in MVC:
[HttpPost]
public ActionResult NewList()
{
//clear session variables, load fresh data from API
return RedirectToAction("List");
}
public ActionResult List()
{
// whatever needs to happen to display the state
}
This does mean that you can't provide the "New List" action directly as a hyperlink in the page, as these will always issue GET requests. You will need to use a minimal form like so: <form method="post" action="#Url.Action("NewList", "Alert")"><button type="submit">New List</button></form>. You can style the button to look like a normal hyperlink as desired.
The reason it storing NewList is because you are redirecting to "Alert/NewList", and its the string in your URL for making hit to "NewList" Action, So whenever you are try back button the browser gets this "Alert/NewList" URL, hence its making hit to action "NewList".
But now, I am not getting why the session gets clear. because you are initializing the session in "NewList" itself. Still i suggest you to use local-storage to assign values with session.

JavaScript populated DropDown not valid in ModelState

What I'm doing: I have 3 combo boxes. The selection in the first 2 boxes determines the list in the 3rd box. Whenever a selection is made in either of the first 2 boxes, I run some jQuery that makes an $.ajax call to get the list for the 3rd box. The 3rd box is my "Item Description" box.
The problem: The jquery ajax call works. When I pick the values in the first 2 boxes, the Item Descriptions are loaded into the 3rd box. However, when I try to submit the form, the ModelState.IsValid = false and the ModelState error message says, "Item Description is required."
This is from the meta data for my model:
[Required(ErrorMessage = "Item Description is required.")]
public int ItemDescriptionID { get; set; }
I don't want the user to be able to submit the form without selecting a description. I could just validate after submission, but I was hoping MVC would do the validation.
Here's the JavaScript I am using...
function getModels() {
catId = $('#ItemModel_ItemCategoryID').val();
manuId = $('#ItemModel_ItemManufacturerID').val();
// remove all of the current options from the list
$('#ItemDescriptionID').empty();
// send request for model list based on category and manufacturer
var jqxhr = $.ajax({
url: '/Threshold/GetModels',
type: 'POST',
data: '{ CategoryID: ' + catId.toString() + ', ManufacturerID: ' + manuId.toString() + ' }',
contentType: 'application/json',
dataType: 'json',
cache: false,
async: true
});
// received list of models...
jqxhr.done(function (data) {
if (data == null) return;
try {
var ddl = $('#ItemDescriptionID');
// add each item to DDL
$.each(data, function (index, value) {
ddl.append($('<option/>', {value: data[index].ItemDescriptionID}).html(data[index].ItemDescription))
});
}
catch (result) {
alert("Done, but with errors! " + result.responseText);
}
});
// failed to retrieve data
jqxhr.error(function (result) {
alert("Error! Failed to retrieve models! " + result.responseText);
});
}
So what am I doing wrong? The form will submit if I remove the Required metadata tag. Also, after the form submits, I am able to grab the ID from the dropdown and everything works. The problem is with the modelstate/validation somehow.
EDIT:
This is my Razor for the description box...
#Html.DropDownListFor(m => m.ItemDescriptionID, new SelectList(new List<SelectListItem>()) )
#Html.ValidationMessageFor(m => m.ItemDescriptionID)
If I understand your question correctly the problem you perceive is that even if the user hasn't selected anything valid from your dropdown they can still post to the server. The Model appears to be validating correctly, when the user hasn't selected anything ModelState.IsValid is false and you get the correct error message. But you want to avoid the data being posted at all if a selection hasn't been made.
The issue here is that ModelState and all the MVC validation is actually done server side. So in order for your application to detect that the user's choices aren't valid according to your model's Data Annotations a form submit really is required.
What you want in order to stop users posting is client side validation, which is not part of MVC Model validation but MVC 4 has built in support for it.
Built-in MVC 4 client validation
In order to enable client side validation with the built-in MVC 4 jQuery validation do the following.
Enable MVC client validation support
Ensure that both "Unobtrusive JavaScript" (which will output the required HTML attributes) and "Client side validation" (which will wire up the javascript).
In web.config (for entire site)
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
Or in code - global.asax.cs, Views or Controllers depending on the scope you want
HtmlHelper.ClientValidationEnabled = true;
HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
Include the required scripts in the Views
MVC 4 has prepared a bundle including jQuery validation and the unobtrusive validation scripts. This must be added after the jQuery bundle.
#Scripts.Render("~/bundles/jqueryval")
Always check ModelState.IsValid
Even if you have client-side validation enabled you have to perform server-side validation of posted data before you perform any actions. Whether the data that a user submits is valid or invalid your ActionMethod will still execute... So you need to check IsValid before you take action on the data that has been submitted.
[HttpPost]
public ActionResult ActionMethodName(ModelClass modelParameterName)
{
//exit directly on invalid data
if(!ModelState.IsValid) return View(modelParameterName);
//okay to save data etc...
...
...
//return whatever makes sense for your scenario
return View(modelParameterName);
}
Other options
You could also use jQuery validation manually or another third party javascript validation library.
Also custom javascript form validation is an option - return false from a function which is called from the form's submit event.
HTML5 has support for client-side validation attributes ('required' in your case) although not all browsers implement it.
If you perform validation yourself without using the MVC 4 unobtrusive scripts you should turn of the built-in feature to avoid conflicts and reduce the size of the returned HTML.
<appSettings>
<add key="ClientValidationEnabled" value="false"/>
<add key="UnobtrusiveJavaScriptEnabled" value="false"/>
</appSettings>
Or in code where you can control the scope you want it to apply to.
HtmlHelper.ClientValidationEnabled = false;
HtmlHelper.UnobtrusiveJavaScriptEnabled = false;

Redirect without post back

I have a user registration form. Here I have link to another page (card info page) which has to be filled for the registration. User fills the few fields in the user registration form and click on the link that takes to card info page. When user clicks the link in card info page to navigate back to registration page, the previous details entered in registration got vanished. So I need to redirect from card info page to registration page without postback. How can i accomplish that?
Response.Redirect() is used for redirection.
You can't do this without a postback I don't think. I'd recommend storing the details from your registration page in session state then if the user returns to that page, re-populate the fields from session state.
//eg in registration page
protected void CardInfoLink_Click(object sender, EventArgs e)
{
//store details entered
Session["Registered"] = true;
Session["Username"] = txtUserName.Text;
//etc for any other fields
Response.Redirect("~/CardDetailsPage.aspx");
}
then in the Page_Load method you could, if the session data exists, pre-populate the form from session. e.g
if (!Page.IsPostback && (bool)Session["Registered"])
{
txtUserName.Text = (string)Session["Username"];
//repopulate other fields as necessary
}
When you redirect to another page it will lose all the context of that first page unless you do something to stop it. The options that spring to mind are:
Create the card info page in a popup window. This will mean that your main window is unchanged in the background. You'd preferably use purely client side code to do this but if you need server side code to do it its still possible, just slightly more fiddly.
Save the information on postback before redirect. This could either be just in session or in a database or you could even do it clientside in cookies if you want. Then when you revisit the page you can check if you have saved information and load it up automatically.
If you redirect the user to another page all captured info on that screen WILL be lost. View-state is not kept on redirects, but only on post-backs / callbacks
The only way to maintain information across redirects is to make use of Session Variables, Cookies, or even persisting the data to a Database / XML file and repopulate on return to that page.
I would suggest you save your info as the user gets directed to the info card, then on return, check for the values and re-populate it.
You can store the values in ViewState/Session and redirects to another page (card info page) and then re-populate the values when returning to registration page. Or showing pop-ups or panels (show/hide using Visible property) in the same page you can retain the user inputs. (If you are used server side controls the values are not cleared).
Server.Transfer() will perform a transfer to another page on the server-side.
Update: it would be possible to populate the current pages Context.Items property with the state originally being transferred by query string. This behaves similarly to session state but is limited to the current request.
Update 2: the Server.Transfer() method has an overload that accepts a bool parameter preserveForm, this preserves query string parameters:
http://msdn.microsoft.com/en-us/library/aa332847(v=VS.71).aspx
You can use any kind of ajax request on "go to the next page" button click to copy the registration data into session. Then after the returning you can populate the data again and to remove the session. Your code should be similar to this one:
----------------jquery ajax request-----------------------
function SetValuesIntoSession(value1, value2, value3) {
$.ajax(
{
type: "POST",
url: WebServicePathAndName.asmx/InsertIntoSessionMethodName",
contentType: "application/json; charset=utf-8",
data: "{value1:'" + value1 + "', value2:'" + value2 + "', value3:'" + value3 + "'}",
dataType: "json",
success: function(response) {
if (response.d == "Yes") {
//do something in correct response
}
if (response.d == "No") {
//do something for incorrect response
}
},
error: function(xhr) {
alert('Error! Status = ' + xhr.status);
}
});
}
below is the code for the web service, that should insert the data into the session. Note, that you must set "EnableSession = true" if you want to use session state in your web service
---------------------WebServicePathAndName.asmx------------------
[WebMethod( EnableSession = true )]
public void InsertIntoSessionMethodName( string value1, string value2, string value3 )
{
Session[ "value1" ] = value1;
Session[ "value2" ] = value2;
Session[ "value2" ] = value3;
}
I think, that the rest of the code should be easy to be implemented.

Categories