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,277 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class OptimalPaymentGateway < Gateway
4
+ TEST_URL = 'https://webservices.test.optimalpayments.com/creditcardWS/CreditCardServlet/v1'
5
+ LIVE_URL = 'https://webservices.optimalpayments.com/creditcardWS/CreditCardServlet/v1'
6
+
7
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
8
+ self.supported_countries = ['CA', 'US', 'GB']
9
+
10
+ # The card types supported by the payment gateway
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :solo] # :switch?
12
+
13
+ # The homepage URL of the gateway
14
+ self.homepage_url = 'http://www.optimalpayments.com/'
15
+
16
+ # The name of the gateway
17
+ self.display_name = 'Optimal Payments'
18
+
19
+ def initialize(options = {})
20
+ #requires!(options, :login, :password)
21
+ @options = options
22
+ super
23
+ end
24
+
25
+ def authorize(money, card_or_auth, options = {})
26
+ parse_card_or_auth(card_or_auth, options)
27
+ commit("cc#{@stored_data}Authorize", money, options)
28
+ end
29
+ alias stored_authorize authorize # back-compat
30
+
31
+ def purchase(money, card_or_auth, options = {})
32
+ parse_card_or_auth(card_or_auth, options)
33
+ commit("cc#{@stored_data}Purchase", money, options)
34
+ end
35
+ alias stored_purchase purchase # back-compat
36
+
37
+ def refund(money, authorization, options = {})
38
+ options[:confirmationNumber] = authorization
39
+ commit('ccCredit', money, options)
40
+ end
41
+
42
+ def void(authorization, options = {})
43
+ options[:confirmationNumber] = authorization
44
+ commit('ccAuthorizeReversal', nil, options)
45
+ end
46
+
47
+ def capture(money, authorization, options = {})
48
+ options[:confirmationNumber] = authorization
49
+ commit('ccSettlement', money, options)
50
+ end
51
+
52
+ def test?
53
+ super || @options[:test]
54
+ end
55
+
56
+ private
57
+
58
+ def parse_card_or_auth(card_or_auth, options)
59
+ if card_or_auth.respond_to?(:number)
60
+ @credit_card = card_or_auth
61
+ @stored_data = ""
62
+ else
63
+ options[:confirmationNumber] = card_or_auth
64
+ @stored_data = "StoredData"
65
+ end
66
+ end
67
+
68
+ def parse(body)
69
+ REXML::Document.new(body || '')
70
+ end
71
+
72
+ def commit(action, money, post)
73
+ post[:order_id] ||= 'order_id'
74
+
75
+ xml = case action
76
+ when 'ccAuthorize', 'ccPurchase', 'ccVerification'
77
+ cc_auth_request(money, post)
78
+ when 'ccCredit', 'ccSettlement'
79
+ cc_post_auth_request(money, post)
80
+ when 'ccStoredDataAuthorize', 'ccStoredDataPurchase'
81
+ cc_stored_data_request(money, post)
82
+ when 'ccAuthorizeReversal'
83
+ cc_auth_reversal_request(post)
84
+ #when 'ccCancelSettle', 'ccCancelCredit', 'ccCancelPayment'
85
+ # cc_cancel_request(money, post)
86
+ #when 'ccPayment'
87
+ # cc_payment_request(money, post)
88
+ #when 'ccAuthenticate'
89
+ # cc_authenticate_request(money, post)
90
+ else
91
+ raise 'Unknown Action'
92
+ end
93
+ txnRequest = URI.encode(xml)
94
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, "txnMode=#{action}&txnRequest=#{txnRequest}"))
95
+
96
+ Response.new(successful?(response), message_from(response), hash_from_xml(response),
97
+ :test => test?,
98
+ :authorization => authorization_from(response)
99
+ )
100
+ end
101
+
102
+ def successful?(response)
103
+ REXML::XPath.first(response, '//decision').text == 'ACCEPTED' rescue false
104
+ end
105
+
106
+ def message_from(response)
107
+ REXML::XPath.each(response, '//detail') do |detail|
108
+ if detail.is_a?(REXML::Element) && detail.elements['tag'].text == 'InternalResponseDescription'
109
+ return detail.elements['value'].text
110
+ end
111
+ end
112
+ nil
113
+ end
114
+
115
+ def authorization_from(response)
116
+ REXML::XPath.first(response, '//confirmationNumber').text rescue nil
117
+ end
118
+
119
+ def hash_from_xml(response)
120
+ hsh = {}
121
+ %w(confirmationNumber authCode
122
+ decision code description
123
+ actionCode avsResponse cvdResponse
124
+ txnTime duplicateFound
125
+ ).each do |tag|
126
+ node = REXML::XPath.first(response, "//#{tag}")
127
+ hsh[tag] = node.text if node
128
+ end
129
+ REXML::XPath.each(response, '//detail') do |detail|
130
+ next unless detail.is_a?(REXML::Element)
131
+ tag = detail.elements['tag'].text
132
+ value = detail.elements['value'].text
133
+ hsh[tag] = value
134
+ end
135
+ hsh
136
+ end
137
+
138
+ def xml_document(root_tag)
139
+ xml = Builder::XmlMarkup.new :indent => 2
140
+ xml.tag!(root_tag, schema) do
141
+ yield xml
142
+ end
143
+ xml.target!
144
+ end
145
+
146
+ def cc_auth_request(money, opts)
147
+ xml_document('ccAuthRequestV1') do |xml|
148
+ build_merchant_account(xml, @options)
149
+ xml.merchantRefNum opts[:order_id]
150
+ xml.amount(money/100.0)
151
+ build_card(xml, opts)
152
+ build_billing_details(xml, opts)
153
+ end
154
+ end
155
+
156
+ def cc_auth_reversal_request(opts)
157
+ xml_document('ccAuthReversalRequestV1') do |xml|
158
+ build_merchant_account(xml, @options)
159
+ xml.confirmationNumber opts[:confirmationNumber]
160
+ xml.merchantRefNum opts[:order_id]
161
+ end
162
+ end
163
+
164
+ def cc_post_auth_request(money, opts)
165
+ xml_document('ccPostAuthRequestV1') do |xml|
166
+ build_merchant_account(xml, @options)
167
+ xml.confirmationNumber opts[:confirmationNumber]
168
+ xml.merchantRefNum opts[:order_id]
169
+ xml.amount(money/100.0)
170
+ end
171
+ end
172
+
173
+ def cc_stored_data_request(money, opts)
174
+ xml_document('ccStoredDataRequestV1') do |xml|
175
+ build_merchant_account(xml, @options)
176
+ xml.merchantRefNum opts[:order_id]
177
+ xml.confirmationNumber opts[:confirmationNumber]
178
+ xml.amount(money/100.0)
179
+ end
180
+ end
181
+
182
+ # untested
183
+ #
184
+ # def cc_cancel_request(opts)
185
+ # xml_document('ccCancelRequestV1') do |xml|
186
+ # build_merchant_account(xml, @options)
187
+ # xml.confirmationNumber opts[:confirmationNumber]
188
+ # end
189
+ # end
190
+ #
191
+ # def cc_payment_request(money, opts)
192
+ # xml_document('ccPaymentRequestV1') do |xml|
193
+ # build_merchant_account(xml, @options)
194
+ # xml.merchantRefNum opts[:order_id]
195
+ # xml.amount(money/100.0)
196
+ # build_card(xml, opts)
197
+ # build_billing_details(xml, opts)
198
+ # end
199
+ # end
200
+ #
201
+ # def cc_authenticate_request(opts)
202
+ # xml_document('ccAuthenticateRequestV1') do |xml|
203
+ # build_merchant_account(xml, @options)
204
+ # xml.confirmationNumber opts[:confirmationNumber]
205
+ # xml.paymentResponse 'myPaymentResponse'
206
+ # end
207
+ # end
208
+
209
+ def schema
210
+ { 'xmlns' => 'http://www.optimalpayments.com/creditcard/xmlschema/v1',
211
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
212
+ 'xsi:schemaLocation' => 'http://www.optimalpayments.com/creditcard/xmlschema/v1'
213
+ }
214
+ end
215
+
216
+ def build_merchant_account(xml, opts)
217
+ xml.tag! 'merchantAccount' do
218
+ xml.tag! 'accountNum' , opts[:account]
219
+ xml.tag! 'storeID' , opts[:login]
220
+ xml.tag! 'storePwd' , opts[:password]
221
+ end
222
+ end
223
+
224
+ def build_card(xml, opts)
225
+ xml.tag! 'card' do
226
+ xml.tag! 'cardNum' , @credit_card.number
227
+ xml.tag! 'cardExpiry' do
228
+ xml.tag! 'month' , @credit_card.month
229
+ xml.tag! 'year' , @credit_card.year
230
+ end
231
+ if type = card_type(@credit_card.type)
232
+ xml.tag! 'cardType' , type
233
+ end
234
+ if @credit_card.verification_value
235
+ xml.tag! 'cvdIndicator' , '1' # Value Provided
236
+ xml.tag! 'cvd' , @credit_card.verification_value
237
+ end
238
+ end
239
+ end
240
+
241
+ def build_billing_details(xml, opts)
242
+ xml.tag! 'billingDetails' do
243
+ addr = opts[:billing_address]
244
+ xml.tag! 'cardPayMethod', 'WEB'
245
+ if addr[:name]
246
+ xml.tag! 'firstName', CGI.escape(addr[:name].split(' ').first) # TODO: parse properly
247
+ xml.tag! 'lastName' , CGI.escape(addr[:name].split(' ').last )
248
+ end
249
+ xml.tag! 'street' , CGI.escape(addr[:address1]) if addr[:address1].present?
250
+ xml.tag! 'street2', CGI.escape(addr[:address2]) if addr[:address2].present?
251
+ xml.tag! 'city' , CGI.escape(addr[:city] ) if addr[:city].present?
252
+ if addr[:state].present?
253
+ state_tag = %w(US CA).include?(addr[:country]) ? 'state' : 'region'
254
+ xml.tag! state_tag, CGI.escape(addr[:state])
255
+ end
256
+ xml.tag! 'country', CGI.escape(addr[:country] ) if addr[:country].present?
257
+ xml.tag! 'zip' , CGI.escape(addr[:zip] ) # this one's actually required
258
+ xml.tag! 'phone' , CGI.escape(addr[:phone] ) if addr[:phone].present?
259
+ #xml.tag! 'email' , ''
260
+ end
261
+ end
262
+
263
+ def card_type(key)
264
+ { 'visa' => 'VI',
265
+ 'master' => 'MC',
266
+ 'american_express'=> 'AM',
267
+ 'discover' => 'DI',
268
+ 'diners_club' => 'DC',
269
+ #'switch' => '',
270
+ 'solo' => 'SO'
271
+ }[key]
272
+ end
273
+
274
+ end
275
+ end
276
+ end
277
+
@@ -78,10 +78,9 @@ module ActiveMerchant #:nodoc:
78
78
  }
