mangopay 3.0.30.1 → 3.0.35

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -1
  3. data/CHANGELOG.md +36 -0
  4. data/README.md +5 -5
  5. data/lib/generators/mangopay/install_generator.rb +1 -1
  6. data/lib/generators/templates/mangopay.rb.erb +1 -1
  7. data/lib/mangopay.rb +36 -13
  8. data/lib/mangopay/authorization_token.rb +2 -2
  9. data/lib/mangopay/bank_account.rb +11 -0
  10. data/lib/mangopay/card.rb +15 -0
  11. data/lib/mangopay/filter_parameters.rb +6 -1
  12. data/lib/mangopay/http_calls.rb +7 -7
  13. data/lib/mangopay/legal_user.rb +0 -8
  14. data/lib/mangopay/mandate.rb +10 -0
  15. data/lib/mangopay/pay_in.rb +31 -0
  16. data/lib/mangopay/pay_out.rb +11 -0
  17. data/lib/mangopay/refund.rb +11 -0
  18. data/lib/mangopay/transfer.rb +11 -0
  19. data/lib/mangopay/ubo.rb +26 -0
  20. data/lib/mangopay/ubo_declaration.rb +22 -11
  21. data/lib/mangopay/user.rb +18 -7
  22. data/lib/mangopay/version.rb +1 -1
  23. data/spec/mangopay/bank_account_spec.rb +8 -0
  24. data/spec/mangopay/card_registration_spec.rb +7 -0
  25. data/spec/mangopay/client_spec.rb +17 -6
  26. data/spec/mangopay/configuration_spec.rb +53 -1
  27. data/spec/mangopay/event_spec.rb +12 -10
  28. data/spec/mangopay/mandate_spec.rb +7 -0
  29. data/spec/mangopay/payin_applepay_direct_spec.rb +20 -0
  30. data/spec/mangopay/payin_bankwire_external_instruction_spec.rb +43 -4
  31. data/spec/mangopay/payin_card_web_spec.rb +17 -0
  32. data/spec/mangopay/payin_paypal_web_spec.rb +13 -1
  33. data/spec/mangopay/payout_bankwire_spec.rb +8 -0
  34. data/spec/mangopay/preauthorization_spec.rb +9 -0
  35. data/spec/mangopay/refund_spec.rb +13 -0
  36. data/spec/mangopay/shared_resources.rb +346 -240
  37. data/spec/mangopay/transfer_spec.rb +8 -0
  38. data/spec/mangopay/ubo_declaration_spec.rb +13 -17
  39. data/spec/mangopay/ubo_spec.rb +39 -0
  40. data/spec/mangopay/user_spec.rb +34 -16
  41. data/spec/spec_helper.rb +1 -1
  42. metadata +9 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6346db992adff682f58586a8435d12bf7bb0ff9811927b4084c476264dc326d0
4
- data.tar.gz: 9cb2ff496c327154aea0b02f5672efa697f2ee139730ec7710bbe481d068e353
3
+ metadata.gz: da86f380ab29a146392b6ba79b5d5518f9dc898c5a22abadfbdfc1e3a93443bc
4
+ data.tar.gz: 6a938c0de24f0018ae2c6f0ab4f52ef01334f73a6131aea02b1d5affff317540
5
5
  SHA512:
6
- metadata.gz: 16c654da6df6533750d9d432139098596e281cd346634a75ff10106dbf90394e700b7d793fc3b48456a1766b616bd0e186a36390deafe4e4553ded25ebb28ae8
7
- data.tar.gz: c0d2989975569aab32456edba096693bfc8f2f3aa2b376b44a37135686087d3390b083c0fa08a974078d653712f4917763854ffbea2d5cb61c1879510e1a5a30
6
+ metadata.gz: 112d5afb9ec05f57cf718598c4aebb92f0d5bb68e2647c49706c598e0a98b983067b023a78e7b78075951a160f23fb21c3e033b66c41de03cfe6676867d8276c
7
+ data.tar.gz: fd0b5fe4b3be196ca0969d8be3c71308b532d64f6d0710f8a2b379e797a0c05d648c8cdce1719f4d51bcfa7c7641324a418b877d5864e732974f1b65ab4706a7
@@ -1,6 +1,11 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0.0
3
+ - 2.0.0
4
+ - 2.1
5
+ - 2.2
6
+ - 2.3
7
+ - 2.4
8
+ - 2.5
4
9
  script: bundle exec rspec
