activemerchant 1.126.0 → 1.129.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +241 -0
  3. data/lib/active_merchant/billing/check.rb +40 -8
  4. data/lib/active_merchant/billing/credit_card.rb +28 -1
  5. data/lib/active_merchant/billing/credit_card_methods.rb +79 -23
  6. data/lib/active_merchant/billing/gateways/adyen.rb +67 -8
  7. data/lib/active_merchant/billing/gateways/airwallex.rb +40 -11
  8. data/lib/active_merchant/billing/gateways/alelo.rb +256 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -4
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +18 -0
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -1
  12. data/lib/active_merchant/billing/gateways/bogus.rb +4 -0
  13. data/lib/active_merchant/billing/gateways/borgun.rb +56 -16
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +64 -17
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +27 -9
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +23 -0
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +228 -57
  18. data/lib/active_merchant/billing/gateways/commerce_hub.rb +361 -0
  19. data/lib/active_merchant/billing/gateways/credorax.rb +47 -27
  20. data/lib/active_merchant/billing/gateways/cyber_source/cyber_source_common.rb +36 -0
  21. data/lib/active_merchant/billing/gateways/cyber_source.rb +100 -26
  22. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +456 -0
  23. data/lib/active_merchant/billing/gateways/d_local.rb +44 -5
  24. data/lib/active_merchant/billing/gateways/decidir.rb +15 -4
  25. data/lib/active_merchant/billing/gateways/ebanx.rb +36 -24
  26. data/lib/active_merchant/billing/gateways/element.rb +21 -1
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +73 -22
  28. data/lib/active_merchant/billing/gateways/ipg.rb +13 -8
  29. data/lib/active_merchant/billing/gateways/iveri.rb +39 -3
  30. data/lib/active_merchant/billing/gateways/kushki.rb +21 -1
  31. data/lib/active_merchant/billing/gateways/litle.rb +25 -5
  32. data/lib/active_merchant/billing/gateways/mastercard.rb +1 -8
  33. data/lib/active_merchant/billing/gateways/mercado_pago.rb +17 -0
  34. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +44 -10
  35. data/lib/active_merchant/billing/gateways/monei.rb +2 -0
  36. data/lib/active_merchant/billing/gateways/moneris.rb +20 -5
  37. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -0
  38. data/lib/active_merchant/billing/gateways/ogone.rb +35 -7
  39. data/lib/active_merchant/billing/gateways/openpay.rb +20 -3
  40. data/lib/active_merchant/billing/gateways/orbital.rb +43 -22
  41. data/lib/active_merchant/billing/gateways/pay_trace.rb +64 -18
  42. data/lib/active_merchant/billing/gateways/payeezy.rb +59 -4
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +18 -6
  44. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
  45. data/lib/active_merchant/billing/gateways/paysafe.rb +22 -14
  46. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -0
  47. data/lib/active_merchant/billing/gateways/plexo.rb +308 -0
  48. data/lib/active_merchant/billing/gateways/priority.rb +29 -6
  49. data/lib/active_merchant/billing/gateways/rapyd.rb +110 -49
  50. data/lib/active_merchant/billing/gateways/reach.rb +277 -0
  51. data/lib/active_merchant/billing/gateways/redsys.rb +9 -5
  52. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/securion_pay.rb +40 -0
  54. data/lib/active_merchant/billing/gateways/shift4.rb +342 -0
  55. data/lib/active_merchant/billing/gateways/simetrik.rb +28 -22
  56. data/lib/active_merchant/billing/gateways/stripe.rb +21 -1
  57. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +62 -22
  58. data/lib/active_merchant/billing/gateways/tns.rb +2 -5
  59. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  60. data/lib/active_merchant/billing/gateways/trust_commerce.rb +14 -3
  61. data/lib/active_merchant/billing/gateways/vanco.rb +12 -3
  62. data/lib/active_merchant/billing/gateways/visanet_peru.rb +1 -1
  63. data/lib/active_merchant/billing/gateways/vpos.rb +7 -4
  64. data/lib/active_merchant/billing/gateways/wompi.rb +8 -4
  65. data/lib/active_merchant/billing/gateways/worldpay.rb +117 -9
  66. data/lib/active_merchant/billing/response.rb +15 -1
  67. data/lib/active_merchant/connection.rb +0 -2
  68. data/lib/active_merchant/country.rb +1 -0
  69. data/lib/active_merchant/errors.rb +4 -1
  70. data/lib/active_merchant/version.rb +1 -1
  71. metadata +24 -3
