activemerchant 1.15.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +21 -0
  3. data/CONTRIBUTORS +13 -1
  4. data/lib/active_merchant.rb +2 -5
  5. data/lib/active_merchant/billing/avs_result.rb +2 -2
  6. data/lib/active_merchant/billing/credit_card.rb +6 -0
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +1 -1
  8. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +1 -2
  9. data/lib/active_merchant/billing/gateways/bogus.rb +34 -24
  10. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/cyber_source.rb +20 -0
  12. data/lib/active_merchant/billing/gateways/moneris.rb +1 -1
  13. data/lib/active_merchant/billing/gateways/orbital.rb +6 -2
  14. data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
  15. data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
  16. data/lib/active_merchant/billing/gateways/qbms.rb +1 -3
  17. data/lib/active_merchant/billing/gateways/quickpay.rb +56 -51
  18. data/lib/active_merchant/billing/gateways/stripe.rb +212 -0
  19. data/lib/active_merchant/billing/gateways/worldpay.rb +1 -1
  20. data/lib/active_merchant/billing/integrations/action_view_helper.rb +2 -2
  21. data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
  22. data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
  23. data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
  24. data/lib/active_merchant/billing/integrations/helper.rb +3 -3
  25. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +11 -0
  26. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +16 -7
  27. data/lib/active_merchant/billing/integrations/world_pay.rb +4 -15
  28. data/lib/active_merchant/version.rb +1 -1
  29. metadata +46 -21
  30. metadata.gz.sig +0 -0
  31. data/README.rdoc +0 -164
@@ -1,5 +1,3 @@
1
- require 'securerandom'
2
-
3
1
  module ActiveMerchant #:nodoc:
4
2
  module Billing #:nodoc:
5
3
  class QbmsGateway < Gateway
@@ -118,7 +116,7 @@ module ActiveMerchant #:nodoc:
118
116
  end
119
117
 
120
118
  private
121
-
119
+
122
120
  def hosted?
123
121
  @options[:pem]
124
122
  end
@@ -6,50 +6,51 @@ module ActiveMerchant #:nodoc:
6
6
  class QuickpayGateway < Gateway
7
7
  URL = 'https://secure.quickpay.dk/api'
8
8
 
9
- self.default_currency = 'DKK'
9
+ self.default_currency = 'DKK'
10
10
  self.money_format = :cents
11
11
  self.supported_cardtypes = [ :dankort, :forbrugsforeningen, :visa, :master, :american_express, :diners_club, :jcb, :maestro ]
12
12
  self.supported_countries = ['DK', 'SE']
13
13
  self.homepage_url = 'http://quickpay.dk/'
14
14
  self.display_name = 'Quickpay'
15
-
15
+
16
16
  PROTOCOL = 3
17
-
17
+
18
18
  MD5_CHECK_FIELDS = {
19
- :authorize => [:protocol, :msgtype, :merchant, :ordernumber, :amount, :currency, :autocapture, :cardnumber, :expirationdate, :cvd, :cardtypelock],
19
+ :authorize => [:protocol, :msgtype, :merchant, :ordernumber, :amount, :currency, :autocapture, :cardnumber, :expirationdate, :cvd, :cardtypelock, :testmode],
20
20
  :capture => [:protocol, :msgtype, :merchant, :amount, :transaction],
21
21
  :cancel => [:protocol, :msgtype, :merchant, :transaction],
22
22
  :refund => [:protocol, :msgtype, :merchant, :amount, :transaction],
23
- :subscribe => [:protocol, :msgtype, :merchant, :ordernumber, :cardnumber, :expirationdate, :cvd, :cardtypelock, :description],
23
+ :subscribe => [:protocol, :msgtype, :merchant, :ordernumber, :cardnumber, :expirationdate, :cvd, :cardtypelock, :description, :testmode],
24
24
  :recurring => [:protocol, :msgtype, :merchant, :ordernumber, :amount, :currency, :autocapture, :transaction],
25
25
  :status => [:protocol, :msgtype, :merchant, :transaction],
26
26
  :chstatus => [:protocol, :msgtype, :merchant],
27
27
  }
28
-
28
+
29
29
  APPROVED = '000'
30
-
30
+
31
31
  # The login is the QuickpayId
32
32
  # The password is the md5checkword from the Quickpay admin interface
33
33
  def initialize(options = {})
34
34
  requires!(options, :login, :password)
35
35
  @options = options
36
36
  super
37
- end
38
-
37
+ end
38
+
39
39
  def authorize(money, credit_card_or_reference, options = {})
40
40
  post = {}
41
-
41
+
42
42
  add_amount(post, money, options)
43
43
  add_invoice(post, options)
