activemerchant 1.41.0 → 1.42.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG +23 -1
  3. data/CONTRIBUTORS +12 -0
  4. data/README.md +3 -0
  5. data/lib/active_merchant/billing/gateway.rb +8 -1
  6. data/lib/active_merchant/billing/gateways/app55.rb +185 -0
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +12 -2
  8. data/lib/active_merchant/billing/gateways/balanced.rb +9 -3
  9. data/lib/active_merchant/billing/gateways/card_stream_modern.rb +2 -1
  10. data/lib/active_merchant/billing/gateways/elavon.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/eway_rapid.rb +2 -2
  12. data/lib/active_merchant/billing/gateways/litle.rb +5 -5
  13. data/lib/active_merchant/billing/gateways/mercury.rb +22 -0
  14. data/lib/active_merchant/billing/gateways/orbital.rb +25 -2
  15. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +187 -0
  16. data/lib/active_merchant/billing/gateways/paymill.rb +60 -28
  17. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +1 -1
  18. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +4 -2
  19. data/lib/active_merchant/billing/gateways/spreedly_core.rb +4 -2
  20. data/lib/active_merchant/billing/gateways/stripe.rb +35 -15
  21. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +158 -0
  22. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +62 -46
  23. data/lib/active_merchant/billing/gateways/webpay.rb +1 -2
  24. data/lib/active_merchant/billing/integrations/bit_pay.rb +2 -2
  25. data/lib/active_merchant/billing/integrations/bit_pay/helper.rb +15 -19
  26. data/lib/active_merchant/billing/integrations/bit_pay/notification.rb +38 -20
  27. data/lib/active_merchant/billing/integrations/notification.rb +2 -2
  28. data/lib/active_merchant/billing/integrations/wirecard_checkout_page.rb +39 -0
  29. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/common.rb +104 -0
  30. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/helper.rb +145 -0
  31. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/notification.rb +101 -0
  32. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/return.rb +35 -0
  33. data/lib/active_merchant/version.rb +1 -1
  34. metadata +16 -59
  35. data.tar.gz.sig +0 -0
  36. metadata.gz.sig +0 -0
