samurai 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ <% transaction ||= Samurai::Transaction.new %>
2
+ <% payment_method ||= Samurai::PaymentMethod.new %>
3
+ <% if transaction.errors.any? || payment_method.errors.any? %>
4
+ <div id="error_explanation">
5
+ <h4>This transaction could not be processed:</h4>
6
+ <ul>
7
+ <% transaction.errors.full_messages.each do |msg| %>
8
+ <li><%= msg %></li>
9
+ <% end %>
10
+ <% payment_method.errors.full_messages.each do |msg| %>
11
+ <li><%= msg %></li>
12
+ <% end %>
13
+ </ul>
14
+ </div>
15
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <% sandbox ||= false %>
2
- <% payment_method ||= OpenStruct.new %>
2
+ <% payment_method ||= Samurai::PaymentMethod.new %>
3
3
  <form action="<%= Samurai.site %>/payment_methods" method="POST">
4
4
  <fieldset>
5
5
  <input name="redirect_url" type="hidden" value="<%= redirect_url %>" />
@@ -1,16 +1,7 @@
1
1
  <h3><%= transaction.errors.empty? ? 'Successful' : 'Failed' %> <%= transaction.transaction_type.titleize %> Transaction</h3>
2
2
  <h4>Reference ID: <%= transaction.reference_id %></h4>
3
3
 
4
- <% if transaction.errors.any? %>
5
- <div id="error_explanation">
6
- <h4>This transaction could not be processed:</h4>
7
- <ul>
8
- <% transaction.errors.full_messages.each do |msg| %>
9
- <li><%= msg %></li>
10
- <% end %>
11
- </ul>
12
- </div>
13
- <% end %>
4
+ <%= render Samurai::Views.errors :transaction=>transaction %>
14
5
 
15
6
  <p>
16
7
  <strong>Amount:</strong>
@@ -1,5 +1,5 @@
1
1
  <% authenticity_token ||= '' %>
2
- <% transaction ||= OpenStruct.new %>
2
+ <% transaction ||= Samurai::Transaction.new %>
3
3
  <form accept-charset="UTF-8" action="<%= post_url %>" class="new_transaction" id="new_transaction" method="post">
4
4
  <%= authenticity_token %>
5
5
  <input id="processor_token" name="processor_token" type="hidden" value="<%= processor_token %>"/>
data/lib/samurai.rb CHANGED
@@ -30,7 +30,7 @@ module Samurai
30
30
  @@options = (value || {}).reverse_merge(DEFAULT_OPTIONS)
31
31
  Samurai::Base.setup_site!
32
32
  end
33
-
33
+
34
34
  end
35
35
 
36
36
  require 'samurai/cacheable_by_token'
@@ -40,3 +40,5 @@ require 'samurai/payment_method'
40
40
  require 'samurai/transaction'
41
41
  require 'samurai/message'
42
42
  require 'samurai/processor_response'
43
+
44
+ require 'samurai/rails'
data/lib/samurai/base.rb CHANGED
@@ -11,11 +11,18 @@ class Samurai::Base < ActiveResource::Base
11
11
  self.password = Samurai.merchant_password
12
12
  end
13
13
 
14
+ def has_errors?
15
+ respond_to?(:errors) && !errors.empty?
16
+ end
17
+
14
18
  protected
15
19
 
16
20
  def load_attributes_from_response(response)
17
21
  super.tap { |instance| instance.process_response_errors }
18
22
  end
23
+ def self.instantiate_record(record, prefix_options = {})
24
+ super.tap { |instance| instance.send :process_response_errors }
25
+ end
19
26
 
20
27
  def process_response_errors
21
28
  # Do nothing by default, subclasses may override this to process specific error messages
@@ -27,13 +27,38 @@ class Samurai::PaymentMethod < Samurai::Base
27
27
  @custom_data ||= self.custom && (JSON.parse(self.custom) rescue {}).symbolize_keys
28
28
  end
29
29
 
30
+ def process_response_errors
31
+ if respond_to?(:messages) && self.messages
32
+ self.messages.each do |message|
33
+ #if (message.respond_to?(:subclass) && message.subclass == 'error')
34
+ self.errors.add message.context.gsub(/\./, ' '), message.key
35
+ #end
36
+ end
37
+ end
38
+ end
39
+ protected :process_response_errors
30
40
 
31
- require 'pathname'
32
- def self.form_html
33
- File.read(form_partial_path)
41
+ # Initialize the known attributes from the schema as empty strings, so that they can be accessed via method-missing
42
+ KNOWN_ATTRIBUTES = [
43
+ :first_name, :last_name, :address_1, :address_2, :city, :state, :zip,
44
+ :card_number, :cvv, :expiry_month, :expiry_year
45
+ ]
46
+ EMPTY_ATTRIBUTES = KNOWN_ATTRIBUTES.inject({}) {|h, k| h[k] = ''; h}
47
+ def initialize(attrs={})
48
+ super(EMPTY_ATTRIBUTES.merge(attrs))
34
49
  end