5
10
  deploy:
6
11
  provider: rubygems
@@ -0,0 +1,36 @@
1
+ ## [3.0.35] - 2020-08-24
2
+ -> Improvement to Net::ReadTimeout handling
3
+ -> "User-agent" format in the headers changed, aligned to other assets 👤
4
+
5
+ ## [3.0.34] - 2020-06-25
6
+ ### Added
7
+ - This SDK is now GooglePay-ready ! Feel free to ask our lovely support for more infos about its activation.
8
+ - `UBODeclaration` is now directly available through its ID.
9
+ - If a bankwire is done from a UK bankaccount on one of your `BankingAlias`, you could find its `AccountNumber` on `GET /payins/` response
10
+ - You can now send a `Culture` parameter for Paypal PayIns. Thanks to it, payment page can be displayed in various languages.
11
+
12
+ ### Changed
13
+ - `PAYLINEV2`value for Payin Web has been added on `TemplateURLOptions` object. You now should use it instead of `PAYLINE` for page customization.
14
+
15
+ ### Fixed
16
+ - Missing filters parameters have been added
17
+ - You can now send headers in update requests
18
+ - Loggers have been enhanced
19
+
20
+ ## [3.0.33] - 2019-09-23
21
+ ### Added
22
+ - ApplePay `Payin` functions are now available. More info about activation to come in the following weeks...
23
+ ### Changed
24
+ - GET EMoney method now supports year and month parameters. More info on our [docs](https://docs.mangopay.com/endpoints/v2.01/user-emoney#e895_view-a-users-emoney)
25
+
26
+ ## [v3.0.32] - 2019-06-19
27
+ ### Added
28
+ - New UBO Declaration system (more info [here](https://docs.mangopay.com/endpoints/v2.01/ubo-declarations#e1024_the-ubo-declaration-object))
29
+ ### Changed
30
+ - Paypal buyer account email that has been used is now available for Payin Paypal
31
+ - Your `HeadquartersPhoneNumber` can now be updated for your client account directly from our API
32
+ ### BREAKING
33
+ - `APIKey` is now replacing `passphrase` property for credentials. You must update it by updating to 3.0.32 SDK version and upper ones.
34
+
35
+
36
+
data/README.md CHANGED
@@ -4,7 +4,7 @@ The gem for interacting with the version 2 of the Mangopay API.
4
4
  See the [API documentation](http://docs.mangopay.com/api-references/)
5
5
  for more details on the API.
6
6
 
7
- Tested on the following versions of Ruby: 1.9.2, 1.9.3, 2.0.0
7
+ Tested on the following versions of Ruby: 1.9.2, 1.9.3, 2.0.0 and 2.x up to 2.5
8
8
 
9
9
  ## NEWS
10
10
 
@@ -37,7 +37,7 @@ require 'mangopay'
37
37
  MangoPay.configure do |c|
38
38
  c.preproduction = true
39
39
  c.client_id = 'YOUR_CLIENT_ID'
40
- c.client_passphrase = 'YOUR_CLIENT_PASSWORD'
40
+ c.client_apiKey = 'YOUR_CLIENT_API_KEY'
41
41
  c.log_file = File.join('mypath', 'mangopay.log')
42
42
  c.http_timeout = 10000
43
43
  end
@@ -96,13 +96,13 @@ Along with each request, the rate limiting headers are automatically updated in
96
96
  MangoPay.ratelimit
97
97
 
98
98
  {
99
- :limit=>["74", "74", "75", "908"],
100
- :remaining=>["2226", "4426", "8725", "104692"],
99
+ :limit=>["74", "74", "75", "908"],
100
+ :remaining=>["2226", "4426", "8725", "104692"],
101
101
  :reset=>["1495615620", "1495616520", "1495618320", "1495701060"]
102
102
  }
103
103
  ```
104
104
 
105
- Read more about rate limiting on [our documetiation](https://docs.mangopay.com/guide/rate-limiting).
105
+ Read more about rate limiting on [our documentation](https://docs.mangopay.com/guide/rate-limiting).
106
106
 
107
107
  ### Log requests and responses
108
108
  You can easily enable logs by setting the ```log_file``` configuration option (see the section **configuration** above). If you don't want logs, remove the ```log_file``` line.
@@ -17,7 +17,7 @@ module Mangopay
17
17
  client = client_id_valid?
18
18
  remove_file 'config/initializers/mangopay.rb'
19
19
  @client_id = client_id
20
- @client_passphrase = client['Passphrase']
20
+ @client_apiKey = client['APIKey']
21
21
  template 'mangopay.rb.erb', 'config/initializers/mangopay.rb'
22
22
  rescue => e
23
23
  puts e.message
@@ -1,5 +1,5 @@
1
1
  MangoPay.configure do |c|
2
2
  c.preproduction = <%= options[:preproduction] %>
3
3
  c.client_id = '<%= @client_id %>'
4
- c.client_passphrase = '<%= @client_passphrase %>'
4
+ c.client_apiKey = '<%= @client_apiKey %>'
5
5
  end
@@ -41,14 +41,16 @@ module MangoPay
41
41
  autoload :BankingAliases, 'mangopay/bankingaliases'
42
42
  autoload :BankingAliasesIBAN, 'mangopay/bankingaliases_iban'
43
43
  autoload :UboDeclaration, 'mangopay/ubo_declaration'
44
+ autoload :Ubo, 'mangopay/ubo'
44
45
 
45
46
  # temporary
46
47
  autoload :Temp, 'mangopay/temp'
47
48
 
48
49
  class Configuration
49
50
  attr_accessor :preproduction, :root_url,
50
- :client_id, :client_passphrase,
51
- :temp_dir, :log_file, :http_timeout
51
+ :client_id, :client_apiKey,
52
+ :temp_dir, :log_file, :http_timeout,
53
+ :logger
52
54
 
53
55
  def preproduction
54
56
  @preproduction || false
@@ -140,6 +142,8 @@ module MangoPay
140
142
  do_request(http, req, uri)
141
143
  end
142
144
 
145
+ raise MangoPay::ResponseError.new(uri, '408', {'Message' => 'Request Timeout'}) if res.nil?
146
+
143
147
  # decode json data
144
148
  data = res.body.to_s.empty? ? {} : JSON.load(res.body.to_s)
145
149
 
@@ -157,7 +161,7 @@ module MangoPay
157
161
  limit: res['x-ratelimit'].split(", "),
158
162
  remaining: res['x-ratelimit-remaining'].split(", "),
159
163
  reset: res['x-ratelimit-reset'].split(", ")
160
- }
164
+ }
161
165
  end
162
166
 
163
167
  data
@@ -191,7 +195,7 @@ module MangoPay
191
195
  def request_headers
192
196
  auth_token = AuthorizationToken::Manager.get_token
193
197
  headers = {
194
- 'User-Agent' => "MANGOPAY V2 RubyBindings/#{VERSION}",
198
+ 'User-Agent' => "MangoPay V2 SDK Ruby Bindings #{VERSION}",
195
199
  'Authorization' => "#{auth_token['token_type']} #{auth_token['access_token']}",
196
200
  'Content-Type' => 'application/json'
197
201
  }
@@ -203,10 +207,10 @@ module MangoPay
203
207
  end
204
208
 
205
209
  def do_request(http, req, uri)
206
- if configuration.log_file.nil?
207
- do_request_without_log(http, req)
208
- else
210
+ if logs_required?
209
211
  do_request_with_log(http, req, uri)
212
+ else
213
+ do_request_without_log(http, req)
210
214
  end
211
215
  end
212
216
 
@@ -215,12 +219,23 @@ module MangoPay
215
219
  params = FilterParameters.request(req.body)
216
220
  line = "[#{Time.now.iso8601}] #{req.method.upcase} \"#{uri.to_s}\" #{params}"
217
221
  begin
218
- time = Benchmark.realtime { res = do_request_without_log(http, req) }
222
+ time = Benchmark.realtime {
223
+ begin
224
+ res = do_request_without_log(http, req)
225
+ rescue Net::ReadTimeout
226
+ res = nil
227
+ end
228
+ }
219
229
  res
220
230
  ensure
221
- params = FilterParameters.response(res.body)
222
231
  line = "#{log_severity(res)} #{line}"
223
- line += "\n [#{(time * 1000).round(1)}ms] #{res.code} #{params}\n"
232
+ if res.nil?
233
+ params = ''
234
+ line += "\n [#{(time * 1000).round(1)}ms] 408 Request Timeout #{params}\n"
235
+ else
236
+ params = FilterParameters.response(res.body)
237
+ line += "\n [#{(time * 1000).round(1)}ms] #{res.code} #{params}\n"
238
+ end
224
239
  logger.info { line }
225
240
  end
226
241
  end
@@ -235,15 +250,23 @@ module MangoPay
235
250
  end
236
251
 
237
252
  def logger
238
- raise NotImplementedError if configuration.log_file.nil?
239
- if @logger.nil?
253
+ raise NotImplementedError unless logs_required?
254
+ return @logger if @logger
255
+
256
+ if !configuration.logger.nil?
257
+ @logger = configuration.logger
258
+ elsif !configuration.log_file.nil?
240
259
  @logger = Logger.new(configuration.log_file)
241
- @logger.formatter = proc do |severity, datetime, progname, msg|
260
+ @logger.formatter = proc do |_, _, _, msg|
242
261
  "#{msg}\n"
243
262
  end
244
263
  end
264
+
245
265
  @logger
246
266
  end
247
267
 
268
+ def logs_required?
269
+ !configuration.log_file.nil? || !configuration.logger.nil?
270
+ end
248
271
  end
249
272
  end
@@ -19,7 +19,7 @@ module MangoPay
19
19
  if token.nil? || token['timestamp'].nil? || token['timestamp'] <= Time.now || token['environment_key'] != env_key
20
20
  token = MangoPay.request(:post, "/#{MangoPay.version_code}/oauth/token", {}, {}, {}, Proc.new do |req|
21
21
  cfg = MangoPay.configuration
22
- req.basic_auth cfg.client_id, cfg.client_passphrase
22
+ req.basic_auth cfg.client_id, cfg.client_apiKey
23
23
  req.body = 'grant_type=client_credentials'
24
24
  req.add_field('Content-Type', 'application/x-www-form-urlencoded')
25
25
  end)
@@ -32,7 +32,7 @@ module MangoPay
32
32
 
33
33
  def get_environment_key_for_token
34
34
  cfg = MangoPay.configuration
35
- key = "#{cfg.root_url}|#{cfg.client_id}|#{cfg.client_passphrase}"
35
+ key = "#{cfg.root_url}|#{cfg.client_id}|#{cfg.client_apiKey}"
36
36
  key = Digest::MD5.hexdigest(key)
37
37
  key
38
38
  end
@@ -26,6 +26,17 @@ module MangoPay
26
26
  MangoPay.request(:put, url(user_id, bank_account_id), params)
27
27
  end
28
28
 
29
+ # Fetches list of transactions belonging to given +bank_account_id+.
30
+ #
31
+ # Optional +filters+ is a hash accepting following keys:
32
+ # - +page+, +per_page+, +sort+: pagination and sorting params (see MangoPay::HTTPCalls::Fetch::ClassMethods#fetch)
33
+ # - +Status+: TransactionStatus {CREATED, SUCCEEDED, FAILED}
34
+ # - +ResultCode+: string representing the transaction result
35
+ def transactions(bank_account_id, filters = {})
36
+ url = "#{MangoPay.api_path}/bankaccounts/#{bank_account_id}/transactions"
37
+ MangoPay.request(:get, url, {}, filters)
38
+ end
39
+
29
40
  def url(user_id, bank_account_id = nil)
30
41
  if bank_account_id
31
42
  "#{MangoPay.api_path}/users/#{CGI.escape(user_id.to_s)}/bankaccounts/#{CGI.escape(bank_account_id.to_s)}"
@@ -17,9 +17,24 @@ module MangoPay
17
17
  MangoPay.request(:get, fingerprint_url(fingerprint), {}, filters)
18
18
  end
19
19
 
20
+ # Retrieves a list of transactions belonging to given +card_id+.
21
+ #
22
+ # Optional +filters+ is a hash accepting following keys:
23
+ # - +page+, +per_page+, +sort+: pagination and sorting params (see MangoPay::HTTPCalls::Fetch::ClassMethods#fetch)
24
+ # - +Status+: TransactionStatus {CREATED, SUCCEEDED, FAILED}
25
+ # - +ResultCode+: string representing the transaction result
26
+ def transactions(card_id, filters = {})
27
+ url = url(card_id) + '/transactions'
28
+ MangoPay.request(:get, url, {}, filters)
29
+ end
30
+
20
31
  def fingerprint_url(fingerprint)
21
32
  "#{MangoPay.api_path}/cards/fingerprints/#{fingerprint}"
22
33
  end
34
+
35
+ def get_pre_authorizations(card_id, filters = {})
36
+ MangoPay.request(:get, "#{MangoPay.api_path}/cards/#{card_id}/preauthorizations")
37
+ end
23
38
  end
24
39
  end
25
40
  end
@@ -13,7 +13,12 @@ module MangoPay
13
13
 
14
14
  def self.response(body)
15
15
  return '' if body.to_s.empty?
16
- body = JSON.load(body)
16
+
17
+ begin
18
+ body = JSON.load(body)
19
+ rescue MultiJson::LoadError => e
20
+ return body
21
+ end
17
22
  filter_hash(body, res_confidential_params)
18
23
  JSON.dump(body)
19
24
  end
@@ -21,8 +21,8 @@ module MangoPay
21
21
 
22
22
  module Update
23
23
  module ClassMethods
24
- def update(id = nil, params = {})
25
- MangoPay.request(:put, url(id), params)
24
+ def update(id = nil, params = {}, idempotency_key = nil)
25
+ MangoPay.request(:put, url(id), params, {}, idempotency_key)
26
26
  end
27
27
  end
28
28
 
@@ -35,20 +35,20 @@ module MangoPay
35
35
  module ClassMethods
36
36
 
37
37
  # - Fetching _single_entity_ by +id+:
38
- #
38
+ #
39
39
  # MangoPay::User.fetch("user-id") # => {"FirstName"=>"Mango", "LastName"=>"Pay", ...}
40
- #
40
+ #
41
41
  # - or fetching _multiple_entities_ with _optional_ +filters+ hash,
42
42
  # including _pagination_ and _sorting_ params
43
43
  # +page+, +per_page+, +sort+ (see http://docs.mangopay.com/api-references/pagination/):
44
- #
44
+ #
45
45
  # MangoPay::User.fetch() # => [{...}, ...]: list of user data hashes (10 per page by default)
46
46
  # MangoPay::User.fetch({'page' => 2, 'per_page' => 3}) # => list of 3 hashes from 2nd page
47
47
  # MangoPay::BankAccount.fetch(user_id, {'sort' => 'CreationDate:desc'}) # => bank accounts by user, sorting by date descending (with default pagination)
48
48
  # MangoPay::BankAccount.fetch(user_id, {'sort' => 'CreationDate:desc', 'page' => 2, 'per_page' => 3}) # both sorting and pagination params provided
49
- #
49
+ #
50
50
  # - For paginated queries the +filters+ param will be supplemented by +total_pages+ and +total_items+ info:
51
- #
51
+ #
52
52
  # MangoPay::User.fetch(filter = {'page' => 2, 'per_page' => 3})
53
53
  # filter # => {"page"=>2, "per_page"=>3, "total_pages"=>1969, "total_items"=>5905}
54
54
  #
@@ -13,14 +13,6 @@ module MangoPay
13
13
  end
14
14
 
15
15
  class << self
16
-
17
- # Create a UBO declaration.
18
- # @param +user_id+ ID of the legal user owning the declaration
19
- # @param +ubo_declaration+ Object containing UBO declaration data
20
- # @return Newly-created UBO declaration entity data
21
- def create_ubo_declaration(user_id, ubo_declaration)
22
- MangoPay.request(:post, "#{url(user_id)}/ubodeclarations", ubo_declaration)
23
- end
24
16
  end
25
17
  end
26
18
  end
@@ -27,6 +27,16 @@ module MangoPay
27
27
  MangoPay.request(:get, url, {}, filters)
28
28
  end
29
29
 
30
+ # Fetches list of transactions belonging to given +mandate_id+.
31
+ #
32
+ # Optional +filters+ is a hash accepting following keys:
33
+ # - +page+, +per_page+, +sort+: pagination and sorting params (see MangoPay::HTTPCalls::Fetch::ClassMethods#fetch)
34
+ # - +Status+: TransactionStatus {CREATED, SUCCEEDED, FAILED}
35
+ # - +ResultCode+: string representing the transaction result
36
+ def transactions(mandate_id, filters = {})
37
+ url = url(mandate_id) + '/transactions'
38
+ MangoPay.request(:get, url, {}, filters)
39
+ end
30
40
  end
31
41
  end
32
42
  end
@@ -5,6 +5,17 @@ module MangoPay
5
5
  include HTTPCalls::Fetch
6
6
  include HTTPCalls::Refund
7
7
 
8
+ # Fetches list of refunds belonging to the given +pay_in_id+.
9
+ #
10
+ # Optional +filters+ is a hash accepting following keys:
11
+ # - +page+, +per_page+, +sort+: pagination and sorting params (see MangoPay::HTTPCalls::Fetch::ClassMethods#fetch)
12
+ # - +Status+: TransactionStatus {CREATED, SUCCEEDED, FAILED}
13
+ # - +ResultCode+: string representing the transaction result
14
+ def self.refunds(pay_in_id, filters = {})
15
+ url = url(pay_in_id) + '/refunds'
16
+ MangoPay.request(:get, url, {}, filters)
17
+ end
18
+
8
19
  module Card
9
20
 
10
21
  # See http://docs.mangopay.com/api-references/payins/payins-card-web/
@@ -95,5 +106,25 @@ module MangoPay
95
106
 
96
107
  end
97
108
 
109
+ module ApplePay
110
+ class Direct < Resource
111
+ include HTTPCalls::Create
112
+
113
+ def self.url(*)
114
+ "#{MangoPay.api_path}/payins/applepay/#{CGI.escape(class_name.downcase)}"
115
+ end
116
+ end
117
+ end
118
+
119
+ module GooglePay
120
+ class Direct < Resource
121
+ include HTTPCalls::Create
122
+
123
+ def self.url(*)
124
+ "#{MangoPay.api_path}/payins/googlepay/#{CGI.escape(class_name.downcase)}"
125
+ end
126
+ end
127
+ end
128
+
98
129
  end
99
130
  end
@@ -2,6 +2,17 @@ module MangoPay
2
2
  class PayOut < Resource
3
3
  include HTTPCalls::Fetch
4
4
 
5
+ # Fetches list of refunds belonging to the given +pay_out_id+.
6
+ #
7
+ # Optional +filters+ is a hash accepting following keys:
8
+ # - +page+, +per_page+, +sort+: pagination and sorting params (see MangoPay::HTTPCalls::Fetch::ClassMethods#fetch)
9
+ # - +Status+: TransactionStatus {CREATED, SUCCEEDED, FAILED}
10
+ # - +ResultCode+: string representing the transaction result
11
+ def self.refunds(pay_out_id, filters = {})
12
+ url = url(pay_out_id) + '/refunds'
13
+ MangoPay.request(:get, url, {}, filters)
14
+ end
15
+
5
16
  # See http://docs.mangopay.com/api-references/pay-out-bank-wire/
6
17
  class BankWire < Resource
7
18
  include HTTPCalls::Create
@@ -3,5 +3,16 @@ module MangoPay
3
3
  # See http://docs.mangopay.com/api-references/refund/
4
4
  class Refund < Resource
5
5
  include HTTPCalls::Fetch
6
+
7
+ # Fetches list of refunds belonging to given +repudiation_id+
8
+ #
9
+ # Optional +filters+ is a hash accepting following keys:
10
+ # - +page+, +per_page+, +sort+: pagination and sorting params (see MangoPay::HTTPCalls::Fetch::ClassMethods#fetch)
11
+ # - +Status+: TransactionStatus {CREATED, SUCCEEDED, FAILED}
12
+ # - +ResultCode+: string representing the transaction result
13
+ def self.of_repudiation(repudiation_id, filters = {})
14
+ url = "#{MangoPay.api_path}/repudiations/#{repudiation_id}/refunds"
15
+ MangoPay.request(:get, url, {}, filters)
16
+ end
6
17
  end
7
18
  end