44
44
  add_creditcard_or_reference(post, credit_card_or_reference, options)
45
45
  add_autocapture(post, false)
46
+ add_testmode(post)
46
47
 
47
48
  commit(recurring_or_authorize(credit_card_or_reference), post)
48
49
  end
49
-
50
+
50
51
  def purchase(money, credit_card_or_reference, options = {})
51
52
  post = {}
52
-
53
+
53
54
  add_amount(post, money, options)
54
55
  add_creditcard_or_reference(post, credit_card_or_reference, options)
55
56
  add_invoice(post, options)
@@ -57,24 +58,24 @@ module ActiveMerchant #:nodoc:
57
58
 
58
59
  commit(recurring_or_authorize(credit_card_or_reference), post)
59
60
  end
60
-
61
+
61
62
  def capture(money, authorization, options = {})
62
63
  post = {}
63
-
64
+
64
65
  add_reference(post, authorization)
65
66
  add_amount_without_currency(post, money)
66
-
67
+
67
68
  commit(:capture, post)
68
69
  end
69
-
70
+
70
71
  def void(identification, options = {})
71
72
  post = {}
72
-
73
+
73
74
  add_reference(post, identification)
74
-
75
+
75
76
  commit(:cancel, post)
76
77
  end
77
-
78
+
78
79
  def refund(money, identification, options = {})
79
80
  post = {}
80
81
 
@@ -86,57 +87,58 @@ module ActiveMerchant #:nodoc:
86
87
 
87
88
  def credit(money, identification, options = {})
88
89
  deprecated CREDIT_DEPRECATION_MESSAGE
89
- refund(money, identification, options)
90
+ refund(money, identification, options)
90
91
  end
91
-
92
- def store(creditcard, options = {})
92
+
93
+ def store(creditcard, options = {})
93
94
  post = {}
94
-
95
+
95
96
  add_creditcard(post, creditcard, options)
96
97
  add_invoice(post, options)
97
98
  add_description(post, options)
99
+ add_testmode(post)
98
100
 
99
101
  commit(:subscribe, post)
100
102
  end
101
-
102
- private
103
-
103
+
104
+ private
105
+
104
106
  def add_amount(post, money, options = {})
105
107
  post[:amount] = amount(money)
106
108
  post[:currency] = options[:currency] || currency(money)
107
109
  end
108
-
110
+
109
111
  def add_amount_without_currency(post, money, options = {})
110
112
  post[:amount] = amount(money)
111
113
  end
112
-
114
+
113
115
  def add_invoice(post, options)
114
116
  post[:ordernumber] = format_order_number(options[:order_id])
115
117
  end
116
-
118
+
117
119
  def add_creditcard(post, credit_card, options)
118
- post[:cardnumber] = credit_card.number
120
+ post[:cardnumber] = credit_card.number
119
121
  post[:cvd] = credit_card.verification_value
120
122
  post[:expirationdate] = expdate(credit_card)
121
123
  post[:cardtypelock] = options[:cardtypelock] unless options[:cardtypelock].blank?
122
124
  end
123
-
125
+
124
126
  def add_reference(post, identification)
125
127
  post[:transaction] = identification
126
128
  end
127
-
129
+
128
130
  def add_creditcard_or_reference(post, credit_card_or_reference, options)
129
131
  if credit_card_or_reference.is_a?(String)
130
132
  add_reference(post, credit_card_or_reference)
131
133
  else
132
134
  add_creditcard(post, credit_card_or_reference, options)
133
135
  end
134
- end
135
-
136
+ end
137
+
136
138
  def add_autocapture(post, autocapture)
137
139
  post[:autocapture] = autocapture ? 1 : 0
138
140
  end
139
-
141
+
140
142
  def recurring_or_authorize(credit_card_or_reference)
141
143
  credit_card_or_reference.is_a?(String) ? :recurring : :authorize
142
144
  end
@@ -144,29 +146,33 @@ module ActiveMerchant #:nodoc:
144
146
  def add_description(post, options)
145
147
  post[:description] = options[:description]
146
148
  end
147
-
149
+
150
+ def add_testmode(post)
151
+ post[:testmode] = test? ? '1' : '0'
152
+ end
153
+
148
154
  def commit(action, params)
149
155
  response = parse(ssl_post(URL, post_data(action, params)))