@@ -0,0 +1,158 @@
1
+ require 'json'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class SwipeCheckoutGateway < Gateway
6
+ TRANSACTION_APPROVED_MSG = 'Transaction approved'
7
+ TRANSACTION_DECLINED_MSG = 'Transaction declined'
8
+
9
+ LIVE_URLS = {
10
+ 'NZ' => 'https://api.swipehq.com',
11
+ 'CA' => 'https://api.swipehq.ca'
12
+ }
13
+ self.test_url = 'https://api.swipehq.com'
14
+
15
+ TRANSACTION_API = '/createShopifyTransaction.php'
16
+
17
+ self.supported_countries = %w[ NZ CA ]
18
+ self.default_currency = 'NZD'
19
+ self.supported_cardtypes = [:visa, :master]
20
+ self.homepage_url = 'https://www.swipehq.com/checkout'
21
+ self.display_name = 'Swipe Checkout'
22
+ self.money_format = :dollars
23
+
24
+ # Swipe Checkout requires the merchant's email and API key for authorization.
25
+ # This can be found under Settings > API Credentials after logging in to your
26
+ # Swipe Checkout merchant console at https://merchant.swipehq.[com|ca]
27
+ #
28
+ # :region determines which Swipe URL is used, this can be one of "NZ" or "CA".
29
+ # Currently Swipe Checkout has New Zealand and Canadian domains (swipehq.com
30
+ # and swipehq.ca respectively). Merchants must use the region that they
31
+ # signed up in for authentication with their merchant ID and API key to succeed.
32
+ def initialize(options = {})
33
+ requires!(options, :login, :api_key, :region)
34
+ super
35
+ end
36
+
37
+ # Transfers funds immediately.
38
+ # Note that Swipe Checkout only supports purchase at this stage
39
+ def purchase(money, creditcard, options = {})
40
+ post = {}
41
+ add_invoice(post, options)
42
+ add_creditcard(post, creditcard)
43
+ add_customer_data(post, creditcard, options)
44
+ add_amount(post, money, options)
45
+
46
+ commit('sale', money, post)
47
+ end
48
+
49
+ private
50
+
51
+ def add_customer_data(post, creditcard, options)
52
+ post[:email] = options[:email]
53
+ post[:ip_address] = options[:ip]
54
+
55
+ address = options[:billing_address] || options[:address]
56
+ return if address.nil?
57
+
58
+ post[:company] = address[:company]
59
+
60
+ # groups all names after the first into the last name param
61
+ post[:first_name], post[:last_name] = address[:name].split(' ', 2)
62
+ post[:address] = "#{address[:address1]}, #{address[:address2]}"
63
+ post[:city] = address[:city]
64
+ post[:country] = address[:country]
65
+ post[:mobile] = address[:phone] # API only has a "mobile" field, no "phone"
66
+ end
67
+
68
+ def add_invoice(post, options)
69
+ # store shopping-cart order ID in Swipe for merchant's records
70
+ post[:td_user_data] = options[:order_id] if options[:order_id]
71
+ post[:td_item] = options[:description] if options[:description]
72
+ post[:td_description] = options[:description] if options[:description]
73
+ post[:item_quantity] = "1"
74
+ end
75
+
76
+ def add_creditcard(post, creditcard)
77
+ post[:card_number] = creditcard.number
78
+ post[:card_type] = creditcard.brand
79
+ post[:name_on_card] = "#{creditcard.first_name} #{creditcard.last_name}"
80
+ post[:card_expiry] = expdate(creditcard)
81
+ post[:secure_number] = creditcard.verification_value
82
+ end
83
+
84
+ def expdate(creditcard)
85
+ year = format(creditcard.year, :two_digits)
86
+ month = format(creditcard.month, :two_digits)
87
+
88
+ "#{month}#{year}"
89
+ end
90
+
91
+ def add_amount(post, money, options)
92
+ post[:amount] = money.to_s
93
+
94
+ post[:currency] = (options[:currency] || currency(money))
95
+ end
96
+
97
+ def commit(action, money, parameters)
98
+ case action
99
+ when "sale"
100
+ begin
101
+ response = call_api(TRANSACTION_API, parameters)
102
+
103
+ # response code and message params should always be present
104
+ code = response["response_code"]
105
+ message = response["message"]
106
+
107
+ if code == 200
108
+ result = response["data"]["result"]
109
+ success = (result == 'accepted' || (test? && result == 'test-accepted'))
110
+
111
+ Response.new(success,
112
+ success ?
113
+ TRANSACTION_APPROVED_MSG :
114
+ TRANSACTION_DECLINED_MSG,
115
+ response,
116
+ :test => test?
117
+ )
118
+ else
119
+ build_error_response(message, response)
120
+ end
121
+ rescue ResponseError => e
122
+ raw_response = e.response.body
123
+ build_error_response("ssl_post() with url #{url} raised ResponseError: #{e}")
124
+ rescue JSON::ParserError => e
125
+ msg = 'Invalid response received from the Swipe Checkout API. ' +
126
+ 'Please contact support@optimizerhq.com if you continue to receive this message.' +
127
+ " (Full error message: #{e})"
128
+ build_error_response(msg)
129
+ end
130
+ end
131
+ end
132
+
133
+ def call_api(api, params=nil)
134
+ params ||= {}
135
+ params[:merchant_id] = @options[:login]
136
+ params[:api_key] = @options[:api_key]
137
+
138
+ # ssl_post() returns the response body as a string on success,
139
+ # or raises a ResponseError exception on failure
140
+ JSON.parse(ssl_post(url(@options[:region], api), params.to_query))
141
+ end
142
+
143
+ def url(region, api)
144
+ ((test? ? self.test_url : LIVE_URLS[region]) + api)
145
+ end
146
+
147
+ def build_error_response(message, params={})
148
+ Response.new(
149
+ false,
150
+ message,
151
+ params,
152
+ :test => test?
153
+ )
154
+ end
155
+ end
156
+ end
157
+ end
158
+
@@ -2,20 +2,20 @@ module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
 
4
4
  class UsaEpayTransactionGateway < Gateway
5
- self.test_url = 'https://sandbox.usaepay.com/gate.php'
6
- self.live_url = 'https://www.usaepay.com/gate.php'
5
+ self.live_url = 'https://www.usaepay.com/gate'
6
+ self.test_url = 'https://sandbox.usaepay.com/gate'
7
7
 
8
- self.supported_cardtypes = [:visa, :master, :american_express]
9
- self.supported_countries = ['US']
10
- self.homepage_url = 'http://www.usaepay.com/'
11
- self.display_name = 'USA ePay'
8
+ self.supported_cardtypes = [:visa, :master, :american_express]
9
+ self.supported_countries = ['US']
10
+ self.homepage_url = 'http://www.usaepay.com/'
11
+ self.display_name = 'USA ePay'
12
12
 
