spreedly-core-ruby 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,9 +21,9 @@ module SpreedlyCore
21
21
  }
22
22
 
23
23
  class Error < RuntimeError; end
24
-
24
+
25
25
  # Custom exception which occurs when a request to SpreedlyCore times out
26
- # See SpreedlyCore::Base.default_timeout
26
+ # See SpreedlyCore::Base.default_timeout
27
27
  class TimeOutError < Error; end
28
28
  class InvalidResponse < Error
29
29
  def initialize(response, message)
@@ -37,7 +37,7 @@ module SpreedlyCore
37
37
  def initialize(errors)
38
38
  errors = [errors] unless errors.is_a?(Array)
39
39
  @errors = errors
40
-
40
+
41
41
  super(errors.join("\n"))
42
42
  end
43
43
  end
@@ -75,6 +75,9 @@ module SpreedlyCore
75
75
  if login.nil? || secret.nil?
76
76
  raise ArgumentError.new("You must provide a login and a secret. Gem will look for ENV['SPREEDLYCORE_API_LOGIN'] and ENV['SPREEDLYCORE_API_SECRET'], but you may also pass in a hash with :api_login and :api_secret keys.")
77
77
  end
78
+
79
+ options[:endpoint] ||= "https://spreedlycore.com/#{SpreedlyCore::API_VERSION}"
80
+
78
81
  Base.configure(login, secret, options)
79
82
  end
80
83
 
@@ -93,6 +96,6 @@ module SpreedlyCore
93
96
  class Response < Base
94
97
  attr_reader(:success, :message, :avs_code, :avs_message, :cvv_code,
95
98
  :cvv_message, :error_code, :error_detail, :created_at,
96
- :updated_at)
99
+ :updated_at)
97
100
  end
98
101
  end
@@ -4,23 +4,22 @@ module SpreedlyCore
4
4
  # Base class for all SpreedlyCore API requests
5
5
  class Base
6
6
  include HTTParty
7
-
7
+
8
8
  # Net::HTTP::Options is configured to not have a body.
9
9
  # Lets give it the body it's always dreamed of
10
10
  old_verbose, $VERBOSE = $VERBOSE, nil
11
11
  Net::HTTP::Options::RESPONSE_HAS_BODY = true
12
12
  $VERBOSE = old_verbose
13
-
13
+
14
14
  format :xml
15
15
 
16
16
  # timeout requests after 10 seconds
17
17
  default_timeout 10
18
18
 
19
- base_uri "https://spreedlycore.com/#{SpreedlyCore::API_VERSION}"
20
-
21
19
  def self.configure(login, secret, options = {})
22
20
  @@login = login
23
21
  self.basic_auth(@@login, secret)
22
+ base_uri options[:endpoint]
24
23
  @@gateway_token = options.delete(:gateway_token)
25
24
  end
26
25
 
@@ -31,7 +30,7 @@ module SpreedlyCore
31
30
  # make a post request to path
32
31
  # If the request succeeds, provide the respones to the &block
33
32
  def self.verify_post(path, options={}, &block)
34
- verify_request(:post, path, options, 200, 201, 422, &block)
33
+ verify_request(:post, path, options, 200, 201, 202, 422, &block)
35
34
  end
36
35
 
37
36
  # make a put request to path
@@ -58,12 +57,12 @@ module SpreedlyCore
58
57
  # If *allowed_codes is empty, don't check the response code, but set an instance
59
58
  # variable on the object created in the block containing the response code.
60
59
  def self.verify_request(request_type, path, options, *allowed_codes, &block)
61
- begin
60
+ begin
62
61
  response = self.send(request_type, path, options)
63
62
  rescue Timeout::Error, Errno::ETIMEDOUT => e
64
63
  raise TimeOutError.new("Request to #{path} timed out. Is Spreedly Core down?")
65
64
  end
66
-
65
+
67
66
  if allowed_codes.any? && !allowed_codes.include?(response.code)
68
67
  raise InvalidResponse.new(response, "Error retrieving #{path}. Got status of #{response.code}. Expected status to be in #{allowed_codes.join(",")}")
69
68
  end
@@ -89,7 +88,7 @@ module SpreedlyCore
89
88
  attrs.each do |k, v|
90
89
  instance_variable_set("@#{k}", v)
91
90
  end
92
- # errors may be nil, empty, a string, or an array of strings.
91
+ # errors may be nil, empty, a string, or an array of strings.
93
92
  @errors = if @errors.nil? || @errors["error"].blank?
94
93
  []
95
94
  elsif @errors["error"].is_a?(String)
@@ -1,9 +1,9 @@
1
1
  module SpreedlyCore