@@ -33,7 +33,14 @@ module ActiveMerchant #:nodoc:
33
33
  store: 'customer/create',
34
34
  redact: 'customer/delete',
35
35
  level_3_visa: 'level_three/visa',
36
- level_3_mastercard: 'level_three/mastercard'
36
+ level_3_mastercard: 'level_three/mastercard',
37
+ ach_sale: 'checks/sale/by_account',
38
+ ach_customer_sale: 'checks/sale/by_customer',
39
+ ach_authorize: 'checks/hold/by_account',
40
+ ach_customer_authorize: 'checks/hold/by_customer',
41
+ ach_refund: 'checks/refund/by_transaction',
42
+ ach_capture: 'checks/manage/fund',
43
+ ach_void: 'checks/manage/void'
37
44
  }
38
45
 
39
46
  def initialize(options = {})
@@ -52,7 +59,15 @@ module ActiveMerchant #:nodoc:
52
59
  end
53
60
  else
54
61
  post = build_purchase_request(money, payment_or_customer_id, options)
55
- post[:customer_id] ? endpoint = ENDPOINTS[:customer_id_sale] : endpoint = ENDPOINTS[:keyed_sale]
62
+ endpoint = if payment_or_customer_id.kind_of?(Check)
63
+ ENDPOINTS[:ach_sale]
64
+ elsif options[:check_transaction]
65
+ ENDPOINTS[:ach_customer_sale]
66
+ elsif post[:customer_id]
67
+ ENDPOINTS[:customer_id_sale]
68
+ else
69
+ ENDPOINTS[:keyed_sale]
70
+ end
56
71
  response = commit(endpoint, post)
57
72
  check_token_response(response, endpoint, post, options)
58
73
  end
@@ -63,12 +78,16 @@ module ActiveMerchant #:nodoc:
63
78
  add_amount(post, money, options)
64
79
  if customer_id?(payment_or_customer_id)
65
80
  post[:customer_id] = payment_or_customer_id
66
- endpoint = ENDPOINTS[:customer_id_auth]
81
+ endpoint = if options[:check_transaction]
82
+ ENDPOINTS[:ach_customer_authorize]
83
+ else
84
+ ENDPOINTS[:customer_id_auth]
85
+ end
67
86
  else
68
87
  add_payment(post, payment_or_customer_id)
69
88
  add_address(post, payment_or_customer_id, options)
70
89
  add_customer_data(post, options)
71
- endpoint = ENDPOINTS[:keyed_auth]
90
+ endpoint = payment_or_customer_id.kind_of?(Check) ? ENDPOINTS[:ach_authorize] : ENDPOINTS[:keyed_auth]
72
91
  end
73
92
  response = commit(endpoint, post)
74
93
  check_token_response(response, endpoint, post, options)
@@ -82,8 +101,13 @@ module ActiveMerchant #:nodoc:
82
101
  end
83
102
  else
84
103
  post = build_capture_request(money, authorization, options)
85
- response = commit(ENDPOINTS[:capture], post)
86
- check_token_response(response, ENDPOINTS[:capture], post, options)
104
+ endpoint = if options[:check_transaction]
105
+ ENDPOINTS[:ach_capture]
106
+ else
107
+ ENDPOINTS[:capture]
108
+ end
109
+ response = commit(endpoint, post)
110
+ check_token_response(response, endpoint, post, options)
87
111
  end
88
112
  end
89
113
 