79
79
 
80
80
  def initialize(options = {})
81
- unless options[:ip_authentication] == true
82
- requires!(options, :login, :password, :merchant_id)
83
- @options = options
84
- end
81
+ requires!(options, :merchant_id)
82
+ requires!(options, :login, :password) unless options[:ip_authentication]
83
+ @options = options
85
84
  super
86
85
  end
87
86
 
@@ -122,8 +121,8 @@ module ActiveMerchant #:nodoc:
122
121
  end
123
122
 
124
123
  # setting money to nil will perform a full void
125
- def void(money, authorization, options = {})
126
- order = build_void_request_xml(money, authorization, options)
124
+ def void(authorization, options = {})
125
+ order = build_void_request_xml(authorization, options)
127
126
  commit(order)
128
127
  end
129
128
 
@@ -167,6 +166,7 @@ module ActiveMerchant #:nodoc:
167
166
  xml.tag! :CurrencyCode, currency_code(currency)
168
167
  xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen.
169
168
 
169
+ xml.tag! :CardSecValInd, 1 if creditcard.verification_value? && %w( visa discover ).include?(creditcard.brand)
170
170
  xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
171
171
  end
172
172
 
@@ -223,11 +223,11 @@ module ActiveMerchant #:nodoc:
223
223
  end
