activemerchant 1.15.0 → 1.16.0

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.
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