Views: 11667
Number of votes: 2
Average rating:

Credit Card Number Storage and PCI

The opinions, estimates and projections in this article accurately constitute the current judgment and culmination of views of industry practices at the date of the article. They do not form any form of warranty, assurance or advice and do not necessarily reflect the opinions of EPiServer AB and are subject to change without notice.

[This is an edited version of the original] 
Storing credit card details in a database is obviously a very sensitive issue because of the threat of credit card data theft. So the question naturally arises - what should one do with credit card information in your implementation? All too often, the issue of credit card security has been overlooked on a rushed site and its later discovered that credit card information is accidentally stored unencrypted in the commerce database. Credit card data compromise is a big liability (in the form of penalties and bad press) that should be addressed early on in the design phase of developing a commerce site - regardless of the framework used to build an ECommerce site. EPiServer Commerce *enables* you to be PCI-compliant... but it doesn't ensure it without action and planning on the part of those designing and building the site. This blog is to introduce PCI and cover one way to reduce your PCI-compliance exposure - namely how to ensure that credit card information is never stored in your EPiServer Commerce system.

What is PCI?
Because of the security issues surrounding credit cards, a standard called Payment Card Industry Data Security Standard, commonly referred to as PCI (or PCI DSS), was developed by the major credit card processors through the PCI Security Standards Council (www.pcisecuritystandards.org). The PCI standard requires a number of security measures be in place for any business that handles credit card information to ensure the data is well protected from theft. PCI compliance is a requirement in using payment providers to process credit card payments.

The PCI standard consists of 12 requirements that pertain to the processing, storing, and transmission of credit card information. The requirements cover how credit card information should to be handled including database encryption, encrypting data transmission (SSL), system access management practices, system modification logging, firewall configuration, anti-virus software, and physical access to hardware containing credit card information. There are also different levels of monitoring and reporting required based on the number of credit card transactions that a business handles annually. The PCI standard isn't limited to web site credit card processing - it relates to all credit card handling and processing. That's a really high-level overview of PCI and there are a lot of great sites and articles that provide much great insight and direction for how to be PCI compliant (see below for several of those links).

 
Note that, in addition to PCI compliance, there are also often state and national laws regarding reporting credit card data compromise depending on where consumers who purchase items/services from your site live. Its also important to be be aware of and, of course, comply with these requirements. The European Data Protection Act and personal data theft reporting laws in almost all US states are some examples of this.