224
224
 
225
225
  def success?(response)
226
- if response[:message_type] == "R"
226
+ if response[:message_type].nil? || response[:message_type] == "R"
227
227
  response[:proc_status] == SUCCESS
228
228
  else
229
229
  response[:proc_status] == SUCCESS &&
230
- response[:resp_code] == APPROVED
230
+ response[:resp_code] == APPROVED
231
231
  end
232
232
  end
233
233
 
@@ -241,22 +241,18 @@ module ActiveMerchant #:nodoc:
241
241
 
242
242
  def build_new_order_xml(action, money, parameters = {})
243
243
  requires!(parameters, :order_id)
244
- xml = Builder::XmlMarkup.new(:indent => 2)
245
- xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
244
+ xml = xml_envelope
246
245
  xml.tag! :Request do
247
246
  xml.tag! :NewOrder do
248
- xml.tag! :OrbitalConnectionUsername, @options[:login] unless ip_authentication?
249
- xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
250
- xml.tag! :IndustryType, "EC" # E-Commerce transaction
247
+ add_xml_credentials(xml)
248
+ xml.tag! :IndustryType, parameters[:industry_type] || "EC"
251
249
  xml.tag! :MessageType, action
252
- xml.tag! :BIN, '000002' # PNS Tampa
253
- xml.tag! :MerchantID, @options[:merchant_id]
254
- xml.tag! :TerminalID, parameters[:terminal_id] || '001'
250
+ add_bin_merchant_and_terminal(xml, parameters)
255
251
 
