Is it possible to validate credit card information on the client side without transmitting any data to the server for PCI compliance?
The API I am using responds with a token after I send them the credit card information, which is the only data I need to store in my database. However, currently, the information is being passed through my server as seen in the server log:
Started POST "/api_checkout" for 127.0.0.1 at 2014-08-04 21:53:02 -0300
Processing by Marketplace::CheckoutController#api_buy as JS
Parameters: {"utf8"=>"✓", "number"=>"4111 1111 1111 1111", "verification_value"=>"123", "full_name"=>"Test user", "expiration"=>"14/15", "token"=>"B5E7A1F1-9822-4433-9FEE-30B625B8B070"}
Rendered marketplace/checkout/api_buy.js.erb (0.4ms)
Completed 200 OK in 43ms (Views: 39.2ms | ActiveRecord: 0.0ms | Solr: 0.0ms)
Started POST "/api_checkout" for 127.0.0.1 at 2014-08-04 21:53:03 -0300
Processing by Marketplace::CheckoutController#api_buy as HTML
Parameters: {"utf8"=>"✓", "number"=>"4111 1111 1111 1111", "verification_value"=>"123", "full_name"=>"Test user", "expiration"=>"14/15", "token"=>"B5E7A1F1-9822-4433-9FEE-30B625B8B070"}
Completed 500 Internal Server Error in 21ms
Despite having methods to validate credit cards via JavaScript and make an API call on the client side, I want to eliminate the need to pass the information through my server. Here is what my current setup consists of:
Here is the form structure:
<%= form_tag api_buy_path,:method => :post, :id => 'payment-form', :remote => true do %>
<div class="usable-creditcard-form">
<div class="wrapper">
<div class="input-group nmb_a">
<div class="icon ccic-brand"></div>
<%= text_field_tag :number, params[:number], :class=>"credit_card_number", :"data-api"=>"number" %>
</div>
<div class="input-group nmb_b">
<div class="icon ccic-cvv"></div>
<%= text_field_tag :verification_value, params[:verification_value], :class=>"credit_card_cvv", :"data-api"=>"verification_value" %>
</div>
<div class="input-group nmb_c">
<div class="icon ccic-name"></div>
<%= text_field_tag :full_name, params[:full_name], :class=>"credit_card_name", :"data-api"=>"full_name" %>
</div>
<div class="input-group nmb_d">
<div class="icon ccic-exp"></div>
<%= text_field_tag :expiration, params[:expiration], :class=>"credit_card_expiration", :"data-api"=>"expiration" %>
</div>
</div>
</div>
<div class="token-area">
<%= label_tag :token, "Card token:"%>
<%= text_field_tag :token, params[:token],:id=>"token", :readonly=> true, :value=>""%>
</div>
<%= submit_tag 'Submit' %>
<% end %>
And here is the JavaScript implementation:
SomeAPI.setAccountID("some-id");
SomeAPI.setTestMode(true);
jQuery(function($) {
$('#payment-form').submit(function(evt) {
var form = $(this);
var cardNumber = document.getElementById("number").value;
//Check if the number is valid
if(SomeAPI.utils.validateCreditCardNumber(cardNumber)){
var brand = SomeAPI.utils.getBrandByCreditCardNumber(cardNumber);
var cvv = document.getElementById("verification_value").value;
//Validate CVV based on the brand
if(SomeAPI.utils.validateCVV(cvv, brand)){
var expiration = document.getElementById("expiration").value.split("/");
var expiration_year = expiration[1];
var expiration_month = expiration[0];
//Validate the expiration date
if(SomeAPI.utils.validateExpiration(expiration_month, expiration_year)){
var name = document.getElementById("full_name").value.split(" ");
var firstName = name[0];
var lastName = name[name.length - 1];
//Perform all checks
cc = SomeAPI.CreditCard(cardNumber, expiration_month, expiration_year, firstName, lastName, cvv);
var tokenResponseHandler = function(data) {
if (data.errors) {
alert("Error: " + JSON.stringify(data.errors));
}
else {
$("#token").val( data.id );
form.get(0).submit();
}
form.trigger("submit.rails");
}
SomeAPI.createPaymentToken(cc, tokenResponseHandler);
return true;
}else{
alert("Invalid")
return false;
}
}else{
alert("Invalid")
return false;
}
}else{
alert("Invalid")
return false
}
});
});