35
- def self.form_partial_path
36
- Pathname.new(__FILE__).dirname.join('..', '..', 'app', 'views', 'application', '_payment_method_form.html.erb')
50
+
51
+ # Prepare a new PaymentMethod for use with a transparent redirect form
52
+ def self.for_transparent_redirect(params)
53
+ if params[:payment_method_token].blank?
54
+ Samurai::PaymentMethod.new(params)
55
+ else
56
+ Samurai::PaymentMethod.find(params[:payment_method_token]).tap do |pm|
57
+ pm.card_number = "************#{pm.last_four_digits}"
58
+ pm.cvv = "***"
59
+ pm.errors[:base] << 'The card number or CVV are not valid.' if !pm.is_sensitive_data_valid
60
+ end
61
+ end
37
62
  end
38
63
 
39
64
  end
@@ -0,0 +1,7 @@
1
+ module Samurai
2
+ module Rails
3
+ end
4
+ end
5
+
6
+ require 'samurai/rails/views'
7
+ require 'samurai/rails/helpers'
@@ -0,0 +1,28 @@
1
+ require 'pathname'
2
+
3
+ module Samurai::Rails
4
+ module Helpers
5
+
6
+ def setup_for_transparent_redirect(params)
7
+ @transaction = Samurai::Transaction.find params[:reference_id] unless params[:reference_id].blank?
8
+ @payment_method = Samurai::PaymentMethod.for_transparent_redirect(params)
9
+ end
10
+
11
+ def load_and_verify_payment_method(params)
12
+ if params[:payment_method_token].blank?
13
+ @payment_method = Samurai::PaymentMethod.new :is_sensitive_data_valid=>false
14
+ else
15
+ @payment_method = Samurai::PaymentMethod.find params[:payment_method_token]
16
+ @payment_method = nil if @payment_method && !@payment_method.is_sensitive_data_valid?
17
+ end
18
+ @payment_method
19
+ end
20
+
21
+ def payment_method_params
22
+ { :payment_method_token => params[:payment_method_token] }.tap do |_params|
23
+ _params[:reference_id] = @transaction.reference_id if @transaction
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require 'pathname'
2
+
3
+ module Samurai::Rails
4
+ class Views
5
+ class << self
6
+
7
+ PARTIALS = [ 'payment_method_form', 'transaction_form', 'errors', 'transaction' ]
8
+
9
+ PARTIALS.each do |partial|
10
+ define_method partial do |attrs|
11
+ attrs ||= {}
12
+ {:file=>send("#{partial}_file"), :locals=>attrs}
13
+ end
14
+ define_method "#{partial}_file" do
15
+ Pathname.new(__FILE__).dirname.join('../../..', 'app', 'views', 'application', "_#{partial}.html.erb").to_s
16
+ end
17
+ define_method "#{partial}_html" do
18
+ File.read(send partial)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -27,6 +27,21 @@ class Samurai::Transaction < Samurai::Base
27
27
  execute(:credit, {:amount => amount || self.amount}.reverse_merge(options))
28
28
  end
29
29
 
30
+ # Reverse this transaction. First, tries a void.
31
+ # If a void is unsuccessful, (because the transaction has already settled) perform a credit for the full amount.
32
+ def reverse(options = {})
33
+ transaction = void(options)
34
+ return transaction if transaction.processor_response.success
35
+ return credit(nil, options)
36
+ end
37
+
38
+ def success?
39
+ respond_to?(:processor_response) && processor_response.success
40
+ end
41
+ def failed?
42
+ !success?
43
+ end
44
+
30
45
  private
31
46
 
32
47
  def execute(action, options = {})
@@ -62,6 +77,17 @@ class Samurai::Transaction < Samurai::Base
62
77
  to_xml(:skip_instruct => true, :root => 'transaction', :dasherize => false)
63
78
  end
64
79
 
80
+ # Initialize the known attributes from the schema as empty strings, so that they can be accessed via method-missing
81
+ KNOWN_ATTRIBUTES = [
82
+ :amount, :type, :payment_method_token, :currency_code,
83
+ :descriptor, :custom, :customer_reference, :billing_reference
84
+ ]
85
+ EMPTY_ATTRIBUTES = KNOWN_ATTRIBUTES.inject({}) {|h, k| h[k] = ''; h}
86
+ def initialize(attrs={})
87
+ super(EMPTY_ATTRIBUTES.merge(attrs))
88
+ end
89
+
90
+
65
91
  require 'pathname'
66
92
  def self.form_html
67
93
  File.read(form_partial_path)