256
252
  yield xml if block_given?
257
253
 
258
254
  xml.tag! :Comments, parameters[:comments] if parameters[:comments]
259
- xml.tag! :OrderID, parameters[:order_id].to_s[0...22]
255
+ xml.tag! :OrderID, format_order_id(parameters[:order_id])
260
256
  xml.tag! :Amount, amount(money)
261
257
 
262
258
  # Append Transaction Reference Number at the end for Refund transactions
@@ -271,39 +267,29 @@ module ActiveMerchant #:nodoc:
271
267
 
272
268
  def build_mark_for_capture_xml(money, authorization, parameters = {})
273
269
  tx_ref_num, order_id = authorization.split(';')
274
- xml = Builder::XmlMarkup.new(:indent => 2)
275
- xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
270
+ xml = xml_envelope
276
271
  xml.tag! :Request do
277
272
  xml.tag! :MarkForCapture do
278
- xml.tag! :OrbitalConnectionUsername, @options[:login] unless ip_authentication?
279
- xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
273
+ add_xml_credentials(xml)
280
274
  xml.tag! :OrderID, order_id
281
275
  xml.tag! :Amount, amount(money)
282
- xml.tag! :BIN, '000002' # PNS Tampa
283
- xml.tag! :MerchantID, @options[:merchant_id]
284
- xml.tag! :TerminalID, parameters[:terminal_id] || '001'
276
+ add_bin_merchant_and_terminal(xml, parameters)
285
277
  xml.tag! :TxRefNum, tx_ref_num
286
278
  end
287
279
  end
288
280
  xml.target!
289
281
  end
290
282
 
291
- def build_void_request_xml(money, authorization, parameters = {})
292
- requires!(parameters, :transaction_index)
283
+ def build_void_request_xml(authorization, parameters = {})
293
284
  tx_ref_num, order_id = authorization.split(';')
294
- xml = Builder::XmlMarkup.new(:indent => 2)
295
- xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
285
+ xml = xml_envelope
296
286
  xml.tag! :Request do
297
287
  xml.tag! :Reversal do
298
- xml.tag! :OrbitalConnectionUsername, @options[:login] unless ip_authentication?
299
- xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
288
+ add_xml_credentials(xml)
300
289
  xml.tag! :TxRefNum, tx_ref_num
301
290
  xml.tag! :TxRefIdx, parameters[:transaction_index]
302
- xml.tag! :AdjustedAmt, amount(money)
303
291
  xml.tag! :OrderID, order_id
304
- xml.tag! :BIN, '000002' # PNS Tampa
305
- xml.tag! :MerchantID, @options[:merchant_id]
306
- xml.tag! :TerminalID, parameters[:terminal_id] || '001'
292
+ add_bin_merchant_and_terminal(xml, parameters)
307
293
  end
308
294
  end
309
295
  xml.target!
@@ -316,6 +302,43 @@ module ActiveMerchant #:nodoc:
316
302
  def expiry_date(credit_card)
317
303
  "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
318
304
  end
