84
From 1,000 Transactions a Month to 1 Million in a Day Lessons in Credit Card Processing from Tuesday, May 17, 2011

From 1,000 Transactions a Month to 1 Million in a Dayassets.en.oreilly.com/1/event/59/From 1,000 Transactions a Month to... · Lessons in Credit Card Processing from Tuesday, May

  • Upload
    ngokien

  • View
    214

  • Download
    0

Embed Size (px)

Citation preview

From 1,000 Transactions a Month to 1 Million in a DayLessons in Credit Card Processing from

Tuesday, May 17, 2011

Patrick Joyce@KeeperPat

Tuesday, May 17, 2011

Tuesday, May 17, 2011

Tuesday, May 17, 2011

Tuesday, May 17, 2011

July 27th, 200965 Deals Sold

Tuesday, May 17, 2011

Tuesday, May 17, 2011

January 19th, 20111 Million+ Deals Sold

Tuesday, May 17, 2011

If at all possible, avoid processing credit cards

Tuesday, May 17, 2011

What do you need to acceptCredit Cards?

Tuesday, May 17, 2011

3 things

Tuesday, May 17, 2011

1. Bank Account

Tuesday, May 17, 2011

2. Merchant Account

Tuesday, May 17, 2011

3. Payment Gateway

Tuesday, May 17, 2011

Bank Account

Tuesday, May 17, 2011

Merchant Account

Tuesday, May 17, 2011

Payment Gateway

Tuesday, May 17, 2011

Authorization

Tuesday, May 17, 2011

Customer

Merchant(You)

Tuesday, May 17, 2011

Customer

Payment Gateway

Merchant(You)

Tuesday, May 17, 2011

Customer

Payment Gateway

Merchant(You)

Merchant Account(Acquiring

Bank)

Tuesday, May 17, 2011

Customer

Payment Gateway

Merchant(You)

Merchant Account(Acquiring

Bank)

Customer’s Bank

(Issuing Bank)

Credit Card Network

Tuesday, May 17, 2011

Customer

Payment Gateway

Merchant(You)

Merchant Account(Acquiring

Bank)

Customer’s Bank

(Issuing Bank)

Credit Card Network

Tuesday, May 17, 2011

Settlement

Tuesday, May 17, 2011

ActiveMerchantgithub.com/Shopify/active_merchant

Tuesday, May 17, 2011

One time transaction

Tuesday, May 17, 2011

Creating the Gateway

@gateway = ActiveMerchant::Billing:: AuthorizeNetGateway.new( :login => 'LOGIN', :password => 'PASSWORD' )

Tuesday, May 17, 2011

@credit_card = ActiveMerchant::Billing::CreditCard.new( :number => '4242424242424242', :month => 9, :year => 2013, :first_name => 'Longbob', :last_name => 'Longsen', :verification_value => '123', :type => 'visa')

Tuesday, May 17, 2011

# Charge Credit Card for [email protected](50, @credit_card)

Tuesday, May 17, 2011

#<ActiveMerchant::Billing::Response:0x108f13a98 @authorization="2159970110", @avs_result= {"code"=>"Y", "postal_match"=>"Y", "street_match"=>"Y", "message"=>"Street address and 5-digit postal code match."}, @cvv_result={"code"=>"P", "message"=>"Not Processed"}, @fraud_review=false, @message="This transaction has been approved", @params= {"response_reason_text"=>"This transaction has been approved.", "transaction_id"=>"2159970110", "response_code"=>1, "response_reason_code"=>"1", "avs_result_code"=>"Y", "card_code"=>"P"}, @success=true, @test=true>

Tuesday, May 17, 2011

Don’t Log Sensitive Information

Tuesday, May 17, 2011

Rails 2

Tuesday, May 17, 2011

class ApplicationController < ActionController::Base filter_parameter_logging :card_number, :verification_valueend

/app/controllers/application_controller.rbTuesday, May 17, 2011

Rails 3

Tuesday, May 17, 2011

config.filter_parameters += [:card_number, :verification_value]

/config/application.rbTuesday, May 17, 2011

