samurai 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/samurai.rb CHANGED
@@ -1,6 +1,19 @@
1
- # Author:: Graeme Rouse
2
- # Copyright:: Copyright (c) 2011 Arizona Bay, LLC
1
+ # Samurai Gem
2
+ # -----------------
3
3
 
4
+ # Core Samurai Module
5
+ # Contains accessors for the important Samurai configuration settings
6
+ #
7
+ # To configure the Samurai gem:
8
+ #
9
+ # ```ruby
10
+ # require 'samurai'
11
+ # Samurai.options = {
12
+ # :merchant_key => 'a1ebafb6da5238fb8a3ac9f6',
13
+ # :merchant_password => 'ae1aa640f6b735c4730fbb56',
14
+ # :processor_token => '69ac9c704329bb067d427bf0'
15
+ # }
16
+ # ```
4
17
  module Samurai
5
18
  SITE = 'https://api.samurai.feefighters.com/v1/'
6
19
  DEFAULT_OPTIONS = {:site => SITE}
@@ -33,6 +46,7 @@ module Samurai
33
46
 
34
47
  end
35
48
 
49
+ # Require each of the samurai components
36
50
  require 'samurai/cacheable_by_token'
37
51
  require 'samurai/base'
38
52
  require 'samurai/processor'
@@ -40,7 +54,6 @@ require 'samurai/payment_method'
40
54
  require 'samurai/transaction'
41
55
  require 'samurai/message'
42
56
  require 'samurai/processor_response'
43
-
44
57
  require 'samurai/rails'
45
58
 
46
59
 
data/lib/samurai/base.rb CHANGED
@@ -3,10 +3,16 @@ begin
3
3
  rescue LoadError
4
4
  require 'activeresource' # for older versions of activeresource
5
5
  end
6
+
7
+ # Samurai::Base
8
+ # -----------------
9
+
10
+ # Base class that all Samurai ActiveResource models inherit from
11
+ # Provides some common error-handling functionality, as well as the AR site settings
6
12
  class Samurai::Base < ActiveResource::Base
7
13
  self.format = ActiveResource::Formats::XmlFormat
8
14
 
9
- def self.setup_site! # :nodoc:
15
+ def self.setup_site!
10
16
  self.site = Samurai.site
11
17
  self.user = Samurai.merchant_key
12
18
  self.password = Samurai.merchant_password
@@ -1,7 +1,13 @@
1
+ # Samurai::CacheableByToken
2
+ # -----------------
3
+
4
+ # Module for enabling caching of ActiveResource classes by token
5
+ # Essentially functions as a simple Identity Map
1
6
  module Samurai::CacheableByToken
2
7
 
3
8
  # The default cache stores the values for the duration of the request
4
9
  # Different caching strategies can be employed to keep the data around longer:
10
+ #
5
11
  # * class variables
6
12
  # * Rails.cache
7
13
  # * memcached
@@ -13,7 +19,7 @@ module Samurai::CacheableByToken
13
19
  end
14
20
 
15
21
  module ClassExtensions
16
- # Override the ActiveResource +find+ method to query the cache before hitting the provider.
22
+ # Override the ActiveResource `find` method to query the cache before hitting the provider.
17
23
  def find(*arguments)
18
24
  token = arguments.first
19
25
  if token.is_a?(String) && self.cache[token]
@@ -27,7 +33,7 @@ module Samurai::CacheableByToken
27
33
  end
28
34
  end
29
35
 
30
- # Overrides the ActiveResource +save+ method to update the current
36
+ # Overrides the ActiveResource `save` method to update the current
31
37
  # model in the cache
32
38
  def save
33
39
  super
@@ -1,4 +1,9 @@
1
- class Samurai::Message < Samurai::Base # :nodoc:
1
+ # Samurai::Message
2
+ # -----------------
3
+
4
+ # Simple class for serializing Samurai <message> responses
5
+ class Samurai::Message < Samurai::Base
6
+
2
7
  def to_xml(options = {})
3
8
  builder = options[:builder] || Builder::XmlMarkup.new(options)