2
2
  class PaymentMethod < Base
3
- attr_reader( :address1, :address2, :card_type, :city, :country, :created_at,
4
- :data, :email, :errors, :first_name, :last_four_digits,
5
- :last_name, :month, :number, :payment_method_type, :phone_number,
6
- :state, :token, :updated_at, :verification_value, :year, :zip)
3
+ attr_reader :address1, :address2, :card_type, :city, :country, :created_at,
4
+ :data, :email, :errors, :first_name, :last_four_digits,
5
+ :last_name, :month, :number, :payment_method_type, :phone_number,
6
+ :state, :token, :updated_at, :verification_value, :year, :zip
7
7
 
8
8
  # configure additional required fiels. Like :address1, :city, :state
9
9
  def self.additional_required_cc_fields *fields
@@ -18,7 +18,7 @@ module SpreedlyCore
18
18
 
19
19
  # Lookup the PaymentMethod by token
20
20
  def self.find(token)
21
- return nil if token.nil?
21
+ return nil if token.nil?
22
22
  verify_get("/payment_methods/#{token}.xml",
23
23
  :has_key => "payment_method") do |response|
24
24
  new(response.parsed_response["payment_method"])
@@ -26,7 +26,7 @@ module SpreedlyCore
26
26
  end
27
27
 
28
28
  def self.create(credit_card)
29
- verify_post("/payment_methods.xml", :body => {:credit_card => credit_card}) do |response|
29
+ verify_post("/payment_methods.xml", :body => {:payment_method => { :credit_card => credit_card }}) do |response|
30
30
  AddPaymentMethodTransaction.new(response.parsed_response["transaction"])
31
31
  end
32
32
  end
@@ -52,13 +52,13 @@ module SpreedlyCore
52
52
  end
53
53
 
54
54
  # Make a purchase against the payment method
55
- def purchase(amount, currency=nil, _gateway_token=nil, ip_address=nil)
56
- purchase_or_authorize(:purchase, amount, currency, _gateway_token, ip_address)
55
+ def purchase(amount, *args)
56
+ purchase_or_authorize(:purchase, amount, *args)
57
57
  end
58
58
 
59
59
  # Make an authorize against payment method. You can then later capture against the authorize
60
- def authorize(amount, currency=nil, _gateway_token=nil, ip_address=nil)
61
- purchase_or_authorize(:authorize, amount, currency, _gateway_token, ip_address)
60
+ def authorize(amount, *args)
61
+ purchase_or_authorize(:authorize, amount, *args)
62
62
  end
63
63
 
64
64
  # Update the attributes of a payment method
@@ -82,13 +82,7 @@ module SpreedlyCore
82
82
  @errors.empty?
83
83
  end
84
84
 
85
-
86
-
87
-
88
- protected
89
-
90
-
91
-
85
+ protected
92
86
 
93
87
  # Validate additional cc fields like first_name, last_name, etc when
94
88
  # configured to do so
@@ -103,27 +97,39 @@ protected
103
97
  else
104
98
  str_field.split("_").join(" ")
105
99
  end
106
-
100
+
107
101
  @errors << "#{friendly_name.capitalize} can't be blank"
108
102
  end
109
103
  end
110
104
  @errors = @errors.sort
111
105
  end
112
106
 
113
- def purchase_or_authorize(tran_type, amount, currency, _gateway_token, ip_address)
107
+ def purchase_or_authorize(tran_type, amount, *args)
108
+ options = if(args.size == 1 && args.first.kind_of?(Hash))
109
+ args.first
110
+ else
111
+ {
112
+ :currency => args[0],
113
+ :gateway_token => args[1],
114
+ :ip_address => args[2]
115
+ }
116
+ end
117
+
114
118
  transaction_type = tran_type.to_s
115
119
  raise "Unknown transaction type" unless %w{purchase authorize}.include?(transaction_type)
116
120
 
117
- currency ||= "USD"
118
- _gateway_token ||= self.class.gateway_token
119
- path = "/gateways/#{_gateway_token}/#{transaction_type}.xml"
121
+ currency = (options[:currency] || "USD")
122
+ gateway_token = (options[:gateway_token] || self.class.gateway_token)
123
+ path = "/gateways/#{gateway_token}/#{transaction_type}.xml"
120
124
  data = {
121
125
  :transaction => {
122
- :transaction_type => transaction_type,
126
+ :transaction_type => transaction_type,
123
127
  :payment_method_token => token,
124
128
  :amount => amount,
125
129
  :currency_code => currency,
126
- :ip => ip_address
130
+ :ip => options[:ip_address],
131
+ :redirect_url => options[:redirect_url],
132
+ :callback_url => options[:callback_url]
127
133
  }
128
134
  }