@@ -1,3 +1,3 @@
1
1
  module Samurai
2
- VERSION = "0.2.4".freeze
2
+ VERSION = "0.2.5".freeze
3
3
  end
@@ -1,10 +1,10 @@
1
- require 'test/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe "processing authorizations" do
4
4
 
5
5
  before :each do
6
- register_transaction_response(:type => 'authorize')
7
- @authorization = Samurai::Processor.authorize(PAYMENT_METHOD_TOKEN, @@seed)
6
+ payment_method_token = PAYMENT_METHOD_TOKENS[:success]
7
+ @authorization = Samurai::Processor.authorize(payment_method_token, @@seed)
8
8
  end
9
9
 
10
10
  it "should create a new authorization transaction" do
@@ -12,39 +12,33 @@ describe "processing authorizations" do
12
12
  end
13
13
 
14
14
  it "should find the authorization" do
15
- register_transaction_response(:method => :get, :path => "transactions/#{@authorization.reference_id}", :type => 'authorize')
16
15
  transaction = Samurai::Transaction.find(@authorization.reference_id)
17
16
  transaction.reference_id.intern.should be_equal(@authorization.reference_id.intern)
18
17
  end
19
18
 
20
19
  it "should successfully capture" do
21
- register_transaction_response(:method => :post, :path => "transactions/#{@authorization.id}/capture", :type => 'capture')
22
20
  capture = @authorization.capture(@@seed)
23
21
  capture.processor_response.success.should be_true
24
22
  end
25
23
 
26
24
  it "should capture an authorization without specifying an amount" do
27
- register_transaction_response(:method => :post, :path => "transactions/#{@authorization.id}/capture", :type => 'capture', :amount => @@seed)
28
25
  capture = @authorization.capture
29
26
  capture.amount.intern.should be_equal "#{@@seed}".intern
30
27
  capture.processor_response.success.should be_true
31
28
  end
32
29
 
33
30
  it "should partially capture an authorization" do
34
- register_transaction_response(:method => :post, :path => "transactions/#{@authorization.id}/capture", :type => 'capture', :amount => @@seed - 1.0)
35
31
  capture = @authorization.capture(@@seed - 1.0)
36
32
  capture.amount.intern.should be_equal "#{@@seed - 1.0}".intern
37
33
  capture.processor_response.success.should be_true
38
34
  end
39
35
 
40
36
  it "should void an authorization" do
41
- register_transaction_response(:method => :post, :path => "transactions/#{@authorization.id}/void", :type => 'void', :amount => @@seed)
42
37
  void = @authorization.void
43
38
  void.processor_response.success.should be_true
44
39
  end
45
40
 
46
41
  it "should credit an authorization for the full amount by default" do
47
- register_transaction_response(:method => :post, :path => "transactions/#{@authorization.id}/credit", :type => 'credit', :amount => @@seed, :success => 'false')
48
42
  credit = @authorization.credit
49
43
  credit.amount.intern.should be_equal "#{@@seed}".intern
50
44
  pending "the response is not successful since the authorization hasn't settled" do
@@ -53,7 +47,6 @@ describe "processing authorizations" do
53
47
  end
54
48
 
55
49
  it "should partially credit an authorization" do
56
- register_transaction_response(:method => :post, :path => "transactions/#{@authorization.id}/credit", :type => 'credit', :amount => @@seed - 1.0, :success => 'false')
57
50
  credit = @authorization.credit(@@seed - 1.0)
58
51
  credit.amount.intern.should be_equal "#{@@seed - 1.0}".intern
59
52
  pending "the response is not successful since the authorization hasn't settled" do
