activemerchant 1.124.0 → 1.125.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +85 -0
  3. data/lib/active_merchant/billing/check.rb +5 -8
  4. data/lib/active_merchant/billing/credit_card.rb +10 -0
  5. data/lib/active_merchant/billing/gateway.rb +1 -1
  6. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  7. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
  8. data/lib/active_merchant/billing/gateways/cyber_source.rb +11 -3
  9. data/lib/active_merchant/billing/gateways/d_local.rb +4 -5
  10. data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
  11. data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
  12. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  13. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  14. data/lib/active_merchant/billing/gateways/global_collect.rb +106 -16
  15. data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
  16. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  17. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  18. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  19. data/lib/active_merchant/billing/gateways/nmi.rb +2 -1
  20. data/lib/active_merchant/billing/gateways/orbital.rb +17 -2
  21. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/payflow.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/paymentez.rb +9 -2
  24. data/lib/active_merchant/billing/gateways/paysafe.rb +41 -5
  25. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -1
  26. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  27. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  28. data/lib/active_merchant/billing/gateways/priority.rb +347 -0
  29. data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -0
  30. data/lib/active_merchant/billing/gateways/stripe.rb +20 -10
  31. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +37 -3
  32. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  33. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  34. data/lib/active_merchant/billing/gateways/worldpay.rb +181 -64
  35. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  36. data/lib/active_merchant/version.rb +1 -1
  37. metadata +6 -2
@@ -8,7 +8,7 @@ module ActiveMerchant #:nodoc:
8
8
 
9
9
  self.test_url = 'https://eu.sandbox.api-ingenico.com'
10
10
  self.preproduction_url = 'https://world.preprod.api-ingenico.com'
11
- self.live_url = 'https://api.globalcollect.com'
11
+ self.live_url = 'https://world.api-ingenico.com'
12
12
 
13
13
  self.supported_countries = %w[AD AE AG AI AL AM AO AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BW BY BZ CA CC CD CF CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HN HR HT HU ID IE IL IM IN IS IT JM JO JP KE KG KH KI KM KN KR KW KY KZ LA LB LC LI LK LR LS LT LU LV MA MC MD ME MF MG MH MK MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PL PN PS PT PW QA RE RO RS RU RW SA SB SC SE SG SH SI SJ SK SL SM SN SR ST SV SZ TC TD TG TH TJ TL TM TN TO TR TT TV TW TZ UA UG US UY UZ VC VE VG VI VN WF WS ZA ZM ZW]
14
14
  self.default_currency = 'USD'
@@ -103,36 +103,126 @@ module ActiveMerchant #:nodoc:
103
103
  post['order']['references']['invoiceData'] = {
104
104
  'invoiceNumber' => options[:invoice]
105
105
  }
106
- add_airline_data(post, options) if options[:airline_data]
106
+ add_airline_data(post, options)
107
+ add_lodging_data(post, options)
107
108
  add_number_of_installments(post, options) if options[:number_of_installments]
108
109
  end
109
110
 
110
111
  def add_airline_data(post, options)
112
+ return unless airline_options = options[:airline_data]
113
+
111
114
  airline_data = {}
112
115
 
113
- flight_date = options[:airline_data][:flight_date]
114
- passenger_name = options[:airline_data][:passenger_name]
115
- code = options[:airline_data][:code]
116
- name = options[:airline_data][:name]
116
+ airline_data['flightDate'] = airline_options[:flight_date] if airline_options[:flight_date]
117
+ airline_data['passengerName'] = airline_options[:passenger_name] if airline_options[:passenger_name]
118
+ airline_data['code'] = airline_options[:code] if airline_options[:code]
119
+ airline_data['name'] = airline_options[:name] if airline_options[:name]
120
+ airline_data['invoiceNumber'] = options[:airline_data][:invoice_number] if options[:airline_data][:invoice_number]
121
+ airline_data['isETicket'] = options[:airline_data][:is_eticket] if options[:airline_data][:is_eticket]
122
+ airline_data['isRestrictedTicket'] = options[:airline_data][:is_restricted_ticket] if options[:airline_data][:is_restricted_ticket]
123
+ airline_data['isThirdParty'] = options[:airline_data][:is_third_party] if options[:airline_data][:is_third_party]
124
+ airline_data['issueDate'] = options[:airline_data][:issue_date] if options[:airline_data][:issue_date]
125
+ airline_data['merchantCustomerId'] = options[:airline_data][:merchant_customer_id] if options[:airline_data][:merchant_customer_id]
126
+ airline_data['flightLegs'] = add_flight_legs(airline_options)
127
+ airline_data['passengers'] = add_passengers(airline_options)
117
128
 