@@ -91,17 +115,29 @@ module ActiveMerchant #:nodoc:
91
115
  # currently only support full and partial refunds of settled transactions via a transaction ID
92
116
  post = {}
93
117
  add_amount(post, money, options)
94
- post[:transaction_id] = authorization
95
- response = commit(ENDPOINTS[:transaction_refund], post)
96
- check_token_response(response, ENDPOINTS[:transaction_refund], post, options)
118
+ if options[:check_transaction]
119
+ post[:check_transaction_id] = authorization
120
+ endpoint = ENDPOINTS[:ach_refund]
121
+ else
122
+ post[:transaction_id] = authorization
123
+ endpoint = ENDPOINTS[:transaction_refund]
124
+ end
125
+ response = commit(endpoint, post)
126
+ check_token_response(response, endpoint, post, options)
97
127
  end
98
128
 
99
129
  def void(authorization, options = {})
100
130
  post = {}
101
- post[:transaction_id] = authorization
131
+ if options[:check_transaction]
132
+ post[:check_transaction_id] = authorization
133
+ endpoint = ENDPOINTS[:ach_void]
134
+ else
135
+ post[:transaction_id] = authorization
136
+ endpoint = ENDPOINTS[:transaction_void]
137
+ end
102
138
 
103
- response = commit(ENDPOINTS[:transaction_void], post)
104
- check_token_response(response, ENDPOINTS[:transaction_void], post, options)
139
+ response = commit(endpoint, post)
140
+ check_token_response(response, endpoint, post, options)
105
141
  end
106
142
 
107
143
  def verify(credit_card, options = {})
@@ -175,7 +211,11 @@ module ActiveMerchant #:nodoc:
175
211
 
176
212
  def build_capture_request(money, authorization, options)
177
213
  post = {}
178
- post[:transaction_id] = authorization
214
+ if options[:check_transaction]
215
+ post[:check_transaction_id] = authorization
216
+ else
217
+ post[:transaction_id] = authorization
218
+ end
179
219
  add_amount(post, money, options)
180
220
 
181
221
  post
@@ -234,10 +274,16 @@ module ActiveMerchant #:nodoc:
234
274
  end
235
275
 
236
276
  def add_payment(post, payment)
237
- post[:credit_card] = {}
238
- post[:credit_card][:number] = payment.number
239
- post[:credit_card][:expiration_month] = payment.month
240
- post[:credit_card][:expiration_year] = payment.year
277
+ if payment.kind_of?(Check)
278
+ post[:check] = {}
279
+ post[:check][:account_number] = payment.account_number
280
+ post[:check][:routing_number] = payment.routing_number
281
+ else
282
+ post[:credit_card] = {}
283
+ post[:credit_card][:number] = payment.number
284
+ post[:credit_card][:expiration_month] = payment.month
285
+ post[:credit_card][:expiration_year] = payment.year
286
+ end
241
287
  end
242
288
 
243
289
  def add_level_3_data(post, options)
@@ -380,7 +426,7 @@ module ActiveMerchant #:nodoc:
380
426
  if action == ENDPOINTS[:store]
381
427
  response['customer_id']
382
428
  else
383
- response['transaction_id']
429
+ response['transaction_id'] || response['check_transaction_id']
384
430
  end
385
431
  end
386
432
 
@@ -41,6 +41,7 @@ module ActiveMerchant
41
41
  add_soft_descriptors(params, options)
42
42
  add_level2_data(params, options)
43
43
  add_stored_credentials(params, options)
44
+ add_external_three_ds(params, payment_method, options)
44
45
 
45
46
  commit(params, options)
46
47
  end
@@ -56,6 +57,7 @@ module ActiveMerchant
56
57
  add_soft_descriptors(params, options)
57
58
  add_level2_data(params, options)
58
59
  add_stored_credentials(params, options)
60
+ add_external_three_ds(params, payment_method, options)
59
61
 
60
62
  commit(params, options)
61
63
  end
