activemerchant 1.26.0 → 1.27.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 (112) hide show
  1. data/CHANGELOG +17 -0
  2. data/CONTRIBUTORS +20 -0
  3. data/README.md +5 -0
  4. data/lib/active_merchant/billing/base.rb +2 -2
  5. data/lib/active_merchant/billing/gateway.rb +6 -0
  6. data/lib/active_merchant/billing/gateways/authorize_net.rb +1 -1
  7. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +0 -3
  8. data/lib/active_merchant/billing/gateways/balanced.rb +462 -0
  9. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +3 -3
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -2
  12. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +1 -1
  13. data/lib/active_merchant/billing/gateways/braintree.rb +2 -0
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +11 -1
  15. data/lib/active_merchant/billing/gateways/braintree_orange.rb +2 -4
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +2 -2
  17. data/lib/active_merchant/billing/gateways/certo_direct.rb +2 -3
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +3 -3
  19. data/lib/active_merchant/billing/gateways/data_cash.rb +3 -3
  20. data/lib/active_merchant/billing/gateways/efsnet.rb +3 -3
  21. data/lib/active_merchant/billing/gateways/epay.rb +3 -3
  22. data/lib/active_merchant/billing/gateways/eway.rb +8 -7
  23. data/lib/active_merchant/billing/gateways/eway_managed.rb +3 -3
  24. data/lib/active_merchant/billing/gateways/exact.rb +2 -2
  25. data/lib/active_merchant/billing/gateways/fat_zebra.rb +4 -4
  26. data/lib/active_merchant/billing/gateways/federated_canada.rb +2 -2
  27. data/lib/active_merchant/billing/gateways/first_pay.rb +3 -3
  28. data/lib/active_merchant/billing/gateways/garanti.rb +2 -2
  29. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +3 -1
  30. data/lib/active_merchant/billing/gateways/inspire.rb +2 -2
  31. data/lib/active_merchant/billing/gateways/instapay.rb +2 -2
  32. data/lib/active_merchant/billing/gateways/iridium.rb +4 -5
  33. data/lib/active_merchant/billing/gateways/itransact.rb +2 -2
  34. data/lib/active_merchant/billing/gateways/jetpay.rb +3 -3
  35. data/lib/active_merchant/billing/gateways/linkpoint.rb +3 -6
  36. data/lib/active_merchant/billing/gateways/litle.rb +50 -34
  37. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +3 -3
  38. data/lib/active_merchant/billing/gateways/merchant_ware.rb +2 -2
  39. data/lib/active_merchant/billing/gateways/migs.rb +8 -4
  40. data/lib/active_merchant/billing/gateways/modern_payments.rb +2 -0
  41. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +4 -4
  42. data/lib/active_merchant/billing/gateways/moneris.rb +3 -3
  43. data/lib/active_merchant/billing/gateways/moneris_us.rb +3 -3
  44. data/lib/active_merchant/billing/gateways/nab_transact.rb +8 -6
  45. data/lib/active_merchant/billing/gateways/net_registry.rb +2 -2
  46. data/lib/active_merchant/billing/gateways/netaxept.rb +4 -4
  47. data/lib/active_merchant/billing/gateways/netbilling.rb +2 -2
  48. data/lib/active_merchant/billing/gateways/ogone.rb +10 -5
  49. data/lib/active_merchant/billing/gateways/optimal_payment.rb +3 -3
  50. data/lib/active_merchant/billing/gateways/orbital.rb +4 -4
  51. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +2 -2
  52. data/lib/active_merchant/billing/gateways/pay_secure.rb +2 -2
  53. data/lib/active_merchant/billing/gateways/paybox_direct.rb +7 -5
  54. data/lib/active_merchant/billing/gateways/payflow.rb +39 -37
  55. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +4 -3
  56. data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
  57. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +98 -85
  58. data/lib/active_merchant/billing/gateways/paystation.rb +2 -2
  59. data/lib/active_merchant/billing/gateways/payway.rb +3 -2
  60. data/lib/active_merchant/billing/gateways/plugnpay.rb +63 -66
  61. data/lib/active_merchant/billing/gateways/psigate.rb +3 -3
  62. data/lib/active_merchant/billing/gateways/psl_card.rb +3 -3
  63. data/lib/active_merchant/billing/gateways/quantum.rb +2 -2
  64. data/lib/active_merchant/billing/gateways/quickpay.rb +2 -2
  65. data/lib/active_merchant/billing/gateways/realex.rb +7 -7
  66. data/lib/active_merchant/billing/gateways/sage.rb +2 -0
  67. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +1 -1
  68. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +2 -3
  69. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +1 -1
  70. data/lib/active_merchant/billing/gateways/sage_pay.rb +8 -6
  71. data/lib/active_merchant/billing/gateways/sallie_mae.rb +2 -2
  72. data/lib/active_merchant/billing/gateways/secure_net.rb +4 -4
  73. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +9 -6
  74. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +2 -2
  75. data/lib/active_merchant/billing/gateways/skip_jack.rb +3 -3
  76. data/lib/active_merchant/billing/gateways/smart_ps.rb +3 -1
  77. data/lib/active_merchant/billing/gateways/stripe.rb +2 -2
  78. data/lib/active_merchant/billing/gateways/trans_first.rb +2 -2
  79. data/lib/active_merchant/billing/gateways/transax.rb +1 -3
  80. data/lib/active_merchant/billing/gateways/trust_commerce.rb +3 -3
  81. data/lib/active_merchant/billing/gateways/usa_epay.rb +2 -0
  82. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +3 -2
  83. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +6 -6
  84. data/lib/active_merchant/billing/gateways/verifi.rb +2 -2
  85. data/lib/active_merchant/billing/gateways/wirecard.rb +4 -4
  86. data/lib/active_merchant/billing/gateways/worldpay.rb +37 -18
  87. data/lib/active_merchant/billing/integrations/action_view_helper.rb +6 -5
  88. data/lib/active_merchant/billing/integrations/first_data.rb +38 -0
  89. data/lib/active_merchant/billing/integrations/first_data/helper.rb +63 -0
  90. data/lib/active_merchant/billing/integrations/first_data/notification.rb +56 -0
  91. data/lib/active_merchant/billing/integrations/helper.rb +4 -0
  92. data/lib/active_merchant/billing/integrations/maksuturva.rb +86 -0
  93. data/lib/active_merchant/billing/integrations/maksuturva/helper.rb +119 -0
  94. data/lib/active_merchant/billing/integrations/maksuturva/notification.rb +48 -0
  95. data/lib/active_merchant/billing/integrations/paxum.rb +44 -0
  96. data/lib/active_merchant/billing/integrations/paxum/common.rb +24 -0
  97. data/lib/active_merchant/billing/integrations/paxum/helper.rb +42 -0
  98. data/lib/active_merchant/billing/integrations/paxum/notification.rb +33 -0
  99. data/lib/active_merchant/billing/integrations/pxpay.rb +31 -0
  100. data/lib/active_merchant/billing/integrations/pxpay/helper.rb +110 -0
  101. data/lib/active_merchant/billing/integrations/pxpay/notification.rb +157 -0
  102. data/lib/active_merchant/billing/integrations/pxpay/return.rb +25 -0
  103. data/lib/active_merchant/billing/integrations/web_pay.rb +45 -0
  104. data/lib/active_merchant/billing/integrations/web_pay/common.rb +50 -0
  105. data/lib/active_merchant/billing/integrations/web_pay/helper.rb +68 -0
  106. data/lib/active_merchant/billing/integrations/web_pay/notification.rb +51 -0
  107. data/lib/active_merchant/version.rb +1 -1
  108. data/lib/support/gateway_support.rb +9 -2
  109. data/lib/support/ssl_verify.rb +93 -0
  110. metadata +45 -71
  111. data.tar.gz.sig +0 -3
  112. metadata.gz.sig +0 -0
