yetanothernguyen-activemerchant 1.16.0 → 1.21.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 (86) hide show
  1. data/CHANGELOG +95 -0
  2. data/CONTRIBUTORS +29 -0
  3. data/lib/active_merchant/billing/credit_card.rb +105 -19
  4. data/lib/active_merchant/billing/credit_card_methods.rb +5 -1
  5. data/lib/active_merchant/billing/gateway.rb +1 -1
  6. data/lib/active_merchant/billing/gateways/authorize_net.rb +24 -2
  7. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +104 -18
  8. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +110 -4
  9. data/lib/active_merchant/billing/gateways/beanstream.rb +29 -1
  10. data/lib/active_merchant/billing/gateways/braintree_blue.rb +9 -4
  11. data/lib/active_merchant/billing/gateways/braintree_orange.rb +4 -0
  12. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  13. data/lib/active_merchant/billing/gateways/certo_direct.rb +279 -0
  14. data/lib/active_merchant/billing/gateways/efsnet.rb +9 -9
  15. data/lib/active_merchant/billing/gateways/elavon.rb +2 -1
  16. data/lib/active_merchant/billing/gateways/epay.rb +12 -6
  17. data/lib/active_merchant/billing/gateways/eway_managed.rb +46 -12
  18. data/lib/active_merchant/billing/gateways/exact.rb +5 -0
  19. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +3 -3
  20. data/lib/active_merchant/billing/gateways/ipay88.rb +157 -0
  21. data/lib/active_merchant/billing/gateways/iridium.rb +3 -3
  22. data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
  23. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +1 -0
  24. data/lib/active_merchant/billing/gateways/moneris.rb +4 -0
  25. data/lib/active_merchant/billing/gateways/nab_transact.rb +244 -0
  26. data/lib/active_merchant/billing/gateways/ogone.rb +94 -56
  27. data/lib/active_merchant/billing/gateways/optimal_payment.rb +277 -0
  28. data/lib/active_merchant/billing/gateways/orbital.rb +57 -34
  29. data/lib/active_merchant/billing/gateways/pay_junction.rb +6 -1
  30. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  31. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +2 -2
  32. data/lib/active_merchant/billing/gateways/payflow.rb +10 -2
  33. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +8 -5
  34. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +43 -0
  35. data/lib/active_merchant/billing/gateways/paypal_express.rb +93 -40
  36. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +8 -3
  37. data/lib/active_merchant/billing/gateways/qbms.rb +4 -0
  38. data/lib/active_merchant/billing/gateways/quickpay.rb +97 -22
  39. data/lib/active_merchant/billing/gateways/realex.rb +5 -1
  40. data/lib/active_merchant/billing/gateways/samurai.rb +121 -0
  41. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +136 -49
  42. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +1 -1
  43. data/lib/active_merchant/billing/gateways/skip_jack.rb +7 -2
  44. data/lib/active_merchant/billing/gateways/stripe.rb +51 -19
  45. data/lib/active_merchant/billing/gateways/usa_epay.rb +13 -184
  46. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1496 -0
  47. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +206 -0
  48. data/lib/active_merchant/billing/gateways/verifi.rb +2 -2
  49. data/lib/active_merchant/billing/gateways/viaklix.rb +1 -1
  50. data/lib/active_merchant/billing/gateways/worldpay.rb +1 -1
  51. data/lib/active_merchant/billing/integrations/action_view_helper.rb +6 -2
  52. data/lib/active_merchant/billing/integrations/authorize_net_sim/helper.rb +228 -0
  53. data/lib/active_merchant/billing/integrations/authorize_net_sim/notification.rb +340 -0
  54. data/lib/active_merchant/billing/integrations/authorize_net_sim.rb +38 -0
  55. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +4 -4
  56. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +31 -0
  57. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +55 -0
  58. data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
  59. data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
  60. data/lib/active_merchant/billing/integrations/helper.rb +19 -2
  61. data/lib/active_merchant/billing/integrations/ipay88/helper.rb +120 -0
  62. data/lib/active_merchant/billing/integrations/ipay88/return.rb +121 -0
  63. data/lib/active_merchant/billing/integrations/ipay88.rb +40 -0
  64. data/lib/active_merchant/billing/integrations/nochex.rb +1 -1
  65. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +100 -0
  66. data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
  67. data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
  68. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +4 -4
  69. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +18 -2
  70. data/lib/active_merchant/billing/integrations/two_checkout.rb +1 -2
  71. data/lib/active_merchant/railtie.rb +7 -7
  72. data/lib/active_merchant/railtie.rb.orig +19 -0
  73. data/lib/active_merchant/version.rb +1 -1
  74. data/lib/active_merchant.rb +23 -10
  75. data/lib/active_merchant.rb.orig +78 -0
  76. metadata +147 -63
  77. data/lib/active_merchant/common/connection.rb +0 -177
  78. data/lib/active_merchant/common/country.rb +0 -328
  79. data/lib/active_merchant/common/error.rb +0 -26
  80. data/lib/active_merchant/common/post_data.rb +0 -24
  81. data/lib/active_merchant/common/posts_data.rb +0 -63
  82. data/lib/active_merchant/common/requires_parameters.rb +0 -16
  83. data/lib/active_merchant/common/utils.rb +0 -22
  84. data/lib/active_merchant/common/validateable.rb +0 -81
  85. data/lib/active_merchant/common.rb +0 -14
  86. data/lib/certs/cacert.pem +0 -7815