@@ -125,6 +127,7 @@ module ActiveMerchant
125
127
  gsub(%r((Apikey: )(\w|-)+), '\1[FILTERED]').
126
128
  gsub(%r((\\?"card_number\\?":\\?")\d+), '\1[FILTERED]').
127
129
  gsub(%r((\\?"cvv\\?":\\?")\d+), '\1[FILTERED]').
130
+ gsub(%r((\\?"cvv\\?":\\?)\d+), '\1[FILTERED]').
128
131
  gsub(%r((\\?"account_number\\?":\\?")\d+), '\1[FILTERED]').
129
132
  gsub(%r((\\?"routing_number\\?":\\?")\d+), '\1[FILTERED]').
130
133
  gsub(%r((\\?card_number=)\d+(&?)), '\1[FILTERED]').
@@ -132,11 +135,33 @@ module ActiveMerchant
132
135
  gsub(%r((\\?apikey=)\w+(&?)), '\1[FILTERED]').
133
136
  gsub(%r{(\\?"credit_card\.card_number\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
134
137
  gsub(%r{(\\?"credit_card\.cvv\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
135
- gsub(%r{(\\?"apikey\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
138
+ gsub(%r{(\\?"apikey\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
139
+ gsub(%r{(\\?"cavv\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
140
+ gsub(%r{(\\?"xid\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
136
141
  end
137
142
 
138
143
  private
139
144
 
145
+ def add_external_three_ds(params, payment_method, options)
146
+ return unless three_ds = options[:three_d_secure]
147
+
148
+ params[:'3DS'] = {
149
+ program_protocol: three_ds[:version][0],
150
+ directory_server_transaction_id: three_ds[:ds_transaction_id],
151
+ cardholder_name: payment_method.name,
152
+ card_number: payment_method.number,
153
+ exp_date: format_exp_date(payment_method.month, payment_method.year),
154
+ cvv: payment_method.verification_value,
155
+ xid: three_ds[:acs_transaction_id],
156
+ cavv: three_ds[:cavv],
157
+ wallet_provider_id: 'NO_WALLET',
158
+ type: 'D'
159
+ }.compact
160
+
161
+ params[:eci_indicator] = options[:three_d_secure][:eci]
162
+ params[:method] = '3DS'
163
+ end
164
+
140
165
  def add_invoice(params, options)
141
166
  params[:merchant_ref] = options[:order_id]
142
167
  end
@@ -152,6 +177,8 @@ module ActiveMerchant
152
177
  def add_authorization_info(params, authorization, options = {})
153
178
  transaction_id, transaction_tag, method, = authorization.split('|')
154
179
  params[:method] = method == 'token' ? 'credit_card' : method
180
+ # If the previous transaction `method` value was 3DS, it needs to be set to `credit_card` on follow up transactions
181
+ params[:method] = 'credit_card' if method == '3DS'
155
182
 
156
183
  if options[:reversal_id]
157
184
  params[:reversal_id] = options[:reversal_id]
@@ -178,6 +205,8 @@ module ActiveMerchant
178
205
  add_echeck(params, payment_method, options)
179
206
  elsif payment_method.is_a? String
180
207
  add_token(params, payment_method, options)
208
+ elsif payment_method.is_a? NetworkTokenizationCreditCard
209
+ add_network_tokenization(params, payment_method, options)
181
210
  else
182
211
  add_creditcard(params, payment_method)
183
212
  end
@@ -190,7 +219,7 @@ module ActiveMerchant
190
219
  tele_check[:check_type] = 'P'
191
220
  tele_check[:routing_number] = echeck.routing_number
192
221
  tele_check[:account_number] = echeck.account_number
193
- tele_check[:accountholder_name] = "#{echeck.first_name} #{echeck.last_name}"
222
+ tele_check[:accountholder_name] = name_from_payment_method(echeck)
194
223
  tele_check[:customer_id_type] = options[:customer_id_type] if options[:customer_id_type]
195
224
  tele_check[:customer_id_number] = options[:customer_id_number] if options[:customer_id_number]
196
225
  tele_check[:client_email] = options[:client_email] if options[:client_email]
@@ -233,10 +262,37 @@ module ActiveMerchant
233
262
  card
234
263
  end
235
264
 
265
+ def add_network_tokenization(params, payment_method, options)
266
+ nt_card = {}
267
+ nt_card[:type] = 'D'
268
+ nt_card[:cardholder_name] = name_from_payment_method(payment_method) || name_from_address(options)
269
+ nt_card[:card_number] = payment_method.number
270
+ nt_card[:exp_date] = format_exp_date(payment_method.month, payment_method.year)
271
+ nt_card[:cvv] = payment_method.verification_value
272
+ nt_card[:xid] = payment_method.payment_cryptogram unless payment_method.payment_cryptogram.empty? || payment_method.brand.include?('american_express')
273
+ nt_card[:cavv] = payment_method.payment_cryptogram unless payment_method.payment_cryptogram.empty?
274
+ nt_card[:wallet_provider_id] = 'APPLE_PAY'
275
+
276
+ params['3DS'] = nt_card
277
+ params[:method] = '3DS'
278
+ params[:eci_indicator] = payment_method.eci.nil? ? '5' : payment_method.eci
279
+ end
280
+
236
281
  def format_exp_date(month, year)
237
282
  "#{format(month, :two_digits)}#{format(year, :two_digits)}"
238
283
  end
239
284
 
285
+ def name_from_address(options)
286
+ return unless address = options[:billing_address]
287
+ return address[:name] if address[:name]
288
+ end
289
+
290
+ def name_from_payment_method(payment_method)
291
+ return unless payment_method.first_name && payment_method.last_name
292
+
293
+ return "#{payment_method.first_name} #{payment_method.last_name}"
294
+ end
295
+
240
296
  def add_address(params, options)
241
297
  address = options[:billing_address]
242
298
  return unless address
@@ -279,8 +335,7 @@ module ActiveMerchant
279
335
  end
280
336
 
281
337
  def original_transaction_id(options)
282
- return options[:cardbrand_original_transaction_id] if options[:cardbrand_original_transaction_id]
283
- return options[:stored_credential][:network_transaction_id] if options.dig(:stored_credential, :network_transaction_id)
338
+ return options[:cardbrand_original_transaction_id] || options.dig(:stored_credential, :network_transaction_id)
284
339
  end
285
340
 
286
341
  def initiator(options)
@@ -137,6 +137,10 @@ module ActiveMerchant #:nodoc:
137
137
  commit_card('delete', post)
138
138
  end
139
139
 
140
+ def inquire(authorization, options = {})
141
+ commit_transaction('inquire', authorization)
142
+ end
143
+
140
144
  def supports_scrubbing?
141
145
  true
142
146
  end
@@ -232,12 +236,20 @@ module ActiveMerchant #:nodoc:
232
236
  end
233
237
 
234
238
  def commit_raw(object, action, parameters)
235
- url = "#{(test? ? test_url : live_url)}#{object}/#{action}"
236
-
237
- begin
238
- raw_response = ssl_post(url, post_data(parameters), headers)
239
- rescue ResponseError => e
240
- raw_response = e.response.body
239
+ if action == 'inquire'
240
+ url = "#{(test? ? test_url : live_url)}#{object}/#{parameters}"
241
+ begin
242
+ raw_response = ssl_get(url, headers)
243
+ rescue ResponseError => e
244
+ raw_response = e.response.body
245
+ end
246
+ else
247
+ url = "#{(test? ? test_url : live_url)}#{object}/#{action}"
248
+ begin
249
+ raw_response = ssl_post(url, post_data(parameters), headers)
250
+ rescue ResponseError => e
251
+ raw_response = e.response.body
252
+ end
241
253
  end
242
254
 
243
255
  begin
@@ -13,6 +13,10 @@ module ActiveMerchant #:nodoc:
13
13
  (@params['PaymentDetails']||{})
14
14
  end
15
15
 
16
+ def checkout_status
17
+ (@params['CheckoutStatus']||{})
18
+ end
19
+
16
20
  def name
17
21
  payer = (info['PayerName']||{})
18
22
  [payer['FirstName'], payer['MiddleName'], payer['LastName']].compact.join(' ')
@@ -17,14 +17,8 @@ module ActiveMerchant #:nodoc:
17
17
 
18
18
  def purchase(money, payment, options = {})
19
19
  post = {}
20
- add_invoice(post, money, options)
21
- add_payment(post, payment)
22
- add_billing_address(post, options)
23
- add_merchant_details(post, options)
20
+ add_auth_purchase_params(post, money, payment, options)
24
21
  add_airline_travel_details(post, options)
25
- add_customer_data(post, payment, options) unless payment.is_a?(String)
26
- add_three_d_secure(post, payment, options) if options[:three_d_secure]
27
- add_stored_credential(post, options) if options[:stored_credential]
28
22
  add_split_pay_details(post, options)
29
23
  post[:settleWithAuth] = true
30
24
 
@@ -33,13 +27,7 @@ module ActiveMerchant #:nodoc:
33
27
 
34
28
  def authorize(money, payment, options = {})
35
29
  post = {}
36
- add_invoice(post, money, options)
37
- add_payment(post, payment)
38
- add_billing_address(post, options)
39
- add_merchant_details(post, options)
40
- add_customer_data(post, payment, options) unless payment.is_a?(String)
41
- add_three_d_secure(post, payment, options) if options[:three_d_secure]
42
- add_stored_credential(post, options) if options[:stored_credential]
30
+ add_auth_purchase_params(post, money, payment, options)
43
31
 
44
32
  commit(:post, 'auths', post, options)
45
33
  end
@@ -111,6 +99,17 @@ module ActiveMerchant #:nodoc:
111
99
 
112
100
  private
113
101
 
102
+ def add_auth_purchase_params(post, money, payment, options)
103
+ add_invoice(post, money, options)
104
+ add_payment(post, payment)
105
+ add_billing_address(post, options)
106
+ add_merchant_details(post, options)
107
+ add_customer_data(post, payment, options) unless payment.is_a?(String)
108
+ add_three_d_secure(post, payment, options) if options[:three_d_secure]
109
+ add_stored_credential(post, options) if options[:stored_credential]
110
+ add_funding_transaction(post, options)
111
+ end
112
+
114
113
  # Customer data can be included in transactions where the payment method is a credit card
115
114
  # but should not be sent when the payment method is a token
116
115
  def add_customer_data(post, creditcard, options)
@@ -286,6 +285,15 @@ module ActiveMerchant #:nodoc:
286
285
  post[:splitpay] = split_pay
287
286
  end
288
287
 
288
+ def add_funding_transaction(post, options)
289
+ return unless options[:funding_transaction]
290
+
291
+ post[:fundingTransaction] = {}
292
+ post[:fundingTransaction][:type] = options[:funding_transaction]
293
+ post[:profile] ||= {}
294
+ post[:profile][:merchantCustomerId] = options[:customer_id] || SecureRandom.hex(12)
295
+ end
296
+
289
297
  def add_stored_credential(post, options)
290
298
  return unless options[:stored_credential]
291
299
 
@@ -313,6 +313,9 @@ module ActiveMerchant #:nodoc:
313
313
  def add_extra_parameters(post, options)
314
314
  extra_parameters = {}
315
315
  extra_parameters[:INSTALLMENTS_NUMBER] = options[:installments_number] || 1
316
+ extra_parameters[:EXTRA1] = options[:extra_1] if options[:extra_1]
317
+ extra_parameters[:EXTRA2] = options[:extra_2] if options[:extra_2]
318
+ extra_parameters[:EXTRA3] = options[:extra_3] if options[:extra_3]
316
319
  post[:transaction][:extraParameters] = extra_parameters
317
320
  end
318
321