118
- airline_data['flightDate'] = flight_date if flight_date
119
- airline_data['passengerName'] = passenger_name if passenger_name
120
- airline_data['code'] = code if code
121
- airline_data['name'] = name if name
129
+ post['order']['additionalInput']['airlineData'] = airline_data
130
+ end
122
131
 
132
+ def add_flight_legs(airline_options)
123
133
  flight_legs = []
124
- options[:airline_data][:flight_legs]&.each do |fl|
134
+ airline_options[:flight_legs]&.each do |fl|
125
135
  leg = {}
136
+ leg['airlineClass'] = fl[:airline_class] if fl[:airline_class]
126
137
  leg['arrivalAirport'] = fl[:arrival_airport] if fl[:arrival_airport]
127
- leg['originAirport'] = fl[:origin_airport] if fl[:origin_airport]
138
+ leg['arrivalTime'] = fl[:arrival_time] if fl[:arrival_time]
139
+ leg['carrierCode'] = fl[:carrier_code] if fl[:carrier_code]
140
+ leg['conjunctionTicket'] = fl[:conjunction_ticket] if fl[:conjunction_ticket]
141
+ leg['couponNumber'] = fl[:coupon_number] if fl[:coupon_number]
128
142
  leg['date'] = fl[:date] if fl[:date]
143
+ leg['departureTime'] = fl[:departure_time] if fl[:departure_time]
144
+ leg['endorsementOrRestriction'] = fl[:endorsement_or_restriction] if fl[:endorsement_or_restriction]
145
+ leg['exchangeTicket'] = fl[:exchange_ticket] if fl[:exchange_ticket]
146
+ leg['fare'] = fl[:fare] if fl[:fare]
147
+ leg['fareBasis'] = fl[:fare_basis] if fl[:fare_basis]
148
+ leg['fee'] = fl[:fee] if fl[:fee]
149
+ leg['flightNumber'] = fl[:flight_number] if fl[:flight_number]
129
150
  leg['number'] = fl[:number] if fl[:number]
130
- leg['carrierCode'] = fl[:carrier_code] if fl[:carrier_code]
131
- leg['airlineClass'] = fl[:carrier_code] if fl[:airline_class]
151
+ leg['originAirport'] = fl[:origin_airport] if fl[:origin_airport]
152
+ leg['passengerClass'] = fl[:passenger_class] if fl[:passenger_class]
153
+ leg['stopoverCode'] = fl[:stopover_code] if fl[:stopover_code]
154
+ leg['taxes'] = fl[:taxes] if fl[:taxes]
132
155
  flight_legs << leg
133
156
  end