13
13
  TRANSACTIONS = {
14
- :authorization => 'authonly',
15
- :purchase => 'sale',
16
- :capture => 'capture',
17
- :refund => 'refund',
18
- :void => 'void'
14
+ :authorization => 'cc:authonly',
15
+ :purchase => 'cc:sale',
16
+ :capture => 'cc:capture',
17
+ :refund => 'cc:refund',
18
+ :void => 'cc:void'
19
19
  }
20
20
 
21
21
  def initialize(options = {})
@@ -31,6 +31,7 @@ module ActiveMerchant #:nodoc:
31
31
  add_credit_card(post, credit_card)
32
32
  add_address(post, credit_card, options)
33
33
  add_customer_data(post, options)
34
+ add_split_payments(post, options)
34
35
 
35
36
  commit(:authorization, post)
36
37
  end
@@ -43,6 +44,7 @@ module ActiveMerchant #:nodoc:
43
44
  add_credit_card(post, credit_card)
44
45
  add_address(post, credit_card, options)
45
46
  add_customer_data(post, options)
47
+ add_split_payments(post, options)
46
48
 
47
49
  commit(:purchase, post)
48
50
  end
@@ -66,7 +68,7 @@ module ActiveMerchant #:nodoc:
66
68
  commit(:void, post)
67
69
  end
68
70
 
69
- private
71
+ private
70
72
 
71
73
  def add_amount(post, money)
72
74
  post[:amount] = amount(money)
@@ -108,16 +110,16 @@ module ActiveMerchant #:nodoc:
108
110
  def add_address_for_type(type, post, credit_card, address)
109
111
  prefix = address_key_prefix(type)
110
112
 
111
- post[address_key(prefix, 'fname')] = credit_card.first_name
112
- post[address_key(prefix, 'lname')] = credit_card.last_name
113
- post[address_key(prefix, 'company')] = address[:company] unless address[:company].blank?
114
- post[address_key(prefix, 'street')] = address[:address1] unless address[:address1].blank?
115
- post[address_key(prefix, 'street2')] = address[:address2] unless address[:address2].blank?
116
- post[address_key(prefix, 'city')] = address[:city] unless address[:city].blank?
117
- post[address_key(prefix, 'state')] = address[:state] unless address[:state].blank?
118
- post[address_key(prefix, 'zip')] = address[:zip] unless address[:zip].blank?
119
- post[address_key(prefix, 'country')] = address[:country] unless address[:country].blank?
120
- post[address_key(prefix, 'phone')] = address[:phone] unless address[:phone].blank?
113
+ post[address_key(prefix, 'fname')] = credit_card.first_name
114
+ post[address_key(prefix, 'lname')] = credit_card.last_name
115
+ post[address_key(prefix, 'company')] = address[:company] unless address[:company].blank?
116
+ post[address_key(prefix, 'street')] = address[:address1] unless address[:address1].blank?
117
+ post[address_key(prefix, 'street2')] = address[:address2] unless address[:address2].blank?
118
+ post[address_key(prefix, 'city')] = address[:city] unless address[:city].blank?
119
+ post[address_key(prefix, 'state')] = address[:state] unless address[:state].blank?
120
+ post[address_key(prefix, 'zip')] = address[:zip] unless address[:zip].blank?
121
+ post[address_key(prefix, 'country')] = address[:country] unless address[:country].blank?
122
+ post[address_key(prefix, 'phone')] = address[:phone] unless address[:phone].blank?
121
123
  end
122
124
 
123
125
  def address_key_prefix(type)
@@ -132,15 +134,29 @@ module ActiveMerchant #:nodoc:
132
134
  end
133
135
 
134
136
  def add_invoice(post, options)
135
- post[:invoice] = options[:order_id]
136
- post[:description] = options[:description]
137
+ post[:invoice] = options[:order_id]
138
+ post[:description] = options[:description]
137
139
  end
138
140
 
139
141
  def add_credit_card(post, credit_card)
140
- post[:card] = credit_card.number
141
- post[:cvv2] = credit_card.verification_value if credit_card.verification_value?
142
+ post[:card] = credit_card.number
143
+ post[:cvv2] = credit_card.verification_value if credit_card.verification_value?
142
144
  post[:expir] = expdate(credit_card)
143
- post[:name] = credit_card.name
145
+ post[:name] = credit_card.name
146
+ end
147
+
148
+ # see: http://wiki.usaepay.com/developer/transactionapi#split_payments
149
+ def add_split_payments(post, options)
150
+ return unless options[:split_payments].is_a?(Array)
151
+ options[:split_payments].each_with_index do |payment, index|
152
+ prefix = '%02d' % (index + 2)
153
+ post["#{prefix}key"] = payment[:key]
154
+ post["#{prefix}amount"] = amount(payment[:amount])
155
+ post["#{prefix}description"] = payment[:description]
156
+ end
157
+
158
+ # When blank it's 'Stop'. 'Continue' is another one
159
+ post['onError'] = options[:on_error] || 'Void'
144
160
  end
