activemerchant 1.49.0 → 1.50.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +47 -0
  3. data/CONTRIBUTORS +4 -0
  4. data/README.md +5 -1
  5. data/lib/active_merchant/billing/credit_card.rb +9 -0
  6. data/lib/active_merchant/billing/gateways/allied_wallet.rb +203 -0
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +17 -6
  8. data/lib/active_merchant/billing/gateways/beanstream.rb +4 -0
  9. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +0 -4
  10. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +4 -0
  11. data/lib/active_merchant/billing/gateways/bpoint.rb +277 -0
  12. data/lib/active_merchant/billing/gateways/cashnet.rb +19 -8
  13. data/lib/active_merchant/billing/gateways/cenpos.rb +15 -29
  14. data/lib/active_merchant/billing/gateways/conekta.rb +3 -2
  15. data/lib/active_merchant/billing/gateways/dibs.rb +206 -0
  16. data/lib/active_merchant/billing/gateways/fat_zebra.rb +19 -12
  17. data/lib/active_merchant/billing/gateways/merchant_partners.rb +245 -0
  18. data/lib/active_merchant/billing/gateways/netbilling.rb +1 -0
  19. data/lib/active_merchant/billing/gateways/omise.rb +319 -0
  20. data/lib/active_merchant/billing/gateways/optimal_payment.rb +5 -4
  21. data/lib/active_merchant/billing/gateways/orbital.rb +7 -0
  22. data/lib/active_merchant/billing/gateways/payu_in.rb +243 -0
  23. data/lib/active_merchant/billing/gateways/quickpay.rb +11 -357
  24. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +188 -0
  25. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +240 -0
  26. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +227 -0
  27. data/lib/active_merchant/billing/gateways/s5.rb +226 -0
  28. data/lib/active_merchant/billing/gateways/sage_pay.rb +7 -1
  29. data/lib/active_merchant/billing/gateways/secure_net.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/stripe.rb +8 -5
  31. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +2 -2
  32. data/lib/active_merchant/billing/gateways/vanco.rb +280 -0
  33. data/lib/active_merchant/connection.rb +3 -0
  34. data/lib/active_merchant/version.rb +1 -1
  35. metadata +15 -27
  36. checksums.yaml.gz.sig +0 -2
  37. data.tar.gz.sig +0 -0
  38. data/lib/active_merchant/billing/gateways/adyen.rb +0 -209
  39. metadata.gz.sig +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5057426f88d783e8a04528d24fb75c31011eca30
4
- data.tar.gz: de26a0e5fff0c27aad8a96bd8b832e7cd1c53c1f
3
+ metadata.gz: 42f985c77801a167c97a8fd6a09ce6516a75f645
4
+ data.tar.gz: 878116909f094338c4d837cdb33315bd6bff34e7
5
5
  SHA512:
6
- metadata.gz: 4d4df9ee441a742077ebc2109758b58ad76e76668d3e4a4169bf778e365fe300d9b8a1f977abc95df6e606fcd1d09491344453c79055771d7ba2ef4f44a1a52b
7
- data.tar.gz: fdc30b7c943d11a2f7e505f2f978615b27cb4816360a7e5cde48598f8d3ff72ef58df2feb39a93d77984c0a6e44def9f11fd80ae590487e35dcd19482e99e406
6
+ metadata.gz: 22674a72518b5aa6376d75366344dea6e0e4a9d021e0fa38672d8c423bda37459722f8818fa852fba4245b330deec8e7d0d7ee4d4af761c2ba204552dc1d9439
7
+ data.tar.gz: 8e2a471639e936c5ec32465f691ae39df7b535490d533569a8efaf9024342219b4fd16410bd9667daf37688c3f4824b6dd41057b8a7adf3cb08a01518fa188c1
data/CHANGELOG CHANGED
@@ -1,5 +1,52 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.50.0 (June 1, 2015)
4
+
5
+ * Vanco: Add gateway [duff]
6
+ * Conekta: Move device fingerprint to root [MauricioMurga]
7
+ * Conekta: Change default language to Spanish [MauricioMurga]
8
+ * Vanco: Improve authentication handling [duff]
9
+ * Vanco: Allow specification of fund_id [duff]
10
+ * S5: Add gateway [davidsantoso]
11
+ * SecureNet: Truncate order_id [duff]
12
+ * [POSSIBLE BREAKAGE] Stripe: Be explicit about API version [duff]
13
+ * Dibs: Add gateway [mrezentes]
14
+ * Dibs: Rubyize merchant_id and secret_key [mrezentes]
15
+ * Stripe: Add support for reverse_transfer [duff]
16
+ * USA ePay: Add support for manual entry indicator [AnotherJoSmith]
17
+ * Authorize.Net: Add support for manual entry indicator [AnotherJoSmith]
18
+ * CenPOS: Change description to invoice_detail [mrezentes]
19
+ * BPoint: Add gateway [tjstankus]
20
+ * S5: Remove address requirement for purchase and authorize [davidsantoso]
21
+ * Vanco: Add support for eChecks [duff]
22
+ * Remove Adyen support [ntalbott]
23
+ * CenPOS: Use ProcessCreditCard action [duff]
24
+ * CASHnet: uri encode the merchant gateway name [mrezentes]
25
+ * S5: Include card brand in request body [davidsantoso]
26
+ * Vanco: Handle multiple error responses [duff]
27
+ * Merchant Partners gateway support [rwdaigle]
28
+ * BPoint: Update params to contain all response data [tjstankus]
29
+ * BPoint: Support biller_code in options [tjstankus]
30
+ * Sagepay: Add Verify [anellis]
31
+ * S5: Build XML with UTF-8 encoding [tjstankus]
32
+ * Cashnet: Handle unparsable response body [duff]
33
+ * CenPOS: Allow specification of customer_code [duff]
34
+ * Allied Wallet: Add gateway [anellis]
35
+ * S5: set Regex closure on scrubbing method [davidsantoso]
36
+ * Dibs: Require TLSv1 [duff]
37
+ * Optimal: Handle case of no billing address [duff]
38
+ * Omise: Add gateway [zdk]
39
+ * CenPOS: Simplify currency handling [duff]
40
+ * Beanstream: Don't treat redirect as success [aprofeit]
41
+ * Add PayU India gateway [ntalbott]
42
+ * NetBilling: Require TLSv1 [duff]
43
+ * S5: Handle recurring transactions without CVV [davidsantoso]
44
+ * Stripe: Force USD for verify [duff]
45
+ * PayU India: Prevent shadowing in response parsing [ntalbott]
46
+ * QuickPay: Add support for v10 API [ta]
47
+ * Fat Zebra: Fix refund and store signatures [duff]
48
+ * Fat Zebra: Allow transactions without a CVV [duff]
49
+
3
50
  == Version 1.49.0 (May 1, 2015)
4
51
 
5
52
  * Braintree: Add support for AVS error codes [ivanvfer]
@@ -534,3 +534,7 @@ Qvalent (March 2015)
534
534
  Worldpay Online Payments (April 2015)
535
535
 
536
536
  * (ao)