134
- airline_data['flightLegs'] = flight_legs
135
- post['order']['additionalInput']['airlineData'] = airline_data
157
+ flight_legs
158
+ end
159
+
160
+ def add_passengers(airline_options)
161
+ passengers = []
162
+ airline_options[:passengers]&.each do |flyer|
163
+ passenger = {}
164
+ passenger['firstName'] = flyer[:first_name] if flyer[:first_name]
165
+ passenger['surname'] = flyer[:surname] if flyer[:surname]
166
+ passenger['surnamePrefix'] = flyer[:surname_prefix] if flyer[:surname_prefix]
167
+ passenger['title'] = flyer[:title] if flyer[:title]
168
+ passengers << passenger
169
+ end
170
+ passengers
171
+ end
172
+
173
+ def add_lodging_data(post, options)
174
+ return unless lodging_options = options[:lodging_data]
175
+
176
+ lodging_data = {}
177
+
178
+ lodging_data['charges'] = add_charges(lodging_options)
179
+ lodging_data['checkInDate'] = lodging_options[:check_in_date] if lodging_options[:check_in_date]
180
+ lodging_data['checkOutDate'] = lodging_options[:check_out_date] if lodging_options[:check_out_date]
181
+ lodging_data['folioNumber'] = lodging_options[:folio_number] if lodging_options[:folio_number]
182
+ lodging_data['isConfirmedReservation'] = lodging_options[:is_confirmed_reservation] if lodging_options[:is_confirmed_reservation]
183
+ lodging_data['isFacilityFireSafetyConform'] = lodging_options[:is_facility_fire_safety_conform] if lodging_options[:is_facility_fire_safety_conform]
184
+ lodging_data['isNoShow'] = lodging_options[:is_no_show] if lodging_options[:is_no_show]
185
+ lodging_data['isPreferenceSmokingRoom'] = lodging_options[:is_preference_smoking_room] if lodging_options[:is_preference_smoking_room]
186
+ lodging_data['numberOfAdults'] = lodging_options[:number_of_adults] if lodging_options[:number_of_adults]
187
+ lodging_data['numberOfNights'] = lodging_options[:number_of_nights] if lodging_options[:number_of_nights]
188
+ lodging_data['numberOfRooms'] = lodging_options[:number_of_rooms] if lodging_options[:number_of_rooms]
189
+ lodging_data['programCode'] = lodging_options[:program_code] if lodging_options[:program_code]
190
+ lodging_data['propertyCustomerServicePhoneNumber'] = lodging_options[:property_customer_service_phone_number] if lodging_options[:property_customer_service_phone_number]
191
+ lodging_data['propertyPhoneNumber'] = lodging_options[:property_phone_number] if lodging_options[:property_phone_number]
192
+ lodging_data['renterName'] = lodging_options[:renter_name] if lodging_options[:renter_name]
193
+ lodging_data['rooms'] = add_rooms(lodging_options)
194
+
195
+ post['order']['additionalInput']['lodgingData'] = lodging_data
196
+ end
197
+
198
+ def add_charges(lodging_options)
199
+ charges = []
200
+ lodging_options[:charges]&.each do |item|
201
+ charge = {}
202
+ charge['chargeAmount'] = item[:charge_amount] if item[:charge_amount]
203
+ charge['chargeAmountCurrencyCode'] = item[:charge_amount_currency_code] if item[:charge_amount_currency_code]
204
+ charge['chargeType'] = item[:charge_type] if item[:charge_type]
205
+ charges << charge
206
+ end
207
+ charges
208
+ end
209
+
210
+ def add_rooms(lodging_options)
211
+ rooms = []
212
+ lodging_options[:rooms]&.each do |item|
213
+ room = {}
214
+ room['dailyRoomRate'] = item[:daily_room_rate] if item[:daily_room_rate]
215
+ room['dailyRoomRateCurrencyCode'] = item[:daily_room_rate_currency_code] if item[:daily_room_rate_currency_code]
216
+ room['dailyRoomTaxAmount'] = item[:daily_room_tax_amount] if item[:daily_room_tax_amount]
217
+ room['dailyRoomTaxAmountCurrencyCode'] = item[:daily_room_tax_amount_currency_code] if item[:daily_room_tax_amount_currency_code]
218
+ room['numberOfNightsAtRoomRate'] = item[:number_of_nights_at_room_rate] if item[:number_of_nights_at_room_rate]
219
+ room['roomLocation'] = item[:room_location] if item[:room_location]
220
+ room['roomNumber'] = item[:room_number] if item[:room_number]
221
+ room['typeOfBed'] = item[:type_of_bed] if item[:type_of_bed]
222
+ room['typeOfRoom'] = item[:type_of_room] if item[:type_of_room]
223
+ rooms << room
224
+ end
225
+ rooms
136
226
  end
137
227
 
138
228
  def add_creator_info(post, options)