data/CHANGELOG CHANGED
@@ -1,5 +1,22 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.27.0 (August 10, 2012)
4
+
5
+ * Add First Data integration [courtland]
6
+ * Add WebPay integration [nashby]
7
+ * Add Suomen Maksuturva integration [akonan]
8
+ * Payway gateway: Fix card storage [BenZhang]
9
+ * Payflow Pro gateway: Add MaxFailPayments support [gregwinn]
10
+ * Add Paxum integration [Mehonoshin]
11
+ * Add Balanced gateway [mjallday]
12
+ * Plug'n Pay gateway: Add tests for partial capture [csaunders]
13
+ * Braintree Blue gateway: Add credit card details to responses [dougbradbury]
14
+ * PayPal gateway: Support for 'Full' refund type [kurenn]
15
+ * Worldpay: fix refund [jduff/omh]
16
+ * Add PxPay offsite integration [boourns]
17
+ * Wirecard: always capture 'authorization' transaction [ntalbott]
18
+ * Add rake task to verify ssl certs [boourns]
19
+
3
20
  == Version 1.26.0 (July 6, 2012)
4
21
 
5
22
  * Orbital gateway: fix broken requests by ensuring the order of XML elements matches their DTD [Soleone]
data/CONTRIBUTORS CHANGED
@@ -325,3 +325,23 @@ PayGateXML gateway (July 2012)
325
325
  PayWay gateway (July 2012)