305
+
306
+ def bin
307
+ @options[:bin] || (salem_mid? ? '000001' : '000002')
308
+ end
309
+
310
+ def xml_envelope
311
+ xml = Builder::XmlMarkup.new(:indent => 2)
312
+ xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
313
+ xml
314
+ end
315
+
316
+ def add_xml_credentials(xml)
317
+ xml.tag! :OrbitalConnectionUsername, @options[:login] unless ip_authentication?
318
+ xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
319
+ end
320
+
321
+ def add_bin_merchant_and_terminal(xml, parameters)
322
+ xml.tag! :BIN, bin
323
+ xml.tag! :MerchantID, @options[:merchant_id]
324
+ xml.tag! :TerminalID, parameters[:terminal_id] || '001'
325
+ end
326
+
327
+ def salem_mid?
328
+ @options[:merchant_id].length == 6
329
+ end
330
+
331
+ # The valid characters include:
332
+ #
333
+ # 1. all letters and digits
334
+ # 2. - , $ @ & and a space character, though the space character cannot be the leading character
335
+ # 3. PINless Debit transactions can only use uppercase and lowercase alpha (A-Z, a-z) and numeric (0-9)
336
+ def format_order_id(order_id)
337
+ illegal_characters = /[^,$@\- \w]/
338
+ order_id = order_id.to_s.gsub(/\./, '-')
339
+ order_id.gsub!(illegal_characters, '')
340
+ order_id[0...22]
341
+ end
319
342
  end
320
343
  end
321
344
  end
@@ -202,7 +202,7 @@ module ActiveMerchant #:nodoc:
202
202
 
203
203
  # Return money to a card that was previously billed.
204
204
  # _authorization_ should be the transaction id of the transaction we are returning.