@@ -0,0 +1,416 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class IpgGateway < Gateway
4
+ self.test_url = 'https://test.ipg-online.com/ipgapi/services'
5
+ self.live_url = 'https://www5.ipg-online.com'
6
+
7
+ self.supported_countries = %w(UY AR)
8
+ self.default_currency = 'ARS'
9
+ self.supported_cardtypes = %i[visa master american_express discover]
10
+
11
+ self.homepage_url = 'https://www.ipg-online.com'
12
+ self.display_name = 'IPG'
13
+
14
+ CURRENCY_CODES = {
15
+ 'UYU' => '858',
16
+ 'ARS' => '032'
17
+ }
18
+
19
+ ACTION_REQUEST_ITEMS = %w(vault unstore)
20
+
21
+ def initialize(options = {})
22
+ requires!(options, :store_id, :user_id, :password, :pem, :pem_password)
23
+ @credentials = options
24
+ @hosted_data_id = nil
25
+ super
26
+ end
27
+
28
+ def purchase(money, payment, options = {})
29
+ xml = build_purchase_and_authorize_request(money, payment, options)
30
+
31
+ commit('sale', xml)
32
+ end
33
+
34
+ def authorize(money, payment, options = {})
35
+ xml = build_purchase_and_authorize_request(money, payment, options)
36
+
37
+ commit('preAuth', xml)
38
+ end
39
+
40
+ def capture(money, authorization, options = {})
41
+ xml = build_capture_and_refund_request(money, authorization, options)
42
+
43
+ commit('postAuth', xml)
44
+ end
45
+
46
+ def refund(money, authorization, options = {})
47
+ xml = build_capture_and_refund_request(money, authorization, options)
48
+
49
+ commit('return', xml)
50
+ end
51
+
52
+ def void(authorization, options = {})
53
+ xml = Builder::XmlMarkup.new(indent: 2)
54
+ add_transaction_details(xml, options.merge!({ order_id: authorization }))
55
+
56
+ commit('void', xml)
57
+ end
58
+
59
+ def store(credit_card, options = {})
60
+ @hosted_data_id = options[:hosted_data_id] || generate_unique_id
61
+ xml = Builder::XmlMarkup.new(indent: 2)
62
+ add_storage_item(xml, credit_card, options)
63
+
64
+ commit('vault', xml)
65
+ end
66
+
67
+ def unstore(hosted_data_id)
68
+ xml = Builder::XmlMarkup.new(indent: 2)
69
+ add_unstore_item(xml, hosted_data_id)
70
+
71
+ commit('unstore', xml)
72
+ end
73
+
74
+ def verify(credit_card, options = {})
75
+ options[:currency] = self.default_currency unless options[:currency] && !options[:currency].empty?
76
+ MultiResponse.run(:use_first_response) do |r|
77
+ r.process { authorize(100, credit_card, options) }
78
+ r.process(:ignore_result) { void(r.authorization, options) }
79
+ end
80
+ end
81
+
82
+ def supports_scrubbing?
83
+ true
84
+ end
85
+
86
+ def scrub(transcript)
87
+ transcript.
88
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
89
+ gsub(%r((<v1:CardNumber>).+(</v1:CardNumber>)), '\1[FILTERED]\2').
90
+ gsub(%r((<v1:CardCodeValue>).+(</v1:CardCodeValue>)), '\1[FILTERED]\2').
91
+ gsub(%r((<v1:StoreId>).+(</v1:StoreId>)), '\1[FILTERED]\2')
92
+ end
93
+
94
+ private
95
+
96
+ NAMESPACE_BASE_URL = 'http://ipg-online.com'
97
+
98
+ def build_purchase_and_authorize_request(money, payment, options)
99
+ xml = Builder::XmlMarkup.new(indent: 2)
100
+
101
+ add_credit_card(xml, payment, options)
102
+ add_sub_merchant(xml, options[:submerchant]) if options[:submerchant]
103
+ add_three_d_secure(xml, options[:three_d_secure]) if options[:three_d_secure]
104
+ add_stored_credentials(xml, options) if options[:stored_credential] || options[:recurring_type]
105
+ add_payment(xml, money, payment, options)
106
+ add_transaction_details(xml, options)
107
+ add_billing(xml, options[:billing]) if options[:billing]
108
+ add_shipping(xml, options[:shipping]) if options[:shipping]
109
+ xml
110
+ end
111
+
112
+ def build_capture_and_refund_request(money, authorization, options)
113
+ xml = Builder::XmlMarkup.new(indent: 2)
114
+ add_payment(xml, money, nil, options)
115
+ add_transaction_details(xml, options.merge!({ order_id: authorization }), true)
116
+ xml
117
+ end
118
+
119
+ def build_order_request(xml, action, body)
120
+ xml.tag!('ipg:IPGApiOrderRequest') do
121
+ xml.tag!('v1:Transaction') do
122
+ add_transaction_type(xml, action)
123
+ xml << body.target!
124
+ end
125
+ end
126
+ end
127
+
128
+ def build_action_request(xml, action, body)
129
+ xml.tag!('ns4:IPGApiActionRequest', ipg_action_namespaces) do
130
+ xml.tag!('ns2:Action') do
131
+ xml << body.target!
132
+ end
133
+ end
134
+ end
135
+
136
+ def build_soap_request(action, body)
137
+ xml = Builder::XmlMarkup.new(indent: 2)
138
+ xml.tag!('soapenv:Envelope', envelope_namespaces) do
139
+ xml.tag!('soapenv:Header')
140
+ xml.tag!('soapenv:Body') do
141
+ build_order_request(xml, action, body) unless ACTION_REQUEST_ITEMS.include?(action)
142
+ build_action_request(xml, action, body) if ACTION_REQUEST_ITEMS.include?(action)
143
+ end
144
+ end
145
+ xml.target!
146
+ end
147
+
148
+ def add_stored_credentials(xml, params)
149
+ recurring_type = params[:stored_credential][:initial_transaction] ? 'FIRST' : 'REPEAT' if params[:stored_credential]
150
+ recurring_type = params[:recurring_type] if params[:recurring_type]
151
+ xml.tag!('v1:recurringType', recurring_type)
152
+ end
153
+
154
+ def add_storage_item(xml, credit_card, options)
155
+ requires!(options.merge!({ credit_card: credit_card, hosted_data_id: @hosted_data_id }), :credit_card, :hosted_data_id)
156
+ xml.tag!('ns2:StoreHostedData') do
157
+ xml.tag!('ns2:DataStorageItem') do
158
+ add_credit_card(xml, credit_card, {}, 'ns2')
159
+ add_three_d_secure(xml, options[:three_d_secure]) if options[:three_d_secure]
160
+ xml.tag!('ns2:HostedDataID', @hosted_data_id) if @hosted_data_id
161
+ end
162
+ end
163
+ end
164
+
165
+ def add_unstore_item(xml, hosted_data_id)
166
+ requires!({}.merge!({ hosted_data_id: hosted_data_id }), :hosted_data_id)
167
+ xml.tag!('ns2:StoreHostedData') do
168
+ xml.tag!('ns2:DataStorageItem') do
169
+ xml.tag!('ns2:Function', 'delete')
170
+ xml.tag!('ns2:HostedDataID', hosted_data_id)
171
+ end
172
+ end
173
+ end
174
+
175
+ def add_transaction_type(xml, type)
176
+ xml.tag!('v1:CreditCardTxType') do
177
+ xml.tag!('v1:StoreId', @credentials[:store_id])
178
+ xml.tag!('v1:Type', type)
179
+ end
180
+ end
181
+
182
+ def add_credit_card(xml, payment, options = {}, credit_envelope = 'v1')
183
+ if payment&.is_a?(CreditCard)
184
+ requires!(options.merge!({ card_number: payment.number, month: payment.month, year: payment.year }), :card_number, :month, :year)
185
+
186
+ xml.tag!("#{credit_envelope}:CreditCardData") do
187
+ xml.tag!('v1:CardNumber', payment.number) if payment.number
188
+ xml.tag!('v1:ExpMonth', format(payment.month, :two_digits)) if payment.month
189
+ xml.tag!('v1:ExpYear', format(payment.year, :two_digits)) if payment.year
190
+ xml.tag!('v1:CardCodeValue', payment.verification_value) if payment.verification_value
191
+ xml.tag!('v1:Brand', options[:brand]) if options[:brand]
192
+ end
193
+ end
194
+
195
+ if options[:card_function_type]
196
+ xml.tag!('v1:cardFunction') do
197
+ xml.tag!('v1:Type', options[:card_function_type])
198
+ end
199
+ end
200
+
201
+ if options[:track_data]
202
+ xml.tag!("#{credit_envelope}:CreditCardData") do
203
+ xml.tag!('v1:TrackData', options[:track_data])
204
+ end
205
+ end
206
+ end
207
+
208
+ def add_sub_merchant(xml, submerchant)
209
+ xml.tag!('v1:SubMerchant') do
210
+ xml.tag!('v1:Mcc', submerchant[:mcc]) if submerchant[:mcc]
211
+ xml.tag!('v1:LegalName', submerchant[:legal_name]) if submerchant[:legal_name]
212
+ add_address(xml, submerchant[:address]) if submerchant[:address]
213
+ add_document(xml, submerchant[:document]) if submerchant[:document]
214
+ xml.tag!('v1:MerchantID', submerchant[:merchant_id]) if submerchant[:merchant_id]
215
+ end
216
+ end
217
+
218
+ def add_address(xml, address)
219
+ xml.tag!('v1:Address') do
220
+ xml.tag!('v1:Address1', address[:address1]) if address[:address1]
221
+ xml.tag!('v1:Address2', address[:address2]) if address[:address2]
222
+ xml.tag!('v1:Zip', address[:zip]) if address[:zip]
223
+ xml.tag!('v1:City', address[:city]) if address[:city]
224
+ xml.tag!('v1:State', address[:state]) if address[:state]
225
+ xml.tag!('v1:Country', address[:country]) if address[:country]
226
+ end
227
+ end
228
+
229
+ def add_document(xml, document)
230
+ xml.tag!('v1:Document') do
231
+ xml.tag!('v1:Type', document[:type]) if document[:type]
232
+ xml.tag!('v1:Number', document[:number]) if document[:number]
233
+ end
234
+ end
235
+
236
+ def add_three_d_secure(xml, three_d_secure)
237
+ xml.tag!('v1:CreditCard3DSecure') do
238
+ xml.tag!('v1:AuthenticationValue', three_d_secure[:cavv]) if three_d_secure[:cavv]
239
+ xml.tag!('v1:XID', three_d_secure[:xid]) if three_d_secure[:xid]
240
+ xml.tag!('v1:Secure3D2TransactionStatus', three_d_secure[:directory_response_status]) if three_d_secure[:directory_response_status]
241
+ xml.tag!('v1:Secure3D2AuthenticationResponse', three_d_secure[:authentication_response_status]) if three_d_secure[:authentication_response_status]
242
+ xml.tag!('v1:Secure3DProtocolVersion', three_d_secure[:version]) if three_d_secure[:version]
243
+ xml.tag!('v1:DirectoryServerTransactionId', three_d_secure[:ds_transaction_id]) if three_d_secure[:ds_transaction_id]
244
+ end
245
+ end
246
+
247
+ def add_transaction_details(xml, options, pre_order = false)
248
+ requires!(options, :order_id) if pre_order
249
+ xml.tag!('v1:TransactionDetails') do
250
+ xml.tag!('v1:OrderId', options[:order_id]) if options[:order_id]
251
+ xml.tag!('v1:MerchantTransactionId', options[:merchant_transaction_id]) if options[:merchant_transaction_id]
252
+ xml.tag!('v1:Ip', options[:ip]) if options[:ip]
253
+ xml.tag!('v1:Tdate', options[:t_date]) if options[:t_date]
254
+ xml.tag!('v1:IpgTransactionId', options[:ipg_transaction_id]) if options[:ipg_transaction_id]
255
+ xml.tag!('v1:ReferencedMerchantTransactionId', options[:referenced_merchant_transaction_id]) if options[:referenced_merchant_transaction_id]
256
+ xml.tag!('v1:TransactionOrigin', options[:transaction_origin]) if options[:transaction_origin]
257
+ xml.tag!('v1:InvoiceNumber', options[:invoice_number]) if options[:invoice_number]
258
+ xml.tag!('v1:DynamicMerchantName', options[:dynamic_merchant_name]) if options[:dynamic_merchant_name]
259
+ xml.tag!('v1:Comments', options[:comments]) if options[:comments]
260
+ if options[:terminal_id]
261
+ xml.tag!('v1:Terminal') do
262
+ xml.tag!('v1:TerminalID', options[:terminal_id]) if options[:terminal_id]
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ def add_payment(xml, money, payment, options)
269
+ requires!(options.merge!({ money: money }), :currency, :money)
270
+ xml.tag!('v1:Payment') do
271
+ xml.tag!('v1:HostedDataID', payment) if payment&.is_a?(String)
272
+ xml.tag!('v1:HostedDataStoreID', options[:hosted_data_store_id]) if options[:hosted_data_store_id]
273
+ xml.tag!('v1:DeclineHostedDataDuplicates', options[:decline_hosted_data_duplicates]) if options[:decline_hosted_data_duplicates]
274
+ xml.tag!('v1:SubTotal', options[:sub_total]) if options[:sub_total]
275
+ xml.tag!('v1:ValueAddedTax', options[:value_added_tax]) if options[:value_added_tax]
276
+ xml.tag!('v1:DeliveryAmount', options[:delivery_amount]) if options[:delivery_amount]
277
+ xml.tag!('v1:ChargeTotal', money)
278
+ xml.tag!('v1:Currency', CURRENCY_CODES[options[:currency]])
279
+ xml.tag!('v1:numberOfInstallments', options[:number_of_installments]) if options[:number_of_installments]
280
+ end
281
+ end
282
+
283
+ def add_billing(xml, billing)
284
+ xml.tag!('v1:Billing') do
285
+ xml.tag!('v1:CustomerID', billing[:customer_id]) if billing[:customer_id]
286
+ xml.tag!('v1:Name', billing[:name]) if billing[:name]
287
+ xml.tag!('v1:Company', billing[:company]) if billing[:company]
288
+ xml.tag!('v1:Address1', billing[:address_1]) if billing[:address_1]
289
+ xml.tag!('v1:Address2', billing[:address_2]) if billing[:address_2]
290
+ xml.tag!('v1:City', billing[:city]) if billing[:city]
291
+ xml.tag!('v1:State', billing[:state]) if billing[:state]
292
+ xml.tag!('v1:Zip', billing[:zip]) if billing[:zip]
293
+ xml.tag!('v1:Country', billing[:country]) if billing[:country]
294
+ xml.tag!('v1:Phone', billing[:phone]) if billing[:phone]
295
+ xml.tag!('v1:Fax', billing[:fax]) if billing[:fax]
296
+ xml.tag!('v1:Email', billing[:email]) if billing[:email]
297
+ end
298
+ end
299
+
300
+ def add_shipping(xml, shipping)
301
+ xml.tag!('v1:Shipping') do
302
+ xml.tag!('v1:Type', shipping[:type]) if shipping[:type]
303
+ xml.tag!('v1:Name', shipping[:name]) if shipping[:name]
304
+ xml.tag!('v1:Address1', shipping[:address_1]) if shipping[:address_1]
305
+ xml.tag!('v1:Address2', shipping[:address_2]) if shipping[:address_2]
306
+ xml.tag!('v1:City', shipping[:city]) if shipping[:city]
307
+ xml.tag!('v1:State', shipping[:state]) if shipping[:state]
308
+ xml.tag!('v1:Zip', shipping[:zip]) if shipping[:zip]
309
+ xml.tag!('v1:Country', shipping[:country]) if shipping[:country]
310
+ end
311
+ end
312
+
313
+ def build_header
314
+ {
315
+ 'Content-Type' => 'text/xml; charset=utf-8',
316
+ 'Authorization' => "Basic #{encoded_credentials}"
317
+ }
318
+ end
319
+
320
+ def encoded_credentials
321
+ Base64.encode64("WS#{@credentials[:store_id]}._.#{@credentials[:user_id]}:#{@credentials[:password]}").delete("\n")
322
+ end
323
+
324
+ def envelope_namespaces
325
+ {
326
+ 'xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/',
327
+ 'xmlns:ipg' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/ipgapi",
328
+ 'xmlns:v1' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/v1"
329
+ }
330
+ end
331
+
332
+ def ipg_order_namespaces
333
+ {
334
+ 'xmlns:v1' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/v1",
335
+ 'xmlns:ipgapi' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/ipgapi"
336
+ }
337
+ end
338
+
339
+ def ipg_action_namespaces
340
+ {
341
+ 'xmlns:ns4' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/ipgapi",
342
+ 'xmlns:ns2' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/a1",
343
+ 'xmlns:ns3' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/v1"
344
+ }
345
+ end
346
+
347
+ def commit(action, request)
348
+ url = (test? ? test_url : live_url)
349
+ soap_request = build_soap_request(action, request)
350
+ response = parse(ssl_post(url, soap_request, build_header))
351
+ Response.new(
352
+ response[:success],
353
+ message_from(response),
354
+ response,
355
+ authorization: authorization_from(action, response),
356
+ avs_result: AVSResult.new(code: response[:AVSResponse]),
357
+ cvv_result: CVVResult.new(response[:ProcessorCCVResponse]),
358
+ test: test?,
359
+ error_code: error_code_from(response)
360
+ )
361
+ end
362
+
363
+ def parse(xml)
364
+ reply = {}
365
+ xml = REXML::Document.new(xml)
366
+ root = REXML::XPath.first(xml, '//ipgapi:IPGApiOrderResponse') || REXML::XPath.first(xml, '//ipgapi:IPGApiActionResponse') || REXML::XPath.first(xml, '//SOAP-ENV:Fault') || REXML::XPath.first(xml, '//ns4:IPGApiActionResponse')
367
+ reply[:success] = REXML::XPath.first(xml, '//faultcode') ? false : true
368
+ if REXML::XPath.first(xml, '//ns4:IPGApiActionResponse')
369
+ reply[:tpv_error_code] = REXML::XPath.first(root, '//ns2:Error').attributes['Code']
370
+ reply[:tpv_error_msg] = REXML::XPath.first(root, '//ns2:ErrorMessage').text
371
+ reply[:success] = false
372
+ end
373
+ root.elements.to_a.each do |node|
374
+ parse_element(reply, node)
375
+ end
376
+ reply[:hosted_data_id] = @hosted_data_id if @hosted_data_id
377
+ return reply
378
+ end
379
+
380
+ def parse_element(reply, node)
381
+ if node.has_elements?
382
+ node.elements.each { |e| parse_element(reply, e) }
383
+ else
384
+ if /item/.match?(node.parent.name)
385
+ parent = node.parent.name
386
+ parent += '_' + node.parent.attributes['id'] if node.parent.attributes['id']
387
+ parent += '_'
388
+ end
389
+ reply["#{parent}#{node.name}".to_sym] ||= node.text
390
+ end
391
+ return reply
392
+ end
393
+
394
+ def message_from(response)
395
+ response[:TransactionResult]
396
+ end
397
+
398
+ def authorization_from(action, response)
399
+ return (action == 'vault' ? response[:hosted_data_id] : response[:OrderId])
400
+ end
401
+
402
+ def error_code_from(response)
403
+ response[:ErrorMessage]&.split(':')&.first unless response[:success]
404
+ end
405
+
406
+ def handle_response(response)
407
+ case response.code.to_i
408
+ when 200...300, 500
409
+ response.body
410
+ else
411
+ raise ResponseError.new(response)
412
+ end
413
+ end
414
+ end
415
+ end
416
+ end
@@ -82,6 +82,7 @@ module ActiveMerchant #:nodoc:
82
82
  add_invoice(action, post, amount, options)