@@ -0,0 +1,157 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class Ipay88Gateway < Gateway
4
+ TEST_URL = ''
5
+ LIVE_URL = 'https://www.ipay88.com/recurringpayment/webservice/RecurringPayment.asmx/Subscription'
6
+
7
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
8
+ self.supported_countries = ['MY']
9
+
10
+ # The card types supported by the payment gateway
11
+ self.supported_cardtypes = [:visa, :master]
12
+
13
+ # The homepage URL of the gateway
14
+ self.homepage_url = 'https://www.ipay88.com/recurringpayment/webservice'
15
+
16
+ # The name of the gateway
17
+ self.display_name = 'iPay88'
18
+
19
+ # Creates a new Ipay88Gateway
20
+ #
21
+ # The gateway requires that a valid login and password be passed
22
+ # in the +options+ hash.
23
+ #
24
+ # ==== Options
25
+ #
26
+ # * <tt>:login</tt> -- The ipay88 Merchant Code (REQUIRED)
27
+ # * <tt>:password</tt> -- The ipay88 Merchant Code (REQUIRED)
28
+ def initialize(options = {})
29
+ requires!(options, :login, :password)
30
+ @options = options
31
+ super
32
+ end
33
+
34
+ def recurring(money, creditcard, options = {})
35
+ post = {}
36
+ add_merchant_authentication(post, options)
37
+ add_invoice(post, options)
38
+ add_creditcard(post, creditcard, options)
39
+ add_customer_data(post, options)
40
+ add_address(post, options)
41
+ add_signature(post, options)
42
+
43
+ recurring_commit("subscribe", post)
44
+ end
45
+
46
+ private
47
+
48
+ def add_merchant_authentication(post, options)
49
+ post["MerchantCode"] = @options[:login]
50
+ post["BackendURL"] = options[:backend_url]
51
+ end
52
+
53
+ def add_invoice(post, options)
54
+ post["RefNo"] = options[:reference_no]
55
+ post["FirstPaymentDate"] = options[:first_payment_date]
56
+ post["Currency"] = "MYR"
57
+ post["Amount"] = amount(options[:amount])
58
+ post["NumberOfPayments"] = options[:number_of_payments]
59
+ post["Frequency"] = options[:frequency]
60
+ post["Desc"] = options[:description]
61
+ end
62
+
63
+ def add_creditcard(post, creditcard, options)
64
+ post["CC_PAN"] = creditcard.number
65
+ post["CC_CVC"] = creditcard.verification_value if creditcard.verification_value?
66
+ post["CC_ExpiryDate"] = expdate(creditcard)
67
+ post["CC_Name"] = creditcard.name
68
+ post["CC_Country"] = options[:cc_country]
69
+ post["CC_Bank"] = options[:cc_bank]
70
+ post["CC_Ic"] = options[:cc_ic]
71
+ post["CC_Email"] = options[:cc_email]
72
+ post["CC_Phone"] = options[:cc_phone]
73
+ post["CC_Remark"] = options[:cc_remark]
74
+ end
75
+
76
+ def add_customer_data(post, options)
77
+ post["P_Name"] = options[:name]
78
+ post["P_Email"] = options[:email]
79
+ post["P_Phone"]= options[:phone]
80
+ end
81
+
82
+ def add_address(post, options)
83
+ post["P_Addrl1"] = options[:address_line1]
84
+ post["P_Addrl2"] = options[:address_line2]
85
+ post["P_City"] = options[:city]
86
+ post["P_State"] = options[:state]
87
+ post["P_Zip"] = options[:zipcode]
88
+ post["P_Country"] = options[:country]
89
+ end
90
+
91
+ def add_signature(post, options)
92
+ hash_components = [post["MerchantCode"]]
93
+ hash_components << @options[:password]
94
+ hash_components << post["RefNo"]
95
+ hash_components << post["FirstPaymentDate"]
96
+ hash_components << post["Currency"]
97
+ hash_components << options[:amount] # Amount in cents
98
+ hash_components << post["NumberOfPayments"]
99
+ hash_components << post["Frequency"]
100
+ hash_components << post["CC_PAN"]
101
+
102
+ hash_string = hash_components.join
103
+
104
+ post["Signature"] = [Digest::SHA1.digest(hash_string)].pack("m").chomp
105
+ end
106
+
107
+ def recurring_commit(action, parameters)
108
+ request = post_data(action, parameters)
109
+ xml = ssl_post LIVE_URL, request
110
+
111
+ response = recurring_parse(xml)
112
+ message = response[:errdesc]
113
+ success = response[:status] == "1"
114
+
115
+ Response.new(success, message, response,
116
+ :authorization => response[:subscription_no]
117
+ )
118
+ end
119
+
120
+ def recurring_parse(xml)
121
+ response = {}
122
+ xml = REXML::Document.new(xml)
123
+ root = REXML::XPath.first(xml, "//SubscriptionDetails")
124
+ if root
125
+ root.elements.to_a.each do |node|
126
+ recurring_parse_element(response, node)
127
+ end
128
+ end
129
+
130
+ response
131
+ end
132
+
133
+ def recurring_parse_element(response, node)
134
+ if node.has_elements?
135
+ node.elements.each{|e| recurring_parse_element(response, e) }
136
+ else
137
+ response[node.name.underscore.to_sym] = node.text
138
+ end
139
+ end
140
+
141
+ def post_data(action, parameters = {})
142
+ post = {}
143
+
144
+ request = post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
145
+ request
146
+ end
147
+
148
+ def expdate(creditcard)
149
+ year = sprintf("%.4i", creditcard.year)
150
+ month = sprintf("%.2i", creditcard.month)
151
+
152
+ "#{month}#{year}"
153
+ end
154
+ end
155
+ end
156
+ end
157
+
@@ -7,8 +7,6 @@ module ActiveMerchant #:nodoc:
7
7
  # login to the Iridium Merchant Management System. Instead, you will