@@ -0,0 +1,207 @@
1
+ require 'spec_helper'
2
+
3
+ describe "generate documentation" do
4
+ include TransparentRedirectHelper
5
+ include ResponseLoggerHelper
6
+
7
+ before(:all) do
8
+ @logger = ResponseLogger.new(File.open('response_log.html', 'w'))
9
+ end
10
+ after(:all) do
11
+ @logger.close!
12
+ end
13
+
14
+ before do
15
+ @logger.begin_section example.full_description.sub(/generate documentation/, '').titleize
16
+
17
+ @params = {
18
+ 'redirect_url' => 'http://test.host',
19
+ 'merchant_key' => Samurai.merchant_key,
20
+ 'custom' => 'custom',
21
+ 'credit_card[first_name]' => 'FirstName',
22
+ 'credit_card[last_name]' => 'LastName',
23
+ 'credit_card[address_1]' => '123 Main St',
24
+ 'credit_card[address_2]' => '',
25
+ 'credit_card[city]' => 'Chicago',
26
+ 'credit_card[state]' => 'IL',
27
+ 'credit_card[zip]' => '60610',
28
+ 'credit_card[card_number]' => '4222222222222',
29
+ 'credit_card[cvv]' => '123',
30
+ 'credit_card[expiry_month]' => '05',
31
+ 'credit_card[expiry_year]' => '2014',
32
+ }
33
+
34
+ Samurai::Base.instance_eval do
35
+ def connection(refresh = false)
36
+ if defined?(@connection) || superclass == ActiveResource::Base
37
+ @connection ||= begin
38
+ c = HttpProxyConnection.new(site, format)
39
+ c.proxy = proxy if proxy
40
+ c.user = user if user
41
+ c.password = password if password
42
+ c.auth_type = auth_type if auth_type
43
+ c.timeout = timeout if timeout
44
+ c.ssl_options = ssl_options if ssl_options
45
+ c
46
+ end
47
+ else
48
+ superclass.connection
49
+ end
50
+ end
51
+ end
52
+ Samurai::Base.connection
53
+ end
54
+
55
+ after do
56
+ @logger.end_section
57
+ end
58
+
59
+ describe 'with an invalid payment method' do
60
+ it 'should not create the payment method with missing card_number' do
61
+ @params.delete 'credit_card[card_number]'
62
+ data = create_payment_method(@params)
63
+ log_request_response! data[:request], data[:response]
64
+ data[:payment_method_token].should be_nil
65
+ end
66
+
67
+ it 'should create the payment method with invalid card_number format' do
68
+ @params['credit_card[card_number]'] = '12345'
69
+ data = create_payment_method(@params)
70
+ log_request_response! data[:request], data[:response]
71
+ data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
72
+ Samurai::PaymentMethod.find data[:payment_method_token]
73
+ log_http!
74
+ end
75
+
76
+ it 'should create the payment method with blank cvv' do
77
+ @params['credit_card[cvv]'] = ''
78
+ data = create_payment_method(@params)
79
+ log_request_response! data[:request], data[:response]
80
+ data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
81
+ Samurai::PaymentMethod.find data[:payment_method_token]
82
+ log_http!
83
+ end
84
+ end
85
+
86
+ describe 'with a valid payment method' do
87
+ before do
88
+ @amount = '1.00' # response code: card number is declined
89
+ @data = create_payment_method(@params)
90
+ end
91
+ it 'should create the payment method' do
92
+ log_request_response! @data[:request], @data[:response]
93
+ @data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
94
+ Samurai::PaymentMethod.find @data[:payment_method_token]
95
+ log_http!
96
+ end
97
+ it 'should create a valid transaction' do
98
+ purchase = Samurai::Processor.purchase(@data[:payment_method_token], @amount)
99
+ log_http!
100
+ purchase.processor_response.success.should be_true
101
+ end
102
+ it 'should create an invalid transaction' do
103
+ lambda do
104
+ Samurai::Processor.purchase(@data[:payment_method_token], '')
105
+ end.should raise_error(ActiveResource::ServerError)
106
+ log_http!
107
+ end
108
+ end
109
+
110
+ describe 'with a declined card' do
111
+ before do
112
+ @amount = '3.00' # response code: card number is declined
113
+ @data = create_payment_method(@params)
114
+ end
115
+ it 'should create the payment method' do
116
+ log_request_response! @data[:request], @data[:response]
117
+ @data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
118
+ Samurai::PaymentMethod.find @data[:payment_method_token]
119
+ log_http!
120
+ end
121
+ it 'should create a valid transaction' do
122
+ purchase = Samurai::Processor.purchase(@data[:payment_method_token], @amount)
123
+ log_http!
124
+ purchase.processor_response.success.should be_false
125
+ end
126
+ it 'should create an invalid transaction' do
127
+ lambda do
128
+ Samurai::Processor.purchase(@data[:payment_method_token], '')
129
+ end.should raise_error(ActiveResource::ServerError)
130
+ log_http!
131
+ end
132
+ end
133
+
134
+ describe 'with an expired card' do
135
+ before do
136
+ @amount = '8.00' # response code: card is expired
137
+ @data = create_payment_method(@params)
138
+ end
139
+ it 'should create the payment method' do
140
+ log_request_response! @data[:request], @data[:response]
141
+ @data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
142
+ Samurai::PaymentMethod.find @data[:payment_method_token]
143
+ log_http!
144
+ end
145
+ it 'should create a valid transaction' do
146
+ purchase = Samurai::Processor.purchase(@data[:payment_method_token], @amount)
147
+ log_http!
148
+ purchase.processor_response.success.should be_false
149
+ end
150
+ it 'should create an invalid transaction' do
151
+ lambda do
152
+ Samurai::Processor.purchase(@data[:payment_method_token], '')
153
+ end.should raise_error(ActiveResource::ServerError)
154
+ log_http!
155
+ end
156
+ end
157
+
158
+ describe 'with a card with incorrect cvv' do
159
+ before do
160
+ @amount = '6.00' # response code: card number is invalid
161
+ @data = create_payment_method(@params)
162
+ end
163
+ it 'should create the payment method' do
164
+ log_request_response! @data[:request], @data[:response]
165
+ @data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
166
+ Samurai::PaymentMethod.find @data[:payment_method_token]
167
+ log_http!
168
+ end
169
+ it 'should create a valid transaction' do
170
+ purchase = Samurai::Processor.purchase(@data[:payment_method_token], @amount)
171
+ log_http!
172
+ purchase.processor_response.success.should be_false
173
+ end
174
+ it 'should create an invalid transaction' do
175
+ lambda do
176
+ Samurai::Processor.purchase(@data[:payment_method_token], '')
177
+ end.should raise_error(ActiveResource::ServerError)
178
+ log_http!
179
+ end
180
+ end
181
+
182
+ describe 'with an nonexistant card' do
183
+ before do
184
+ @params['credit_card[card_number]'] = '4222222222222'
185
+ @amount = '6.00' # response code: card number is invalid
186
+ @data = create_payment_method(@params)
187
+ end
188
+ it 'should create the payment method' do
189
+ log_request_response! @data[:request], @data[:response]
190
+ @data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
191
+ Samurai::PaymentMethod.find @data[:payment_method_token]
192
+ log_http!
193
+ end
194
+ it 'should create a valid transaction' do
195
+ purchase = Samurai::Processor.purchase(@data[:payment_method_token], @amount)
196
+ log_http!
197
+ purchase.processor_response.success.should be_false
198
+ end
199
+ it 'should create an invalid transaction' do
200
+ lambda do
201
+ Samurai::Processor.purchase(@data[:payment_method_token], '')
202
+ end.should raise_error(ActiveResource::ServerError)
203
+ log_http!
204
+ end
205
+ end
206
+
207
+ end
@@ -1,24 +1,23 @@
1
- require 'test/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe "Processor actions" do
4
-
4
+ before :each do
5
+ @payment_method_token = PAYMENT_METHOD_TOKENS[:success]
6
+ end
7
+
5
8
  it "should return an empty processor" do