83
83
  add_payment_method(post, payment_method, options)
84
84
  add_full_response(post, options)
85
+ add_metadata(post, options)
85
86
 
86
87
  commit(action, post)
87
88
  end
@@ -94,6 +95,7 @@ module ActiveMerchant #:nodoc:
94
95
  add_invoice(action, post, amount, options)
95
96
  add_contact_details(post, options[:contact_details]) if options[:contact_details]
96
97
  add_full_response(post, options)
98
+ add_metadata(post, options)
97
99
 
98
100
  commit(action, post)
99
101
  end
@@ -105,6 +107,7 @@ module ActiveMerchant #:nodoc:
105
107
  add_reference(post, authorization, options)
106
108
  add_invoice(action, post, amount, options)
107
109
  add_full_response(post, options)
110
+ add_metadata(post, options)
108
111
 
109
112
  commit(action, post)
110
113
  end
@@ -177,6 +180,10 @@ module ActiveMerchant #:nodoc:
177
180
  post[:fullResponse] = options[:full_response].to_s.casecmp('true').zero? if options[:full_response]
178
181
  end
179
182
 
183
+ def add_metadata(post, options)
184
+ post[:metadata] = options[:metadata] if options[:metadata]
185
+ end
186
+
180
187
  ENDPOINT = {
181
188
  'tokenize' => 'tokens',
182
189
  'charge' => 'charges',
@@ -53,8 +53,10 @@ module ActiveMerchant #:nodoc:
53
53
  end
54
54
 
55
55
  def verify(credit_card, options = {})
56
+ verify_amount = 100
57
+ verify_amount = options[:amount].to_i if options[:amount]
56
58
  MultiResponse.run(:use_first_response) do |r|
57
- r.process { authorize(100, credit_card, options) }
59
+ r.process { authorize(verify_amount, credit_card, options) }
58
60
  r.process(:ignore_result) { void(r.authorization, options) }
59
61
  end
60
62
  end
@@ -276,14 +276,14 @@ module ActiveMerchant #:nodoc:
276
276
  end
277
277
 
278
278
  Response.new(
279
- success_from(response),
279
+ success_from(response, action),
280
280
  message_from(response),
281
281
  response,
282
282
  authorization: authorization_from(response, action),
283
283
  avs_result: AVSResult.new(code: response['some_avs_response_key']),
284
284
  cvv_result: CVVResult.new(response['some_cvv_response_key']),
285
285
  test: test?,
286
- error_code: error_code_from(response)
286
+ error_code: error_code_from(response, action)
287
287
  )
288
288
  rescue ResponseError => e
289
289
  message = get_error_messages(e)
@@ -297,8 +297,10 @@ module ActiveMerchant #:nodoc:
297
297
  )
