require 'braintree' Braintree::Configuration.environment = :production Braintree::Configuration.merchant_id = "xxx" Braintree::Configuration.public_key = "xxx" Braintree::Configuration.private_key = "xxx" require 'digest/md5' require 'sham_rack' class FakeBraintree cattr_accessor :customers, :subscriptions, :failures, :transaction @@customers = {} @@subscriptions = {} @@failures = {} @@transaction = {} def self.clear! @@customers = {} @@subscriptions = {} @@failures = {} @@transaction = {} end def self.failure?(card_number) self.failures.include?(card_number) end def self.failure_response(card_number) failure = self.failures[card_number] failure["errors"] ||= { "errors" => [] } { "message" => failure["message"], "verification" => { "status" => failure["status"], "processor_response_text" => failure["message"], "processor-response-code" => failure["code"], "gateway_rejection_reason" => "cvv", "cvv_response_code" => failure["code"] }, "errors" => failure["errors"], "params" => {}} end def self.generated_transaction {"status_history"=>[{"timestamp"=>Time.now, "amount"=>FakeBraintree.transaction[:amount], "transaction_source"=>"CP", "user"=>"copycopter", "status"=>"authorized"}, {"timestamp"=>Time.now, "amount"=>FakeBraintree.transaction[:amount], "transaction_source"=>"CP", "user"=>"copycopter", "status"=>FakeBraintree.transaction[:status]}], "created_at"=>(FakeBraintree.transaction[:created_at] || Time.now), "currency_iso_code"=>"USD", "settlement_batch_id"=>nil, "processor_authorization_code"=>"ZKB4VJ", "avs_postal_code_response_code"=>"I", "order_id"=>nil, "updated_at"=>Time.now, "refunded_transaction_id"=>nil, "amount"=>FakeBraintree.transaction[:amount], "credit_card"=>{"last_4"=>"1111", "card_type"=>"Visa", "token"=>"8yq7", "customer_location"=>"US", "expiration_year"=>"2013", "expiration_month"=>"02", "bin"=>"411111", "cardholder_name"=>"Chad Lee Pytel"}, "refund_id"=>nil, "add_ons"=>[], "shipping"=>{"region"=>nil, "company"=>nil, "country_name"=>nil, "extended_address"=>nil, "postal_code"=>nil, "id"=>nil, "street_address"=>nil, "country_code_numeric"=>nil, "last_name"=>nil, "locality"=>nil, "country_code_alpha2"=>nil, "country_code_alpha3"=>nil, "first_name"=>nil}, "id"=>"49sbx6", "merchant_account_id"=>"Thoughtbot", "type"=>"sale", "cvv_response_code"=>"I", "subscription_id"=>FakeBraintree.transaction[:subscription_id], "custom_fields"=>"\n ", "discounts"=>[], "billing"=>{"region"=>nil, "company"=>nil, "country_name"=>nil, "extended_address"=>nil, "postal_code"=>nil, "id"=>nil, "street_address"=>nil, "country_code_numeric"=>nil, "last_name"=>nil, "locality"=>nil, "country_code_alpha2"=>nil, "country_code_alpha3"=>nil, "first_name"=>nil}, "processor_response_code"=>"1000", "refund_ids"=>[], "customer"=>{"company"=>nil, "id"=>"108427", "last_name"=>nil, "fax"=>nil, "phone"=>nil, "website"=>nil, "first_name"=>nil, "email"=>"cpytel@thoughtbot.com"}, "avs_error_response_code"=>nil, "processor_response_text"=>"Approved", "avs_street_address_response_code"=>"I", "status"=>FakeBraintree.transaction[:status], "gateway_rejection_reason"=>nil} end end ShamRack.at("www.braintreegateway.com", 443).sinatra do set :show_exceptions, false set :dump_errors, true set :raise_errors, true disable :logging post "/merchants/:merchant_id/customers" do customer = Hash.from_xml(request.body).delete("customer") if !FakeBraintree.failure?(customer["credit_card"]["number"]) customer["id"] ||= Digest::MD5.hexdigest("#{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"]["last_4"] = customer["credit_card"].delete("number")[-4..-1] customer["credit_card"]["token"] = Digest::MD5.hexdigest("#{customer['merchant_id']}#{customer['id']}#{Time.now.to_f}") credit_card = customer.delete("credit_card") customer["credit_cards"] = [credit_card] end FakeBraintree.customers[customer["id"]] = customer [201, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(customer.to_xml(:root => 'customer'))] else [422, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(FakeBraintree.failure_response(customer["credit_card"]["number"]).to_xml(:root => 'api_error_response'))] end end get "/merchants/:merchant_id/customers/:id" do customer = FakeBraintree.customers[params[:id]] [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(customer.to_xml(:root => 'customer'))] end put "/merchants/:merchant_id/customers/:id" do customer = Hash.from_xml(request.body).delete("customer") if !FakeBraintree.failure?(customer["credit_card"]["number"]) customer["id"] = params[:id] customer["merchant-id"] = params[:merchant_id] if customer["credit_card"] && customer["credit_card"].is_a?(Hash) customer["credit_card"]["last_4"] = customer["credit_card"].delete("number")[-4..-1] customer["credit_card"]["token"] = Digest::MD5.hexdigest("#{customer['merchant_id']}#{customer['id']}#{Time.now.to_f}") credit_card = customer.delete("credit_card") customer["credit_cards"] = [credit_card] end FakeBraintree.customers[params["id"]] = customer [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(customer.to_xml(:root => 'customer'))] else [422, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(FakeBraintree.failure_response(customer["credit_card"]["number"]).to_xml(:root => 'api_error_response'))] end end delete "/merchants/:merchant_id/customers/:id" do FakeBraintree.customers[params["id"]] = nil [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress("")] end post "/merchants/:merchant_id/subscriptions" do "\n\n 2\n b22x\n\n" subscription = Hash.from_xml(request.body).delete("subscription") subscription["id"] ||= Digest::MD5.hexdigest("#{subscription["payment_method_token"]}#{Time.now.to_f}") subscription["transactions"] = [] subscription["add_ons"] = [] subscription["discounts"] = [] subscription["next_billing_date"] = 1.month.from_now subscription["status"] = Braintree::Subscription::Status::Active FakeBraintree.subscriptions[subscription["id"]] = subscription [201, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))] end get "/merchants/:merchant_id/subscriptions/:id" do subscription = FakeBraintree.subscriptions[params[:id]] [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))] end put "/merchants/:merchant_id/subscriptions/:id" do subscription = Hash.from_xml(request.body).delete("subscription") subscription["transactions"] = [] subscription["add_ons"] = [] subscription["discounts"] = [] FakeBraintree.subscriptions[params["id"]] = subscription [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))] end post "/merchants/:merchant_id/transactions/advanced_search_ids" do # "\n\n \n 2011-01-10T14:14:26Z\n \n\n" [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress("\n 50\n \n 49sbx6\n \n\n")] end post "/merchants/:merchant_id/transactions/advanced_search" do # "\n\n \n 49sbx6\n \n \n 2011-01-10T14:14:26Z\n \n\n" [200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(FakeBraintree.generated_transaction.to_xml)] end end