stymiee

Handling Online Payments Part 4 - Handling and Displaying Errors

by All Star ‎02-15-2011 10:46 AM - edited ‎10-20-2011 09:59 AM (8,487 Views)

This is part four of a multi-part series on handling online payments.

 

In Part 1 of this series we identified our goals (creating a payment form that was usable, accessible, and secure) and began by creating the form we will use to capture payment information. In Part 2 of this series we continued this process by exploring how we will handle the data submitted by that form. In Part 3 of this series we took the data received and sanitized in Part 3 and validated that it was in a format we required. In this installment we will demonstrate how to take any errors we found validating our data and effectively demonstrating it to our users.

 

Picking Up Where We Left Off

 

If you remember from Part 3 of this series we validated our user's input and collected all of the errors we found in an array called $errors. Now we're going to put that list of errors we collected to good use by letting our users know what they did incorrectly and how to fix it. Since problems of any kind are a good excuse for users to leave a website, or worse, abandon a shopping cart, we want to make sure we make the messages as clear as possible. To do this we will want to keep two things in mind:

 

  1. We want a clear message for the user to read which tells them what they did wrong and how to fix it
  2. We make it very clear exactly where on the form the error occurred so they know where to fix it

 

Listing The Error Messages

 

We'll begin by listing the errors we encountered so our users know they made an error and have a clear message describing the problem(s). This can be placed at the top of the page, just above the form, or anywhere in between. As long as the user is aware of it as soon as the page loads it's ok. If they can't find it as soon as the page loads they will be confused and the chances of them abandoning the form increase significantly.

 

Here's some sample code you can use:

 