Is EPiServer Commerce PCI-Compliant?
As stated above, EPiServer Commerce enables you to be PCI-compliant (http://www.episerver.com/Commerce/Security/). However, no software comes with hardware access restriction policies or firewall configuration or most of what is in the PCI standard. PCI compliance is really a more holistic view of how your business practices can ensure credit card security, rather than simply whether your deployed site and database meets the standard. Its safe to say that storing credit card information unencrypted in your Commerce database is not PCI-compliant.


How credit card data should be handled in EPiServer Commerce sites
In most cases, credit card information shouldn't be stored in the database at any time. Storing this information is not only a liability for security reasons, but it results in more compliance actions that need to be taken to be PCI-compliant. Credit card information is stored by the credit card provider you integrate with (Paypal, DataCash, DIBS, etc), which are required to be PCI-compliant. There are ways that these providers allow you to access those cards for processing later in certain circumstances (more on this later). Simply using a third-party credit card processing company is not sufficient to ensure full PCI-compliance by itself - but it reduces the number of measures you'll need to implement to be PCI compliant.

 
The Technical Stuff
First a little background on how credit card information is processed in EPiServer Commerce…Some basics on this topic include:
Order System documentation:
http://sdk.episerver.com/commerce/1.1.2/Content/Developers%20Guide/Order%20System/Order%20System.htm
Aspects of checkout in EPiServer Commerce:
http://sdk.episerver.com/commerce/1.1.2/Content/Developers%20Guide/Order%20System/devOrderSystem/Checkout%20Process/Checkout%20Process.htm
Order system explanation by Roger Cevung:
http://world.episerver.com/Articles/Items/Fundamental-Order-System-Classes-in-EPiServer-Commerce/

Fundamentally, the most important task in preventing credit card data from being saved to the database is to clear out the CreditCardPayment properties containing sensitive data after processing the payments and prior to saving the cart. If you use the EPiServer Commerce payment provider model, payment processing occurs when the CartCheckout workflow is executed. At no time during the workflow is data saved to the database (unless a custom activity is added in an implementation which does this, which is counter to the design of the workflow). The CartCheckout workflow has activities in it to process order payment with the associated payment provider, update catalog item inventory, and update promotion usage properties. After the workflow is run, the sensitive data must be set to innocuous values. Only then should the cart be saved/updated in the database.

Here's a code example that demonstrates this:

  //put credit card details in the cart
  CreditCardPayment pymt = new CreditCardPayment();
  pymt.CardType = ddlCardType.SelectedValue;
  pymt.CreditCardNumber = txtCardNumber.Text;
  //... other details added
  ch.Cart.OrderForms[0].Payments.Add(pymt);

  //It's vital that a try/catch wrapper be put around this statement to
  //ensure that the credit card details are *always* cleared
  try
  {
    //now the payments can be processed and the order completed
    ch.RunWorkflow(OrderGroupWorkflowManager.CartCheckOutWorkflowName);
  }
  catch (Exception ex)
  {
    //whatever other handling of errors you need to add... 
    //if another exeception is going to be thrown in this 'catch' to exit 
    //this method, call the ClearCCDetails method just to be sure the credit 
    //card details are removed
  }

  //This method removes sensitive credit card data
  ClearCCDetails(ch.Cart);

  //now you can finally save the order to the database
  ch.Cart.AcceptChanges();
}


private void ClearCCDetails(Cart cart)
{
  //clear out the credit card details for all credit card payments in the order
  foreach (Payment payment in cart.OrderForms[0].Payments)
  {
    CreditCardPayment cc = payment as CreditCardPayment;
    if (cc != null)
    {
      cc.CreditCardNumber = string.Empty;
      cc.CardType = string.Empty;
      cc.CreditCardSecurityCode = string.Empty;
      cc.CustomerName = string.Empty;
      cc.ExpirationMonth = 0;
      cc.ExpirationYear = 0;
    }
  }
}


Deleting this information from the order naturally raises a few concerns :

But what about showing the last 4 digits on the "thank you" page?
If you need to do this, one way to handle this is to put that information in the session before deleting it. After its retrieved in the thank you page, make sure to empty the session variable. For example:
...
pymt.CreditCardNumber = txtCardNumber.Text;
ch.Cart.OrderForms[0].Payments.Add(pymt);
string lastFour = pymt.CreditCardNumber;
lastFour = string.Format("{0}{1}", (string.Empty).PadLeft(lastFour.Length - 4, '*'),
  lastFour.Substring(lastFour.Length - 4));
Session["LastFour"] = lastFour;


How about getting access to that credit card information later for charging the card (when only authorization is done during checkout)?
This is the purpose of the TransactionId response property associated with the payment provider transaction. The built-in providers (e.g. Klarna, Authorize.NET, etc) already accept that TransactionId property from successful credit card transactions and set it in the CreditCardPayment.TransactionId property. This value shouldn't be blanked out before saving the cart to the database - it should be retained (and is not considered sensitive credit card information). This is the reference id that allows you to do a capture on a credit card after an authorization. This TransactionId field maps to the credit card details stored by the payment provider. It's not a given that this is supported by all payment providers (and in this fashion) so check the provider's documentation.
To execute the charge aka capture of the credit card at a later time, the provider can be called, passing in the transactionid rather than the other credit card details with a transaction type of capture.


