PayPal amount tampering

2020-02-13 01:48发布

问题:

I've never done a paypal integration before, however i have worked with other gateways.

With other gateways there is a hash which is also sent in the form post, this stops people from tampering with the data ie changing the amount.

How is this tampering stopped with paypal, there doesnt appear to be any hash.

<form method="post" action="https://www.sandbox.paypal.com/cgi-bin/webscr">
  <input type="hidden" value="_xclick" name="cmd">
  <input type="hidden" value="online****@theg*****.com" name="business">
  <!-- <input type="hidden" name="undefined_quantity" value="1" /> -->
  <input type="hidden" value="Order" name="item_name">
  <input type="hidden" value="NA" name="item_number">
  <input type="hidden" value="22.16" name="amount">
  <input type="hidden" value="5.17" name="shipping">
  <input type="hidden" value="0" name="discount_amount">        
  <input type="hidden" value="0" name="no_shipping">
  <input type="hidden" value="No comments" name="cn">
  <input type="hidden" value="USD" name="currency_code">
  <input type="hidden" value="http://XXX/XXX/XXX/paypal/return" name="return">
  <input type="hidden" value="2" name="rm">      
  <input type="hidden" value="11255XXX" name="invoice">
  <input type="hidden" value="US" name="lc">
  <input type="hidden" value="PP-BuyNowBF" name="bn">
  <input type="submit" value="Place Order!" name="finalizeOrder" id="finalizeOrder" class="submitButton">
</form>

So how can i stop people amending the amount before posting to paypal? I.e amount should be 100 but people changing it to 1.

回答1:

There are a couple ways to prevent this. The first is using PayPal's Instant Payment Notification (IPN). Using this, you would compare the prices that PayPal posts back to you to confirm that they match what you are expecting. If they don't match, you cancel the order.

Example Workflow:

  • User Orders an item and modifies price to $0.01
  • Order is posted to PayPal, which shows price of $0.01
  • User accepts price and pays $0.01
  • PayPal calls your IPN URL and posts transaction details, showing that the user paid $0.01 for a thing
  • Your IPN checks the price that PayPal received ($0.01) verus what you were expecting ( > $0.01). Since they don't match, you cancel the order

Another option, is to use PayPal's Button API, to create dynamic, encrypted buttons. These are embedded into your page and the user clicks it to make their order. Since it is encrypted, the user is unable to reliably modify the source code during the transaction. A nice example of this is available in this answer. Additionally, you are able to combine this with the IPN option listed above to serve as a nice audit of the transaction



回答2:

What you need to do is implement a simple invoice system. Have a table in your database called invoices (ID, User_Id, Invoice_Value, Payment_Status) (example).

When the user gets to the checkout page, by now you should have inserted an entry in the db table for that user, for the total amount they have to pay and a initial payment status of "Pending"). After inserting the invoice table row, get the last insert id and to a variable called $invoice_id.

Now, you output the html paypal checkout button form and one of the hidden input field should be like this:

<input type="hidden" value="<?php echo $invoice_id; ?>" name="custom">

Now, when paypal responds with the IPN to your return URL, your IPN handler should behave something along this way:

<?php

// read the post from PayPal system and add 'cmd'  
$req = 'cmd=_notify-validate';  
foreach ($_POST as $key => $value) {  
    $value = urlencode(stripslashes($value));  
    $req .= "&$key=$value";  
}

// post back to PayPal system to validate  
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";  
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";  
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";  

$fp = fsockopen ('ssl://sandbox.www.paypal.com', 443, $errno, $errstr, 30);  
if (!$fp) {  
    // HTTP ERROR  
}
else
{
    // Make Request To PayPal
    fputs ($fp, $header . $req);  
    while (!feof($fp))
    { 
        // Read Response 
        $res = fgets ($fp, 1024);

        // Check Response  
        if (strcmp ($res, "VERIFIED") == 0)
        {  
            // PAYMENT VALIDATED & VERIFIED!

            // Load the Invoice_Value from invoices table for $_POST['custom']
            // and compare it with paypal posted amount held in $_POST['mc_gross']
            // if it matches, paypal has authenticated the payment and the value has not been tampered with
            // update the invoice table and set the payment status
        }  
        else if (strcmp ($res, "INVALID") == 0)
        {  
            // PAYMENT INVALID & INVESTIGATE MANUALY!  
        }
    }  
    fclose ($fp);  
}  

?>