tlconnor-activemerchant 1.20.4

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 (195) hide show
  1. data/CHANGELOG +805 -0
  2. data/CONTRIBUTORS +274 -0
  3. data/MIT-LICENSE +20 -0
  4. data/gem-public_cert.pem +20 -0
  5. data/lib/active_merchant.rb +63 -0
  6. data/lib/active_merchant/billing.rb +9 -0
  7. data/lib/active_merchant/billing/avs_result.rb +98 -0
  8. data/lib/active_merchant/billing/base.rb +57 -0
  9. data/lib/active_merchant/billing/check.rb +68 -0
  10. data/lib/active_merchant/billing/credit_card.rb +264 -0
  11. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  12. data/lib/active_merchant/billing/credit_card_methods.rb +129 -0
  13. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  14. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  15. data/lib/active_merchant/billing/gateway.rb +170 -0
  16. data/lib/active_merchant/billing/gateways.rb +18 -0
  17. data/lib/active_merchant/billing/gateways/authorize_net.rb +694 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +944 -0
  19. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
  20. data/lib/active_merchant/billing/gateways/beanstream.rb +167 -0
  21. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +388 -0
  22. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  23. data/lib/active_merchant/billing/gateways/blue_pay.rb +11 -0
  24. data/lib/active_merchant/billing/gateways/bogus.rb +142 -0
  25. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  26. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  27. data/lib/active_merchant/billing/gateways/braintree_blue.rb +308 -0
  28. data/lib/active_merchant/billing/gateways/braintree_orange.rb +21 -0
  29. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  30. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  31. data/lib/active_merchant/billing/gateways/certo_direct.rb +279 -0
  32. data/lib/active_merchant/billing/gateways/cyber_source.rb +430 -0
  33. data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
  34. data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
  35. data/lib/active_merchant/billing/gateways/elavon.rb +135 -0
  36. data/lib/active_merchant/billing/gateways/epay.rb +274 -0
  37. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  38. data/lib/active_merchant/billing/gateways/eway_managed.rb +265 -0
  39. data/lib/active_merchant/billing/gateways/exact.rb +227 -0
  40. data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
  41. data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
  42. data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
  43. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -0
  44. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  45. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  46. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
  47. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  48. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  49. data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
  50. data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
  51. data/lib/active_merchant/billing/gateways/linkpoint.rb +454 -0
  52. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +157 -0
  53. data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
  54. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  55. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  56. data/lib/active_merchant/billing/gateways/moneris.rb +239 -0
  57. data/lib/active_merchant/billing/gateways/nab_transact.rb +244 -0
  58. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  59. data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
  60. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  61. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  62. data/lib/active_merchant/billing/gateways/ogone.rb +330 -0
  63. data/lib/active_merchant/billing/gateways/optimal_payment.rb +277 -0
  64. data/lib/active_merchant/billing/gateways/orbital.rb +344 -0
  65. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  66. data/lib/active_merchant/billing/gateways/pay_junction.rb +397 -0
  67. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  68. data/lib/active_merchant/billing/gateways/paybox_direct.rb +207 -0
  69. data/lib/active_merchant/billing/gateways/payflow.rb +261 -0
  70. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +208 -0
  71. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  72. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  73. data/lib/active_merchant/billing/gateways/payflow_express.rb +222 -0
  74. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  75. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  76. data/lib/active_merchant/billing/gateways/payment_express.rb +235 -0
  77. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  78. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +354 -0
  79. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
  80. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  81. data/lib/active_merchant/billing/gateways/paypal_express.rb +229 -0
  82. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +25 -0
  83. data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
  84. data/lib/active_merchant/billing/gateways/plugnpay.rb +298 -0
  85. data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
  86. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  87. data/lib/active_merchant/billing/gateways/qbms.rb +297 -0
  88. data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
  89. data/lib/active_merchant/billing/gateways/quickpay.rb +298 -0
  90. data/lib/active_merchant/billing/gateways/realex.rb +315 -0
  91. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  92. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  93. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  94. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  95. data/lib/active_merchant/billing/gateways/sage_pay.rb +320 -0
  96. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  97. data/lib/active_merchant/billing/gateways/samurai.rb +121 -0
  98. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  99. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  100. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +280 -0
  101. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  102. data/lib/active_merchant/billing/gateways/skip_jack.rb +458 -0
  103. data/lib/active_merchant/billing/gateways/smart_ps.rb +271 -0
  104. data/lib/active_merchant/billing/gateways/stripe.rb +244 -0
  105. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  106. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  107. data/lib/active_merchant/billing/gateways/trust_commerce.rb +423 -0
  108. data/lib/active_merchant/billing/gateways/usa_epay.rb +23 -0
  109. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1496 -0
  110. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +206 -0
  111. data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
  112. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  113. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  114. data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
  115. data/lib/active_merchant/billing/integrations.rb +17 -0
  116. data/lib/active_merchant/billing/integrations/action_view_helper.rb +72 -0
  117. data/lib/active_merchant/billing/integrations/authorize_net_sim.rb +38 -0
  118. data/lib/active_merchant/billing/integrations/authorize_net_sim/helper.rb +228 -0
  119. data/lib/active_merchant/billing/integrations/authorize_net_sim/notification.rb +340 -0
  120. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  121. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  122. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  123. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  124. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  125. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  126. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  127. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  128. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  129. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
  130. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  131. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  132. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  133. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  134. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  135. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  136. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  137. data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
  138. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +31 -0
  139. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +55 -0
  140. data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
  141. data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
  142. data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
  143. data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
  144. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  145. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  146. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  147. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  148. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  149. data/lib/active_merchant/billing/integrations/helper.rb +113 -0
  150. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  151. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  152. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  153. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  154. data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
  155. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
  156. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
  157. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  158. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  159. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  160. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  161. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  162. data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
  163. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +100 -0
  164. data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
  165. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  166. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  167. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  168. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  169. data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
  170. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  171. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  172. data/lib/active_merchant/billing/integrations/return.rb +42 -0
  173. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  174. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  175. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +127 -0
  176. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
  177. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
  178. data/lib/active_merchant/billing/integrations/two_checkout.rb +22 -0
  179. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  180. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  181. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  182. data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
  183. data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
  184. data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
  185. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
  186. data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
  187. data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
  188. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  189. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  190. data/lib/active_merchant/billing/response.rb +32 -0
  191. data/lib/active_merchant/version.rb +3 -0
  192. data/lib/activemerchant.rb +1 -0
  193. data/lib/support/gateway_support.rb +58 -0
  194. data/lib/support/outbound_hosts.rb +25 -0
  195. metadata +411 -0