145
161
 
146
162
  def parse(body)
@@ -151,32 +167,32 @@ module ActiveMerchant #:nodoc:
151
167
  end
152
168
 
153
169
  {
154
- :status => fields['UMstatus'],
155
- :auth_code => fields['UMauthCode'],
156
- :ref_num => fields['UMrefNum'],
157
- :batch => fields['UMbatch'],
158
- :avs_result => fields['UMavsResult'],
159
- :avs_result_code => fields['UMavsResultCode'],
160
- :cvv2_result => fields['UMcvv2Result'],
170
+ :status => fields['UMstatus'],
171
+ :auth_code => fields['UMauthCode'],
172
+ :ref_num => fields['UMrefNum'],
173
+ :batch => fields['UMbatch'],
174
+ :avs_result => fields['UMavsResult'],
175
+ :avs_result_code => fields['UMavsResultCode'],
176
+ :cvv2_result => fields['UMcvv2Result'],
161
177
  :cvv2_result_code => fields['UMcvv2ResultCode'],
162
178
  :vpas_result_code => fields['UMvpasResultCode'],
163
- :result => fields['UMresult'],
164
- :error => fields['UMerror'],
165
- :error_code => fields['UMerrorcode'],
166
- :acs_url => fields['UMacsurl'],
167
- :payload => fields['UMpayload']
179
+ :result => fields['UMresult'],
180
+ :error => fields['UMerror'],
181
+ :error_code => fields['UMerrorcode'],
182
+ :acs_url => fields['UMacsurl'],
183
+ :payload => fields['UMpayload']
168
184
  }.delete_if{|k, v| v.nil?}
169
185
  end
170
186
 
171
187
  def commit(action, parameters)
172
- url = (self.test? ? self.test_url : self.live_url)
173
- response = parse( ssl_post(url, post_data(action, parameters)) )
188
+ url = (test? ? self.test_url : self.live_url)
189
+ response = parse(ssl_post(url, post_data(action, parameters)))
174
190
 
175
191
  Response.new(response[:status] == 'Approved', message_from(response), response,
176
- :test => test?,
177
- :authorization => response[:ref_num],
178
- :cvv_result => response[:cvv2_result_code],
179
- :avs_result => { :code => response[:avs_result_code] }
192
+ :test => test?,
193
+ :authorization => response[:ref_num],
194
+ :cvv_result => response[:cvv2_result_code],
195
+ :avs_result => { :code => response[:avs_result_code] }
180
196
  )
181
197
  end
182
198
 
@@ -191,7 +207,7 @@ module ActiveMerchant #:nodoc:
191
207
 
192
208
  def post_data(action, parameters = {})
193
209
  parameters[:command] = TRANSACTIONS[action]
194
- parameters[:key] = @options[:login]
210
+ parameters[:key] = @options[:login]
195
211
  parameters[:software] = 'Active Merchant'
196
212
  parameters[:testmode] = (@options[:test] ? 1 : 0)
197
213
 
@@ -68,8 +68,7 @@ module ActiveMerchant #:nodoc:
68
68
  :lang => 'ruby',
69
69
  :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
70
70
  :platform => RUBY_PLATFORM,
71
- :publisher => 'active_merchant',
72
- :uname => (RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
71
+ :publisher => 'active_merchant'
73
72
  })
74
73
 
