Skip to content

Commit

Permalink
Allow for basic card verification.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabe Berke-Williams committed Oct 24, 2011
1 parent 62feac8 commit 6bd119e
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 18 deletions.
10 changes: 10 additions & 0 deletions README.md
Expand Up @@ -16,3 +16,13 @@ Example, in spec\_helper.rb:
FakeBraintree.clear!
end
end

## Verifying credit cards

To verify every credit card you try to use, call
`FakeBraintree.verify\_all\_cards!`. This will stay "on" until you set
`FakeBraintree.verify_all_cards = false`. Calling FakeBraintree.clear! _will
not_ change it. It does very basic verification: it only matches the credit card
number against these:
http://www.braintreepayments.com/docs/ruby/reference/sandbox and rejects them if
they aren't one of the listed numbers.
10 changes: 8 additions & 2 deletions lib/fake_braintree.rb
Expand Up @@ -3,6 +3,7 @@
require 'sham_rack'

require 'fake_braintree/sinatra_app'
require 'fake_braintree/valid_credit_cards'
require 'fake_braintree/version'

module FakeBraintree
Expand All @@ -13,7 +14,8 @@ class << self
@transaction = {}

@decline_all_cards = false
attr_accessor :customers, :subscriptions, :failures, :transaction, :decline_all_cards
@verify_all_cards = false
attr_accessor :customers, :subscriptions, :failures, :transaction, :decline_all_cards, :verify_all_cards
end

def self.activate!
Expand Down Expand Up @@ -48,7 +50,7 @@ def self.failure?(card_number)
end

def self.failure_response(card_number)
failure = self.failures[card_number]
failure = self.failures[card_number] || {}
failure["errors"] ||= { "errors" => [] }

{ "message" => failure["message"],
Expand Down Expand Up @@ -78,6 +80,10 @@ def self.decline_all_cards?
self.decline_all_cards
end

def self.verify_all_cards!
self.verify_all_cards = true
end

def self.credit_card_from_token(token)
self.customers.values.detect do |customer|
next unless customer.key?("credit_cards")
Expand Down
46 changes: 31 additions & 15 deletions lib/fake_braintree/sinatra_app.rb
Expand Up @@ -22,6 +22,18 @@ def gzipped_response(status_code, uncompressed_content)
def md5(content)
Digest::MD5.hexdigest(content)
end

def verify_credit_card?(customer_hash)
return true if FakeBraintree.verify_all_cards

customer_hash["credit_card"].key?("options") &&
customer_hash["credit_card"]["options"].is_a?(Hash) &&
customer_hash["credit_card"]["options"]["verify_card"] == true
end

def has_invalid_credit_card?(customer_hash)
! FakeBraintree::VALID_CREDIT_CARDS.include?(customer_hash["credit_card"]["number"])
end
end

# Braintree::Customer.create
Expand All @@ -30,23 +42,27 @@ def md5(content)
if FakeBraintree.failure?(customer["credit_card"]["number"])
gzipped_response(422, FakeBraintree.failure_response(customer["credit_card"]["number"]).to_xml(:root => 'api_error_response'))
else
customer["id"] ||= md5("#{params[:merchant_id]}#{Time.now.to_f}")
customer["merchant-id"] = params[:merchant_id]
if customer["credit_card"] && customer["credit_card"].is_a?(Hash)
customer["credit_card"].delete("__content__")
if !customer["credit_card"].empty?
customer["credit_card"]["last_4"] = customer["credit_card"].delete("number")[-4..-1]
customer["credit_card"]["token"] = md5("#{customer['merchant_id']}#{customer['id']}#{Time.now.to_f}")
expiration_date = customer["credit_card"].delete("expiration_date")
customer["credit_card"]["expiration_month"] = expiration_date.split('/')[0]
customer["credit_card"]["expiration_year"] = expiration_date.split('/')[1]

credit_card = customer.delete("credit_card")
customer["credit_cards"] = [credit_card]
if verify_credit_card?(customer) && has_invalid_credit_card?(customer)
gzipped_response(422, FakeBraintree.failure_response(customer["credit_card"]["number"]).to_xml(:root => 'api_error_response'))
else
customer["id"] ||= md5("#{params[:merchant_id]}#{Time.now.to_f}")
customer["merchant-id"] = params[:merchant_id]
if customer["credit_card"] && customer["credit_card"].is_a?(Hash)
customer["credit_card"].delete("__content__")
if !customer["credit_card"].empty?
customer["credit_card"]["last_4"] = customer["credit_card"].delete("number")[-4..-1]
customer["credit_card"]["token"] = md5("#{customer['merchant_id']}#{customer['id']}#{Time.now.to_f}")
expiration_date = customer["credit_card"].delete("expiration_date")
customer["credit_card"]["expiration_month"] = expiration_date.split('/')[0]
customer["credit_card"]["expiration_year"] = expiration_date.split('/')[1]

credit_card = customer.delete("credit_card")
customer["credit_cards"] = [credit_card]
end
end
FakeBraintree.customers[customer["id"]] = customer
gzipped_response(201, customer.to_xml(:root => 'customer'))
end
FakeBraintree.customers[customer["id"]] = customer
gzipped_response(201, customer.to_xml(:root => 'customer'))
end
end

Expand Down
9 changes: 9 additions & 0 deletions lib/fake_braintree/valid_credit_cards.rb
@@ -0,0 +1,9 @@
module FakeBraintree
VALID_CREDIT_CARDS = %w(4111111111111111 4005519200000004
4009348888881881 4012000033330026
4012000077777777 4012888888881881
4217651111111119 4500600000000061
5555555555554444 378282246310005
371449635398431 6011111111111117
3530111333300000)
end
50 changes: 49 additions & 1 deletion spec/fake_braintree/sinatra_app_spec.rb
Expand Up @@ -65,7 +65,7 @@ module FakeBraintree
let(:expiration_date) { "04/2016" }
let(:payment_method_token) { braintree_credit_card_token(cc_number, expiration_date) }
let(:subscription_result) { Braintree::Subscription.create(:payment_method_token => payment_method_token,
:plan_id => plan_id) }
:plan_id => 'my-plan-id') }