Payment Tokenization

Tuesday, May 17, 2011

Authorize.net CIM

Tuesday, May 17, 2011

Creating the Gateway

@gateway = ActiveMerchant::Billing:: AuthorizeNetCimGateway.new( :login => 'LOGIN', :password => 'PASSWORD' )

Tuesday, May 17, 2011

@credit_card = ActiveMerchant::Billing::CreditCard.new( :number => '4242424242424242', :month => 9, :year => 2013, :first_name => 'Longbob', :last_name => 'Longsen', :verification_value => '123', :type => 'visa')

Tuesday, May 17, 2011

@gateway.create_customer_profile( :profile => { :description => "meaningful to you", :payment_profiles => { :payment => { :credit_card => @credit_card } } })

Tuesday, May 17, 2011

response.success? => true

response.authorization => 2011761response.params['customer_profile_id'] => 2011761

response.params['customer_payment_profile_id_list']['numeric_string'] => 1646547

Tuesday, May 17, 2011

@gateway.create_customer_profile_transaction( :transaction => { :customer_profile_id => '2011761', :customer_payment_profile_id => '1646547', :type => :auth_capture, :amount => 50 })

Tuesday, May 17, 2011

Hashing Card Numbers

Tuesday, May 17, 2011

4012888888881881(Test Card Number)

Tuesday, May 17, 2011

62163a017b168ad4a229c64ae1bed6ffd5e8fb2d

(SHA1 Hash)

Tuesday, May 17, 2011

Section 3.4:

Render PAN, at minimum, unreadable anywhere it is stored ... by using any of the following approaches: - Strong one-way hash functions (hashed indexes)

Verify that data is rendered unreadable using one of the following methods: - One-way hashes (hashed indexes) such as SHA-1

www.pcisecuritystandards.org/pdfs/pci_audit_procedures_v1-1.pdf

PCI DSS

Tuesday, May 17, 2011

Right?

Tuesday, May 17, 2011

WRONG

Tuesday, May 17, 2011

10^1610,000,000,000,000,000

10 Quadrillion Possible Card Numbers

Tuesday, May 17, 2011

4012-8888-8888-1881

Tuesday, May 17, 2011

4,000,000,000,000,000 4 Quadrillion Possible Card Numbers

Tuesday, May 17, 2011

4012-8888-8888-1881

Tuesday, May 17, 2011

500,000,000,000,000500 Trillion Possible Card Numbers

Tuesday, May 17, 2011

XXXX-XXX-XXX-1881

Tuesday, May 17, 2011

50,000,000,00050 Billion Possible Card Numbers

Tuesday, May 17, 2011

401288******1881

Tuesday, May 17, 2011

1,000,0001 Million Possible Card Numbers

Tuesday, May 17, 2011

hashed_card_number = '62163a017b168ad4a229c64ae1bed6ffd5e8fb2d'

masked_card_number = '401288******1881'

Tuesday, May 17, 2011

require 'digest/sha1'

def reverse_hashed_card_number( hashed_card_number, first_six, last_four) 0.upto(999_999) do |i| card_number_to_test = "#{first_six}%06d#{last_four}" % i hashed_to_test = Digest::SHA1.hexdigest(card_number_to_test)

if hashed_card_number == hashed_card_number_to_test return card_number_to_test end

endend

Tuesday, May 17, 2011

Benchmark.measure do puts reverse_hashed_card_number( '62163a017b168ad4a229c64ae1bed6ffd5e8fb2d', '401288', '1881' )end.real

4012888888881881

=> 5.33522081375122

Tuesday, May 17, 2011

Scaling Card Transactions

Tuesday, May 17, 2011

External API Calls in the request cycle are evil

Tuesday, May 17, 2011

Background expensive operations

Tuesday, May 17, 2011

Security Concerns

Tuesday, May 17, 2011

End to End Encryption

Tuesday, May 17, 2011

var braintree = Braintree.create("PUBLIC_KEY");

Tuesday, May 17, 2011

$('#transaction_form').submit(function () { encryptedForm().submit(); return false;});