4
9
  builder.tag!(:message) do
@@ -7,4 +12,5 @@ class Samurai::Message < Samurai::Base # :nodoc:
7
12
  end
8
13
  end
9
14
  end
15
+
10
16
  end
@@ -1,18 +1,24 @@
1
1
  require 'active_resource/version'
2
+
3
+ # Samurai::PaymentMethod
4
+ # -----------------
5
+
6
+ # Samurai credit card tokenization, including retaining & redacting Payment Methods
2
7
  class Samurai::PaymentMethod < Samurai::Base
3
8
 
4
9
  include Samurai::CacheableByToken
5
10
 
6
- def id # :nodoc:
11
+ def id
7
12
  self.token
8
13
  end
9
14
 
10
- # Alias for +payment_method_token+
15
+ # Alias for `payment_method_token`
11
16
  def token
12
17
  self.payment_method_token
18
+ # self.attributes["payment_method_token"]
13
19
  end
14
20
 
15
- # Retains the payment method on api.samurai.feefighters.com. Retain a payment method if
21
+ # Retains the payment method on `api.samurai.feefighters.com`. Retain a payment method if
16
22
  # it will not be used immediately.
17
23
  def retain
18
24
  self.post(:retain)
@@ -28,17 +34,18 @@ class Samurai::PaymentMethod < Samurai::Base
28
34
  @custom_data ||= self.custom && (JSON.parse(self.custom) rescue {}).symbolize_keys
29
35
  end
30
36
 
37
+ # Override base error processing with specific PaymentMethod behavior
38
+ # Examine the `<messages>` array, and add an error to the Errors object for each `<message>`
31
39
  def process_response_errors
32
40
  if respond_to?(:messages) && self.messages
33
41
  self.messages.each do |message|
34
- #if (message.respond_to?(:subclass) && message.subclass == 'error')
35
- self.errors.add message.context.gsub(/\./, ' '), message.key
36
- #end
42
+ self.errors.add message.context.gsub(/\./, ' '), message.key
37
43
  end
38
44
  end
39
45
  end
40
46
  protected :process_response_errors
41
47
 