8
8
  # use the API username and password you were issued separately.
9
9
  class IridiumGateway < Gateway
10
- TEST_URL = 'https://gw1.iridiumcorp.net/'
11
- LIVE_URL = 'https://gw1.iridiumcorp.net/'
12
10
 
13
11
  # The countries the gateway supports merchants from as 2 digit ISO country codes
14
12
  self.supported_countries = ['GB', 'ES']
@@ -37,6 +35,8 @@ module ActiveMerchant #:nodoc:
37
35
  def initialize(options = {})
38
36
  requires!(options, :login, :password)
39
37
  @options = options
38
+ @test_url = 'https://gw1.iridiumcorp.net/'
39
+ @live_url = 'https://gw1.iridiumcorp.net/'
40
40
  super
41
41
  end
42
42
 
@@ -172,7 +172,7 @@ module ActiveMerchant #:nodoc:
172
172
 
173
173
  def commit(request, options)
174
174
  requires!(options, :action)
175
- response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, request,
175
+ response = parse(ssl_post(test? ? @test_url : @live_url, request,
176
176
  {"SOAPAction" => "https://www.thepaymentgateway.net/#{options[:action]}",
177
177
  "Content-Type" => "text/xml; charset=utf-8" }))
178
178
 
@@ -0,0 +1,450 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # iTransact, Inc. is an authorized reseller of the PaymentClearing gateway. If your merchant service provider uses PaymentClearing.com to process payments, you can use this module.
6
+ #
7
+ #
8
+ # Please note, the username and API Access Key are not what you use to log into the Merchant Control Panel.
9
+ #
10
+ # ==== How to get your GatewayID and API Access Key
11
+ #
12
+ # 1. If you don't already have a Gateway Account, go to http://www.itransact.com/merchant/test.html to sign up.
13
+ # 2. Go to http://support.paymentclearing.com and login or register, if necessary.
14
+ # 3. Click on "Submit a Ticket."
15
+ # 4. Select "Merchant Support" as the department and click "Next"
16
+ # 5. Enter *both* your company name and GatewayID. Put "API Access Key" in the subject. In the body, you can request a username, but it may already be in use.
17
+ #
18
+ # ==== Initialization
19
+ #
20
+ # Once you have the username, API Access Key, and your GatewayId, you're ready
21
+ # to begin. You initialize the Gateway like so:
22
+ #
23
+ # gateway = ActiveMerchant::Billing::ItransactGateway.new(
24
+ # :login => "#{THE_USERNAME}",
25
+ # :password => "#{THE_API_ACCESS_KEY}",
26
+ # :gateway_id => "#{THE_GATEWAY_ID}"
27
+ # )
28
+ #
29
+ # ==== Important Notes
30
+ # 1. Recurring is not implemented
31
+ # 1. CreditTransactions are not implemented (these are credits not related to a previously run transaction).
32
+ # 1. TransactionStatus is not implemented
33
+ #
34
+ class ItransactGateway < Gateway
35
+ URL = 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans2.cgi'
36
+
37
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
38
+ self.supported_countries = ['US']
39
+
40
+ # The card types supported by the payment gateway
41
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
42
+
43
+ # The homepage URL of the gateway
44
+ self.homepage_url = 'http://www.itransact.com/'
45
+
46
+ # The name of the gateway
47
+ self.display_name = 'iTransact'
48
+
49
+ #
50
+ # Creates a new instance of the iTransact Gateway.
51
+ #
52
+ # ==== Parameters
53
+ # * <tt>options</tt> - A Hash of options
54
+ #
55
+ # ==== Options Hash
56
+ # * <tt>:login</tt> - A String containing your PaymentClearing assigned API Access Username
57
+ # * <tt>:password</tt> - A String containing your PaymentClearing assigned API Access Key
58
+ # * <tt>:gateway_id</tt> - A String containing your PaymentClearing assigned GatewayID
59
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Run *all* transactions with the 'TestMode' element set to 'TRUE'.
60
+ #
61
+ def initialize(options = {})
62
+ requires!(options, :login, :password, :gateway_id)
63
+ @options = options
64
+ super
65
+ end
66
+
67
+ # Performs an authorize transaction. In PaymentClearing's documentation
68
+ # this is known as a "PreAuth" transaction.
69
+ #
70
+ # ==== Parameters
71
+ # * <tt>money</tt> - The amount to be captured. Should be an Integer amount in cents.
72
+ # * <tt>creditcard</tt> - The CreditCard details for the transaction
73
+ # * <tt>options</tt> - A Hash of options
74
+ #
75
+ # ==== Options Hash
76
+ # The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
77
+ # * <tt>:order_items</tt> - An Array of Hash objects with the keys <tt>:description</tt>, <tt>:cost</tt> (in cents!), and <tt>:quantity</tt>. If this is provided, <tt>:description</tt> and <tt>money</tt> will be ignored.
78
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
79
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
80
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
81
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
82
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
83
+ #
84
+ # ==== Examples
85
+ # response = gateway.authorize(1000, creditcard,
86
+ # :order_id => '1212', :address => {...}, :email => 'test@test.com',
87
+ # :order_items => [
88
+ # {:description => 'Line Item 1', :cost => '8.98', :quantity => '6'},
89
+ # {:description => 'Line Item 2', :cost => '6.99', :quantity => '4'}
90
+ # ],
91
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
92
+ # :send_customer_email => true,
93
+ # :send_merchant_email => true,
94
+ # :email_text => ['line1', 'line2', 'line3'],
95
+ # :test_mode => true
96
+ # )
97
+ #
98
+ def authorize(money, payment_source, options = {})
99
+ payload = Nokogiri::XML::Builder.new do |xml|
100
+ xml.AuthTransaction {
101
+ xml.Preauth
102
+ add_customer_data(xml, payment_source, options)
103
+ add_invoice(xml, money, options)
104
+ add_payment_source(xml, payment_source)
105
+ add_transaction_control(xml, options)
106
+ add_vendor_data(xml, options)
107
+ }
108
+ end.doc
109
+
110
+ commit(payload)
111
+ end
112
+
113
+ # Performs an authorize and capture in single transaction. In PaymentClearing's
114
+ # documentation this is known as an "Auth" or a "Sale" transaction
115
+ #
116
+ # ==== Parameters
117
+ # * <tt>money</tt> - The amount to be captured. Should be <tt>nil</tt> or an Integer amount in cents.
118
+ # * <tt>creditcard</tt> - The CreditCard details for the transaction
119
+ # * <tt>options</tt> - A Hash of options
120
+ #
121
+ # ==== Options Hash
122
+ # The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
123
+ # * <tt>:order_items</tt> - An Array of Hash objects with the keys <tt>:description</tt>, <tt>:cost</tt> (in cents!), and <tt>:quantity</tt>. If this is provided, <tt>:description</tt> and <tt>money</tt> will be ignored.
124
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
125
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
126
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
127
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
128
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
129
+ #
130
+ # ==== Examples
131
+ # response = gateway.purchase(1000, creditcard,
132
+ # :order_id => '1212', :address => {...}, :email => 'test@test.com',
133
+ # :order_items => [
134
+ # {:description => 'Line Item 1', :cost => '8.98', :quantity => '6'},
135
+ # {:description => 'Line Item 2', :cost => '6.99', :quantity => '4'}
136
+ # ],
137
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
138
+ # :send_customer_email => true,
139
+ # :send_merchant_email => true,
140
+ # :email_text => ['line1', 'line2', 'line3'],
141
+ # :test_mode => true
142
+ # )
143
+ #
144
+ def purchase(money, payment_source, options = {})
145
+ payload = Nokogiri::XML::Builder.new do |xml|
146
+ xml.AuthTransaction {
147
+ add_customer_data(xml, payment_source, options)
148
+ add_invoice(xml, money, options)
149
+ add_payment_source(xml, payment_source)
150
+ add_transaction_control(xml, options)
151
+ add_vendor_data(xml, options)
152
+ }
153
+ end.doc
154
+
155
+ commit(payload)
156
+ end
157
+
158
+ # Captures the funds from an authorize transaction. In PaymentClearing's
159
+ # documentation this is known as a "PostAuth" transaction.
160
+ #
161
+ # ==== Parameters
162
+ # * <tt>money</tt> - The amount to be captured. Should be an Integer amount in cents
163
+ # * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
164
+ # * <tt>options</tt> - A Hash of options, all are optional.
165
+ #
166
+ # ==== Options Hash
167
+ # The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
168
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
169
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
170
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
171
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
172
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
173
+ #
174
+ # ==== Examples
175
+ # response = gateway.capture(1000, creditcard,
176
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
177
+ # :send_customer_email => true,
178
+ # :send_merchant_email => true,
179
+ # :email_text => ['line1', 'line2', 'line3'],
180
+ # :test_mode => true
181
+ # )
182
+ #
183
+ def capture(money, authorization, options = {})
184
+ payload = Nokogiri::XML::Builder.new do |xml|
185
+ xml.PostAuthTransaction {
186
+ xml.OperationXID(authorization)
187
+ add_invoice(xml, money, options)
188
+ add_transaction_control(xml, options)
189
+ add_vendor_data(xml, options)
190
+ }
191
+ end.doc
192
+
193
+ commit(payload)
194
+ end
195
+
196
+ # This will reverse a previously run transaction which *has* *not* settled.
197
+ #
198
+ # ==== Parameters
199
+ # * <tt>money</tt> - This parameter is ignored -- the PaymentClearing gateway does not allow partial voids.
200
+ # * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
201
+ # * <tt>options</tt> - A Hash of options, all are optional
202
+ #
203
+ # ==== Options Hash
204
+ # The standard options (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address) are ignored.
205
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
206
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
207
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
208
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
209
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
210
+ #
211
+ # ==== Examples
212
+ # response = gateway.void(nil, '9999999999',
213
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
214
+ # :send_customer_email => true,
215
+ # :send_merchant_email => true,
216
+ # :email_text => ['line1', 'line2', 'line3'],
217
+ # :test_mode => true
218
+ # )
219
+ #
220
+ def void(money, authorization, options = {})
221
+ payload = Nokogiri::XML::Builder.new do |xml|
222
+ xml.VoidTransaction {
223
+ xml.OperationXID(authorization)
224
+ add_transaction_control(xml, options)
225
+ add_vendor_data(xml, options)
226
+ }
227
+ end.doc
228
+
229
+ commit(payload)
230
+ end
231
+
232
+ # This will reverse a previously run transaction which *has* settled.
233
+ #
234
+ # ==== Parameters
235
+ # * <tt>money</tt> - The amount to be credited. Should be <tt>nil</tt> or an Integer amount in cents
236
+ # * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
237
+ # * <tt>options</tt> - A Hash of options, all are optional
238
+ #
239
+ # ==== Options Hash
240
+ # The standard options (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address) are ignored.
241
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
242
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
243
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
244
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
245
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
246
+ #
247
+ # ==== Examples
248
+ # response = gateway.credit(nil, '9999999999',
249
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
250
+ # :send_customer_email => true,
251
+ # :send_merchant_email => true,
252
+ # :email_text => ['line1', 'line2', 'line3'],
253
+ # :test_mode => true
254
+ # )
255
+ #
256
+ def credit(money, authorization, options = {})
257
+ payload = Nokogiri::XML::Builder.new do |xml|
258
+ xml.TranCreditTransaction {
259
+ xml.OperationXID(authorization)
260
+ add_invoice(xml, money, options)
261
+ add_transaction_control(xml, options)
262
+ add_vendor_data(xml, options)
263
+ }
264
+ end.doc
265
+
266
+ commit(payload)
267
+ end
268
+
269
+ private
270
+
271
+ def add_customer_data(xml, payment_source, options)
272
+ billing_address = options[:billing_address] || options[:address]
273
+ shipping_address = options[:shipping_address] || options[:address]
274
+
275
+ xml.CustomerData {
276
+ xml.Email(options[:email]) unless options[:email].blank?
277
+ xml.CustId(options[:order_id]) unless options[:order_id].blank?
278
+ xml.BillingAddress {
279
+ xml.FirstName(payment_source.first_name || parse_first_name(billing_address[:name]))
280
+ xml.LastName(payment_source.last_name || parse_last_name(billing_address[:name]))
281
+ xml.Address1(billing_address[:address1])
282
+ xml.Address2(billing_address[:address2]) unless billing_address[:address2].blank?
283
+ xml.City(billing_address[:city])
284
+ xml.State(billing_address[:state])
285
+ xml.Zip(billing_address[:zip])
286
+ xml.Country(billing_address[:country])
287
+ xml.Phone(billing_address[:phone])
288
+ }
289
+ xml.ShippingAddress {
290
+ xml.FirstName(payment_source.first_name || parse_first_name(shipping_address[:name]))
291
+ xml.LastName(payment_source.last_name || parse_last_name(shipping_address[:name]))
292
+ xml.Address1(shipping_address[:address1])
293
+ xml.Address2(shipping_address[:address2]) unless shipping_address[:address2].blank?
294
+ xml.City(shipping_address[:city])
295
+ xml.State(shipping_address[:state])
296
+ xml.Zip(shipping_address[:zip])
297
+ xml.Country(shipping_address[:country])
298
+ xml.Phone(shipping_address[:phone])
299
+ } unless shipping_address.blank?
300
+ }
301
+ end
302
+
303
+ def add_invoice(xml, money, options)
304
+ xml.AuthCode options[:force] if options[:force]
305
+ if options[:order_items].blank?
306
+ xml.Total(amount(money)) unless(money.nil? || money < 0.01)
307
+ xml.Description(options[:description]) unless( options[:description].blank?)
308
+ else
309
+ xml.OrderItems {
310
+ options[:order_items].each do |item|
311
+ xml.Item {
312
+ xml.Description(item[:description])
313
+ xml.Cost(amount(item[:cost]))
314
+ xml.Qty(item[:quantity].to_s)
315
+ }
316
+ end
317
+ }
318
+ end
319
+ end
320
+
321
+ def add_payment_source(xml, source)
322
+ case determine_funding_source(source)
323
+ when :credit_card then add_creditcard(xml, source)
324
+ when :check then add_check(xml, source)
325
+ end
326
+ end
327
+
328
+ def determine_funding_source(payment_source)
329
+ case payment_source
330
+ when ActiveMerchant::Billing::CreditCard
331
+ :credit_card
332
+ when ActiveMerchant::Billing::Check
333
+ :check
334
+ end
335
+ end
336
+
337
+ def add_creditcard(xml, creditcard)
338
+ xml.AccountInfo {
339
+ xml.CardAccount {
340
+ xml.AccountNumber(creditcard.number.to_s)
341
+ xml.ExpirationMonth(creditcard.month.to_s.rjust(2,'0'))
342
+ xml.ExpirationYear(creditcard.year.to_s)
343
+ xml.CVVNumber(creditcard.verification_value.to_s) unless creditcard.verification_value.blank?
344
+ }
345
+ }
346
+ end
347
+
348
+ def add_check(xml, check)
349
+ xml.AccountInfo {
350
+ xml.ABA(check.routing_number.to_s)
351
+ xml.AccountNumber(check.account_number.to_s)
352
+ xml.AccountSource(check.account_type.to_s)
353
+ xml.AccountType(check.account_holder_type.to_s)
354
+ xml.CheckNumber(check.number.to_s)
355
+ }
356
+ end
357
+
358
+ def add_transaction_control(xml, options)
359
+ xml.TransactionControl {
360
+ # if there was a 'global' option set...
361
+ xml.TestMode(@options[:test_mode].upcase) if !@options[:test_mode].blank?
362
+ # allow the global option to be overridden...
363
+ xml.TestMode(options[:test_mode].upcase) if !options[:test_mode].blank?
364
+ xml.SendCustomerEmail(options[:send_customer_email].upcase) unless options[:send_customer_email].blank?
365
+ xml.SendMerchantEmail(options[:send_merchant_email].upcase) unless options[:send_merchant_email].blank?
366
+ xml.EmailText {
367
+ options[:email_text].each do |item|
368
+ xml.EmailTextItem(item)
369
+ end
370
+ } if options[:email_text]
371
+ }
372
+ end
373
+
374
+ def add_vendor_data(xml, options)
375
+ return if options[:vendor_data].blank?
376
+ xml.VendorData {
377
+ options[:vendor_data].each do |k,v|
378
+ xml.Element {
379
+ xml.Name(k)
380
+ xml.Key(v)
381
+ }
382
+ end
383
+ }
384
+ end
385
+
386
+ def commit(payload)
387
+ # Set the Content-Type header -- otherwise the URL decoding messes up
388
+ # the Base64 encoded payload signature!
389
+ response = parse(ssl_post(URL, post_data(payload), 'Content-Type' => 'text/xml'))
390
+
391
+ Response.new(successful?(response), response[:error_message], response,
392
+ :test => test?,
393
+ :authorization => response[:xid],
394
+ :avs_result => { :code => response[:avs_response] },
395
+ :cvv_result => response[:cvv_response])
396
+ end
397
+
398
+ def post_data(payload)
399
+ payload_xml = payload.root.to_xml(:indent => 0)
400
+
401
+ payload_signature = sign_payload(payload_xml)
402
+
403
+ request = Nokogiri::XML::Builder.new do |xml|
404
+ xml.GatewayInterface {
405
+ xml.APICredentials {
406
+ xml.Username(@options[:login])
407
+ xml.PayloadSignature(payload_signature)
408
+ xml.TargetGateway(@options[:gateway_id])
409
+ }
410
+ }
411
+ end.doc
412
+
413
+ request.root.children.first.after payload.root
414
+ request.to_xml(:indent => 0)
415
+ end
416
+
417
+ def parse(raw_xml)
418
+ doc = REXML::Document.new(raw_xml)
419
+ response = Hash.new
420
+ transaction_result = doc.root.get_elements('TransactionResponse/TransactionResult/*')
421
+ transaction_result.each do |e|
422
+ response[e.name.to_s.underscore.to_sym] = e.text unless e.text.blank?
423
+ end
424
+ response
425
+ end
426
+
427
+ def successful?(response)
428
+ # Turns out the PaymentClearing gateway is not consistent...
429
+ response[:status].downcase =='ok'
430
+ end
431
+
432
+ def test_mode?(response)
433
+ # The '1' is a legacy thing; most of the time it should be 'TRUE'...
434
+ response[:test_mode] == 'TRUE' || response[:test_mode] == '1'
435
+ end
436
+
437
+ def message_from(response)
438
+ response[:error_message]
439
+ end
440
+
441
+ def sign_payload(payload)
442
+ key = @options[:password].to_s
443
+ digest=OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new(key), key, payload)
444
+ signature = Base64.encode64(digest)
445
+ signature.chomp!
446
+ end
447
+ end
448
+ end
449
+ end
450
+
@@ -90,6 +90,7 @@ module ActiveMerchant #:nodoc:
90
90
  if creditcard_or_card_id.is_a?(String)
91
91
  # using stored card
92
92
  post[:card_id] = creditcard_or_card_id
93
+ post[:card_exp_date] = options[:expiration_date] if options[:expiration_date]
93
94
  else
94
95
  # card info is provided
95
96
  add_creditcard(post, creditcard_or_card_id, options)
@@ -78,6 +78,10 @@ module ActiveMerchant #:nodoc:
78
78
  def refund(money, authorization, options = {})
79
79
  commit 'refund', crediting_params(authorization, :amount => amount(money))
80
80
  end
81
+
82
+ def test?
83
+ @options[:test] || super
84
+ end
81
85
  private # :nodoc: all
82
86
 
83
87
  def expdate(creditcard)