205
- def credit(money, authorization, options = {})
205
+ def refund(money, authorization, options = {})
206
206
  parameters = {
207
207
  :transaction_amount => amount(money),
208
208
  :transaction_id => authorization
@@ -211,6 +211,11 @@ module ActiveMerchant #:nodoc:
211
211
  commit('CREDIT', parameters)
212
212
  end
213
213
 
214
+ def credit(money, authorization, options = {})
215
+ deprecated CREDIT_DEPRECATION_MESSAGE
216
+ refund(money, authorization, options)
217
+ end
218
+
214
219
  # Cancel a transaction that has been charged but has not yet made it
215
220
  # through the batch process.
216
221
  def void(authorization, options = {})
@@ -126,6 +126,7 @@ module ActiveMerchant #:nodoc:
126
126
  xml.tag! 'EMail', options[:email] unless options[:email].blank?
127
127
  xml.tag! 'Phone', address[:phone] unless address[:phone].blank?
128
128
  xml.tag! 'CustCode', options[:customer] if !options[:customer].blank? && tag == 'BillTo'
129
+ xml.tag! 'PONum', options[:po_number] if !options[:po_number].blank? && tag == 'BillTo'
129
130
 
130
131
  xml.tag! 'Address' do
131
132
  xml.tag! 'Street', address[:address1] unless address[:address1].blank?
@@ -26,7 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
  { 'name' => full_name,
27
27
  'company' => nil,
28
28
  'address1' => @params['street'],
29
- 'address2' => @params['shiptostreet2'],
29
+ 'address2' => @params['shiptostreet2'] || @params['street2'],
30
30
  'city' => @params['city'],
31
31
  'state' => @params['state'],
32
32
  'country' => @params['country'],
@@ -36,4 +36,4 @@ module ActiveMerchant #:nodoc:
36
36
  end
37
37
  end
38
38
  end
39
- end
39
+ end
@@ -94,6 +94,10 @@ module ActiveMerchant #:nodoc:
94
94
  xml.tag! 'Description', options[:description] unless options[:description].blank?
95
95
  xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
96
96
  xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
97
+ xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
98
+ xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
99
+ xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
100
+ xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
97
101
 
98
102
  billing_address = options[:billing_address] || options[:address]
99
103
  add_address(xml, 'BillTo', billing_address, options) if billing_address
@@ -122,6 +126,10 @@ module ActiveMerchant #:nodoc:
122
126
  # Comment and Comment2 will show up in manager.paypal.com as Comment1 and Comment2
123
127
  xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
124
128
  xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
129
+ xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
130
+ xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
131
+ xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
132
+ xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
125
133
 
126
134
  billing_address = options[:billing_address] || options[:address]
127
135
  add_address(xml, 'BillTo', billing_address, options) if billing_address
@@ -161,8 +169,8 @@ module ActiveMerchant #:nodoc:
161
169
  end
162
170
 
163
171
  def expdate(creditcard)
164
- year = sprintf("%.4i", creditcard.year)
165
- month = sprintf("%.2i", creditcard.month)
172
+ year = sprintf("%.4i", creditcard.year.to_s.sub(/^0+/, ''))
173
+ month = sprintf("%.2i", creditcard.month.to_s.sub(/^0+/, ''))
166
174
 
167
175
  "#{year}#{month}"
168
176
  end
@@ -8,7 +8,7 @@ module ActiveMerchant #:nodoc:
8
8
  base.cattr_accessor :signature
9
9
  end
10
10
 
11
- API_VERSION = '62.0'
11
+ API_VERSION = '72'
12
12
 
13
13
  URLS = {
14
14
  :test => { :certificate => 'https://api.sandbox.paypal.com/2.0/',
@@ -63,10 +63,13 @@ module ActiveMerchant #:nodoc:
63
63
  def initialize(options = {})
64
64
  requires!(options, :login, :password)
65
65
 
66
+ headers = {'X-PP-AUTHORIZATION' => options.delete(:auth_signature), 'X-PAYPAL-MESSAGE-PROTOCOL' => 'SOAP11'} if options[:auth_signature]
66
67
  @options = {
67
68
  :pem => pem_file,
68
- :signature => signature
69
+ :signature => signature,
70
+ :headers => headers || {}
69
71
  }.update(options)
72
+
70
73
 
71
74
  if @options[:pem].blank? && @options[:signature].blank?
72
75
  raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
@@ -280,7 +283,7 @@ module ActiveMerchant #:nodoc:
280
283
  xml.instruct!
281
284
  xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
282
285
  xml.tag! 'env:Header' do
283
- add_credentials(xml)
286
+ add_credentials(xml) unless @options[:headers] && @options[:headers]['X-PP-AUTHORIZATION']
284
287
  end
285
288
 
286
289
  xml.tag! 'env:Body' do
@@ -310,8 +313,8 @@ module ActiveMerchant #:nodoc:
310
313
  xml.tag! 'n2:CityName', address[:city]
311
314
  xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
312
315
  xml.tag! 'n2:Country', address[:country]
313
- xml.tag! 'n2:PostalCode', address[:zip]
314
316
  xml.tag! 'n2:Phone', address[:phone]
317
+ xml.tag! 'n2:PostalCode', address[:zip]
315
318
  end
316
319
  end
317
320
 
@@ -320,7 +323,7 @@ module ActiveMerchant #:nodoc:
320
323
  end
321
324
 
322
325
  def commit(action, request)
323
- response = parse(action, ssl_post(endpoint_url, build_request(request)))
326
+ response = parse(action, ssl_post(endpoint_url, build_request(request), @options[:headers]))
324
327
 
325
328
  build_response(successful?(response), message_from(response), response,
326
329
  :test => test?,
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/paypal/paypal_common_api'
2
+ require File.dirname(__FILE__) + '/paypal/paypal_express_response'
3
+ require File.dirname(__FILE__) + '/paypal_express_common'
4
+
5
+ module ActiveMerchant #:nodoc:
6
+ module Billing #:nodoc:
7
+ class PaypalDigitalGoodsGateway < PaypalExpressGateway
8
+ self.test_redirect_url = 'https://www.sandbox.paypal.com/incontext'
9
+ self.live_redirect_url = 'https://www.paypal.com/incontext'
10
+
11
+ self.supported_countries = %w(AU CA CN FI GB ID IN IT MY NO NZ PH PL SE SG TH VN)
12
+ self.homepage_url = 'https://www.x.com/community/ppx/xspaces/digital_goods'
13
+ self.display_name = 'PayPal Express Checkout for Digital Goods'
14
+
15
+ def redirect_url_for(token, options = {})
16
+ "#{redirect_url}?token=#{token}&useraction=commit"
17
+ end
18
+
19
+ # GATEWAY.setup_purchase(100,
20
+ # :ip => "127.0.0.1",
21
+ # :description => "Test Title",
22
+ # :return_url => "http://return.url",
23
+ # :cancel_return_url => "http://cancel.url",
24
+ # :items => [ { :name => "Charge",
25
+ # :number => "1",
26
+ # :quantity => "1",
27
+ # :amount => 100,
28
+ # :description => "Description",
29
+ # :category => "Digital" } ] )
30
+ def build_setup_request(action, money, options)
31
+ requires!(options, :items)
32
+ raise ArgumentError, "Must include at least 1 Item" unless options[:items].length > 0
33
+ options[:items].each do |item|
34
+ requires!(item, :name, :number, :quantity, :amount, :description, :category)
35
+ raise ArgumentError, "Each of the items must have the category 'Digital'" unless item[:category] == 'Digital'
36
+ end
37
+
38
+ super
39
+ end
40
+
41
+ end
42
+ end
43
+ end