75
74
  {
@@ -13,8 +13,8 @@ module ActiveMerchant #:nodoc:
13
13
  mattr_accessor :invoicing_url
14
14
  self.invoicing_url = 'https://bitpay.com/api/invoice'
15
15
 
16
- def self.notification(post)
17
- Notification.new(post)
16
+ def self.notification(post, options = {})
17
+ Notification.new(post, options)
18
18
  end
19
19
 
20
20
  def self.helper(order, account, options = {})
@@ -6,20 +6,14 @@ module ActiveMerchant #:nodoc:
6
6
  def initialize(order_id, account, options)
7
7
  super
8
8
  @account = account
9
- add_field('orderID', order_id)
10
- add_field('posData', options[:authcode])
11
- add_field('currency', options[:currency])
12
- add_field('fullNotifications', 'true')
13
- add_field('transactionSpeed', options[:transactionSpeed] || "high")
14
- add_field('address1', options[:address1])
15
9
 
16
- generate_invoice_id
10
+ add_field('posData', {'orderId' => order_id}.to_json)
11
+ add_field('fullNotifications', true)
12
+ add_field('transactionSpeed', 'high')
17
13
  end
18
14
 
19
- # Replace with the real mapping
20
15
  mapping :amount, 'price'
21
-
22
- mapping :order, 'orderID'
16
+ mapping :order, 'orderID'
23
17
  mapping :currency, 'currency'
24
18
 
25
19
  mapping :customer, :first_name => 'buyerName',
@@ -37,20 +31,20 @@ module ActiveMerchant #:nodoc:
37
31
  mapping :return_url, 'redirectURL'
38
32
  mapping :id, 'id'
39
33
 
40
- def generate_invoice_id
41
- invoice_data = ssl_post(BitPay.invoicing_url)
42
-
43
- add_field('id', JSON.parse(invoice_data.body)['id'])
44
- end
45
-
46
34
  def form_method
47
35
  "GET"
48
36
  end
49
37
 
38
+ def form_fields
39
+ invoice = create_invoice
40
+
41
+ {"id" => invoice['id']}
42
+ end
43
+
50
44
  private
51
45
 
52
- def ssl_post(url, options = {})
53
- uri = URI.parse(url)
46
+ def create_invoice
47
+ uri = URI.parse(BitPay.invoicing_url)
54
48
  http = Net::HTTP.new(uri.host, uri.port)
55
49
  http.use_ssl = true
56
50
 
@@ -59,7 +53,9 @@ module ActiveMerchant #:nodoc:
59
53
  request.body = @fields.to_json
60
54
  request.basic_auth @account, ''
61
55
 
62
- http.request(request)
56
+ response = http.request(request)
57
+ JSON.parse(response.body)
58
+ rescue JSON::ParseError
63
59
  end
64
60
  end
65
61
  end
@@ -6,13 +6,29 @@ module ActiveMerchant #:nodoc:
6
6
  module BitPay
7
7
  class Notification < ActiveMerchant::Billing::Integrations::Notification
8
8
  def complete?
9
- status == "complete"
9
+ status == "Completed"
10
10
  end
11
11
 
12
12
  def transaction_id
13
13
  params['id']
14
14
  end
15
15
 
16
+ def item_id
17
+ JSON.parse(params['posData'])['orderId']
18
+ rescue JSON::ParserError
19
+ end
20
+
21
+ def status
22
+ case params['status']
23
+ when 'complete'
24
+ 'Completed'
25
+ when 'confirmed'
26
+ 'Pending'
27
+ when 'invalid'
28
+ 'Failed'
29
+ end
30
+ end
31
+
16
32
  # When was this payment received by the client.
17
33
  def received_at
18
34
  params['invoiceTime'].to_i
@@ -22,32 +38,34 @@ module ActiveMerchant #:nodoc:
22
38
  params['currency']
23
39
  end
24
40
 
25
- def amount
26
- params['price']
41
+ def gross
42
+ params['price'].to_f
27
43
  end
28
44
 
29
- # the money amount we received in X.2 decimal.
30
- def btcPrice
31
- params['btcPrice'].to_f
32
- end
45
+ def acknowledge(authcode = nil)
46
+ uri = URI.parse("#{ActiveMerchant::Billing::Integrations::BitPay.invoicing_url}/#{transaction_id}")
33
47
 
34
- def status
35
- params['status'].downcase
36
- end
48
+ http = Net::HTTP.new(uri.host, uri.port)
49
+ http.use_ssl = true
37
50
 
38
- def acknowledge(authcode = nil)
39
- authcode == params['posData']
51
+ request = Net::HTTP::Get.new(uri.path)
52
+ request.basic_auth @options[:credential1], ''
53
+
54
+ response = http.request(request)
55
+
56
+ posted_json = JSON.parse(@raw).tap { |j| j.delete('currentTime') }
57
+ parse(response.body)
58
+ retrieved_json = JSON.parse(@raw).tap { |j| j.delete('currentTime') }
59
+
60
+ posted_json == retrieved_json
61
+ rescue JSON::ParserError
40
62
  end
41
63
 
42
64
  private
43
-
44
- # Take the posted data and move the relevant data into a hash
45
- def parse(post)
46
- @raw = post.to_s
47
- for line in @raw.split('&')
48
- key, value = *line.scan( %r{^([A-Za-z0-9_.]+)\=(.*)$} ).flatten
49
- params[key] = CGI.unescape(value)
50
- end
65
+ def parse(body)
66
+ @raw = body
67
+ @params = JSON.parse(@raw)
68
+ rescue JSON::ParserError
51
69
  end
52
70
  end
53
71
  end