Tuesday, May 17, 2011

var encryptedForm = function () { var originalForm = $('#transaction_form'); var newForm = originalForm.clone(); newForm.css('display', 'none') updateIds(newForm); copyCheckboxAndRadioButtonState(originalForm, newForm) $('body').append(newForm); encryptFormValues(); return newForm;};

Tuesday, May 17, 2011

var encryptedForm = function () { var originalForm = $('#transaction_form'); var newForm = originalForm.clone(); newForm.css('display', 'none') updateIds(newForm); copyCheckboxAndRadioButtonState(originalForm, newForm); $('body').append(newForm); encryptFormValues(); return newForm;};

Tuesday, May 17, 2011

copyCheckboxAndRadioButtonState = function (originalForm, newForm) { fields = originalForm.find("*").filter(":checkbox | :radio") fields.each(function (index, originalElement) { if ($(originalElement).attr("id")) { id = $(originalElement).attr("id") $("new_" + id).checked = originalElement.checked; } });};

Tuesday, May 17, 2011

var encryptedForm = function () { var originalForm = $('#transaction_form'); var newForm = originalForm.clone(); newForm.css('display', 'none') updateIds(newForm); copyCheckboxAndRadioButtonState(originalForm, newForm); $('body').append(newForm); encryptFormValues(); return newForm;};

Tuesday, May 17, 2011

var encryptFormValues = function () { var inputs = $('#transaction_form').find(":input") inputs.each(function (index, element) { var id = $(element).attr("id"); $("#new_" + id).val(selectivelyEncryptedValue(element)); });};

var selectivelyEncryptedValue = function (element) { if (shouldEncrypt(element)) { return dls.braintree.encrypt($(element).val()); } else { return $(element).val(); }};

Tuesday, May 17, 2011

$('#transaction_form').submit(function () { encryptedForm().submit(); return false;});

Tuesday, May 17, 2011

4012888888881881 =>$bt$JKuPh2et4NznbT4RJYZS5lKEva+saRLjKb6ZLkeTwC4D2g3k6V...

Tuesday, May 17, 2011

Braintree::Customer.create( :first_name => 'Patrick', :last_name => 'Joyce', :credit_card => { :cardholder_name => 'Patrick Joyce', :number => '4012888888881881', :expiration_month => 1, :expiration_year => 2013, :cvv => '123' })

Tuesday, May 17, 2011

Braintree::Customer.create( :first_name => 'Patrick', :last_name => 'Joyce', :credit_card => { :cardholder_name => 'Patrick Joyce', :number => '$bt$JKuPh2et4NznbT4RJYZ...', :expiration_month => 1, :expiration_year => 2013, :cvv => '$bt$H9LKEhZUb0yFbFVAncoenz0...' })

Tuesday, May 17, 2011

class CreditCard < ActiveRecord::Base before_validation :store_at_braintree, :unless => Proc.new {|cc| cc.store_offline? }

after_save :fire_store_at_braintree_event, :if => Proc.new {|cc| cc.store_offline? }

attr_accessor :store_now def store_offline? store_now || encrypted?(card_number) endend

Tuesday, May 17, 2011

def fire_store_at_braintree_event queue_action_event(:store_braintree_credit_card, :credit_card_id => self.id, :card_number => self.card_number, :verification_value => self.verification_value )end

Tuesday, May 17, 2011

class StoreBraintreeCreditCardEvent < ApplicationEvent def process @credit_card = CreditCard.find_by_id(params[:credit_card_id]) @credit_card.update_attributes!( :card_number => params[:card_number], :verification_value => params[:verification_value], :store_now => true ) endend

Tuesday, May 17, 2011

Benefits

Tuesday, May 17, 2011

Reduced time to add a new Credit Card from 3

seconds to <180ms

Tuesday, May 17, 2011

We handled massive traffic

Tuesday, May 17, 2011

Recovery / Robustness

Tuesday, May 17, 2011

We’re Hiring!livingsocial.com/jobs

Patrick Joyce@KeeperPat

Tuesday, May 17, 2011