@@ -0,0 +1,38 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ # Result of the Card Verification Value check
4
+ # http://www.bbbonline.org/eExport/doc/MerchantGuide_cvv2.pdf
5
+ # Check additional codes from cybersource website
6
+ class CVVResult
7
+
8
+ MESSAGES = {
9
+ 'D' => 'Suspicious transaction',
10
+ 'I' => 'Failed data validation check',
11
+ 'M' => 'Match',
12
+ 'N' => 'No Match',
13
+ 'P' => 'Not Processed',
14
+ 'S' => 'Should have been present',
15
+ 'U' => 'Issuer unable to process request',
16
+ 'X' => 'Card does not support verification'
17
+ }
18
+
19
+ def self.messages
20
+ MESSAGES
21
+ end
22
+
23
+ attr_reader :code, :message
24
+
25
+ def initialize(code)
26
+ @code = code.upcase unless code.blank?
27
+ @message = MESSAGES[@code]
28
+ end
29
+
30
+ def to_hash
31
+ {
32
+ 'code' => code,
33
+ 'message' => message
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ require 'date'
2
+
3
+ module ActiveMerchant
4
+ module Billing
5
+ class CreditCard
6
+ class ExpiryDate #:nodoc:
7
+ attr_reader :month, :year
8
+ def initialize(month, year)
9
+ @month = month.to_i
10
+ @year = year.to_i
11
+ end
12
+
13
+ def expired? #:nodoc:
14
+ Time.now.utc > expiration
15
+ end
16
+
17
+ def expiration #:nodoc:
18
+ begin
19
+ Time.utc(year, month, month_days, 23, 59, 59)
20
+ rescue ArgumentError
21
+ Time.at(0).utc
22
+ end
23
+ end
24
+
25
+ private
26
+ def month_days
27
+ mdays = [nil,31,28,31,30,31,30,31,31,30,31,30,31]
28
+ mdays[2] = 29 if Date.leap?(year)
29
+ mdays[month]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,170 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'active_merchant/billing/response'
4
+
5
+ module ActiveMerchant #:nodoc:
6
+ module Billing #:nodoc:
7
+ #
8
+ # == Description
9
+ # The Gateway class is the base class for all ActiveMerchant gateway implementations.
10
+ #
11
+ # The standard list of gateway functions that most concrete gateway subclasses implement is:
12
+ #
13
+ # * <tt>purchase(money, creditcard, options = {})</tt>
14
+ # * <tt>authorize(money, creditcard, options = {})</tt>
15
+ # * <tt>capture(money, authorization, options = {})</tt>
16
+ # * <tt>void(identification, options = {})</tt>
17
+ # * <tt>credit(money, identification, options = {})</tt>
18
+ #
19
+ # Some gateways include features for recurring billing
20
+ #
21
+ # * <tt>recurring(money, creditcard, options = {})</tt>
22
+ #
23
+ # Some gateways also support features for storing credit cards:
24
+ #
25
+ # * <tt>store(creditcard, options = {})</tt>
26
+ # * <tt>unstore(identification, options = {})</tt>
27
+ #
28
+ # === Gateway Options
29
+ # The options hash consists of the following options:
30
+ #
31
+ # * <tt>:order_id</tt> - The order number
32
+ # * <tt>:ip</tt> - The IP address of the customer making the purchase
33
+ # * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
34
+ # * <tt>:invoice</tt> - The invoice number
35
+ # * <tt>:merchant</tt> - The name or description of the merchant offering the product
36
+ # * <tt>:description</tt> - A description of the transaction
37
+ # * <tt>:email</tt> - The email address of the customer
38
+ # * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
39
+ # * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
40
+ # * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
41
+ #
42
+ # The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
43
+ #
44
+ # * <tt>:name</tt> - The full name of the customer.
45
+ # * <tt>:company</tt> - The company name of the customer.
46
+ # * <tt>:address1</tt> - The primary street address of the customer.
47
+ # * <tt>:address2</tt> - Additional line of address information.
48
+ # * <tt>:city</tt> - The city of the customer.
49
+ # * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
50
+ # * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
51
+ # * <tt>:zip</tt> - The zip or postal code of the customer.
52
+ # * <tt>:phone</tt> - The phone number of the customer.
53
+ #
54
+ # == Implmenting new gateways
55
+ #
56
+ # See the {ActiveMerchant Guide to Contributing}[https://github.com/Shopify/active_merchant/wiki/Contributing]
57
+ #
58
+ class Gateway
59
+ include PostsData
60
+ include RequiresParameters
61
+ include CreditCardFormatting
62
+ include Utils
63
+
64
+ DEBIT_CARDS = [ :switch, :solo ]
65
+ CURRENCIES_WITHOUT_FRACTIONS = [ 'JPY' ]
66
+ CREDIT_DEPRECATION_MESSAGE = "Support for using credit to refund existing transactions is deprecated and will be removed from a future release of ActiveMerchant. Please use the refund method instead."
67
+
68
+ cattr_reader :implementations
69
+ @@implementations = []
70
+
71
+ def self.inherited(subclass)
72
+ super
73
+ @@implementations << subclass
74
+ end
75
+
76
+ # The format of the amounts used by the gateway
77
+ # :dollars => '12.50'
78
+ # :cents => '1250'
79
+ class_attribute :money_format
80
+ self.money_format = :dollars
81
+
82
+ # The default currency for the transactions if no currency is provided
83
+ class_attribute :default_currency
84
+
85
+ # The countries of merchants the gateway supports
86
+ class_attribute :supported_countries
87
+ self.supported_countries = []
88
+
89
+ # The supported card types for the gateway
90
+ class_attribute :supported_cardtypes
91
+ self.supported_cardtypes = []
92
+
93
+ class_attribute :homepage_url
94
+ class_attribute :display_name
95
+
96
+ # The application making the calls to the gateway
97
+ # Useful for things like the PayPal build notation (BN) id fields
98
+ superclass_delegating_accessor :application_id
99
+ self.application_id = 'ActiveMerchant'
100
+
101
+ attr_reader :options
102
+
103
+ # Use this method to check if your gateway of interest supports a credit card of some type
104
+ def self.supports?(card_type)
105
+ supported_cardtypes.include?(card_type.to_sym)
106
+ end
107
+
108
+ def self.card_brand(source)
109
+ result = source.respond_to?(:brand) ? source.brand : source.type
110
+ result.to_s.downcase
111
+ end
112
+
113
+ def card_brand(source)
114
+ self.class.card_brand(source)
115
+ end
116
+
117
+ # Initialize a new gateway.
118
+ #
119
+ # See the documentation for the gateway you will be using to make sure there are no other
120
+ # required options.
121
+ def initialize(options = {})
122
+ end
123
+
124
+ # Are we running in test mode?
125
+ def test?
126
+ Base.gateway_mode == :test
127
+ end
128
+
129
+ private # :nodoc: all
130
+
131
+ def name
132
+ self.class.name.scan(/\:\:(\w+)Gateway/).flatten.first
133
+ end
134
+
135
+ def amount(money)
136
+ return nil if money.nil?
137
+ cents = if money.respond_to?(:cents)
138
+ deprecated "Support for Money objects is deprecated and will be removed from a future release of ActiveMerchant. Please use an Integer value in cents"
139
+ money.cents
140
+ else
141
+ money
142
+ end
143
+
144
+ if money.is_a?(String)
145
+ raise ArgumentError, 'money amount must be a positive Integer in cents.'
146
+ end
147
+
148
+ if self.money_format == :cents
149
+ cents.to_s
150
+ else
151
+ sprintf("%.2f", cents.to_f / 100)
152
+ end
153
+ end
154
+
155
+ def localized_amount(money, currency)
156
+ amount = amount(money)
157
+ CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
158
+ end
159
+
160
+ def currency(money)
161
+ money.respond_to?(:currency) ? money.currency : self.default_currency
162
+ end
163
+
164
+ def requires_start_date_or_issue_number?(credit_card)
165
+ return false if card_brand(credit_card).blank?
166
+ DEBIT_CARDS.include?(card_brand(credit_card).to_sym)
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ autoload :Gateway, 'active_merchant/billing/gateway'
4
+
5
+ Dir[File.dirname(__FILE__) + '/gateways/**/*.rb'].each do |f|
6
+
7
+ # Get camelized class name
8
+ filename = File.basename(f, '.rb')
9
+ # Add _gateway suffix
10
+ gateway_name = filename + '_gateway'
11
+ # Camelize the string to get the class name
12
+ gateway_class = gateway_name.camelize
13
+
14
+ # Register for autoloading
15
+ autoload gateway_class, f
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,694 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # For more information on the Authorize.Net Gateway please visit their {Integration Center}[http://developer.authorize.net/]
4
+ #
5
+ # The login and password are not the username and password you use to
6
+ # login to the Authorize.Net Merchant Interface. Instead, you will
7
+ # use the API Login ID as the login and Transaction Key as the
8
+ # password.
9
+ #
10
+ # ==== How to Get Your API Login ID and Transaction Key
11
+ #
12
+ # 1. Log into the Merchant Interface
13
+ # 2. Select Settings from the Main Menu
14
+ # 3. Click on API Login ID and Transaction Key in the Security section
15
+ # 4. Type in the answer to the secret question configured on setup
16
+ # 5. Click Submit
17
+ #
18
+ # ==== Automated Recurring Billing (ARB)
19
+ #
20
+ # Automated Recurring Billing (ARB) is an optional service for submitting and managing recurring, or subscription-based, transactions.
21
+ #
22
+ # To use recurring, update_recurring, cancel_recurring and status_recurring ARB must be enabled for your account.
23
+ #
24
+ # Information about ARB is available on the {Authorize.Net website}[http://www.authorize.net/solutions/merchantsolutions/merchantservices/automatedrecurringbilling/].
25
+ # Information about the ARB API is available at the {Authorize.Net Integration Center}[http://developer.authorize.net/]
26
+ class AuthorizeNetGateway < Gateway
27
+ API_VERSION = '3.1'
28
+
29
+ class_attribute :test_url, :live_url, :arb_test_url, :arb_live_url
30
+
31
+ self.test_url = "https://test.authorize.net/gateway/transact.dll"
32
+ self.live_url = "https://secure.authorize.net/gateway/transact.dll"
33
+
34
+ self.arb_test_url = 'https://apitest.authorize.net/xml/v1/request.api'
35
+ self.arb_live_url = 'https://api.authorize.net/xml/v1/request.api'
36
+
37
+ class_attribute :duplicate_window
38
+
39
+ APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
40
+
41
+ RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
42
+ AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
43
+
44
+ self.supported_countries = ['US']
45
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
46
+ self.homepage_url = 'http://www.authorize.net/'
47
+ self.display_name = 'Authorize.Net'
48
+
49
+ CARD_CODE_ERRORS = %w( N S )
50
+ AVS_ERRORS = %w( A E N R W Z )
51
+ AVS_REASON_CODES = %w(27 45)
52
+
53
+ AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
54
+
55
+ RECURRING_ACTIONS = {
56
+ :create => 'ARBCreateSubscription',
57
+ :update => 'ARBUpdateSubscription',
58
+ :cancel => 'ARBCancelSubscription',
59
+ :status => 'ARBGetSubscriptionStatus'
60
+ }
61
+
62
+ # Creates a new AuthorizeNetGateway
63
+ #
64
+ # The gateway requires that a valid login and password be passed
65
+ # in the +options+ hash.
66
+ #
67
+ # ==== Options
68
+ #
69
+ # * <tt>:login</tt> -- The Authorize.Net API Login ID (REQUIRED)
70
+ # * <tt>:password</tt> -- The Authorize.Net Transaction Key. (REQUIRED)
71
+ # * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
72
+ # Otherwise, perform transactions against the production server.
73
+ def initialize(options = {})
74
+ requires!(options, :login, :password)
75
+ @options = options
76
+ super
77
+ end
78
+
79
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
80
+ # charge the card.
81
+ #
82
+ # ==== Parameters
83
+ #
84
+ # * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
85
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
86
+ # * <tt>options</tt> -- A hash of optional parameters.
87
+ def authorize(money, creditcard, options = {})
88
+ post = {}
89
+ add_invoice(post, options)
90
+ add_creditcard(post, creditcard)
91
+ add_address(post, options)
92
+ add_customer_data(post, options)
93
+ add_duplicate_window(post)
94
+
95
+ commit('AUTH_ONLY', money, post)
96
+ end
97
+
98
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
99
+ #
100
+ # ==== Parameters
101
+ #
102
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
103
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
104
+ # * <tt>options</tt> -- A hash of optional parameters.
105
+ def purchase(money, creditcard, options = {})
106
+ post = {}
107
+ add_invoice(post, options)
108
+ add_creditcard(post, creditcard)
109
+ add_address(post, options)
110
+ add_customer_data(post, options)
111
+ add_duplicate_window(post)
112
+
113
+ commit('AUTH_CAPTURE', money, post)
114
+ end
115
+
116
+ # Captures the funds from an authorized transaction.
117
+ #
118
+ # ==== Parameters
119
+ #
120
+ # * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
121
+ # * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
122
+ def capture(money, authorization, options = {})
123
+ post = {:trans_id => authorization}
124
+ add_customer_data(post, options)
125
+ commit('PRIOR_AUTH_CAPTURE', money, post)
126
+ end
127
+
128
+ # Void a previous transaction
129
+ #
130
+ # ==== Parameters
131
+ #
132
+ # * <tt>authorization</tt> - The authorization returned from the previous authorize request.
133
+ def void(authorization, options = {})
134
+ post = {:trans_id => authorization}
135
+ add_duplicate_window(post)
136
+ commit('VOID', nil, post)
137
+ end
138
+
139
+ # Refund a transaction.
140
+ #
141
+ # This transaction indicates to the gateway that
142
+ # money should flow from the merchant to the customer.
143
+ #
144
+ # ==== Parameters
145
+ #
146
+ # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
147
+ # * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
148
+ # * <tt>options</tt> -- A hash of parameters.
149
+ #
150
+ # ==== Options
151
+ #
152
+ # * <tt>:card_number</tt> -- The credit card number the refund is being issued to. (REQUIRED)
153
+ # * <tt>:first_name</tt> -- The first name of the account being refunded.
154
+ # * <tt>:last_name</tt> -- The last name of the account being refunded.
155
+ # * <tt>:zip</tt> -- The postal code of the account being refunded.
156
+ def refund(money, identification, options = {})
157
+ requires!(options, :card_number)
158
+
159
+ post = { :trans_id => identification,
160
+ :card_num => options[:card_number]
161
+ }
162
+
163
+ post[:first_name] = options[:first_name] if options[:first_name]
164
+ post[:last_name] = options[:last_name] if options[:last_name]
165
+ post[:zip] = options[:zip] if options[:zip]
166
+
167
+ add_invoice(post, options)
168
+ add_duplicate_window(post)
169
+
170
+ commit('CREDIT', money, post)
171
+ end
172
+
173
+ def credit(money, identification, options = {})
174
+ deprecated CREDIT_DEPRECATION_MESSAGE
175
+ refund(money, identification, options)
176
+ end
177
+
178
+ # Create a recurring payment.
179
+ #
180
+ # This transaction creates a new Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
181
+ #
182
+ # ==== Parameters
183
+ #
184
+ # * <tt>money</tt> -- The amount to be charged to the customer at each interval as an Integer value in cents.
185
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
186
+ # * <tt>options</tt> -- A hash of parameters.
187
+ #
188
+ # ==== Options
189
+ #
190
+ # * <tt>:interval</tt> -- A hash containing information about the interval of time between payments. Must
191
+ # contain the keys <tt>:length</tt> and <tt>:unit</tt>. <tt>:unit</tt> can be either <tt>:months</tt> or <tt>:days</tt>.
192
+ # If <tt>:unit</tt> is <tt>:months</tt> then <tt>:length</tt> must be an integer between 1 and 12 inclusive.
193
+ # If <tt>:unit</tt> is <tt>:days</tt> then <tt>:length</tt> must be an integer between 7 and 365 inclusive.
194
+ # For example, to charge the customer once every three months the hash would be
195
+ # +:interval => { :unit => :months, :length => 3 }+ (REQUIRED)
196
+ # * <tt>:duration</tt> -- A hash containing keys for the <tt>:start_date</tt> the subscription begins (also the date the
197
+ # initial billing occurs) and the total number of billing <tt>:occurences</tt> or payments for the subscription. (REQUIRED)
198
+ def recurring(money, creditcard, options={})
199
+ requires!(options, :interval, :duration, :billing_address)
200
+ requires!(options[:interval], :length, [:unit, :days, :months])
201
+ requires!(options[:duration], :start_date, :occurrences)
202
+ requires!(options[:billing_address], :first_name, :last_name)
203
+
204
+ options[:credit_card] = creditcard
205
+ options[:amount] = money
206
+
207
+ request = build_recurring_request(:create, options)
208
+ recurring_commit(:create, request)
209
+ end
210
+
211
+ # Update a recurring payment's details.
212
+ #
213
+ # This transaction updates an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
214
+ # and the subscription must have already been created previously by calling +recurring()+. The ability to change certain
215
+ # details about a recurring payment is dependent on transaction history and cannot be determined until after calling
216
+ # +update_recurring()+. See the ARB XML Guide for such conditions.
217
+ #
218
+ # ==== Parameters
219
+ #
220
+ # * <tt>options</tt> -- A hash of parameters.
221
+ #
222
+ # ==== Options
223
+ #
224
+ # * <tt>:subscription_id</tt> -- A string containing the <tt>:subscription_id</tt> of the recurring payment already in place
225
+ # for a given credit card. (REQUIRED)
226
+ def update_recurring(options={})
227
+ requires!(options, :subscription_id)
228
+ request = build_recurring_request(:update, options)
229
+ recurring_commit(:update, request)
230
+ end
231
+
232
+ # Cancel a recurring payment.
233
+ #
234
+ # This transaction cancels an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
235
+ # and the subscription must have already been created previously by calling recurring()
236
+ #
237
+ # ==== Parameters
238
+ #
239
+ # * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
240
+ # for a given credit card. (REQUIRED)
241
+ def cancel_recurring(subscription_id)
242
+ request = build_recurring_request(:cancel, :subscription_id => subscription_id)
243
+ recurring_commit(:cancel, request)
244
+ end
245
+
246
+ # Get Subscription Status of a recurring payment.
247
+ #
248
+ # This transaction gets the status of an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
249
+ #
250
+ # ==== Parameters
251
+ #
252
+ # * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
253
+ # for a given credit card. (REQUIRED)
254
+ def status_recurring(subscription_id)
255
+ request = build_recurring_request(:status, :subscription_id => subscription_id)
256
+ recurring_commit(:status, request)
257
+ end
258
+
259
+ private
260
+
261
+ def commit(action, money, parameters)
262
+ parameters[:amount] = amount(money) unless action == 'VOID'
263
+
264
+ # Only activate the test_request when the :test option is passed in
265
+ parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
266
+
267
+ url = test? ? self.test_url : self.live_url
268
+ data = ssl_post url, post_data(action, parameters)
269
+
270
+ response = parse(data)
271
+
272
+ message = message_from(response)
273
+
274
+ # Return the response. The authorization can be taken out of the transaction_id
275
+ # Test Mode on/off is something we have to parse from the response text.
276
+ # It usually looks something like this
277
+ #
278
+ # (TESTMODE) Successful Sale
279
+ test_mode = test? || message =~ /TESTMODE/
280
+
281
+ Response.new(success?(response), message, response,
282
+ :test => test_mode,
283
+ :authorization => response[:transaction_id],
284
+ :fraud_review => fraud_review?(response),
285
+ :avs_result => { :code => response[:avs_result_code] },
286
+ :cvv_result => response[:card_code]
287
+ )
288
+ end
289
+
290
+ def success?(response)
291
+ response[:response_code] == APPROVED
292
+ end
293
+
294
+ def fraud_review?(response)
295
+ response[:response_code] == FRAUD_REVIEW
296
+ end
297
+
298
+ def parse(body)
299
+ fields = split(body)
300
+
301
+ results = {
302
+ :response_code => fields[RESPONSE_CODE].to_i,
303
+ :response_reason_code => fields[RESPONSE_REASON_CODE],
304
+ :response_reason_text => fields[RESPONSE_REASON_TEXT],
305
+ :avs_result_code => fields[AVS_RESULT_CODE],
306
+ :transaction_id => fields[TRANSACTION_ID],
307
+ :card_code => fields[CARD_CODE_RESPONSE_CODE]
308
+ }
309
+ results
310
+ end
311
+
312
+ def post_data(action, parameters = {})
313
+ post = {}
314
+
315
+ post[:version] = API_VERSION
316
+ post[:login] = @options[:login]
317
+ post[:tran_key] = @options[:password]
318
+ post[:relay_response] = "FALSE"
319
+ post[:type] = action
320
+ post[:delim_data] = "TRUE"
321
+ post[:delim_char] = ","
322
+ post[:encap_char] = "$"
323
+ post[:solution_ID] = application_id if application_id.present? && application_id != "ActiveMerchant"
324
+
325
+ request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
326
+ request
327
+ end
328
+
329
+ def add_invoice(post, options)
330
+ post[:invoice_num] = options[:order_id]
331
+ post[:description] = options[:description]
332
+ end
333
+
334
+ def add_creditcard(post, creditcard)
335
+ post[:card_num] = creditcard.number
336
+ post[:card_code] = creditcard.verification_value if creditcard.verification_value?
337
+ post[:exp_date] = expdate(creditcard)
338
+ post[:first_name] = creditcard.first_name
339
+ post[:last_name] = creditcard.last_name
340
+ end
341
+
342
+ def add_customer_data(post, options)
343
+ if options.has_key? :email
344
+ post[:email] = options[:email]
345
+ post[:email_customer] = false
346
+ end
347
+
348
+ if options.has_key? :customer
349
+ post[:cust_id] = options[:customer]
350
+ end
351
+
352
+ if options.has_key? :ip
353
+ post[:customer_ip] = options[:ip]
354
+ end
355
+ end
356
+
357
+ # x_duplicate_window won't be sent by default, because sending it changes the response.
358
+ # "If this field is present in the request with or without a value, an enhanced duplicate transaction response will be sent."
359
+ # (as of 2008-12-30) http://www.authorize.net/support/AIM_guide_SCC.pdf
360
+ def add_duplicate_window(post)
361
+ unless duplicate_window.nil?
362
+ post[:duplicate_window] = duplicate_window
363
+ end
364
+ end
365
+
366
+ def add_address(post, options)
367
+ if address = options[:billing_address] || options[:address]
368
+ post[:address] = address[:address1].to_s
369
+ post[:company] = address[:company].to_s
370
+ post[:phone] = address[:phone].to_s
371
+ post[:zip] = address[:zip].to_s
372
+ post[:city] = address[:city].to_s
373
+ post[:country] = address[:country].to_s
374
+ post[:state] = address[:state].blank? ? 'n/a' : address[:state]
375
+ end
376
+
377
+ if address = options[:shipping_address]
378
+ post[:ship_to_first_name] = address[:first_name].to_s
379
+ post[:ship_to_last_name] = address[:last_name].to_s
380
+ post[:ship_to_address] = address[:address1].to_s
381
+ post[:ship_to_company] = address[:company].to_s
382
+ post[:ship_to_phone] = address[:phone].to_s
383
+ post[:ship_to_zip] = address[:zip].to_s
384
+ post[:ship_to_city] = address[:city].to_s
385
+ post[:ship_to_country] = address[:country].to_s
386
+ post[:ship_to_state] = address[:state].blank? ? 'n/a' : address[:state]
387
+ end
388
+ end
389
+
390
+ # Make a ruby type out of the response string
391
+ def normalize(field)
392
+ case field
393
+ when "true" then true
394
+ when "false" then false
395
+ when "" then nil
396
+ when "null" then nil
397
+ else field
398
+ end
399
+ end
400
+
401
+ def message_from(results)
402
+ if results[:response_code] == DECLINED
403
+ return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
404
+ if AVS_REASON_CODES.include?(results[:response_reason_code]) && AVS_ERRORS.include?(results[:avs_result_code])
405
+ return AVSResult.messages[ results[:avs_result_code] ]
406
+ end
407
+ end
408
+
409
+ (results[:response_reason_text] ? results[:response_reason_text].chomp('.') : '')
410
+ end
411
+
412
+ def expdate(creditcard)
413
+ year = sprintf("%.4i", creditcard.year)
414
+ month = sprintf("%.2i", creditcard.month)
415
+
416
+ "#{month}#{year[-2..-1]}"
417
+ end
418
+
419
+ def split(response)
420
+ response[1..-2].split(/\$,\$/)
421
+ end
422
+
423
+ # ARB
424
+
425
+ # Builds recurring billing request
426
+ def build_recurring_request(action, options = {})
427
+ unless RECURRING_ACTIONS.include?(action)
428
+ raise StandardError, "Invalid Automated Recurring Billing Action: #{action}"
429
+ end
430
+
431
+ xml = Builder::XmlMarkup.new(:indent => 2)
432
+ xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
433
+ xml.tag!("#{RECURRING_ACTIONS[action]}Request", :xmlns => AUTHORIZE_NET_ARB_NAMESPACE) do
434
+ add_arb_merchant_authentication(xml)
435
+ # Merchant-assigned reference ID for the request
436
+ xml.tag!('refId', options[:ref_id]) if options[:ref_id]
437
+ send("build_arb_#{action}_subscription_request", xml, options)
438
+ end
439
+ end
440
+
441
+ # Contains the merchant’s payment gateway account authentication information
442
+ def add_arb_merchant_authentication(xml)
443
+ xml.tag!('merchantAuthentication') do
444
+ xml.tag!('name', @options[:login])
445
+ xml.tag!('transactionKey', @options[:password])
446
+ end
447
+ end
448
+
449
+ # Builds body for ARBCreateSubscriptionRequest
450
+ def build_arb_create_subscription_request(xml, options)
451
+ # Subscription
452
+ add_arb_subscription(xml, options)
453
+
454
+ xml.target!
455
+ end
456
+
457
+ # Builds body for ARBUpdateSubscriptionRequest
458
+ def build_arb_update_subscription_request(xml, options)
459
+ xml.tag!('subscriptionId', options[:subscription_id])
460
+ # Adds Subscription
461
+ add_arb_subscription(xml, options)
462
+
463
+ xml.target!
464
+ end
465
+
466
+ # Builds body for ARBCancelSubscriptionRequest
467
+ def build_arb_cancel_subscription_request(xml, options)
468
+ xml.tag!('subscriptionId', options[:subscription_id])
469
+
470
+ xml.target!
471
+ end
472
+
473
+ # Builds body for ARBGetSubscriptionStatusRequest
474
+ def build_arb_status_subscription_request(xml, options)
475
+ xml.tag!('subscriptionId', options[:subscription_id])
476
+
477
+ xml.target!
478
+ end
479
+
480
+ # Adds subscription information
481
+ def add_arb_subscription(xml, options)
482
+ xml.tag!('subscription') do
483
+ # Merchant-assigned name for the subscription (optional)
484
+ xml.tag!('name', options[:subscription_name]) if options[:subscription_name]
485
+ # Contains information about the payment schedule
486
+ add_arb_payment_schedule(xml, options)
487
+ # The amount to be billed to the customer
488
+ # for each payment in the subscription
489
+ xml.tag!('amount', amount(options[:amount])) if options[:amount]
490
+ if trial = options[:trial]
491
+ # The amount to be charged for each payment during a trial period (conditional)
492
+ xml.tag!('trialAmount', amount(trial[:amount])) if trial[:amount]
493
+ end
494
+ # Contains either the customer’s credit card
495
+ # or bank account payment information
496
+ add_arb_payment(xml, options)
497
+ # Contains order information (optional)
498
+ add_arb_order(xml, options)
499
+ # Contains information about the customer
500
+ add_arb_customer(xml, options)
501
+ # Contains the customer's billing address information
502
+ add_arb_address(xml, 'billTo', options[:billing_address])
503
+ # Contains the customer's shipping address information (optional)
504
+ add_arb_address(xml, 'shipTo', options[:shipping_address])
505
+ end
506
+ end
507
+
508
+ # Adds information about the interval of time between payments
509
+ def add_arb_interval(xml, options)
510
+ interval = options[:interval]
511
+ return unless interval
512
+ xml.tag!('interval') do
513
+ # The measurement of time, in association with the Interval Unit,
514
+ # that is used to define the frequency of the billing occurrences
515
+ xml.tag!('length', interval[:length])
516
+ # The unit of time, in association with the Interval Length,
517
+ # between each billing occurrence
518
+ xml.tag!('unit', interval[:unit].to_s)
519
+ end
520
+ end
521
+
522
+ # Adds information about the subscription duration
523
+ def add_arb_duration(xml, options)
524
+ duration = options[:duration]
525
+ return unless duration
526
+ # The date the subscription begins
527
+ # (also the date the initial billing occurs)
528
+ xml.tag!('startDate', duration[:start_date]) if duration[:start_date]
529
+ # Number of billing occurrences or payments for the subscription
530
+ xml.tag!('totalOccurrences', duration[:occurrences]) if duration[:occurrences]
531
+ end
532
+
533
+ def add_arb_payment_schedule(xml, options)
534
+ return unless options[:interval] || options[:duration]
535
+ xml.tag!('paymentSchedule') do
536
+ # Contains information about the interval of time between payments
537
+ add_arb_interval(xml, options)
538
+ add_arb_duration(xml, options)
539
+ if trial = options[:trial]
540
+ # Number of billing occurrences or payments in the trial period (optional)
541
+ xml.tag!('trialOccurrences', trial[:occurrences]) if trial[:occurrences]
542
+ end
543
+ end
544
+ end
545
+
546
+ # Adds customer's credit card or bank account payment information
547
+ def add_arb_payment(xml, options)
548
+ return unless options[:credit_card] || options[:bank_account]
549
+ xml.tag!('payment') do
550
+ # Contains the customer’s credit card information
551
+ add_arb_credit_card(xml, options)
552
+ # Contains the customer’s bank account information
553
+ add_arb_bank_account(xml, options)
554
+ end
555
+ end
556
+
557
+ # Adds customer’s credit card information
558
+ # Note: This element should only be included
559
+ # when the payment method is credit card.
560
+ def add_arb_credit_card(xml, options)
561
+ credit_card = options[:credit_card]
562
+ return unless credit_card
563
+ xml.tag!('creditCard') do
564
+ # The credit card number used for payment of the subscription
565
+ xml.tag!('cardNumber', credit_card.number)
566
+ # The expiration date of the credit card used for the subscription
567
+ xml.tag!('expirationDate', arb_expdate(credit_card))
568
+ end
569
+ end
570
+
571
+ # Adds customer’s bank account information
572
+ # Note: This element should only be included
573
+ # when the payment method is bank account.
574
+ def add_arb_bank_account(xml, options)
575
+ bank_account = options[:bank_account]
576
+ return unless bank_account
577
+ xml.tag!('bankAccount') do
578
+ # The type of bank account used for payment of the subscription
579
+ xml.tag!('accountType', bank_account[:account_type])
580
+ # The routing number of the customer’s bank
581
+ xml.tag!('routingNumber', bank_account[:routing_number])
582
+ # The bank account number used for payment of the subscription
583
+ xml.tag!('accountNumber', bank_account[:account_number])
584
+ # The full name of the individual associated
585
+ # with the bank account number
586
+ xml.tag!('nameOfAccount', bank_account[:name_of_account])
587
+ # The full name of the individual associated
588
+ # with the bank account number (optional)
589
+ xml.tag!('bankName', bank_account[:bank_name]) if bank_account[:bank_name]
590
+ # The type of electronic check transaction used for the subscription
591
+ xml.tag!('echeckType', bank_account[:echeck_type])
592
+ end
593
+ end
594
+
595
+ # Adds order information (optional)
596
+ def add_arb_order(xml, options)
597
+ order = options[:order]
598
+ return unless order
599
+ xml.tag!('order') do
600
+ # Merchant-assigned invoice number for the subscription (optional)
601
+ xml.tag!('invoiceNumber', order[:invoice_number])
602
+ # Description of the subscription (optional)
603
+ xml.tag!('description', order[:description])
604
+ end
605
+ end
606
+
607
+ # Adds information about the customer
608
+ def add_arb_customer(xml, options)
609
+ customer = options[:customer]
610
+ return unless customer
611
+ xml.tag!('customer') do
612
+ xml.tag!('type', customer[:type]) if customer[:type]
613
+ xml.tag!('id', customer[:id]) if customer[:id]
614
+ xml.tag!('email', customer[:email]) if customer[:email]
615
+ xml.tag!('phoneNumber', customer[:phone_number]) if customer[:phone_number]
616
+ xml.tag!('faxNumber', customer[:fax_number]) if customer[:fax_number]
617
+ add_arb_drivers_license(xml, options)
618
+ xml.tag!('taxId', customer[:tax_id]) if customer[:tax_id]
619
+ end
620
+ end
621
+
622
+ # Adds the customer's driver's license information (conditional)
623
+ def add_arb_drivers_license(xml, options)
624
+ return unless customer = options[:customer]
625
+ return unless drivers_license = customer[:drivers_license]
626
+ xml.tag!('driversLicense') do
627
+ # The customer's driver's license number
628
+ xml.tag!('number', drivers_license[:number])
629
+ # The customer's driver's license state
630
+ xml.tag!('state', drivers_license[:state])
631
+ # The customer's driver's license date of birth
632
+ xml.tag!('dateOfBirth', drivers_license[:date_of_birth])
633
+ end
634
+ end
635
+
636
+ # Adds address information
637
+ def add_arb_address(xml, container_name, address)
638
+ return if address.blank?
639
+ xml.tag!(container_name) do
640
+ xml.tag!('firstName', address[:first_name])
641
+ xml.tag!('lastName', address[:last_name])
642
+ xml.tag!('company', address[:company])
643
+ xml.tag!('address', address[:address1])
644
+ xml.tag!('city', address[:city])
645
+ xml.tag!('state', address[:state])
646
+ xml.tag!('zip', address[:zip])
647
+ xml.tag!('country', address[:country])
648
+ end
649
+ end
650
+
651
+ def arb_expdate(credit_card)
652
+ sprintf('%04d-%02d', credit_card.year, credit_card.month)
653
+ end
654
+
655
+ def recurring_commit(action, request)
656
+ url = test? ? arb_test_url : arb_live_url
657
+ xml = ssl_post(url, request, "Content-Type" => "text/xml")
658
+
659
+ response = recurring_parse(action, xml)
660
+
661
+ message = response[:message] || response[:text]
662
+ test_mode = test? || message =~ /Test Mode/
663
+ success = response[:result_code] == 'Ok'
664
+
665
+ Response.new(success, message, response,
666
+ :test => test_mode,
667
+ :authorization => response[:subscription_id]
668
+ )
669
+ end
670
+
671
+ def recurring_parse(action, xml)
672
+ response = {}
673
+ xml = REXML::Document.new(xml)
674
+ root = REXML::XPath.first(xml, "//#{RECURRING_ACTIONS[action]}Response") ||
675
+ REXML::XPath.first(xml, "//ErrorResponse")
676
+ if root
677
+ root.elements.to_a.each do |node|
678
+ recurring_parse_element(response, node)
679
+ end
680
+ end
681
+
682
+ response
683
+ end
684
+
685
+ def recurring_parse_element(response, node)
686
+ if node.has_elements?
687
+ node.elements.each{|e| recurring_parse_element(response, e) }
688
+ else
689
+ response[node.name.underscore.to_sym] = node.text
690
+ end
691
+ end
692
+ end
693
+ end
694
+ end