What about subscriptions?
How this is handled depends on the payment handler. Here are the two ways I've seen this handled:
* Some payment providers allow you to setup a recurring payment order through the API; once setup, the payment provider automatically performs the regular billing per the schedule specified. Of course, you can alter or cancel the recurring payment; however the recurring payments are then automatically managed by the payment provider. This option is great if you have pretty straight-forward monthly rates (including free trial periods) and want all of the charging done independently from your system. These offerings can include rich administrative interfaces and features to manage those subscriptions. Some examples of this are Paypal (https://www.x.com/developers/paypal/documentation-tools/express-checkout/integration-guide/ECGettingStarted)
(which also offers the other option below) and WorldPay (http://www.worldpay.com/support/kb/bg/pdf/rpfp.pdf)


* Other payment providers allow you to store an id representing the credit card (rather than a TransactionId representing a transaction) and periodically charge that credit card from your own system. This may be better if you want to more tightly manage the billing and if your monthly charges vary. Some examples of this are DIBS (http://tech.dibspayment.com/dibs_api/flexwin/payment_functions/ticket_authcgi/), Paypal (https://www.x.com/developers/paypal/documentation-tools/paypal-payments-pro/integration-guide/WPWebsitePaymentsPro), and Authorize.NET (http://developer.authorize.net/api/arb/)


What if I want to store and display the last four digits of a user’s credit cards so customers can select them for reuse during checkout?
There is considerable discussion about whether the PCI standard allows the last four digits of the credit card number (also known as the Primary Account Number or PAN) to be stored in the database without encryption. This is referred to as protecting the PAN through truncation. This is commonly used to display the stored credit card options to a customer from previous purchases. Also note that some payment providers return a token (Cybersource and Braintree are two examples) after processing a credit card transaction representing the credit card (see the previous topic) which contains the last four digits of the credit card. So you could use these tokens to render the last four digits of the users’ credit card PAN. However, there are implications for storing the last four digits of the PAN and you should consult the PCI guides, your payment processor agreement, and your PCI experts regarding this topic.

A few other tips:
* When testing a storefront, you should not use live credit card data but instead use test credit cards provided by your payment provider.
* Don't log credit card details (even in testing)

And finally, here are some resources related to this topic:
EPiServer links
Use SSL for data transmission in checkout
http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-CMS-6/EPiServer-CMS-60/Securing-Edit-and-Admin/


http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-CMS-6/EPiServer-CMS-60/Protecting-Your-Site-From-Session-Hijacking/

PCI Documents
https://www.pcisecuritystandards.org/security_standards/documents.php

PCI Quick Reference
https://www.pcisecuritystandards.org/documents/PCI%20SSC%20Quick%20Reference%20Guide.pdf

Remember that this blog doesn't constitute a warranty, assurance or advice.

    (By kevin.miller , 23 April 2014 09:18, Permanent link)

    Thanks for a great post, however from my research around PCI compliance then you aren't actually allowed to capture credit card details on your website at all, i.e. post them to your page and process them as basically this gives the developer to write code to gain access to these. This means you need to be PCI compliant even if you aren't storing them.

    Therefore a tokenized approach is normally recommended where you post the details to the credit card page and get the token back in order to be able to process recurring payments in the future.

    Would love to know your thoughts on this, and again thanks for the great content.

    (By Shannon Gray , 11 July 2014 17:01, Permanent link)

    Hi Kevin -

    Sorry for the delayed response (I didn't get a notification to your post). I certainly haven't seen that interpretation and if that is true, it would make an awful lot of sites not PCI-compliant. On the other hand, there are a lot of different opinions about how to interpret PCI and that's why hiring PCI experts is important for customer implementations.

    Regarding PCI compliance being applicable regardless of whether credit card information is stored, yes, you're correct. As I mention above, "The PCI standard consists of 12 requirements that pertain to the *processing*, storing, and *transmission* of credit card information."

    This post isn't intended to provide a definitive guide to all PCI compliance possibilities or points of view (and doesn't constitute a warranty, assurance, or advice).

  Please login to post a comment