48
+ # Setup the PaymentMethod schema for ActiveResource, so that new objects contain empty attributes
42
49
  KNOWN_ATTRIBUTES = [
43
50
  :first_name, :last_name, :address_1, :address_2, :city, :state, :zip,
44
51
  :card_number, :cvv, :expiry_month, :expiry_year
@@ -57,7 +64,7 @@ class Samurai::PaymentMethod < Samurai::Base
57
64
  end
58
65
  end
59
66
 
60
- # Prepare a new PaymentMethod for use with a transparent redirect form
67
+ # Convenience method for preparing a new PaymentMethod for use with a transparent redirect form
61
68
  def self.for_transparent_redirect(params)
62
69
  if params[:payment_method_token].blank?
63
70
  Samurai::PaymentMethod.new(params)
@@ -1,3 +1,8 @@
1
+ # Samurai::Processor
2
+ # -----------------
3
+
4
+ # This class represents a Samurai Processor connection
5
+ # It can be used to create purchase & authorize transactions
1
6
  class Samurai::Processor < Samurai::Base
2
7
 
3
8
  # Returns the default processor specified by Samurai.processor_token if you passed it into Samurai.setup_site.
@@ -17,34 +22,40 @@ class Samurai::Processor < Samurai::Base
17
22
 
18
23
  # Convenience method to authorize and capture a payment_method for a particular amount in one transaction.
19
24
  # Parameters:
20
- # +payment_method_token+:: token identifying the payment method to authorize
21
- # +amount+:: amount to authorize
22
- # options:: an optional has of additional values to pass in accepted values are:
23
- # *+descriptor+:: descriptor for the transaction
24
- # *+custom+:: custom data, this data does not get passed to the processor, it is stored within api.samurai.feefighters.com only
25
- # *+customer_reference+:: an identifier for the customer, this will appear in the processor if supported
26
- # *+billing_reference::+ an identifier for the purchase, this will appear in the processor if supported
25
+ #
26
+ # * `payment_method_token`: token identifying the payment method to authorize
27
+ # * `amount`: amount to authorize
28
+ # * `options`: an optional has of additional values to pass in accepted values are:
29
+ # * `descriptor`: descriptor for the transaction
30
+ # * `custom`: custom data, this data does not get passed to the processor, it is stored within `api.samurai.feefighters.com` only
31
+ # * `customer_reference`: an identifier for the customer, this will appear in the processor if supported
32
+ # * `billing_reference`: an identifier for the purchase, this will appear in the processor if supported
33
+ #
27
34
  # Returns a Samurai::Transaction containing the processor's response.
28
35
  def purchase(payment_method_token, amount, options = {})
29
36
  execute(:purchase, options.merge(:payment_method_token => payment_method_token, :amount => amount))
30
37
  end
31
38
 
32
- # Authorize a payment_method for a particular amount.
39
+ # Authorize a payment_method for a particular amount.
33
40
  # Parameters:
34
- # +payment_method_token+:: token identifying the payment method to authorize
35
- # +amount+:: amount to authorize
36
- # options:: an optional has of additional values to pass in accepted values are:
37
- # *+descriptor+:: descriptor for the transaction
38
- # *+custom+:: custom data, this data does not get passed to the processor, it is stored within api.samurai.feefighters.com only
39
- # *+customer_reference+:: an identifier for the customer, this will appear in the processor if supported
40
- # *+billing_reference::+ an identifier for the purchase, this will appear in the processor if supported
41
+ #
42
+ # * `payment_method_token`: token identifying the payment method to authorize
43
+ # * `amount`: amount to authorize
44
+ #
45
+ # * options: an optional has of additional values to pass in accepted values are:
46
+ # * `descriptor`: descriptor for the transaction
47
+ # * `custom`: custom data, this data does not get passed to the processor, it is stored within api.samurai.feefighters.com only
48
+ # * `customer_reference`: an identifier for the customer, this will appear in the processor if supported
49
+ # * `billing_reference`: an identifier for the purchase, this will appear in the processor if supported
50
+ #
41
51
  # Returns a Samurai::Transaction containing the processor's response.
42
52
  def authorize(payment_method_token, amount, options = {})
43
53
  execute(:authorize, options.merge(:payment_method_token => payment_method_token, :amount => amount))
44
54
  end
45
55
 
46
56
  private
47
-
57
+
58
+ # Make the actual ActiveResource POST request, process the response
48
59
  def execute(action, options = {})
49
60
  transaction = Samurai::Transaction.transaction_payload(options)
50
61
  begin
@@ -1,5 +1,10 @@
1
+ # Samurai::ProcessorResponse
2
+ # -----------------
3
+
4
+ # Simple class for serializing Samurai <processor_response> entities
1
5
  class Samurai::ProcessorResponse < Samurai::Base
2
6
 
7
+ # Helper method for accessing the AVS result code from the response messages
3
8
  def avs_result_code
4
9
  avs_result_code_message = self.messages.find {|m| m.context=='processor.avs_result_code' || m.context=='gateway.avs_result_code' }
5
10
  avs_result_code_message && avs_result_code_message.key
@@ -1,5 +1,10 @@
1
1
  require 'pathname'
2
2
 
3
+ # Samurai::Rails::Helpers
4
+ # -----------------
5
+
6
+ # Helper module containing useful methods that can be called from Rails controllers
7
+ # Useful for setting up objects properly for a Transparent Redirect, or displaying errors on payment forms
3
8
  module Samurai::Rails
4
9
  module Helpers
5
10
 
@@ -1,5 +1,19 @@
1
1
  require 'pathname'
2
2
 
3
+ # Samurai::Rails::Views
4
+ # -----------------
5
+
6
+ # Helper module containing methods designed to be called inside Rails views
7
+ # These methods render Samurai partials that ship with the gem,
8
+ # making it possible to create a payment form, and display transaction errors, in a single line of code
9
+ # eg:
10
+ #
11
+ # ```ruby
12
+ # <%= render Samurai::Rails::Views.errors %>
13
+ # <%= render Samurai::Rails::Views.payment_form
14
+ # :redirect_url => purchase_article_orders_url(@article),
15
+ # :sandbox => true %>
16
+ # ```
3
17
  module Samurai::Rails
4
18
  class Views
5
19
  class << self
@@ -1,14 +1,18 @@
1
+ # Samurai::Transaction
2
+ # -----------------
3
+
4
+ # This class represents a Samurai Transaction
5
+ # It can be used to query Transactions & capture/void/credit/reverse
1
6
  class Samurai::Transaction < Samurai::Base
2
-
3
7
  include Samurai::CacheableByToken
4
8
 
5
- # Alias for transaction_token
6
- def id # :nodoc:
9
+ # Alias for `transaction_token`
10
+ def id
7
11
  transaction_token
8
12
  end
9
13
  alias_method :token, :id
10
14
 
11
- # Captures an authorization. Optionally specify an +amount+ to do a partial capture of the initial
15
+ # Captures an authorization. Optionally specify an `amount` to do a partial capture of the initial
12
16
  # authorization. The default is to capture the full amount of the authorization.
13
17
  def capture(amount = nil, options = {})
14
18
  execute(:capture, {:amount => amount || self.amount}.reverse_merge(options))
@@ -21,7 +25,7 @@ class Samurai::Transaction < Samurai::Base
21
25
  end
22
26
 
23
27
  # Create a credit or refund against the original transaction.
24
- # Optionally accepts an +amount+ to credit, the default is to credit the full
28
+ # Optionally accepts an `amount` to credit, the default is to credit the full
25
29
  # value of the original amount
26
30
  def credit(amount = nil, options = {})
27
31
  execute(:credit, {:amount => amount || self.amount}.reverse_merge(options))
@@ -43,7 +47,8 @@ class Samurai::Transaction < Samurai::Base
43
47
  end
44
48
 
45
49
  private
46
-
50
+
51
+ # Make the actual ActiveResource POST request, process the response
47
52
  def execute(action, options = {})
48
53
  begin
49
54
  resp = post(action, {}, self.class.transaction_payload(options))
@@ -59,6 +64,8 @@ class Samurai::Transaction < Samurai::Base
59
64
  end
60
65
  end
61
66
 
67
+ # Override base error processing with specific Transaction behavior
68
+ # Examine the `<processor_response><messages>` array, and add an error to the Errors object for each `<message>`
62
69
  def process_response_errors
63
70
  if self.processor_response && self.processor_response.messages
64
71
  self.processor_response.messages.each do |message|
@@ -86,6 +93,7 @@ class Samurai::Transaction < Samurai::Base
86
93
  to_xml(:skip_instruct => true, :root => 'transaction', :dasherize => false)
87
94
  end
88
95
 
96
+ # Setup the Transaction schema for ActiveResource, so that new objects contain empty attributes
89
97
  KNOWN_ATTRIBUTES = [
90
98
  :amount, :type, :payment_method_token, :currency_code,
91
99
  :descriptor, :custom, :customer_reference, :billing_reference, :processor_response
@@ -1,3 +1,3 @@
1
1
  module Samurai
2
- VERSION = "0.2.18".freeze
2
+ VERSION = "0.2.19".freeze
3
3
  end
data/samurai.gemspec CHANGED
@@ -28,4 +28,4 @@ Gem::Specification.new do |s|
28
28
  s.files = `git ls-files`.split("\n")
29
29
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
30
30
  s.require_path = 'lib'
31
- end
31
+ end
@@ -3,8 +3,8 @@ require 'spec_helper'
3
3
  describe "processing authorizations" do
4
4
 
5
5
  before :each do
6
- payment_method_token = PAYMENT_METHOD_TOKENS[:success]
7
- @authorization = Samurai::Processor.authorize(payment_method_token, @@seed)
6
+ payment_method_token = create_payment_method(default_payment_method_params)[:payment_method_token]
7
+ @authorization = Samurai::Processor.authorize(payment_method_token, 1.0, :billing_reference=>rand(1000))
8
8
  end
9
9
 
10
10
  it "should create a new authorization transaction" do
@@ -17,19 +17,19 @@ describe "processing authorizations" do
17
17
  end
18
18
 
19
19
  it "should successfully capture" do
20
- capture = @authorization.capture(@@seed)
20
+ capture = @authorization.capture(1.0)
21
21
  capture.processor_response.success.should be_true
22
22
  end
23
23
 
24
24
  it "should capture an authorization without specifying an amount" do
25
25
  capture = @authorization.capture
26
- capture.amount.intern.should be_equal "#{@@seed}".intern
26
+ capture.amount.intern.should be_equal "#{1.0}".intern
27
27
  capture.processor_response.success.should be_true
28
28
  end
29
29
 
30
30
  it "should partially capture an authorization" do
31
- capture = @authorization.capture(@@seed - 1.0)
32
- capture.amount.intern.should be_equal "#{@@seed - 1.0}".intern
31
+ capture = @authorization.capture(1.0 - BigDecimal('0.5'))
32
+ capture.amount.intern.should be_equal "#{1.0 - BigDecimal('0.5')}".intern
33
33
  capture.processor_response.success.should be_true
34
34
  end
35
35
 
@@ -40,15 +40,15 @@ describe "processing authorizations" do
40
40
 
41
41
  it "should credit an authorization for the full amount by default" do
42
42
  credit = @authorization.credit
43
- credit.amount.intern.should be_equal "#{@@seed}".intern
43
+ credit.amount.intern.should be_equal "#{1.0}".intern
44
44
  pending "the response is not successful since the authorization hasn't settled" do
45
45
  credit.processor_response.success.should be_true
46
46
  end
47
47
  end
48
48
 
49
49
  it "should partially credit an authorization" do
50
- credit = @authorization.credit(@@seed - 1.0)
51
- credit.amount.intern.should be_equal "#{@@seed - 1.0}".intern
50
+ credit = @authorization.credit(1.0 - BigDecimal('0.5'))
51
+ credit.amount.intern.should be_equal "#{1.0 - BigDecimal('0.5')}".intern
52
52
  pending "the response is not successful since the authorization hasn't settled" do
53
53
  credit.processor_response.success.should be_true
54
54
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "generate documentation" do
4
- include TransparentRedirectHelper
5
4
  include ResponseLoggerHelper
6
5
 
7
6
  before(:all) do
@@ -61,7 +60,9 @@ describe "generate documentation" do
61
60
  @params.delete 'credit_card[card_number]'
62
61
  data = create_payment_method(@params)
63
62
  log_request_response! data[:request], data[:response]
64
- data[:payment_method_token].should be_nil
63
+ data[:payment_method_token].should =~ /^[0-9a-z]{24}$/
64
+ Samurai::PaymentMethod.find data[:payment_method_token]
65
+ log_http!
65
66
  end
66
67
 
67
68
  it 'should create the payment method with invalid card_number format' do
@@ -99,12 +100,6 @@ describe "generate documentation" do
99
100
  log_http!
100
101
  purchase.processor_response.success.should be_true
101
102
  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
103
  end
109
104
 
110
105
  describe 'with a declined card' do
@@ -123,12 +118,6 @@ describe "generate documentation" do
123
118
  log_http!
124
119
  purchase.processor_response.success.should be_false
125
120
  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
121
  end
133
122
 
134
123
  describe 'with an expired card' do
@@ -147,12 +136,6 @@ describe "generate documentation" do
147
136
  log_http!
148
137
  purchase.processor_response.success.should be_false
149
138
  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
139
  end
157
140
 
158
141
  describe 'with a card with incorrect cvv' do
@@ -171,12 +154,6 @@ describe "generate documentation" do
171
154
  log_http!
172
155
  purchase.processor_response.success.should be_false
173
156
  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
157
  end
181
158
 
182
159
  describe 'with an nonexistant card' do
@@ -196,12 +173,6 @@ describe "generate documentation" do
196
173
  log_http!
197
174
  purchase.processor_response.success.should be_false
198
175
  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
176
  end
206
177
 
207
178
  end