<style type="text/css">
    #errormessage
    {
        background-color: #FFE7E7;
        border: 3px solid #CC0033;
        color: #000000;
        margin: 20px auto;
        padding: 10px;
        width: 890px;
        -moz-border-radius: 6px;
        -webkit-border-radius: 6px;
        border-radius: 6px;
        -moz-box-shadow: 5px 5px 5px #ccc;
        -webkit-box-shadow: 5px 5px 5px #ccc;
        box-shadow: 5px 5px 5px #ccc;
        background: -webkit-gradient(linear, 0 0, 0 bottom, from(#FFEAEA), to(#FFB3B3));
        background: -moz-linear-gradient(#FFEAEA, #FFB3B3);
        background: linear-gradient(#FFEAEA, #FFB3B3);
    }
</style>
<div id="errormessage">
    <h2>
        There was an error with your submission. Please make the necessary corrections and try again.
    </h2>
    <ul>
<?php
    foreach ($errors as $error)
    {
?>
        <li><?php echo $error; ?></li>
<?php
    }
?>
    </ul>
</div>

 

The code above is really quite simple. We have <div> which contains a heading which lets the user know there was a problem with the information they submitted to us and that they will need to fix it in order for their payment to succeed. It then loops through our $errors array and lists them in an unordered list below. To help make the list of errors more obvious I included a sample stylesheet for highlighting the <div>. It will place the error list in a box with a light red background and a red border. It even takes advantage of some CSS magic to make it visually appealing while also gracefully degrading in less capable browsers (*cough* *cough* Internet Explorer *cough* *cough*).

 

Highlighting The Incorrect Fields

 

Now that we have let the user know there was one or more problems with their submission, we need to show them where on the form the error occurred so they can fix it. What we're going to do is highlight the form field in red as well as highlight the form element's label is red and make it bold as well. This will make it very obvious that the input is in need of correction.

 

Let's look at how we would do this with out Billing Zip Code field:

 

<style type="text/css">
    .labelerror
    {
        color: #ff0000;
        font-weight: bold;
    }
</style>
<p>
    <label for="billing_zip"<?php if (in_array('billing_zip', $errors)) echo ' class="labelerror"'; ?>>Zip Code</label>
    <input type="text" name="billing_zip" id="billing_zip" maxlength="5" value="">
</p>

 

In our both <label> and <input> tags we check our $errors array and see if the field is listed as containing an error. This is easy to do because we keyed our error messages using the field id making it easy to find. If we find it, we add our special class defined above. These highlight the field labels in red as mentioned above so our users will easily find them and, hopefully, correct their error.

 

Our Form Page So Far

 

Now that we have some more code written, let's see our how our page looks now:

 

<?php
    $errors = array();
    if ('POST' === $_SERVER['REQUEST_METHOD'])
    {
        $credit_card           = sanitize($_POST['credit_card']);
        $expiration_month      = (int) sanitize($_POST['expiration_month']);
        $expiration_year       = (int) sanitize($_POST['expiration_year']);
        $cvv                   = sanitize($_POST['cvv']);
        $cardholder_first_name = sanitize($_POST['cardholder_first_name']);
        $cardholder_last_name  = sanitize($_POST['cardholder_last_name']);
        $billing_address       = sanitize($_POST['billing_address']);
        $billing_address2      = sanitize($_POST['billing_address2']);
        $billing_city          = sanitize($_POST['billing_city']);
        $billing_state         = sanitize($_POST['billing_state']);
        $billing_zip           = sanitize($_POST['billing_zip']);
        $telephone             = sanitize($_POST['telephone']);
        $email                 = sanitize($_POST['email']);
        $recipient_first_name  = sanitize($_POST['recipient_first_name']);
        $recipient_last_name   = sanitize($_POST['recipient_last_name']);
        $shipping_address      = sanitize($_POST['shipping_address']);
        $shipping_address2     = sanitize($_POST['shipping_address2']);
        $shipping_city         = sanitize($_POST['shipping_city']);
        $shipping_state        = sanitize($_POST['shipping_state']);
        $shipping_zip          = sanitize($_POST['shipping_zip']);

        if (!validateCreditcard_number($credit_card))
        {
            $errors['credit_card'] = "Please enter a valid credit card number";
        }
        if (!validateCreditCardExpirationDate($expiration_month, $expiration_year))
        {
            $errors['expiration_month'] = "Please enter a valid exopiration date for your credit card";
        }
        if (!validateCVV($credit_card, $cvv))
        {
            $errors['cvv'] = "Please enter the security code (CVV number) for your credit card";
        }
        if (empty($cardholder_first_name))
        {
            $errors['cardholder_first_name'] = "Please provide the card holder's first name";
        }
        if (empty($cardholder_last_name))
        {
            $errors['cardholder_last_name'] = "Please provide the card holder's last name";
        }
        if (empty($billing_address))
        {
            $errors['billing_address'] = 'Please provide your billing address.';
        }
        if (empty($billing_city))
        {
            $errors['billing_city'] = 'Please provide the city of your billing address.';
        }
        if (empty($billing_state))
        {
            $errors['billing_state'] = 'Please provide the state for your billing address.';
        }
        if (!preg_match("/^\d{5}$/", $billing_zip))
        {
            $errors['billing_zip'] = 'Make sure your billing zip code is 5 digits.';
        }
        if (empty($telephone) || strlen($telephone) > 20)
        {
            $errors['billing_city'] = 'Please provide a telephone number where we can reach you if necessary.';
        }
        if (!filter_var($email, FILTER_VALIDATE_EMAIL))
        {
            $errors['email'] = "Please provide a valid email address";
        }
        if (empty($recipient_first_name))
        {
            $errors['recipient_first_name'] = "Please provide the recipient's first name";
        }
        if (empty($recipient_last_name))
        {
            $errors['recipient_last_name'] = "Please provide the recipient's last name";
        }
        if (empty($shipping_address))
        {
            $errors['shipping_address'] = 'Please provide your shipping address.';
        }
        if (empty($shipping_city))
        {
            $errors['shipping_city'] = 'Please provide the city of your shipping address.';
        }
        if (empty($shipping_state))
        {
            $errors['shipping_state'] = 'Please provide the state for your shipping address.';
        }
        if (!preg_match("/^\d{5}$/", $shipping_zip))
        {
            $errors['shipping_zip'] = 'Make sure your shipping zip code is 5 digits.';
        }

        // More to come here.....

    }

    function sanitize($value)
    {
        return trim(strip_tags($value));
    }

    function validateCreditcard_number($credit_card_number)
    {
        $firstnumber = substr($credit_card_number, 0, 1);

        switch ($firstnumber)
        {
            case 3:
                if (!preg_match('/^3\d{3}[ \-]?\d{6}[ \-]?\d{5}$/', $credit_card_number))
                {
                    return false;
                }
                break;
            case 4:
                if (!preg_match('/^4\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number))
                {
                    return false;
                }
                break;
            case 5:
                if (!preg_match('/^5\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number))
                {
                    return false;
                }
                break;
            case 6:
                if (!preg_match('/^6011[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number))
                {
                    return false;
                }
                break;
            default:
                return false;
        }

        $credit_card_number = str_replace('-', '', $credit_card_number);
        $map = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 2, 4, 6, 8, 1, 3, 5, 7, 9);
        $sum = 0;
        $last = strlen($credit_card_number) - 1;
        for ($i = 0; $i <= $last; $i++)
        {
            $sum += $map[$credit_card_number[$last - $i] + ($i & 1) * 10];
        }
        if ($sum % 10 != 0)
        {
            return false;
        }

        return true;
    }

    function validateCreditCardExpirationDate($month, $year)
    {
        if (!preg_match('/^\d{1,2}$/', $month))
        {
            return false;
        }
        else if (!preg_match('/^\d{4}$/', $year))
        {
            return false;
        }
        else if ($year < date("Y"))
        {
            return false;
        }
        else if ($month < date("m") && $year == date("Y"))
        {
            return false;
        }
        return true;
    }

    function validateCVV($cardNumber, $cvv)
    {
        $firstnumber = (int) substr($cardNumber, 0, 1);
        if ($firstnumber === 3)
        {
            if (!preg_match("/^\d{4}$/", $cvv))
            {
                return false;
            }
        }
        else if (!preg_match("/^\d{3}$/", $cvv))
        {
            return false;
        }
        return true;
    }
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <title>Payment Form</title>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
        <meta http-equiv="Content-Language" content="en-us">
        <style type="text/css">
            #errormessage
            {
                background-color: #FFE7E7;
                border: 3px solid #CC0033;
                color: #000000;
                margin: 20px auto;
                padding: 10px;
                width: 890px;
                -moz-border-radius: 6px;
                -webkit-border-radius: 6px;
                border-radius: 6px;
                -moz-box-shadow: 5px 5px 5px #ccc;
                -webkit-box-shadow: 5px 5px 5px #ccc;
                box-shadow: 5px 5px 5px #ccc;
                background: -webkit-gradient(linear, 0 0, 0 bottom, from(#FFEAEA), to(#FFB3B3));
                background: -moz-linear-gradient(#FFEAEA, #FFB3B3);
                background: linear-gradient(#FFEAEA, #FFB3B3);
            }
            .labelerror
            {
                color: #ff0000;
                font-weight: bold;
            }
        </style>
    </head>
    <body>
<?php
    if (count($errors))
    {
?>
        <div id="errormessage">
            <h2>
                There was an error with your submission. Please make the necessary corrections and try again.
            </h2>
            <ul>
        <?php
            foreach ($errors as $error)
            {
        ?>
                <li><?php echo $error; ?></li>
        <?php
            }
        ?>
            </ul>
        </div>
<?php
    }
?>
        <form action="/payment-form.php" method="post">
            <p>
                <label for="credit_card"<?php if (in_array('credit_card', $errors)) echo ' class="labelerror"'; ?>>Credit Card Number</label>
                <input type="text" name="credit_card" id="credit_card" autocomplete="off" maxlength="19" value="">
            </p>
            <p>
                <label for="expiration_month"<?php if (in_array('expiration_month', $errors)) echo ' class="labelerror"'; ?>>Expiration Date</label>
                <select name="expiration_month" id="expiration_month">
                    <option value="0"> </option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                    <option value="6">6</option>
                    <option value="7">7</option>
                    <option value="8">8</option>
                    <option value="9">9</option>
                    <option value="10">10</option>
                    <option value="11">11</option>
                    <option value="12">12</option>
                </select>
                <select name="expiration_year" id="expiration_year">
                    <option value="0"> </option>
                    <option value="2010">2010</option>
                    <option value="2011">2011</option>
                    <option value="2012">2012</option>
                    <option value="2013">2013</option>
                    <option value="2014">2014</option>
                    <option value="2015">2015</option>
                    <option value="2016">2016</option>
                    <option value="2017">2017</option>
                    <option value="2018">2018</option>
                    <option value="2019">2019</option>
                    <option value="2020">2020</option>
                    <option value="2021">2021</option>
                </select>
            </p>
            <p>
                <label for="cvv"<?php if (in_array('cvv', $errors)) echo ' class="labelerror"'; ?>>Security Code</label>
                <input type="text" name="cvv" id="cvv" autocomplete="off" value="" maxlength="4">
            </p>
            <p>
                <label for="cardholder_first_name"<?php if (in_array('cardholder_first_name', $errors)) echo ' class="labelerror"'; ?>>Cardholder's First Name</label>
                <input type="text" name="cardholder_first_name" id="cardholder_first_name" maxlength="30" value="">
            </p>
            <p>
                <label for="cardholder_last_name"<?php if (in_array('cardholder_last_name', $errors)) echo ' class="labelerror"'; ?>>Cardholder's Last Name</label>
                <input type="text" name="cardholder_last_name" id="cardholder_last_name" maxlength="30" value="">
            </p>
            <p>
                <label for="billing_address"<?php if (in_array('billing_address', $errors)) echo ' class="labelerror"'; ?>>Billing Address</label>
                <input type="text" name="billing_address" id="billing_address" maxlength="45" value="">
            </p>
            <p>
                <label for="billing_address2"<?php if (in_array('billing_address2', $errors)) echo ' class="labelerror"'; ?>>Suite/Apt #</label>
                <input type="text" name="billing_address2" id="billing_address2" maxlength="45" value="">
            </p>
            <p>
                <label for="billing_city"<?php if (in_array('billing_city', $errors)) echo ' class="labelerror"'; ?>>City</label>
                <input type="text" name="billing_city" id="billing_city" maxlength="25" value="">
            </p>
            <p>
                <label for="billing_state"<?php if (in_array('billing_state', $errors)) echo ' class="labelerror"'; ?>>State</label>
                <select id="billing_state" name="billing_state">
                    <option value="0"> </option>
                    <option value="AL">Alabama</option>
                    <option value="AK">Alaska</option>
                    <option value="AZ">Arizona</option>
                    <option value="AR">Arkansas</option>
                    <option value="CA">California</option>
                    <option value="CO">Colorado</option>
                    <option value="CT">Connecticut</option>
                    <option value="DE">Delaware</option>
                    <option value="DC">District Of Columbia</option>
                    <option value="FL">Florida</option>
                    <option value="GA">Georgia</option>
                    <option value="HI">Hawaii</option>
                    <option value="ID">Idaho</option>
                    <option value="IL">Illinois</option>
                    <option value="IN">Indiana</option>
                    <option value="IA">Iowa</option>
                    <option value="KS">Kansas</option>
                    <option value="KY">Kentucky</option>
                    <option value="LA">Louisiana</option>
                    <option value="ME">Maine</option>
                    <option value="MD">Maryland</option>
                    <option value="MA">Massachusetts</option>
                    <option value="MI">Michigan</option>
                    <option value="MN">Minnesota</option>
                    <option value="MS">Mississippi</option>
                    <option value="MO">Missouri</option>
                    <option value="MT">Montana</option>
                    <option value="NE">Nebraska</option>
                    <option value="NV">Nevada</option>
                    <option value="NH">New Hampshire</option>
                    <option value="NJ">New Jersey</option>
                    <option value="NM">New Mexico</option>
                    <option value="NY">New York</option>
                    <option value="NC">North Carolina</option>
                    <option value="ND">North Dakota</option>
                    <option value="OH">Ohio</option>
                    <option value="OK">Oklahoma</option>
                    <option value="OR">Oregon</option>
                    <option value="PA">Pennsylvania</option>
                    <option value="RI">Rhode Island</option>
                    <option value="SC">South Carolina</option>
                    <option value="SD">South Dakota</option>
                    <option value="TN">Tennessee</option>
                    <option value="TX">Texas</option>
                    <option value="UT">Utah</option>
                    <option value="VT">Vermont</option>
                    <option value="VA">Virginia</option>
                    <option value="WA">Washington</option>
                    <option value="WV">West Virginia</option>
                    <option value="WI">Wisconsin</option>
                    <option value="WY">Wyoming</option>
                </select>
            </p>
            <p>
                <label for="billing_zip"<?php if (in_array('billing_zip', $errors)) echo ' class="labelerror"'; ?>>Zip Code</label>
                <input type="text" name="billing_zip" id="billing_zip" maxlength="5" value="">
            </p>
            <p>
                <label for="telephone"<?php if (in_array('telephone', $errors)) echo ' class="labelerror"'; ?>>Telephone Number</label>
                <input type="text" name="telephone" id="telephone" maxlength="20" value="">
            </p>
            <p>
                <label for="email"<?php if (in_array('email', $errors)) echo ' class="labelerror"'; ?>>Email Address</label>
                <input type="text" name="email" id="email" maxlength="20" value="">
            </p>
            <p>
                <label for="recipient_first_name"<?php if (in_array('recipient_first_name', $errors)) echo ' class="labelerror"'; ?>>Recipient's First Name</label>
                <input type="text" name="recipient_first_name" id="recipient_first_name" maxlength="30" value="">
            </p>
            <p>
                <label for="recipient_last_name"<?php if (in_array('recipient_last_name', $errors)) echo ' class="labelerror"'; ?>>Recipient's Last Name</label>
                <input type="text" name="recipient_last_name" id="recipient_last_name" maxlength="30" value="">
            </p>
            <p>
                <label for="shipping_address"<?php if (in_array('shipping_address', $errors)) echo ' class="labelerror"'; ?>>Shipping Address</label>
                <input type="text" name="shipping_address" id="shipping_address" maxlength="45" value="">
            </p>
            <p>
                <label for="shipping_address2"<?php if (in_array('shipping_address2', $errors)) echo ' class="labelerror"'; ?>>Suite/Apt #</label>
                <input type="text" name="shipping_address2" id="shipping_address2" maxlength="45" value="">
            </p>
            <p>
                <label for="shipping_city"<?php if (in_array('shipping_city', $errors)) echo ' class="labelerror"'; ?>>City</label>
                <input type="text" name="shipping_city" id="shipping_city" maxlength="30" value="">
            </p>
            <p>
                <label for="shipping_state"<?php if (in_array('shipping_state', $errors)) echo ' class="labelerror"'; ?>>State</label>
                <select id="shipping_state" name="shipping_state">
                    <option value="0"> </option>
                    <option value="AL">Alabama</option>
                    <option value="AK">Alaska</option>
                    <option value="AZ">Arizona</option>
                    <option value="AR">Arkansas</option>
                    <option value="CA">California</option>
                    <option value="CO">Colorado</option>
                    <option value="CT">Connecticut</option>
                    <option value="DE">Delaware</option>
                    <option value="DC">District Of Columbia</option>
                    <option value="FL">Florida</option>
                    <option value="GA">Georgia</option>
                    <option value="HI">Hawaii</option>
                    <option value="ID">Idaho</option>
                    <option value="IL">Illinois</option>
                    <option value="IN">Indiana</option>
                    <option value="IA">Iowa</option>
                    <option value="KS">Kansas</option>
                    <option value="KY">Kentucky</option>
                    <option value="LA">Louisiana</option>
                    <option value="ME">Maine</option>
                    <option value="MD">Maryland</option>
                    <option value="MA">Massachusetts</option>
                    <option value="MI">Michigan</option>
                    <option value="MN">Minnesota</option>
                    <option value="MS">Mississippi</option>
                    <option value="MO">Missouri</option>
                    <option value="MT">Montana</option>
                    <option value="NE">Nebraska</option>
                    <option value="NV">Nevada</option>
                    <option value="NH">New Hampshire</option>
                    <option value="NJ">New Jersey</option>
                    <option value="NM">New Mexico</option>
                    <option value="NY">New York</option>
                    <option value="NC">North Carolina</option>
                    <option value="ND">North Dakota</option>
                    <option value="OH">Ohio</option>
                    <option value="OK">Oklahoma</option>
                    <option value="OR">Oregon</option>
                    <option value="PA">Pennsylvania</option>
                    <option value="RI">Rhode Island</option>
                    <option value="SC">South Carolina</option>
                    <option value="SD">South Dakota</option>
                    <option value="TN">Tennessee</option>
                    <option value="TX">Texas</option>
                    <option value="UT">Utah</option>
                    <option value="VT">Vermont</option>
                    <option value="VA">Virginia</option>
                    <option value="WA">Washington</option>
                    <option value="WV">West Virginia</option>
                    <option value="WI">Wisconsin</option>
                    <option value="WY">Wyoming</option>
                </select>
            </p>
            <p>
                <label for="shipping_zip"<?php if (in_array('shipping_zip', $errors)) echo ' class="labelerror"'; ?>>Zip Code</label>
                <input type="text" name="shipping_zip" id="shipping_zip" maxlength="5" value="">
            </p>
            <p>
                <input type="submit" value="Checkout">
            </p>
        </form>
    </body>
</html>

 

What's Next?

 

Given that we have received and validated the user's submitted data and handled any erroneous information they may have provided, we are now ready to process a payment. In the next installment, we'll use the PHP SDK to handle our payment and deal with the approved, declined, and error responses.

 

The Handling Online Payments Series

 

  1. Part 1 - Basic Information and Our Form
  2. Part 2 - Reading In And Sanitizing Submitted Data
  3. Part 3 - Data Validation
  4. Part 4 - Handling Validation Errors
  5. Part 5 - Processing Payment and Handling the Response
  6. Part 6 - Preventing Duplicate Submissions with POST/REDIRECT/GET
  7. Part 7 - Preventing Automated Form Submissions
  8. Part 8 - Using JavaScript To Increase Usability
  9. Part 9 - HTML and CSS Enhancements
  10. Part 10 - A Little Bit More PHP
---------------------------------------------------------------------------------------------------


John Conde is a certified Authorize.Net developer

Comments
by yoeauthorize on ‎02-24-2011 03:13 AM

any help on the below error message ??

 

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Could not find trusted certificate.

 

Thanks for the help in advance.

by snowyn on ‎07-27-2011 06:39 PM

I am attempting to use this code, especially for validating credit card numbers. However, it tells me "invalid credit card number" no matter what number I put in there. I have tried two valid debit cards, which I have successfully used in many payment transactions in the past. I've also tried some numbers that are "supposed to" work in these situations. Eg, Visa's 4111111111111111 and Mastercard's 5100000000000008. Is the credit card validation code wrong? I don't want to use it on my online payment page if it is going to reject everyone's card number. Thanks!

by snowyn on ‎07-27-2011 07:32 PM

Turned out I had put (int) in front of the credit card number -- which gummed up the works big time. Removed that, and now it works. Or at least some other code I found with the Luhn algorithm works. So presumably this does too.

by teecehunter on ‎02-06-2012 11:21 PM

After implementing the code into my body section, the page automatically shows up with the error message at the beginning. That would instantly make me not want to buy; as a customer, being told I'm wrong, when all I've done is click the buy button. Lol. I know, I know, i haven't hosted the site yet. Also... Why is it that nothing in any of this code points to the authorize SDK? This is all extremely confusing, and the lack of instructions for these simpler things is very frustrating, still, the most useful bit of info I've found yet on the subject so far.

by lorul2 on ‎12-05-2012 06:59 PM

Please help!

 

Newbie coder, (but i learn fast honest)

 

I keep getting ...

 

There was an error with your submission. Please make the necessary corrections and try again.

 

I'm guessing its getting over to config.php then getting to Authorizenet.php but either im putting in the incorrect credit card info or my data is not being sent anywhere because i keep getting this error.  If I submit one of the suggested card numbers like 411111111111111 I get the message please use a real credit card.  So when I  use a real credit card number i get the above error message.  I believe i should be able to see a log of my transaction attempts with my test account, but it says no records found.  Any help would be greatly appreciated!

About the Author
  • Authorize.Net Developer Community Manager
Announcements
Labels