129
135
  self.class.verify_post(path, :body => data,
@@ -3,7 +3,7 @@ module SpreedlyCore
3
3
  class Transaction < Base
4
4
  attr_reader(:amount, :on_test_gateway, :created_at, :updated_at, :currency_code,
5
5
  :succeeded, :token, :message, :transaction_type, :gateway_token,
6
- :response)
6
+ :response, :signed, :state, :checkout_url)
7
7
  alias :succeeded? :succeeded
8
8
 
9
9
  # Breaks enacapsulation a bit, but allow subclasses to register the 'transaction_type'
@@ -15,35 +15,65 @@ module SpreedlyCore
15
15
 
16
16
  # Lookup the transaction by its token. Returns the correct subclass
17
17
  def self.find(token)
18
- return nil if token.nil?
18
+ return nil if token.nil?
19
19
  verify_get("/transactions/#{token}.xml", :has_key => "transaction") do |response|
20
20
  attrs = response.parsed_response["transaction"]
21
21
  klass = @@transaction_type_to_class[attrs["transaction_type"]] || self
22
- klass.new(attrs)
22
+ klass.new(attrs).tap do |transaction|
23
+ transaction.verified!
24
+ end
23
25
  end
24
26
  end
27
+
28
+ def initialize(attrs={})
29
+ super
30
+ if(valid_signature?)
31
+ verified!
32
+ end
33
+ end
34
+
35
+ def verified!
36
+ @verified = true
37
+ end
38
+
39
+ def verified?
40
+ @verified
41
+ end
42
+
43
+ def pending?
44
+ state == "pending"
45
+ end
46
+
47
+ def valid_signature?
48
+ return false unless signed
49
+ fields = signed["fields"]
50
+ data = fields.collect do |field|
51
+ self.instance_variable_get("@#{field}")
52
+ end.join("|")
53
+ (OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(signed["algorithm"], SpreedlyCore::Base.secret, data)) == signed["signature"])
54
+ end
25
55
  end
26
56
 
27
57
  class RetainTransaction < Transaction
28
58
  handles "RetainPaymentMethod"
29
59
  attr_reader :payment_method
30
-
60
+
31
61
  def initialize(attrs={})
32
62
  @payment_method = PaymentMethod.new(attrs.delete("payment_method") || {})
33
63
  super(attrs)
34
64
  end
35
65
  end
36
-
66
+
37
67
  class RedactTransaction < Transaction
38
68
  handles "RedactPaymentMethod"
39
69
  attr_reader :payment_method
40
-
70
+
41
71
  def initialize(attrs={})
42
72
  @payment_method = PaymentMethod.new(attrs.delete("payment_method") || {})
43
73
  super(attrs)
44
74
  end
45
75
  end
46
-
76
+
47
77
  module NullifiableTransaction
48
78
  # Void is used to cancel out authorizations and, with some gateways, to
49
79
  # cancel actual payment transactions within the first 24 hours
@@ -52,13 +82,13 @@ module SpreedlyCore
52
82
  self.class.verify_post("/transactions/#{token}/void.xml",
53
83
  :body => body, :has_key => "transaction") do |response|
54
84
  VoidedTransaction.new(response.parsed_response["transaction"])
55
- end
85
+ end
56
86
  end
57
87
 
58
88
  # Credit amount. If amount is nil, then credit the entire previous purchase
59
- # or captured amount
89
+ # or captured amount
60
90
  def credit(amount=nil, ip_address=nil)
61
- body = if amount.nil?
91
+ body = if amount.nil?
62
92
  {:ip => ip_address}
63
93
  else
64
94
  {:transaction => {:amount => amount, :ip => ip_address}}
@@ -76,10 +106,10 @@ module SpreedlyCore
76
106
 
77
107
  class AuthorizeTransaction < Transaction
78
108
  include HasIpAddress
79
-
109
+
80
110
  handles "Authorization"
81
111
  attr_reader :payment_method
82
-
112
+
83
113
  def initialize(attrs={})
84
114
  @payment_method = PaymentMethod.new(attrs.delete("payment_method") || {})
85
115
  @response = Response.new(attrs.delete("response") || {})
@@ -120,21 +150,21 @@ module SpreedlyCore
120
150
  class CaptureTransaction < Transaction
121
151
  include NullifiableTransaction
122
152
  include HasIpAddress
123
-
153
+
124
154
  handles "Capture"
125
155
  attr_reader :reference_token
126
156
  end
127
157
 
128
158
  class VoidedTransaction < Transaction