150
-
151
- Response.new(successful?(response), message_from(response), response,
152
- :test => test?,
156
+
157
+ Response.new(successful?(response), message_from(response), response,
158
+ :test => test?,
153
159
  :authorization => response[:transaction]
154
160
  )
155
161
  end
156
-
162
+
157
163
  def successful?(response)
158
164
  response[:qpstat] == APPROVED
159
165
  end
160
166
 
161
167
  def parse(data)
162
168
  response = {}
163
-
169
+
164
170
  doc = REXML::Document.new(data)
165
-
171
+
166
172
  doc.root.elements.each do |element|
167
173
  response[element.name.to_sym] = element.text
168
174
  end
169
-
175
+
170
176
  response
171
177
  end
172
178
 
@@ -175,39 +181,38 @@ module ActiveMerchant #:nodoc:
175
181
  when '008' # Error in request data
176
182
  response[:qpstatmsg].to_s
177
183
  #.scan(/[A-Z][a-z0-9 \/]+/).to_sentence
178
- else
184
+ else
179
185
  response[:qpstatmsg].to_s
180
186
  end
181
187
  end
182
-
188
+
183
189
  def post_data(action, params = {})
184
190
  params[:protocol] = PROTOCOL
185
191
  params[:msgtype] = action.to_s
186
192
  params[:merchant] = @options[:login]
187
- #params[:testmode] = '1' if test?
188
193
  params[:md5check] = generate_check_hash(action, params)
189
-
194
+
190
195
  params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
191
196
  end
192
-
197
+
193
198
  def generate_check_hash(action, params)
194
199
  string = MD5_CHECK_FIELDS[action].collect do |key|
195
200
  params[key]
196
201
  end.join('')
197
-
202
+
198
203
  # Add the md5checkword
199
204
  string << @options[:password].to_s
200
205
 
201
206
  Digest::MD5.hexdigest(string)
202
207
  end
203
-
208
+
204
209
  def expdate(credit_card)
205
210
  year = format(credit_card.year, :two_digits)
206
211
  month = format(credit_card.month, :two_digits)
207
212
 
208
213
  "#{year}#{month}"
209
214
  end
210
-
215
+
211
216
  # Limited to 20 digits max
212
217
  def format_order_number(number)
213
218
  number.to_s.gsub(/[^\w_]/, '').rjust(4, "0")[0...20]
@@ -0,0 +1,212 @@
1
+ require 'json'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class StripeGateway < Gateway
6
+ LIVE_URL = 'https://api.stripe.com/v1/'
7
+
8
+ AVS_CODE_TRANSLATOR = {
9
+ 'line1: pass, zip: pass' => 'Y',
10
+ 'line1: pass, zip: fail' => 'A',
11
+ 'line1: pass, zip: unchecked' => 'B',
12
+ 'line1: fail, zip: pass' => 'Z',
13
+ 'line1: fail, zip: fail' => 'N',
14
+ 'line1: unchecked, zip: pass' => 'P',
15
+ 'line1: unchecked, zip: unchecked' => 'I'
16
+ }
17
+
18
+ CVC_CODE_TRANSLATOR = {
19
+ 'pass' => 'M',
20
+ 'fail' => 'N',
21
+ 'unchecked' => 'P'
22
+ }
23
+
24
+ self.supported_countries = ['US']
25
+ self.default_currency = 'USD'
26
+ self.money_format = :cents
27
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
28
+
29
+ self.homepage_url = 'https://stripe.com/'
30
+ self.display_name = 'Stripe'
31
+
32
+ def initialize(options = {})
33
+ requires!(options, :login)
34
+ @api_key = options[:login]
35
+ super
36
+ end
37
+
38
+ def purchase(money, creditcard, options = {})
39
+ post = {}
40
+
41
+ add_amount(post, money, options)
42
+ add_creditcard(post, creditcard, options)
43
+ add_customer(post, options)
44
+ add_customer_data(post, options)
45
+ add_flags(post, options)
46
+
47
+ raise ArgumentError.new("Customer or Credit Card required.") if !post[:card] && !post[:customer]
48
+
49
+ commit('charges', post)
50
+ end
51
+
52
+ def authorize(money, creditcard, options = {})
53
+ purchase(money, creditcard, options.merge(:uncaptured => true))
54
+ end
55
+
56
+ def capture(money, identification, options = {})
57
+ commit("charges/#{CGI.escape(identification)}/capture", {})
58
+ end
59
+
60
+ def void(identification, options={})
61
+ commit("charges/#{CGI.escape(identification)}/refund", {})
62
+ end
63
+
64
+ def refund(money, identification, options = {})
65
+ post = {}
66
+
67
+ post[:amount] = amount(money) if money
68
+
69
+ commit("charges/#{CGI.escape(identification)}/refund", post)
70
+ end
71
+
72
+ def store(creditcard, options={})
73
+ post = {}
74
+ add_creditcard(post, creditcard, options)
75
+ add_customer_data(post, options)
76
+
77
+ if options[:customer]
78
+ commit("customers/#{CGI.escape(options[:customer])}", post)
79
+ else
80
+ commit('customers', post)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def add_amount(post, money, options)
87
+ post[:amount] = amount(money)
88
+ post[:currency] = (options[:currency] || currency(money)).downcase
89
+ end
90
+
91
+ def add_customer_data(post, options)
92
+ post[:description] = options[:email] || options[:description]
93
+ end
94
+
95
+ def add_address(post, options)
96
+ return unless post[:card] && post[:card].kind_of?(Hash)
97
+ if address = options[:billing_address] || options[:address]
98
+ post[:card][:address_line1] = address[:address1] if address[:address1]
99
+ post[:card][:address_line2] = address[:address2] if address[:address2]
100
+ post[:card][:address_country] = address[:country] if address[:country]
101
+ post[:card][:address_zip] = address[:zip] if address[:zip]
102
+ post[:card][:address_state] = address[:state] if address[:state]
103
+ end
104
+ end
105
+
106
+ def add_creditcard(post, creditcard, options)
107
+ if creditcard.respond_to?(:number)
108
+ card = {}
109
+ card[:number] = creditcard.number
110
+ card[:exp_month] = creditcard.month
111
+ card[:exp_year] = creditcard.year
112
+ card[:cvc] = creditcard.verification_value if creditcard.verification_value?
113
+ card[:name] = creditcard.name if creditcard.name
114
+ post[:card] = card
115
+
116
+ add_address(post, options)
117
+ elsif creditcard.kind_of?(String)
118
+ post[:card] = creditcard
119
+ end
120
+ end
121
+
122
+ def add_customer(post, options)
123
+ post[:customer] = options[:customer] if options[:customer] && !post[:card]
124
+ end
125
+
126
+ def add_flags(post, options)
127
+ post[:uncaptured] = true if options[:uncaptured]
128
+ end
129
+
130
+ def parse(body)
131
+ JSON.parse(body)
132
+ end
133
+
134
+ def post_data(params)
135
+ params.map do |key, value|
136
+ next if value.blank?
137
+ if value.is_a?(Hash)
138
+ h = {}
139
+ value.each do |k, v|
140
+ h["#{key}[#{k}]"] = v unless v.blank?
141
+ end
142
+ post_data(h)
143
+ else
144
+ "#{key}=#{CGI.escape(value.to_s)}"
145
+ end
146
+ end.compact.join("&")
147
+ end
148
+
149
+ def headers
150
+ @@ua ||= JSON.dump({
151
+ :bindings_version => ActiveMerchant::VERSION,
152
+ :lang => 'ruby',
153
+ :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
154
+ :platform => RUBY_PLATFORM,
155
+ :publisher => 'active_merchant',
156
+ :uname => (RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
157
+ })
158
+
159
+ {
160
+ "Authorization" => "Basic " + ActiveSupport::Base64.encode64(@api_key.to_s + ":").strip,
161
+ "User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
162
+ "X-Stripe-Client-User-Agent" => @@ua
163
+ }
164
+ end
165
+
166
+ def commit(url, parameters, method=:post)
167
+ raw_response = response = nil
168
+ success = false
169
+ begin
170
+ raw_response = ssl_request(method, LIVE_URL + url, post_data(parameters), headers)
171
+ response = parse(raw_response)
172
+ success = !response.key?("error")
173
+ rescue ResponseError => e
174
+ raw_response = e.response.body
175
+ response = response_error(raw_response)
176
+ rescue JSON::ParserError
177
+ response = json_error(raw_response)
178
+ end
179
+
180
+ card = response["card"] || {}
181
+ avs_code = AVS_CODE_TRANSLATOR["line1: #{card["address_line1_check"]}, zip: #{card["address_zip_check"]}"]
182
+ cvc_code = CVC_CODE_TRANSLATOR[card["cvc_check"]]
183
+ Response.new(success,
184
+ success ? "Transaction approved" : response["error"]["message"],
185
+ response,
186
+ :test => !response["livemode"],
187
+ :authorization => response["id"],
188
+ :avs_result => { :code => avs_code },
189
+ :cvv_result => cvc_code
190
+ )
191
+ end
192
+
193
+ def response_error(raw_response)
194
+ begin
195
+ parse(raw_response)
196
+ rescue JSON::ParserError
197
+ json_error(raw_response)
198
+ end
199
+ end
200
+
201
+ def json_error(raw_response)
202
+ msg = 'Invalid response received from the Stripe API. Please contact support@stripe.com if you continue to receive this message.'
203
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
204
+ {
205
+ "error" => {
206
+ "message" => msg
207
+ }
208
+ }
209
+ end
210
+ end
211
+ end
212
+ end