537
+
538
+ PayU India (May 2015)
539
+
540
+ * Nathaniel Talbott (ntalbott)
data/README.md CHANGED
@@ -76,7 +76,7 @@ end
76
76
  ```
77
77
 
78
78
  For more in-depth documentation and tutorials, see [GettingStarted.md](GettingStarted.md) and the
79
- [API documentation](http://rubydoc.info/github/Shopify/active_merchant/master/file/README.md).
79
+ [API documentation](http://www.rubydoc.info/github/Shopify/active_merchant/).
80
80
 
81
81
  ## Supported Payment Gateways
82
82
 
@@ -107,6 +107,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
107
107
  * [Commercegate](http://www.commercegate.com/) - AD, AT, AX, BE, BG, CH, CY, CZ, DE, DK, ES, FI, FR, GB, GG, GI, GR, HR, HU, IE, IM, IS, IT, JE, LI, LT, LU, LV, MC, MT, NL, NO, PL, PT, RO, SE, SI, SK, VA
108
108
  * [Conekta](https://conekta.io) - MX
109
109
  * [CyberSource](http://www.cybersource.com) - US, BR, CA, CN, DK, FI, FR, DE, JP, MX, NO, SE, GB, SG
110
+ * [DIBS](http://www.dibspayment.com/) - US, FI, NO, SE, GB
110
111
  * [DataCash](http://www.datacash.com/) - GB
111
112
  * [Efsnet](http://www.concordefsnet.com/) - US
112
113
  * [Elavon MyVirtualMerchant](http://www.elavon.com/) - US, CA
@@ -178,6 +179,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
178
179
  * [Payscout](http://www.payscout.com/) - US
179
180
  * [Paystation](http://paystation.co.nz) - NZ
180
181
  * [Pay Way](http://www.payway.com.au) - AU
182
+ * [PayU India](https://www.payu.in/) - IN
181
183
  * [Pin Payments](http://www.pin.net.au/) - AU
182
184
  * [Plug'n Pay](http://www.plugnpay.com/) - US
183
185
  * [Psigate](http://www.psigate.com/) - CA
@@ -190,6 +192,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
190
192
  * [Raven PacNet](http://www.pacnetservices.com/) - US
191
193
  * [Realex](http://www.realexpayments.com/) - IE, GB, FR, BE, NL, LU, IT
192
194
  * [Redsys](http://www.redsys.es/) - ES
195
+ * [S5](http://www.s5.dk/) - DK
193
196
  * [SagePay](http://www.sagepay.com) - GB, IE
194
197
  * [Sage Payment Solutions](http://www.sagepayments.com) - US, CA
195
198
  * [Sallie Mae](http://www.salliemae.com/) - US
@@ -207,6 +210,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
207
210
  * [Transnational](http://www.tnbci.com/) - US
208
211
  * [TrustCommerce](http://www.trustcommerce.com/) - US
209
212
  * [USA ePay](http://www.usaepay.com/) - US
213
+ * [Vanco Payment Solutions](http://vancopayments.com/) - US
210
214
  * [Verifi](http://www.verifi.com/) - US
211
215
  * [ViaKLIX](http://viaklix.com) - US
212
216
  * [WebPay](https://webpay.jp/) - JP
@@ -138,6 +138,15 @@ module ActiveMerchant #:nodoc:
138
138
  # @return [String]
139
139
  attr_accessor :track_data
140
140
 
141
+ # Returns or sets whether a card has been processed using manual entry.
142
+ #
143
+ # This attribute is optional and is only used by gateways who use this information in their transaction risk
144
+ # calculations. See {this page on 'card not present' transactions}[http://en.wikipedia.org/wiki/Card_not_present_transaction]
145
+ # for further explanation and examples of this kind of transaction.
146
+ #
147
+ # @return [true, false]
148
+ attr_accessor :manual_entry
149
+
141
150
  # Returns or sets the ICC/ASN1 credit card data for a EMV transaction, typically this is a BER-encoded TLV string.
142
151
  #
143
152
  # @return [String]
@@ -0,0 +1,203 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class AlliedWalletGateway < Gateway
4
+ self.display_name = "Allied Wallet"
5
+ self.homepage_url = "https://www.alliedwallet.com"
6
+
7
+ self.live_url = "https://api.alliedwallet.com/merchants/"
8
+
9
+ self.supported_countries = ["US"]
10
+ self.default_currency = "USD"
11
+ self.money_format = :dollars
12
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover,
13
+ :diners_club, :jcb, :maestro]
14
+
15
+ def initialize(options={})
16
+ requires!(options, :site_id, :merchant_id, :token)
17
+ super
18
+ end
19
+
20
+ def purchase(amount, payment_method, options={})
21
+ post = {}
22
+ add_invoice(post, amount, options)
23
+ add_payment_method(post, payment_method)
24
+ add_customer_data(post, options)
25
+
26
+ commit(:purchase, post)
27
+ end
28
+
29
+ def authorize(amount, payment_method, options={})
30
+ post = {}
31
+ add_invoice(post, amount, options)
32
+ add_payment_method(post, payment_method)
33
+ add_customer_data(post, options)
34
+
35
+ commit(:authorize, post)
36
+ end
37
+
38
+ def capture(amount, authorization, options={})
39
+ post = {}
40
+ add_invoice(post, amount, options)
41
+ add_reference(post, authorization, :capture)
42
+ add_customer_data(post, options)
43
+
44
+ commit(:capture, post)
45
+ end
46
+
47
+ def void(authorization, options={})
48
+ post = {}
49
+ add_reference(post, authorization, :void)
50
+
51
+ commit(:void, post)
52
+ end
53
+
54
+ def refund(amount, authorization, options={})
55
+ post = {}
56
+ add_invoice(post, amount, options)
57
+ add_reference(post, authorization, :refund)
58
+ add_amount(post, amount)
59
+ add_customer_data(post, options)
60
+
61
+ commit(:refund, post)
62
+ end
63
+
64
+ def verify(credit_card, options={})
65
+ MultiResponse.run(:use_first_response) do |r|
66
+ r.process { authorize(100, credit_card, options) }
67
+ r.process(:ignore_result) { void(r.authorization, options) }
68
+ end
69
+ end
70
+
71
+ def supports_scrubbing?
72
+ true
73
+ end
74
+
75
+ def scrub(transcript)
76
+ transcript.
77
+ gsub(%r((Authorization: Bearer )[a-zA-Z0-9._-]+)i, '\1[FILTERED]').
78
+ gsub(%r(("cardNumber\\?":\\?")[^"]*)i, '\1[FILTERED]').
79
+ gsub(%r(("cVVCode\\?":\\?")[^"]*)i, '\1[FILTERED]')
80
+ end
81
+
82
+ private
83
+
84
+ def add_amount(post, amount)
85
+ post[:amount] = amount
86
+ end
87
+
88
+ def add_invoice(post, money, options)
89
+ post[:siteId] = @options[:site_id]
90
+ post[:amount] = amount(money)
91
+ post[:trackingId] = options[:order_id]
92
+ post[:currency] = options[:currency] || currency(money)
93
+ end
94
+
95
+ def add_payment_method(post, payment_method)
96
+ post[:nameOnCard] = payment_method.name
97
+ post[:cardNumber] = payment_method.number
98
+ post[:cVVCode] = payment_method.verification_value
99
+ post[:expirationYear] = format(payment_method.year, :four_digits)
100
+ post[:expirationMonth] = format(payment_method.month, :two_digits)
101
+ end
102
+
103
+ def add_customer_data(post, options)
104
+ post[:email] = options[:email] || "unspecified@example.com"
105
+ post[:iPAddress] = options[:ip]
106
+ if (billing_address = options[:billing_address])
107
+ post[:firstName], post[:lastName] = billing_address[:name].split
108
+ post[:addressLine1] = billing_address[:address1]
109
+ post[:addressLine2] = billing_address[:address2]
110
+ post[:city] = billing_address[:city]
111
+ post[:state] = billing_address[:state]
112
+ post[:countryId] = billing_address[:country]
113
+ post[:postalCode] = billing_address[:zip]
114
+ post[:phone] = billing_address[:phone]
115
+ end
116
+ end
117
+
118
+ def add_reference(post, authorization, action)
119
+ transactions = {
120
+ capture: :authorizetransactionid,
121
+ void: :authorizeTransactionid,
122
+ refund: :referencetransactionid,
123
+ recurring: :saleTransactionid
124
+ }
125
+ post[transactions[action]] = authorization
126
+ end
127
+
128
+
129
+ ACTIONS = {
130
+ purchase: "SALE",
131
+ authorize: "AUTHORIZE",
132
+ capture: "CAPTURE",
133
+ void: "VOID",
134
+ refund: "REFUND"
135
+ }
136
+
137
+ def commit(action, post)
138
+ begin
139
+ raw_response = ssl_post(url(action), post.to_json, headers)
140
+ response = parse(raw_response)
141
+ rescue ResponseError => e
142
+ raise unless(e.response.code.to_s =~ /4\d\d/)
143
+ response = parse(e.response.body)
144
+ end
145
+
146
+ succeeded = success_from(response["status"])
147
+ Response.new(
148
+ succeeded,
149
+ message_from(succeeded, response),
150
+ response,
151
+ authorization: response["id"],
152
+ :avs_result => AVSResult.new(code: response["avs_response"]),
153
+ :cvv_result => CVVResult.new(response["cvv2_response"]),
154
+ test: test?
155
+ )
156
+ rescue JSON::ParserError
157
+ unparsable_response(raw_response)
158
+ end
159
+
160
+ def unparsable_response(raw_response)
161
+ message = "Unparsable response received from Allied Wallet. Please contact Allied Wallet if you continue to receive this message."
162
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
163
+ return Response.new(false, message)
164
+ end
165
+
166
+ def headers
167
+ {
168
+ "Content-type" => "application/json",
169
+ "Authorization" => "Bearer " + @options[:token]
170
+ }
171
+ end
172
+
173
+ def url(action)
174
+ live_url + CGI.escape(@options[:merchant_id]) + "/" + ACTIONS[action] + "transactions"
175
+ end
176
+
177
+ def parse(body)
178
+ JSON.parse(body)
179
+ end
180
+
181
+ def parse_element(response, node)
182
+ if node.has_elements?
183
+ node.elements.each{|element| parse_element(response, element) }
184
+ else
185
+ response[node.name.underscore.to_sym] = node.text
186
+ end
187
+ end
188
+
189
+ def success_from(response)
190
+ response == "Successful"
191
+ end
192
+
193
+ def message_from(succeeded, response)
194
+ if succeeded
195
+ "Succeeded"
196
+ else
197
+ response["message"] || "Unable to read error message"
198
+ end
199
+ end
200
+
201
+ end
202
+ end
203
+ end
@@ -35,6 +35,11 @@ module ActiveMerchant #:nodoc:
35
35
  '24' => STANDARD_ERROR_CODE[:pickup_card]
36
36
  }
37
37
 
38
+ MARKET_TYPE = {
39
+ :moto => '1',
40
+ :retail => '2'
41
+ }
42
+
38
43
  class_attribute :duplicate_window
39
44
 
40
45
  APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
@@ -66,7 +71,7 @@ module ActiveMerchant #:nodoc:
66
71
  add_payment_source(xml, payment)
67
72
  add_invoice(xml, options)
68
73
  add_customer_data(xml, payment, options)
69
- add_retail_data(xml, payment)
74
+ add_market_type(xml, payment)
70
75
  add_settings(xml, payment, options)
71
76
  add_user_fields(xml, amount, options)
72
77
  end
@@ -83,6 +88,7 @@ module ActiveMerchant #:nodoc:
83
88
  add_payment_source(xml, payment)
84
89
  add_invoice(xml, options)
85
90
  add_customer_data(xml, payment, options)
91
+ add_market_type(xml, payment)
86
92
  add_settings(xml, payment, options)
87
93
  add_user_fields(xml, amount, options)
88
94
  end
@@ -268,11 +274,16 @@ module ActiveMerchant #:nodoc:
268
274
  end
269
275
  end
270
276
 
271
- def add_retail_data(xml, payment)
272
- return unless valid_track_data
273
- xml.retail do
274
- # As per http://www.authorize.net/support/CP_guide.pdf, '2' is for Retail, the only current market_type
275
- xml.marketType(2)
277
+ def add_market_type(xml, payment)
278
+ return if card_brand(payment) == 'check' or card_brand(payment) == 'apple_pay'
279
+ if valid_track_data
280
+ xml.retail do
281
+ xml.marketType(MARKET_TYPE[:retail])
282
+ end
283
+ elsif payment.manual_entry
284
+ xml.retail do
285
+ xml.marketType(MARKET_TYPE[:moto])
286
+ end
276
287
  end
277
288
  end
278
289
 
@@ -99,6 +99,10 @@ module ActiveMerchant #:nodoc:
99
99
  commit(post)
100
100
  end
101
101
 
102
+ def success?(response)
103
+ response[:trnApproved] == '1' || response[:responseCode] == '1'
104
+ end
105
+
102
106
  def recurring(money, source, options = {})
103
107
  ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
104
108
 
@@ -351,10 +351,6 @@ module ActiveMerchant #:nodoc:
351
351
  response[:message]
352
352
  end
353
353
 
354
- def success?(response)
355
- response[:responseType] == 'R' || response[:trnApproved] == '1' || response[:responseCode] == '1'
356
- end
357
-
358
354
  def recurring_success?(response)
359
355
  response[:code] == '1'
360
356
  end
@@ -32,6 +32,10 @@ module ActiveMerchant #:nodoc:
32
32
  commit(post)
33
33
  end
34
34
 
35
+ def success?(response)
36
+ response[:responseType] == 'R' || response[:trnApproved] == '1' || response[:responseCode] == '1'
37
+ end
38
+
35
39
  # Confirm a transaction posted back from the bank to Beanstream.
36
40
  def confirm(transaction)
37
41
  post(transaction)
@@ -0,0 +1,277 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class BpointGateway < Gateway
6
+ self.live_url = 'https://www.bpoint.com.au/evolve/service_1_4_4.asmx'
7
+
8
+ self.supported_countries = ['AU']
9
+ self.default_currency = 'AUD'
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
11
+
12
+ self.homepage_url = 'https://www.bpoint.com.au/bpoint'
13
+ self.display_name = 'BPoint'
14
+
15
+ def initialize(options={})
16
+ requires!(options, :username, :password, :merchant_number)
17
+ super
18
+ end
19
+
20
+ def store(credit_card, options={})
21
+ options[:crn1] ||= 'DEFAULT'
22
+ request_body = soap_request do |xml|
23
+ add_token(xml, credit_card, options)
24
+ end
25
+ commit(request_body)
26
+ end
27
+
28
+ def purchase(amount, credit_card, options={})
29
+ request_body = soap_request do |xml|
30
+ process_payment(xml) do |payment_xml|
31
+ add_purchase(payment_xml, amount, credit_card, options)
32
+ end
33
+ end
34
+ commit(request_body)
35
+ end
36
+
37
+ def authorize(amount, credit_card, options={})
38
+ request_body = soap_request do |xml|
39
+ process_payment(xml) do |payment_xml|
40
+ add_authorize(payment_xml, amount, credit_card, options)
41
+ end
42
+ end
43
+ commit(request_body)
44
+ end
45
+
46
+ def capture(amount, authorization, options={})
47
+ request_body = soap_request do |xml|
48
+ process_payment(xml) do |payment_xml|
49
+ add_capture(payment_xml, amount, authorization, options)
50
+ end
51
+ end
52
+ commit(request_body)
53
+ end
54
+
55
+ def refund(amount, authorization, options={})
56
+ request_body = soap_request do |xml|
57
+ process_payment(xml) do |payment_xml|
58
+ add_refund(payment_xml, amount, authorization, options)
59
+ end
60
+ end
61
+ commit(request_body)
62
+ end
63
+
64
+ def void(amount, authorization, options={})
65
+ request_body = soap_request do |xml|
66
+ process_payment(xml) do |payment_xml|
67
+ add_void(payment_xml, amount, authorization, options)
68
+ end
69
+ end
70
+ commit(request_body)
71
+ end
72
+
73
+ def verify(credit_card, options={})
74
+ MultiResponse.run(:use_first_response) do |r|
75
+ r.process { authorize(100, credit_card, options) }
76
+ r.process(:ignore_result) { void(100, r.authorization, options) }
77
+ end
78
+ end
79
+
80
+ def supports_scrubbing?
81
+ true
82
+ end
83
+
84
+ def scrub(transcript)
85
+ transcript.
86
+ gsub(%r((<password>).+(</password>)), '\1[FILTERED]\2').
87
+ gsub(%r((<CardNumber>).+(</CardNumber>)), '\1[FILTERED]\2').
88
+ gsub(%r((<CVC>).+(</CVC>)), '\1[FILTERED]\2')
89
+ end
90
+
91
+ private
92
+
93
+ def soap_request
94
+ Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
95
+ xml.send('soap12:Envelope', soap_envelope_attributes) {
96
+ xml.send('soap12:Body') {
97
+ yield(xml) if block_given?
98
+ }
99
+ }
100
+ end.to_xml
101
+ end
102
+
103
+ def soap_envelope_attributes
104
+ { 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
105
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
106
+ 'xmlns:soap12' => 'http://www.w3.org/2003/05/soap-envelope' }
107
+ end
108
+
109
+ def process_payment(xml)
110
+ xml.send('ProcessPayment', { 'xmlns' => 'urn:Eve_1_4_4' }) {
111
+ credentials_xml(xml)
112
+ xml.send('txnReq') {
113
+ yield(xml) if block_given?
114
+ }
115
+ }
116
+ end
117
+
118
+ def add_token(xml, credit_card, options)
119
+ xml.send('AddToken', { 'xmlns' => 'urn:Eve_1_4_4' }) {
120
+ credentials_xml(xml)
121
+ xml.send('tokenRequest') {
122
+ xml.send('CRN1', options[:crn1])
123
+ xml.send('CRN2', '')
124
+ xml.send('CRN3', '')
125
+ xml.send('CardNumber', credit_card.number)
126
+ xml.send('ExpiryDate', expdate(credit_card))
127
+ }
128
+ }
129
+ end
130
+
131
+ def credentials_xml(xml)
132
+ xml.send('username', @options[:username])
133
+ xml.send('password', @options[:password])
134
+ xml.send('merchantNumber', @options[:merchant_number])
135
+ end
136
+
137
+ def add_purchase(xml, amount, credit_card, options)
138
+ payment_xml(xml, 'PAYMENT', amount, options)
139
+ credit_card_xml(xml, credit_card)
140
+ end
141
+
142
+ def add_authorize(xml, amount, credit_card, options)
143
+ payment_xml(xml, 'PREAUTH', amount, options)
144
+ credit_card_xml(xml, credit_card)
145
+ end
146
+
147
+ def add_capture(xml, amount, transaction_number, options)
148
+ payment_xml(xml, 'CAPTURE', amount, options)
149
+ transaction_number_xml(xml, transaction_number)
150
+ end
151
+
152
+ def add_refund(xml, amount, transaction_number, options)
153
+ payment_xml(xml, 'REFUND', amount, options)
154
+ transaction_number_xml(xml, transaction_number)
155
+ end
156
+
157
+ def add_void(xml, amount, transaction_number, options)
158
+ payment_xml(xml, 'REVERSAL', amount, options)
159
+ transaction_number_xml(xml, transaction_number)
160
+ end
161
+
162
+ def payment_xml(xml, payment_type, amount, options)
163
+ xml.send('PaymentType', payment_type)
164
+ xml.send('TxnType', 'WEB_SHOP')
165
+ xml.send('BillerCode', options.fetch(:biller_code, ''))
166
+ xml.send('MerchantReference', '')
167
+ xml.send('CRN1', '')
168
+ xml.send('CRN2', '')
169
+ xml.send('CRN3', '')
170
+ xml.send('Amount', amount)
171
+ end
172
+
173
+ def credit_card_xml(xml, credit_card)
174
+ xml.send('CardNumber', credit_card.number)
175
+ xml.send('ExpiryDate', expdate(credit_card))
176
+ xml.send('CVC', credit_card.verification_value)
177
+ end
178
+
179
+ def transaction_number_xml(xml, transaction_number)
180
+ xml.send('OriginalTransactionNumber', transaction_number)
181
+ end
182
+
183
+ def commit(request_body)
184
+ parse(ssl_post(live_url, request_body, request_headers))
185
+ end
186
+
187
+ def request_headers
188
+ { "Content-Type" => "application/soap+xml; charset=utf-8" }
189
+ end
190
+
191
+ def parse(body)
192
+ response_for(Nokogiri::XML(body).remove_namespaces!)
193
+ end
194
+
195
+ def response_for(xml_doc)
196
+ if xml_doc.xpath('//ProcessPaymentResponse').any?
197
+ ProcessPaymentResponse.new(xml_doc, self).to_response
198
+ elsif xml_doc.xpath('//AddTokenResponse').any?
199
+ AddTokenResponse.new(xml_doc, self).to_response
200
+ end
201
+ end
202
+
203
+ class BPointResponse
204
+ attr_reader :xml_doc, :gateway, :params
205
+
206
+ def initialize(xml_doc, gateway)
207
+ @xml_doc = xml_doc
208
+ @gateway = gateway
209
+ @params = init_params
210
+ end
211
+
212
+ def to_response
213
+ Response.new(success?, message, params, options)
214
+ end
215
+
216
+ private
217
+
218
+ def init_params
219
+ {}.tap do |h|
220
+ xml_doc.xpath(response_node).each do |node|
221
+ if node.elements.empty?
222
+ h[node.name.to_sym] = node.text
223
+ else
224
+ node.elements.each do |childnode|
225
+ name = "#{node.name}_#{childnode.name}"
226
+ h[name.to_sym] = childnode.text
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ def response_node
234
+ "//#{self.class.name.split('::').last}/*"
235
+ end
236
+
237
+ def options
238
+ { authorization: params[authorization_key], test: gateway.test? }
239
+ end
240
+ end
241
+
242
+ class ProcessPaymentResponse < BPointResponse
243
+
244
+ private
245
+
246
+ def authorization_key
247
+ :ProcessPaymentResult_TransactionNumber
248
+ end
249
+
250
+ def success?
251
+ params[:ProcessPaymentResult_ResponseCode] == '0'
252
+ end
253
+
254
+ def message
255
+ params[:ProcessPaymentResult_AuthorisationResult]
256
+ end
257
+ end
258
+
259
+ class AddTokenResponse < BPointResponse
260
+
261
+ private
262
+
263
+ def authorization_key
264
+ :AddTokenResult_Token
265
+ end
266
+
267
+ def success?
268
+ params[:response_ResponseCode] == 'SUCCESS'
269
+ end
270
+
271
+ def message
272
+ params[:response_ResponseCode].capitalize
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end