6
9
  processor = Samurai::Processor.the_processor
7
10
  processor.should_not be_nil
8
11
  end
9
12
 
10
13
  it "should create a new purchase" do
11
- register_transaction_response(:type => 'purchase')
12
-
13
- purchase = Samurai::Processor.purchase(PAYMENT_METHOD_TOKEN, @@seed)
14
+ purchase = Samurai::Processor.purchase(@payment_method_token, @@seed)
14
15
  purchase.processor_response.success.should be_true
15
16
  # FakeWeb.last_request
16
17
  end
17
18
 
18
19
  it "should create a new purchase with tracking data" do
19
- register_transaction_response(:type => 'purchase')
20
-
21
- purchase = Samurai::Processor.purchase(PAYMENT_METHOD_TOKEN, @@seed, {
20
+ purchase = Samurai::Processor.purchase(@payment_method_token, @@seed, {
22
21
  :descriptor => "A test purchase",
23
22
  :custom => "some optional custom data",
24
23
  :billing_reference => "ABC123",
@@ -29,8 +28,7 @@ describe "Processor actions" do
29
28
  end
30
29
 
31
30
  it "should create a non-new authorization" do
32
- register_transaction_response(:type => 'authorize')
33
- authorization = Samurai::Processor.authorize(PAYMENT_METHOD_TOKEN, @@seed)
31
+ authorization = Samurai::Processor.authorize(@payment_method_token, @@seed)
34
32
  authorization.processor_response.success.should be_true
35
33
  end
36
34
 
@@ -1,10 +1,10 @@
1
- require 'test/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe "processing purchases" do
4
4
 
5
5
  before :each do
6
- register_transaction_response(:type => 'purchase')
7
- @purchase = Samurai::Processor.purchase(PAYMENT_METHOD_TOKEN, @@seed)
6
+ payment_method_token = PAYMENT_METHOD_TOKENS[:success]
7
+ @purchase = Samurai::Processor.purchase(payment_method_token, @@seed)
8
8
  end
9
9
 
10
10
  it "should process successfully" do
@@ -12,20 +12,28 @@ describe "processing purchases" do
12
12
  end
13
13
 
14
14
  it "should be able to void a recent purchase" do
15
- register_transaction_response(:method => :post, :path => "transactions/#{@purchase.id}/void", :type => 'void', :success => 'false')
16
15
  void = @purchase.void
17
16
  void.processor_response.success.should be_true
18
17
  end
19
18
 
20
19
  it "should not be able to credit a recent purchase" do
21
- register_transaction_response(:method => :post, :path => "transactions/#{@purchase.id}/credit", :type => 'void', :success => 'false')
22
20
  credit = @purchase.credit
23
21
  credit.processor_response.success.should be_false
24
22
  end
25
23
 
24
+ it "should be able to reverse a recent purchase" do
25
+ reverse = @purchase.reverse
26
+ reverse.processor_response.success.should be_true
27
+ end
28
+
29
+ it "should be able to reverse a settled purchase" do
30
+ pending "currently we cannot force settle a purchase, so can't test this properly"
31
+ reverse = @purchase.reverse
32
+ reverse.processor_response.success.should be_true
33
+ end
34
+
26
35
  it "should be able to credit a settled purchase" do
27
36
  pending "currently we cannot force settle a purchase, so can't test this properly" do
28
- register_transaction_response(:method => :post, :path => "transactions/#{@purchase.id}/credit", :type => 'void', :success => 'false')
29
37
  credit = @purchase.credit
30
38
  credit.processor_response.success.should be_true
31
39
  end
@@ -0,0 +1,34 @@
1
+ require 'rspec'
2
+ require 'ruby-debug'
3
+ require 'pp'
4
+ Debugger.start
5
+ Debugger.settings[:autoeval] = true
6
+ Debugger.settings[:autolist] = 5
7
+ Debugger.settings[:reload_source_on_change] = true
8
+
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
11
+ SITE = ENV['site'] || 'https://samurai.feefighters.com/v1/'
12
+ USE_MOCK = !ENV['site']
13
+
14
+ PAYMENT_METHOD_TOKENS = {
15
+ :success => 'b7c966452702282b32a4c65d'
16
+ }
17
+
18
+ RSpec.configure do |c|
19
+ c.before :all do
20
+ @@seed = rand(1000).to_f / 100.0
21
+ end
22
+ c.before :each do
23
+ @@seed += 1.0
24
+ end
25
+ end
26
+
27
+ require 'samurai'
28
+ Samurai.options = {
29
+ :site => SITE,
30
+ :merchant_key => ENV['merchant_key'] || 'f4b17359f267915e705fdcb6',
31
+ :merchant_password => ENV['merchant_password'] || 'd7bf19a8aa1051335b83b349',
32
+ :processor_token => ENV['processor_token'] || 'c5823b5f1616ed6c0891d167'
33
+ }
34
+
@@ -0,0 +1,40 @@
1
+ require 'active_resource/connection'
2
+
3
+ class HttpProxy
4
+ attr_accessor :request, :response
5
+
6
+ def initialize(http, options={})
7
+ @http = http
8
+ @response = nil
9
+ @request = {}
10
+ end
11
+
12
+ def method_missing(meth, *args, &block)
13
+ case meth
14
+ when :post, :put
15
+ @request = {
16
+ :method => meth,
17
+ :path => args[0],
18
+ :body => args[1],
19
+ :headers => args[2],
20
+ }
21
+ when :get, :delete
22
+ @request = {
23
+ :method => meth,
24
+ :path => args[0],
25
+ :headers => args[1],
26
+ }
27
+ end
28
+ @response = @http.send(meth, *args, &block)
29
+ end
30
+
31
+ def respond_to?(meth)
32
+ super || @http.respond_to?(meth)
33
+ end
34
+ end
35
+
36
+ class HttpProxyConnection < ActiveResource::Connection
37
+ def http
38
+ @http ||= HttpProxy.new(configure_http(new_http))
39
+ end
40
+ end
@@ -0,0 +1,71 @@
1
+ require "erb"
2
+
3
+ class ResponseLogger
4
+ include ERB::Util
5
+
6
+ def initialize(io)
7
+ @sections = []
8
+ @io = io
9
+ @io.puts '<div class="wrapper">'
10
+ end
11
+
12
+ def close!
13
+ @io.puts '</div>'
14
+ @sections.each do |section|
15
+ @io.puts "<a href='##{section[:id]}'>#{section[:name]}</a><br>"
16
+ end
17
+ end
18
+
19
+ def begin_section(name)
20
+ @sections << {:name=>name, :id=>h(name.parameterize)}
21
+ @io.puts "<article class='example span-8' id='#{h name.parameterize}'>"
22
+ @io.puts " <h3>#{h name}</h3>"
23
+ end
24
+
25
+ def end_section
26
+ @io.puts "</article>"
27
+ @io.puts "<hr>"
28
+ end
29
+
30
+ def log(request, response, options={})
31
+ @io.puts ' <div class="code http-request"><em class="lang">HTTP Request</em>'
32
+ @io.puts " <pre><strong>#{h request[:method].to_s.upcase} #{h request[:path]}</strong><br>"
33
+ @io.puts "Headers: #{h request[:headers].inject({}) {|h, (k,v)| h[k] = (v.is_a?(Array) ? v.first : v); h }}</pre>"
34
+ @io.puts ' </div>'
35
+ if request[:body]
36
+ @io.puts ' <div class="code xml">'
37
+ @io.puts ' <em class="lang">XML Payload</em>'
38
+ @io.puts " <pre class='prettyprint lang-xml'>#{h request[:body]}</pre>"
39
+ @io.puts ' </div>'
40
+ end
41
+ @io.puts ' <div class="code http-response">'
42
+ @io.puts " <em class='lang'>HTTP Response: #{h response.code}</em>"
43
+ @io.puts " <pre class='prettyprint lang-xml'><code>#{h response.body}</code></pre>"
44
+ @io.puts " </div>"
45
+ end
46
+
47
+ end
48
+
49
+ module ResponseLoggerHelper
50
+ def log_http! options={}
51
+ @logger.log Samurai::Base.connection.http.request,
52
+ Samurai::Base.connection.http.response,
53
+ options
54
+ end
55
+
56
+ def log_request_response! request, response, options={}
57
+ def request.[](v)
58
+ case v
59
+ when :method
60
+ return self.method
61
+ when :path
62
+ return self.path
63
+ when :headers
64
+ return self.to_hash
65
+ else
66
+ super
67
+ end
68
+ end
69
+ @logger.log request, response, options
70
+ end
71
+ end
@@ -0,0 +1,26 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module TransparentRedirectHelper
5
+
6
+ def create_payment_method(params = {}, options={})
7
+ url = Samurai.site + 'payment_methods'
8
+ url.sub! %r{https://}, "https://#{Samurai.merchant_key}:#{Samurai.merchant_password}@"
9
+
10
+ uri = URI.parse url
11
+ req = Net::HTTP::Post.new uri.path
12
+ req.set_form_data params
13
+ req.basic_auth uri.user, uri.password
14
+
15
+ res = Net::HTTP.new(uri.host, uri.port)
16
+ res.use_ssl = true
17
+
18
+ response = res.start {|http| http.request(req) }
19
+ {
20
+ :payment_method_token => response['Location'] && response['Location'].sub(%r{#{Regexp.escape params['redirect_url']}\?payment_method_token=}, ''),
21
+ :response => response,
22
+ :request => req,
23
+ }
24
+ end
25
+
26
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samurai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-08-09 00:00:00.000000000Z
13
+ date: 2011-08-18 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activeresource
17
- requirement: &70158124256520 !ruby/object:Gem::Requirement
17
+ requirement: &70227589695120 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 2.2.2
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70158124256520
25
+ version_requirements: *70227589695120
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: bundler
28
- requirement: &70158124256060 !ruby/object:Gem::Requirement
28
+ requirement: &70227589694660 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 1.0.0
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *70158124256060
36
+ version_requirements: *70227589694660
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rspec
39
- requirement: &70158124255600 !ruby/object:Gem::Requirement
39
+ requirement: &70227589694200 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 2.6.0
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70158124255600
47
+ version_requirements: *70227589694200
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: fakeweb
50
- requirement: &70158124255220 !ruby/object:Gem::Requirement
50
+ requirement: &70227589693820 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *70158124255220
58
+ version_requirements: *70227589693820
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: ruby-debug19
61
- requirement: &70158124254720 !ruby/object:Gem::Requirement
61
+ requirement: &70227589693320 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,7 +66,7 @@ dependencies:
66
66
  version: '0'
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70158124254720
69
+ version_requirements: *70227589693320
70
70
  description: If you are an online merchant and using samurai.feefighters.com, this
71
71
  gem will make your life easy. Integrate with the samurai.feefighters.com portal
72
72
  and process transaction.
@@ -83,6 +83,7 @@ files:
83
83
  - Gemfile.lock
84
84
  - README.markdown
85
85
  - Rakefile
86
+ - app/views/application/_errors.html.erb
86
87
  - app/views/application/_payment_method_form.html.erb
87
88
  - app/views/application/_transaction.html.erb
88
89
  - app/views/application/_transaction_form.html.erb
@@ -93,13 +94,20 @@ files:
93
94
  - lib/samurai/payment_method.rb
94
95
  - lib/samurai/processor.rb
95
96
  - lib/samurai/processor_response.rb
97
+ - lib/samurai/rails.rb
98
+ - lib/samurai/rails/helpers.rb
99
+ - lib/samurai/rails/views.rb
96
100
  - lib/samurai/transaction.rb
97
101
  - lib/samurai/version.rb
98
102
  - samurai.gemspec
99
- - test/spec/authorization_spec.rb
100
- - test/spec/processor_spec.rb
101
- - test/spec/purchase_spec.rb
102
- - test/spec_helper.rb
103
+ - spec/lib/authorization_spec.rb
104
+ - spec/lib/generate_docs_spec.rb
105
+ - spec/lib/processor_spec.rb
106
+ - spec/lib/purchase_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/support/http_proxy.rb
109
+ - spec/support/response_logger.rb
110
+ - spec/support/transparent_redirect_helper.rb
103
111
  homepage: http://rubygems.org/gems/samurai
104
112
  licenses: []
105
113
  post_install_message:
data/test/spec_helper.rb DELETED
@@ -1,91 +0,0 @@
1
- require 'ruby-debug'
2
- Debugger.start
3
- Debugger.settings[:autoeval] = true
4
- Debugger.settings[:autolist] = 5
5
- Debugger.settings[:reload_source_on_change] = true
6
-
7
- SITE = ENV['site'] || 'http://localhost:3002/v1/'
8
- USE_MOCK = !ENV['site']
9
- PAYMENT_METHOD_TOKEN = ENV['payment_method_token'] || 'asdf'
10
-
11
- RSpec.configure do |c|
12
- c.before :all do
13
- @@seed = rand(1000).to_f / 100.0
14
- end
15
- c.before :each do
16
- @@seed += 1.0
17
- end
18
- end
19
-
20
- require 'fakeweb'
21
- FakeWeb.allow_net_connect = !USE_MOCK
22
-
23
- require 'samurai'
24
- Samurai.options = {
25
- :site => SITE,
26
- :merchant_key => ENV['merchant_key'] || 'e62c5a006cdd9908234193bc',
27
- :merchant_password => ENV['merchant_password'] || '18e87d97b3a44b56fe07497e4812f14555db69df9e6ca16f',
28
- :processor_token => ENV['processor_token'] || 'af762c3499f77c5f181650a7'
29
- }
30
-
31
- def register_transaction_response(options)
32
- return unless USE_MOCK
33
-
34
- options.symbolize_keys!
35
-
36
- method = options[:method] && options[:method].to_sym || :post
37
- type = options[:type]
38
- path = options[:path] || "processors/af762c3499f77c5f181650a7/#{type}"
39
- payment_method_token = options[:payment_method_token] || PAYMENT_METHOD_TOKEN
40
- amount = options[:amount] || 15.00
41
- success = options[:success].blank? ? true : options[:success]
42
-
43
- FakeWeb.register_uri(method,
44
- "http://e62c5a006cdd9908234193bc:18e87d97b3a44b56fe07497e4812f14555db69df9e6ca16f@localhost:3002/v1/#{path}.xml",
45
- :body => <<-EOF
46
- <transaction>
47
- <reference_id>3dcFjTC7LDjIjTY3nkKjBVZ8qkZ</reference_id>
48
- <transaction_token>53VFyQKYBmN9vKfA9mHCTs79L9a</transaction_token>
49
- <created_at type="datetime">2011-04-22T17:57:56Z</created_at>
50
- <descriptor>Custom descriptor here if your processor supports it.</descriptor>
51
- <custom>Any value you like.</custom>
52
- <transaction_type>#{type}</transaction_type>
53
- <amount>#{amount}</amount>
54
- <currency_code>USD</currency_code>
55
- <processor_token>af762c3499f77c5f181650a7</processor_token>
56
- <processor_response>
57
- <success type="boolean">#{success}</success>
58
- <messages type="array">
59
- <message class="error" context="processor.avs" key="country_not_supported" />
60
- <message class="error" context="input.cvv" key="too_short" />
61
- </messages>
62
- </processor_response>
63
- <payment_method>
64
- <payment_method_token>#{payment_method_token}</payment_method_token>
65
- <created_at type="datetime">2011-02-12T20:20:46Z</created_at>
66
- <updated_at type="datetime">2011-04-22T17:57:30Z</updated_at>
67
- <custom>Any value you want us to save with this payment method.</custom>
68
- <is_retained type="boolean">true</is_retained>
69
- <is_redacted type="boolean">false</is_redacted>
70
- <is_sensitive_data_valid type="boolean">true</is_sensitive_data_valid>
71
- <messages type="array">
72
- <message class="error" context="input.cvv" key="too_long" />
73
- <message class="error" context="input.card_number" key="failed_checksum" />
74
- </messages>
75
- <last_four_digits>1111</last_four_digits>
76
- <card_type>visa</card_type>
77
- <first_name>Bob</first_name>
78
- <last_name>Smith</last_name>
79
- <expiry_month type="integer">1</expiry_month>
80
- <expiry_year type="integer">2020</expiry_year>
81
- <address_1 nil="true"></address_1>
82
- <address_2 nil="true"></address_2>
83
- <city nil="true"></city>
84
- <state nil="true"></state>
85
- <zip nil="true"></zip>
86
- <country nil="true"></country>
87
- </payment_method>
88
- </transaction>
89
- EOF
90
- )
91
- end