it "can find a created subscription" do
Braintree::Subscription.find(subscription_result.subscription.id).should be
Expand All @@ -75,4 +75,52 @@ module FakeBraintree
expect { Braintree::Subscription.find('abc123') }.to raise_error(Braintree::NotFoundError, /abc123/)
end
end

describe SinatraApp, "Braintree::Customer.create" do
let(:cc_number) { %w(4111 1111 1111 1111).join }
let(:expiration_date) { "04/2016" }
after { FakeBraintree.verify_all_cards = false }

it "successfully creates a customer" do
result = Braintree::Customer.create(
:credit_card => {
:number => cc_number,
:expiration_date => expiration_date
})

result.should be_success
end

it "verifies the card number when passed :verify_card => true" do
Braintree::Customer.create(
:credit_card => {
:number => cc_number,
:expiration_date => expiration_date,
:options => { :verify_card => true }
}).should be_success

Braintree::Customer.create(
:credit_card => {
:number => '123456',
:expiration_date => expiration_date,
:options => { :verify_card => true }
}).should_not be_success
end

it "verifies the card number when FakeBraintree.verify_all_cards == true" do
FakeBraintree.verify_all_cards!

Braintree::Customer.create(
:credit_card => {
:number => cc_number,
:expiration_date => expiration_date
}).should be_success

Braintree::Customer.create(
:credit_card => {
:number => '123456',
:expiration_date => expiration_date
}).should_not be_success
end
end
end
17 changes: 17 additions & 0 deletions spec/fake_braintree_spec.rb
Expand Up @@ -84,3 +84,20 @@
FakeBraintree.clear!
end
end

describe FakeBraintree, "VALID_CREDIT_CARDS" do
let(:valid_credit_cards) do
%w(4111111111111111 4005519200000004
4009348888881881 4012000033330026
4012000077777777 4012888888881881
4217651111111119 4500600000000061
5555555555554444 378282246310005
371449635398431 6011111111111117
3530111333300000
)
end

it "includes only credit cards that are valid in production" do
FakeBraintree::VALID_CREDIT_CARDS.sort.should == valid_credit_cards.sort
end
end

0 comments on commit 6bd119e

Please sign in to comment.