298
298
  end
299
299
 
300
- def success_from(response)
301
- %w[pending paid processing canceled active].include? response['status']
300
+ def success_from(response, action)
301
+ success = response.try(:[], 'last_transaction').try(:[], 'success') unless action == 'store'
302
+ success = !response.try(:[], 'id').nil? if action == 'store'
303
+ success
302
304
  end
303
305
 
304
306
  def message_from(response)
@@ -350,8 +352,8 @@ module ActiveMerchant #:nodoc:
350
352
  parameters.to_json
351
353
  end
352
354
 
353
- def error_code_from(response)
354
- return if success_from(response)
355
+ def error_code_from(response, action)
356
+ return if success_from(response, action)
355
357
  return response['last_transaction']['acquirer_return_code'] if response['last_transaction']
356
358
 
357
359
  STANDARD_ERROR_CODE[:processing_error]
@@ -206,7 +206,8 @@ module ActiveMerchant #:nodoc:
206
206
  post[:stored_credential_indicator] = 'stored'
207
207
  else
208
208
  post[:stored_credential_indicator] = 'used'
209
- post[:initial_transaction_id] = stored_credential[:network_transaction_id]
209
+ # should only send :initial_transaction_id if it is a MIT
210
+ post[:initial_transaction_id] = stored_credential[:network_transaction_id] if post[:initiated_by] == 'merchant'
210
211
  end
211
212
  end
212
213