129
159
  include HasIpAddress
130
-
160
+
131
161
  handles "Void"
132
162
  attr_reader :reference_token
133
163
  end
134
164
 
135
165
  class CreditTransaction < Transaction
136
166
  include HasIpAddress
137
-
167
+
138
168
  handles "Credit"
139
169
  attr_reader :reference_token
140
170
  end
@@ -145,5 +175,5 @@ module SpreedlyCore
145
175
  handles "AddPaymentMethod"
146
176
  attr_reader :payment_method
147
177
  end
148
-
178
+
149
179
  end
@@ -1,4 +1,4 @@
1
1
  module SpreedlyCore
2
- Version = VERSION = "0.2.0"
2
+ Version = VERSION = "0.2.1"
3
3
  ApiVersion = API_VERSION = "v1"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spreedly-core-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
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: 2012-09-26 00:00:00.000000000 Z
13
+ date: 2012-10-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
17
- requirement: &70095372683980 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,15 @@ dependencies:
22
22
  version: '0.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70095372683980
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '0.0'
26
31
  - !ruby/object:Gem::Dependency
27
32
  name: activesupport
28
- requirement: &70095372705660 !ruby/object:Gem::Requirement
33
+ requirement: !ruby/object:Gem::Requirement
29
34
  none: false
30
35
  requirements:
31
36
  - - ~>
@@ -33,10 +38,15 @@ dependencies:
33
38
  version: '3.0'
34
39
  type: :runtime
35
40
  prerelease: false
36
- version_requirements: *70095372705660
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
37
47
  - !ruby/object:Gem::Dependency
38
48
  name: builder
39
- requirement: &70095372702860 !ruby/object:Gem::Requirement
49
+ requirement: !ruby/object:Gem::Requirement
40
50
  none: false
41
51
  requirements:
42
52
  - - ! '>='
@@ -44,10 +54,15 @@ dependencies:
44
54
  version: '0'
45
55
  type: :runtime
46
56
  prerelease: false
47
- version_requirements: *70095372702860
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
48
63
  - !ruby/object:Gem::Dependency
49
64
  name: ruby-debug19
50
- requirement: &70095372715820 !ruby/object:Gem::Requirement
65
+ requirement: !ruby/object:Gem::Requirement
51
66
  none: false
52
67
  requirements:
53
68
  - - ! '>='
@@ -55,21 +70,31 @@ dependencies:
55
70
  version: '0'
56
71
  type: :development
57
72
  prerelease: false
58
- version_requirements: *70095372715820
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
59
79
  - !ruby/object:Gem::Dependency
60
80
  name: rake
61
- requirement: &70095372761540 !ruby/object:Gem::Requirement
81
+ requirement: !ruby/object:Gem::Requirement
62
82
  none: false
63
83
  requirements:
64
- - - =
84
+ - - '='
65
85
  - !ruby/object:Gem::Version
66
86
  version: 0.8.7
67
87
  type: :development
68
88
  prerelease: false
69
- version_requirements: *70095372761540
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - '='
93
+ - !ruby/object:Gem::Version
94
+ version: 0.8.7
70
95
  - !ruby/object:Gem::Dependency
71
96
  name: webmock
72
- requirement: &70095372768840 !ruby/object:Gem::Requirement
97
+ requirement: !ruby/object:Gem::Requirement
73
98
  none: false
74
99
  requirements:
75
100
  - - ~>
@@ -77,7 +102,12 @@ dependencies:
77
102
  version: 1.6.2
78
103
  type: :development
79
104
  prerelease: false
80
- version_requirements: *70095372768840
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.6.2
81
111
  description: Spreedly Core is a cloud service that allows you to store credit cards
82
112
  and run transactions against them, enabling you to accept payments on your website
83
113
  while avoiding all liability and PCI compliance requirements.
@@ -117,15 +147,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
147
  - - ! '>='
118
148
  - !ruby/object:Gem::Version
119
149
  version: '0'
150
+ segments:
151
+ - 0
152
+ hash: -3960261074814685909
120
153
  required_rubygems_version: !ruby/object:Gem::Requirement
121
154
  none: false
122
155
  requirements:
123
156
  - - ! '>='
124
157
  - !ruby/object:Gem::Version
125
158
  version: '0'
159
+ segments:
160
+ - 0
161
+ hash: -3960261074814685909
126
162
  requirements: []
127
163
  rubyforge_project:
128
- rubygems_version: 1.8.11
164
+ rubygems_version: 1.8.24
129
165
  signing_key:
130
166
  specification_version: 3
131
167
  summary: Ruby interface for Spreedly Core