326
326
 
327
327
  * Ben Zhang (BenZhang)
328
+
329
+ First Data integration (July 2012)
330
+
331
+ * Nick Rogers (courtland)
332
+
333
+ WebPay integration (July 2012)
334
+
335
+ * Vasiliy Ermolovich (nashby)
336
+
337
+ Suomen Maksuturva integration (July 2012)
338
+
339
+ * Antti Akonniemi (akonan)
340
+
341
+ Paxum integration (July 2012)
342
+
343
+ * Stanislav Mekhonoshin (Mehonoshin)
344
+
345
+ Balanced gateway (July 2012)
346
+
347
+ * Marshall Jones (mjallday)
data/README.md CHANGED
@@ -86,6 +86,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
86
86
 
87
87
  * [Authorize.Net](http://www.authorize.net/) - US
88
88
  * [Authorize.Net CIM](http://www.authorize.net/) - US
89
+ * [Balanced](https://www.balancedpayments.com/) - US
89
90
  * [Barclays ePDQ](http://www.barclaycard.co.uk/business/accepting-payments/epdq-mpi/) - UK
90
91
  * [Beanstream.com](http://www.beanstream.com/) - CA
91
92
  * [BluePay](http://www.bluepay.com/) - US
@@ -174,14 +175,18 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
174
175
  * [Dotpay](http://dotpay.pl)
175
176
  * [Dwolla](https://www.dwolla.com/default.aspx)
176
177
  * [ePay](http://www.epay.dk/epay-payment-solutions/)
178
+ * [First Data](https://firstdata.zendesk.com/entries/407522-first-data-global-gateway-e4sm-payment-pages-integration-manual)
177
179
  * [HiTRUST](http://www.hitrust.com.hk/)
178
180
  * [Moneybookers](http://www.moneybookers.com)
179
181
  * [Nochex](http://www.nochex.com)
182
+ * [Paxum](https://www.paxum.com/)
180
183
  * [PayPal Website Payments Standard](https://www.paypal.com/cgi-bin/webscr?cmd#_wp-standard-overview-outside)
181
184
  * [Robokassa](http://robokassa.ru/)
182
185
  * [SagePay Form](http://www.sagepay.com/products_services/sage_pay_go/integration/form)
186
+ * [Suomen Maksuturva](https://www.maksuturva.fi/services/vendor_services/integration_guidelines.html)
183
187
  * [Valitor](http://www.valitor.is/) - IS
184
188
  * [Verkkomaksut](http://www.verkkomaksut.fi) - FI
189
+ * [WebPay](http://webpay.by/)
185
190
  * [WorldPay](http://www.worldpay.com)
186
191
 
187
192
  ## Contributing
@@ -37,8 +37,8 @@ module ActiveMerchant #:nodoc:
37
37
  # Return the matching integration module
38
38
  # You can then get the notification from the module
39
39
  # * <tt>bogus</tt>: Bogus - Does nothing (for testing)
40
- # * <tt>chronopay</tt>: Chronopay - Does nothing (for testing)
41
- # * <tt>paypal</tt>: Chronopay - Does nothing (for testing)
40
+ # * <tt>chronopay</tt>: Chronopay
41
+ # * <tt>paypal</tt>: Paypal
42
42
  #
43
43
  # chronopay = ActiveMerchant::Billing::Base.integration('chronopay')
44
44
  # notification = chronopay.notification(raw_post)
@@ -93,6 +93,12 @@ module ActiveMerchant #:nodoc:
93
93
  class_attribute :homepage_url
94
94
  class_attribute :display_name
95
95
 
96
+ class_attribute :test_url, :live_url
97
+
98
+ class_attribute :abstract_class
99
+
100
+ self.abstract_class = false
101
+
96
102
  # The application making the calls to the gateway
97
103
  # Useful for things like the PayPal build notation (BN) id fields
98
104
  superclass_delegating_accessor :application_id
@@ -26,7 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
  class AuthorizeNetGateway < Gateway
27
27
  API_VERSION = '3.1'
28
28
 
29
- class_attribute :test_url, :live_url, :arb_test_url, :arb_live_url
29
+ class_attribute :arb_test_url, :arb_live_url
30
30
 
31
31
  self.test_url = "https://test.authorize.net/gateway/transact.dll"
32
32
  self.live_url = "https://secure.authorize.net/gateway/transact.dll"
@@ -27,9 +27,6 @@ module ActiveMerchant #:nodoc:
27
27
  # 4. Type in the answer to the secret question configured on setup
28
28
  # 5. Click Submit
29
29
  class AuthorizeNetCimGateway < Gateway
30
-
31
- class_attribute :test_url, :live_url
32
-
33
30
  self.test_url = 'https://apitest.authorize.net/xml/v1/request.api'
34
31
  self.live_url = 'https://api.authorize.net/xml/v1/request.api'
35
32
 
@@ -0,0 +1,462 @@
1
+ require 'json'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+
6
+ # For more information on Balanced visit https://www.balancedpayments.com
7
+ # or visit #balanced on irc.freenode.net
8
+ #
9
+ # Instantiate a instance of BalancedGateway by passing through your
10
+ # Balanced API key secret.
11
+ #
12
+ # ==== To obtain an API key of your own
13
+ #
14
+ # 1. Visit https://www.balancedpayments.com
15
+ # 2. Click "Get started"
16
+ # 3. The next screen will give you a test API key of your own
17
+ # 4. When you're ready to generate a production API key click the "Go
18
+ # live" button on the Balanced dashboard and fill in your marketplace
19
+ # details.
20
+ #
21
+ # ==== Overview
22
+ #
23
+ # Balanced provides a RESTful API, all entities within Balanced are
24
+ # represented by their respective URIs, these are returned in the
25
+ # `authorization` parameter of the Active Merchant Response object.
26
+ #
27
+ # All Response objects will contain a hash property called `params` which
28
+ # holds the raw JSON dictionary returned by Balanced. You can find
29
+ # properties about the operation performed and the object that represents
30
+ # it within this hash.
31
+ #
32
+ # All operations within Balanced are tied to an account, as such, when you
33
+ # perform an `authorization` or a `capture` with a new credit card you
34
+ # must ensure you also pass the `:email` property within the `options`
35
+ # parameter.
36
+ #
37
+ # For more details about Balanced's API visit:
38
+ # https://www.balancedpayments.com/docs
39
+ #
40
+ # ==== Terminology & Transaction Flow
41
+ #
42
+ # * An `authorization` operation will return a Hold URI. An `authorization`
43
+ # within Balanced is valid until the `expires_at` property. You can see the
44
+ # exact date of the expiry on the Response object by inspecting the
45
+ # property `response.params['expires_at']`. The resulting Hold may be
46
+ # `capture`d or `void`ed at any time before the `expires_at` date for
47
+ # any amount up to the full amount of the original `authorization`.
48
+ # * A `capture` operation will return a Debit URI. You must pass the URI of
49
+ # the previously performed `authorization`
50
+ # * A `purchase` will create a Hold and Debit in a single operation and
51
+ # return the URI of the resulting Debit.
52
+ # * A `void` operation must be performed on an existing `authorization`
53
+ # and will result in releasing the funds reserved by the
54
+ # `authorization`.
55
+ # * The `refund` operation must be performed on a previously captured
56
+ # Debit URI. You may refund any fraction of the original amount of the
57
+ # debit up to the original total.
58
+ #
59
+ class BalancedGateway < Gateway
60
+ VERSION = '1.0.0'
61
+
62
+ TEST_URL = LIVE_URL = 'https://api.balancedpayments.com'
63
+
64
+ # The countries the gateway supports merchants from as 2 digit ISO
65
+ # country codes
66
+ self.supported_countries = ['US']
67
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
68
+ self.homepage_url = 'https://www.balancedpayments.com/'
69
+ self.display_name = 'Balanced'
70
+ self.money_format = :cents
71
+
72
+ class Error < StandardError
73
+ attr_reader :response
74
+
75
+ def initialize(response, msg=nil)
76
+ @response = response
77
+ super(msg || response['description'])
78
+ end
79
+ end
80
+
81
+ class CardDeclined < Error
82
+ end
83
+
84
+ # Creates a new BalancedGateway
85
+ #
86
+ # The gateway requires that a valid api_key be passed in the +options+
87
+ # hash.
88
+ #
89
+ # ==== Options
90
+ #
91
+ # * <tt>:login</tt> -- The Balanced API Secret (REQUIRED)
92
+ def initialize(options = {})
93
+ requires!(options, :login)
94
+ @options = options
95
+ initialize_marketplace(options[:marketplace] || load_marketplace)
96
+ super
97
+ end
98
+
99
+ # Performs an authorization (Hold in Balanced nonclementure), which
100
+ # reserves the funds on the customer's credit card, but does not charge
101
+ # the card. An authorization is valid until the `expires_at` field in
102
+ # the params Hash passes. See `response.params['expires_at']`. The exact
103
+ # amount of time until an authorization expires depends on the card
104
+ # issuer.
105
+ #
106
+ # If you pass a previously tokenized `credit_card` URI the only other
107
+ # parameter required is `money`. If you pass `credit_card` as a hash of
108
+ # credit card information you must also pass `options` with a `:email`
109
+ # entry.
110
+ #
111
+ # ==== Parameters
112
+ #
113
+ # * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
114
+ # * <tt>credit_card</tt> -- A hash of credit card details for this
115
+ # transaction or the URI of a card previously stored in Balanced.
116
+ # * <tt>options</tt> -- A hash of optional parameters.
117
+ #
118
+ # ==== Options
119
+ #
120
+ # If you are passing a new credit card you must pass one of these two
121
+ # parameters
122
+ #
123
+ # * <tt>email</tt> -- the email address of user associated with this
124
+ # purchase.
125
+ # * <tt>account_uri</tt> -- `account_uri` is the URI of an existing
126
+ # Balanced account.
127
+ def authorize(money, credit_card, options = {})
128
+ if credit_card.respond_to?(:number)
129
+ requires!(options, :email) unless options[:account_uri]
130
+ end
131
+
132
+ post = {}
133
+ post[:amount] = money
134
+ post[:description] = options[:description]
135
+
136
+ create_or_find_account(post, options)
137
+ add_credit_card(post, credit_card, options)
138
+ add_address(credit_card, options)
139
+
140
+ create_transaction(:post, @holds_uri, post)
141
+ rescue Error => ex
142
+ failed_response(ex.response)
143
+ end
144
+
145
+ # Perform a purchase, which is an authorization and capture in a single
146
+ # operation.
147
+ #
148
+ # ==== Parameters
149
+ #
150
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
151
+ # * <tt>credit_card</tt> -- A hash of credit card details for this
152
+ # transaction or the URI of a card previously stored in Balanced.
153
+ # * <tt>options</tt> -- A hash of optional parameters.
154
+ #
155
+ # ==== Options
156
+ #
157
+ # If you are passing a new credit card you must pass one of these two
158
+ # parameters
159
+ #
160
+ # * <tt>email</tt> -- the email address of user associated with this
161
+ # purchase.
162
+ # * <tt>account_uri</tt> -- `account_uri` is the URI of an existing
163
+ # Balanced account.
164
+ def purchase(money, credit_card, options = {})
165
+ if credit_card.respond_to?('number')
166
+ requires!(options, :email) unless options[:account_uri]
167
+ end
168
+
169
+ post = {}
170
+ post[:amount] = money
171
+ post[:description] = options[:description]
172
+
173
+ create_or_find_account(post, options)
174
+ add_credit_card(post, credit_card, options)
175
+ add_address(credit_card, options)
176
+
177
+ create_transaction(:post, @debits_uri, post)
178
+ rescue Error => ex
179
+ failed_response(ex.response)
180
+ end
181
+
182
+ # Captures the funds from an authorized transaction (Hold).
183
+ #
184
+ # ==== Parameters
185
+ #
186
+ # * <tt>money</tt> -- The amount to be captured as an Integer value in
187
+ # cents. If omitted the full amount of the original authorization
188
+ # transaction will be captured.
189
+ # * <tt>authorization</tt> -- The uri of an authorization returned from
190
+ # an authorize request.
191
+ #
192
+ # ==== Options
193
+ #
194
+ # * <tt>description</tt> -- A string that will be displayed on the
195
+ # Balanced dashboard
196
+ def capture(money, authorization, options = {})
197
+ post = {}
198
+ post[:hold_uri] = authorization
199
+ post[:amount] = money if money
200
+ post[:description] = options[:description] if options[:description]
201
+
202
+ create_transaction(:post, @debits_uri, post)
203
+ rescue Error => ex
204
+ failed_response(ex.response)
205
+ end
206
+
207
+ # Void a previous authorization (Hold)
208
+ #
209
+ # ==== Parameters
210
+ #
211
+ # * <tt>authorization</tt> -- The uri of the authorization returned from
212
+ # an `authorize` request.
213
+ def void(authorization)
214
+ post = {}
215
+ post[:is_void] = true
216
+
217
+ create_transaction(:put, authorization, post)
218
+ rescue Error => ex
219
+ failed_response(ex.response)
220
+ end
221
+
222
+ # Refund a transaction.
223
+ #
224
+ # Returns the money debited from a card to the card from the
225
+ # marketplace's escrow balance.
226
+ #
227
+ # ==== Parameters
228
+ #
229
+ # * <tt>debit_uri</tt> -- The uri of the original transaction against
230
+ # which the refund is being issued.
231
+ # * <tt>options</tt> -- A hash of parameters.
232
+ #
233
+ # ==== Options
234
+ #
235
+ # * <tt>`:amount`<tt> -- specify an amount if you want to perform a
236
+ # partial refund. This value will default to the total amount of the
237
+ # debit that has not been refunded so far.
238
+ def refund(debit_uri, options = {})
239
+ requires!(debit_uri)
240
+ post = {}
241
+ post[:debit_uri] = debit_uri
242
+ post[:amount] = options[:amount] if options[:amount]
243
+ post[:description] = options[:description]
244
+ create_transaction(:post, @refunds_uri, post)
245
+ rescue Error => ex
246
+ failed_response(ex.response)
247
+ end
248
+
249
+ # Stores a card and email address
250
+ #
251
+ # ==== Parameters
252
+ #
253
+ # * <tt>credit_card</tt> --
254
+ def store(credit_card, options = {})
255
+ requires!(options, :email)
256
+ post = {}
257
+ account_uri = create_or_find_account(post, options)
258
+ if credit_card.respond_to? :number
259
+ add_credit_card(post, credit_card, options)
260
+ else
261
+ associate_card_to_account(account_uri, credit_card)
262
+ credit_card
263
+ end
264
+ rescue Error => ex
265
+ failed_response(ex.response)
266
+ end
267
+
268
+ private
269
+
270
+ # Load URIs for this marketplace by inspecting the marketplace object
271
+ # returned from the uri. http://en.wikipedia.org/wiki/HATEOAS
272
+ def load_marketplace
273
+ response = http_request(:get, '/v1/marketplaces')
274
+ if error?(response)
275
+ raise Error.new(response, 'Invalid login credentials supplied')
276
+ end
277
+ response['items'][0]
278
+ end
279
+
280
+ def initialize_marketplace(marketplace)
281
+ @marketplace_uri = marketplace['uri']
282
+ @holds_uri = marketplace['holds_uri']
283
+ @debits_uri = marketplace['debits_uri']
284
+ @cards_uri = marketplace['cards_uri']
285
+ @accounts_uri = marketplace['accounts_uri']
286
+ @refunds_uri = marketplace['refunds_uri']
287
+ end
288
+
289
+ def create_or_find_account(post, options)
290
+ account_uri = nil
291
+
292
+ if options.has_key? :account_uri
293
+ account_uri = options[:account_uri]
294
+ end
295
+
296
+ if account_uri == nil
297
+ post[:email_address] = options[:email]
298
+
299
+ # create an account
300
+ response = http_request(:post, @accounts_uri, post)
301
+
302
+ if response.has_key? 'uri'
303
+ account_uri = response['uri']
304
+ elsif error?(response)
305
+ # lookup account from Balanced, account_uri should be in the
306
+ # exception in a dictionary called extras
307
+ account_uri = response['extras']['account_uri']
308
+ end
309
+ end
310
+
311
+ post[:account_uri] = account_uri
312
+
313
+ account_uri
314
+ end
315
+
316
+ def add_address(credit_card, options)
317
+ return unless credit_card.kind_of?(Hash)
318
+ if address = options[:billing_address] || options[:address]
319
+ credit_card[:street_address] = address[:address1] if address[:address1]
320
+ credit_card[:street_address] += ' ' + address[:address2] if address[:address2]
321
+ credit_card[:postal_code] = address[:zip] if address[:zip]
322
+ credit_card[:country] = address[:country] if address[:country]
323
+ end
324
+ end
325
+
326
+ def add_credit_card(post, credit_card, options)
327
+ if credit_card.respond_to? :number
328
+ card = {}
329
+ card[:card_number] = credit_card.number
330
+ card[:expiration_month] = credit_card.month
331
+ card[:expiration_year] = credit_card.year
332
+ card[:security_code] = credit_card.verification_value if credit_card.verification_value?
333
+ card[:name] = credit_card.name if credit_card.name
334
+
335
+ add_address(card, options)
336
+
337
+ response = http_request(:post, @cards_uri, card)
338
+ if error?(response)
339
+ raise CardDeclined, response
340
+ end
341
+ card_uri = response['uri']
342
+
343
+ associate_card_to_account(post[:account_uri], card_uri)
344
+
345
+ post[:card_uri] = card_uri
346
+ elsif credit_card.kind_of?(String)
347
+ post[:card_uri] = credit_card
348
+ end
349
+
350
+ post[:card_uri]
351
+ end
352
+
353
+ def associate_card_to_account(account_uri, card_uri)
354
+ http_request(:put, account_uri, :card_uri => card_uri)
355
+ end
356
+
357
+ def http_request(method, url, parameters={}, meta={})
358
+ begin
359
+ if method == :get
360
+ raw_response = ssl_get(LIVE_URL + url, headers(meta))
361
+ else
362
+ raw_response = ssl_request(method,
363
+ LIVE_URL + url,
364
+ post_data(parameters),
365
+ headers(meta))
366
+ end
367
+ parse(raw_response)
368
+ rescue ResponseError => e
369
+ raw_response = e.response.body
370
+ response_error(raw_response)
371
+ rescue JSON::ParserError
372
+ json_error(raw_response)
373
+ end
374
+ end
375
+
376
+ def create_transaction(method, url, parameters, meta={})
377
+ response = http_request(method, url, parameters, meta)
378
+ success = !error?(response)
379
+
380
+ Response.new(success,
381
+ (success ? "Transaction approved" : response["description"]),
382
+ response,
383
+ :test => (@marketplace_uri.index("TEST") ? true : false),
384
+ :authorization => response["uri"]
385
+ )
386
+ end
387
+
388
+ def failed_response(response)
389
+ is_test = false
390
+ if @marketplace_uri
391
+ is_test = (@marketplace_uri.index("TEST") ? true : false)
392
+ end
393
+
394
+ Response.new(false,
395
+ response["description"],
396
+ response,
397
+ :test => is_test
398
+ )
399
+ end
400
+
401
+ def parse(body)
402
+ JSON.parse(body)
403
+ end
404
+
405
+ def response_error(raw_response)
406
+ begin
407
+ parse(raw_response)
408
+ rescue JSON::ParserError
409
+ json_error(raw_response)
410
+ end
411
+ end
412
+
413
+ def json_error(raw_response)
414
+ msg = 'Invalid response received from the Balanced API. Please contact support@balancedpayments.com if you continue to receive this message.'
415
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
416
+ {
417
+ "error" => {
418
+ "message" => msg
419
+ }
420
+ }
421
+ end
422
+
423
+ def error?(response)
424
+ response.key?('status_code')
425
+ end
426
+
427
+ def post_data(params)
428
+ return nil unless params
429
+
430
+ params.map do |key, value|
431
+ next if value.blank?
432
+ if value.is_a?(Hash)
433
+ h = {}
434
+ value.each do |k, v|
435
+ h["#{key}[#{k}]"] = v unless v.blank?
436
+ end
437
+ post_data(h)
438
+ else
439
+ "#{key}=#{CGI.escape(value.to_s)}"
440
+ end
441
+ end.compact.join("&")
442
+ end
443
+
444
+ def headers(meta={})
445
+ @@ua ||= JSON.dump({
446
+ :bindings_version => ActiveMerchant::VERSION,
447
+ :lang => 'ruby',
448
+ :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
449
+ :lib_version => BalancedGateway::VERSION,
450
+ :platform => RUBY_PLATFORM,
451
+ :publisher => 'active_merchant'
452
+ })
453
+
454
+ {
455
+ "Authorization" => "Basic " + Base64.encode64(@options[:login].to_s + ":").strip,
456
+ "User-Agent" => "Balanced/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
457
+ "X-Balanced-User-Agent" => @@ua,
458
+ }
459
+ end
460
+ end
461
+ end
462
+ end