tikkie-api 0.1.0 → 2.0.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 (84) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +31 -7
  3. data/.travis.yml +8 -4
  4. data/Gemfile +3 -1
  5. data/Gemfile.lock +39 -33
  6. data/LICENSE.txt +1 -1
  7. data/README.md +211 -61
  8. data/lib/tikkie/api.rb +52 -25
  9. data/lib/tikkie/api/amount.rb +35 -0
  10. data/lib/tikkie/api/client.rb +16 -8
  11. data/lib/tikkie/api/clients/base.rb +16 -0
  12. data/lib/tikkie/api/clients/payment_requests.rb +25 -0
  13. data/lib/tikkie/api/clients/payment_requests_subscription.rb +20 -0
  14. data/lib/tikkie/api/clients/payments.rb +20 -0
  15. data/lib/tikkie/api/clients/refunds.rb +20 -0
  16. data/lib/tikkie/api/clients/sandbox_apps.rb +15 -0
  17. data/lib/tikkie/api/configuration.rb +8 -47
  18. data/lib/tikkie/api/exception.rb +28 -8
  19. data/lib/tikkie/api/request.rb +51 -27
  20. data/lib/tikkie/api/resources/base.rb +66 -0
  21. data/lib/tikkie/api/{responses → resources}/error.rb +11 -9
  22. data/lib/tikkie/api/resources/list.rb +52 -0
  23. data/lib/tikkie/api/resources/payment.rb +68 -0
  24. data/lib/tikkie/api/resources/payment_request.rb +97 -0
  25. data/lib/tikkie/api/resources/payment_requests.rb +40 -0
  26. data/lib/tikkie/api/resources/payment_requests_subscription.rb +24 -0
  27. data/lib/tikkie/api/resources/payments.rb +48 -0
  28. data/lib/tikkie/api/resources/refund.rb +71 -0
  29. data/lib/tikkie/api/resources/sandbox_app.rb +20 -0
  30. data/lib/tikkie/api/response.rb +64 -0
  31. data/lib/tikkie/api/v1/access_token.rb +21 -0
  32. data/lib/tikkie/api/v1/authentication.rb +67 -0
  33. data/lib/tikkie/api/v1/client.rb +26 -0
  34. data/lib/tikkie/api/v1/configuration.rb +64 -0
  35. data/lib/tikkie/api/v1/exception.rb +24 -0
  36. data/lib/tikkie/api/v1/request.rb +59 -0
  37. data/lib/tikkie/api/v1/requests/payment_requests.rb +59 -0
  38. data/lib/tikkie/api/v1/requests/platforms.rb +34 -0
  39. data/lib/tikkie/api/v1/requests/users.rb +33 -0
  40. data/lib/tikkie/api/v1/responses/bank_account.rb +24 -0
  41. data/lib/tikkie/api/v1/responses/base.rb +69 -0
  42. data/lib/tikkie/api/v1/responses/error.rb +36 -0
  43. data/lib/tikkie/api/v1/responses/pagination.rb +22 -0
  44. data/lib/tikkie/api/v1/responses/payment.rb +50 -0
  45. data/lib/tikkie/api/v1/responses/payment_request.rb +68 -0
  46. data/lib/tikkie/api/v1/responses/payment_request_created.rb +24 -0
  47. data/lib/tikkie/api/v1/responses/payment_requests.rb +44 -0
  48. data/lib/tikkie/api/v1/responses/platform.rb +46 -0
  49. data/lib/tikkie/api/v1/responses/platforms.rb +34 -0
  50. data/lib/tikkie/api/v1/responses/user.rb +43 -0
  51. data/lib/tikkie/api/v1/responses/users.rb +38 -0
  52. data/lib/tikkie/api/v1/types/payment_request_status.rb +17 -0
  53. data/lib/tikkie/api/v1/types/payment_status.rb +16 -0
  54. data/lib/tikkie/api/v1/types/platform_status.rb +14 -0
  55. data/lib/tikkie/api/v1/types/platform_usage.rb +14 -0
  56. data/lib/tikkie/api/v1/types/user_status.rb +14 -0
  57. data/lib/tikkie/api/version.rb +1 -1
  58. data/lib/tikkie/notification.rb +23 -0
  59. data/lib/tikkie/notifications/bundle_notification.rb +28 -0
  60. data/lib/tikkie/notifications/payment_notification.rb +32 -0
  61. data/lib/tikkie/notifications/refund_notification.rb +36 -0
  62. data/tikkie-api.gemspec +4 -3
  63. metadata +69 -44
  64. data/lib/tikkie/api/access_token.rb +0 -19
  65. data/lib/tikkie/api/authentication.rb +0 -60
  66. data/lib/tikkie/api/requests/payment_requests.rb +0 -56
  67. data/lib/tikkie/api/requests/platforms.rb +0 -32
  68. data/lib/tikkie/api/requests/users.rb +0 -31
  69. data/lib/tikkie/api/responses/bank_account.rb +0 -22
  70. data/lib/tikkie/api/responses/base.rb +0 -53
  71. data/lib/tikkie/api/responses/pagination.rb +0 -20
  72. data/lib/tikkie/api/responses/payment.rb +0 -48
  73. data/lib/tikkie/api/responses/payment_request.rb +0 -64
  74. data/lib/tikkie/api/responses/payment_request_created.rb +0 -22
  75. data/lib/tikkie/api/responses/payment_requests.rb +0 -42
  76. data/lib/tikkie/api/responses/platform.rb +0 -44
  77. data/lib/tikkie/api/responses/platforms.rb +0 -30
  78. data/lib/tikkie/api/responses/user.rb +0 -39
  79. data/lib/tikkie/api/responses/users.rb +0 -34
  80. data/lib/tikkie/api/types/payment_request_status.rb +0 -15
  81. data/lib/tikkie/api/types/payment_status.rb +0 -14
  82. data/lib/tikkie/api/types/platform_status.rb +0 -12
  83. data/lib/tikkie/api/types/platform_usage.rb +0 -12
  84. data/lib/tikkie/api/types/user_status.rb +0 -12
@@ -1,32 +1,59 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "tikkie/api/access_token"
4
- require "tikkie/api/authentication"
3
+ require "tikkie/api/version"
4
+
5
+ require "tikkie/api/amount"
5
6
  require "tikkie/api/client"
6
7
  require "tikkie/api/configuration"
7
8
  require "tikkie/api/exception"
8
9
  require "tikkie/api/request"
9
- require "tikkie/api/version"
10
+ require "tikkie/api/response"
11
+
12
+ require "tikkie/api/clients/base"
13
+ require "tikkie/api/clients/payment_requests"
14
+ require "tikkie/api/clients/payment_requests_subscription"
15
+ require "tikkie/api/clients/payments"
16
+ require "tikkie/api/clients/refunds"
17
+ require "tikkie/api/clients/sandbox_apps"
18
+
19
+ require "tikkie/api/resources/base"
20
+ require "tikkie/api/resources/error"
21
+ require "tikkie/api/resources/list"
22
+ require "tikkie/api/resources/payment_request"
23
+ require "tikkie/api/resources/payment_requests"
24
+ require "tikkie/api/resources/payment_requests_subscription"
25
+ require "tikkie/api/resources/payment"
26
+ require "tikkie/api/resources/payments"
27
+ require "tikkie/api/resources/refund"
28
+ require "tikkie/api/resources/sandbox_app"
29
+
30
+ # Below the deprecated Tikkie v1 API code which will be removed after 01-01-2021.
31
+ require "tikkie/api/v1/access_token"
32
+ require "tikkie/api/v1/authentication"
33
+ require "tikkie/api/v1/client"
34
+ require "tikkie/api/v1/configuration"
35
+ require "tikkie/api/v1/exception"
36
+ require "tikkie/api/v1/request"
37
+
38
+ require "tikkie/api/v1/requests/payment_requests"
39
+ require "tikkie/api/v1/requests/platforms"
40
+ require "tikkie/api/v1/requests/users"
41
+
42
+ require "tikkie/api/v1/responses/base"
43
+ require "tikkie/api/v1/responses/bank_account"
44
+ require "tikkie/api/v1/responses/error"
45
+ require "tikkie/api/v1/responses/pagination"
46
+ require "tikkie/api/v1/responses/payment_request_created"
47
+ require "tikkie/api/v1/responses/payment_request"
48
+ require "tikkie/api/v1/responses/payment_requests"
49
+ require "tikkie/api/v1/responses/payment"
50
+ require "tikkie/api/v1/responses/platform"
51
+ require "tikkie/api/v1/responses/platforms"
52
+ require "tikkie/api/v1/responses/user"
53
+ require "tikkie/api/v1/responses/users"
10
54
 
11
- require "tikkie/api/requests/payment_requests"
12
- require "tikkie/api/requests/platforms"
13
- require "tikkie/api/requests/users"
14
-
15
- require "tikkie/api/responses/base"
16
- require "tikkie/api/responses/bank_account"
17
- require "tikkie/api/responses/error"
18
- require "tikkie/api/responses/pagination"
19
- require "tikkie/api/responses/payment_request_created"
20
- require "tikkie/api/responses/payment_request"
21
- require "tikkie/api/responses/payment_requests"
22
- require "tikkie/api/responses/payment"
23
- require "tikkie/api/responses/platform"
24
- require "tikkie/api/responses/platforms"
25
- require "tikkie/api/responses/user"
26
- require "tikkie/api/responses/users"
27
-
28
- require "tikkie/api/types/payment_request_status"
29
- require "tikkie/api/types/payment_status"
30
- require "tikkie/api/types/platform_status"
31
- require "tikkie/api/types/platform_usage"
32
- require "tikkie/api/types/user_status"
55
+ require "tikkie/api/v1/types/payment_request_status"
56
+ require "tikkie/api/v1/types/payment_status"
57
+ require "tikkie/api/v1/types/platform_status"
58
+ require "tikkie/api/v1/types/platform_usage"
59
+ require "tikkie/api/v1/types/user_status"
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module Tikkie
6
+ module Api
7
+ # Helper for converting amounts to cents and back.
8
+ class Amount
9
+ class << self
10
+ def from_cents(cents)
11
+ amount = BigDecimal(cents) / 100
12
+ new(amount)
13
+ end
14
+ end
15
+
16
+ def initialize(amount)
17
+ @amount = BigDecimal(amount.to_s)
18
+ end
19
+
20
+ def to_d
21
+ @amount
22
+ end
23
+
24
+ # Convert the amount to a String with 2 decimals.
25
+ def to_s
26
+ format("%.2f", @amount)
27
+ end
28
+
29
+ def to_cents
30
+ cents = @amount * 100
31
+ cents.to_i
32
+ end
33
+ end
34
+ end
35
+ end
@@ -4,20 +4,28 @@ module Tikkie
4
4
  module Api
5
5
  # Tikkie API client.
6
6
  class Client
7
- def initialize(config)
8
- @request = Tikkie::Api::Request.new(config)
7
+ def initialize(api_key:, app_token: nil, sandbox: false)
8
+ @config = Tikkie::Api::Configuration.new(api_key: api_key, app_token: app_token, sandbox: sandbox)
9
9
  end
10
10
 
11
- def platforms
12
- Tikkie::Api::Requests::Platforms.new(@request)
11
+ def payment_requests_subscription
12
+ Tikkie::Api::Clients::PaymentRequestsSubscription.new(@config)
13
13
  end
14
14
 
15
- def users
16
- Tikkie::Api::Requests::Users.new(@request)
15
+ def payment_requests
16
+ Tikkie::Api::Clients::PaymentRequests.new(@config)
17
17
  end
18
18
 
19
- def payment_requests
20
- Tikkie::Api::Requests::PaymentRequests.new(@request)
19
+ def payments
20
+ Tikkie::Api::Clients::Payments.new(@config)
21
+ end
22
+
23
+ def refunds
24
+ Tikkie::Api::Clients::Refunds.new(@config)
25
+ end
26
+
27
+ def sandbox_apps
28
+ Tikkie::Api::Clients::SandboxApps.new(@config)
21
29
  end
22
30
  end
23
31
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Clients
6
+ # Base class for all clients.
7
+ class Base
8
+ attr_reader :config
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Clients
6
+ # Payment Request endpoint at Tikkie.
7
+ class PaymentRequests < Base
8
+ def list(options = {})
9
+ payment_requests = Tikkie::Api::Resources::PaymentRequests.new(config, options)
10
+ payment_requests.load
11
+ end
12
+
13
+ def get(payment_request_token, options = {})
14
+ payment_request = Tikkie::Api::Resources::PaymentRequest.new(config, options.merge(payment_request_token: payment_request_token))
15
+ payment_request.load
16
+ end
17
+
18
+ def create(attributes = {}, options = {})
19
+ payment_request = Tikkie::Api::Resources::PaymentRequest.new(config, options)
20
+ payment_request.save(attributes)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Clients
6
+ # Payment Request Subscription endpoint at Tikkie.
7
+ class PaymentRequestsSubscription < Base
8
+ def create(attributes = {}, options = {})
9
+ payment_request = Tikkie::Api::Resources::PaymentRequestsSubscription.new(config, options)
10
+ payment_request.save(attributes)
11
+ end
12
+
13
+ def delete(options = {})
14
+ payment_request = Tikkie::Api::Resources::PaymentRequestsSubscription.new(config, options)
15
+ payment_request.delete
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Clients
6
+ # Payment endpoint at Tikkie.
7
+ class Payments < Base
8
+ def list(payment_request_token, options = {})
9
+ payments = Tikkie::Api::Resources::Payments.new(config, options.merge(payment_request_token: payment_request_token))
10
+ payments.load
11
+ end
12
+
13
+ def get(payment_request_token, payment_token, options = {})
14
+ payment = Tikkie::Api::Resources::Payment.new(config, options.merge(payment_request_token: payment_request_token, payment_token: payment_token))
15
+ payment.load
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Clients
6
+ # Refund endpoint at Tikkie.
7
+ class Refunds < Base
8
+ def get(payment_request_token, payment_token, refund_token, options = {})
9
+ payment = Tikkie::Api::Resources::Refund.new(config, options.merge(payment_request_token: payment_request_token, payment_token: payment_token, refund_token: refund_token))
10
+ payment.load
11
+ end
12
+
13
+ def create(payment_request_token, payment_token, attributes = {}, options = {})
14
+ payment = Tikkie::Api::Resources::Refund.new(config, options.merge(payment_request_token: payment_request_token, payment_token: payment_token))
15
+ payment.save(attributes)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Clients
6
+ # Sandbox App endpoint at Tikkie.
7
+ class SandboxApps < Base
8
+ def create(attributes = {}, options = {})
9
+ payment = Tikkie::Api::Resources::SandboxApp.new(config, options)
10
+ payment.save(attributes)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,60 +2,21 @@
2
2
 
3
3
  module Tikkie
4
4
  module Api
5
- # Tikkie API Configuration. An API Key and private key are mandatory.
6
- # see https://developer.abnamro.com/get-started
5
+ # Tikkie API configuration.
7
6
  class Configuration
8
- SANDBOX_API_URL = "https://api-sandbox.abnamro.com/v1/"
9
- PRODUCTION_API_URL = "https://api.abnamro.com/v1/"
7
+ SANDBOX_API_URL = "https://api-sandbox.abnamro.com/v2/tikkie/"
8
+ PRODUCTION_API_URL = "https://api.abnamro.com/v2/tikkie/"
10
9
 
11
- SANDBOX_OAUTH_TOKEN_URL = "https://auth-sandbox.abnamro.com/oauth/token"
12
- PRODUCTION_OAUTH_TOKEN_URL = "https://auth.abnamro.com/oauth/token"
10
+ attr_reader :api_key, :app_token, :sandbox
13
11
 
14
- DEFAULT_HASHING_ALGORITHM = "RS256"
15
- VALID_HASHING_ALGORITHMS = %w[RS256 RS384 RS512].freeze
16
-
17
- attr_reader :api_key, :private_key, :options
18
-
19
- def initialize(api_key, private_key, options = {})
12
+ def initialize(api_key:, app_token: nil, sandbox: false)
20
13
  @api_key = api_key
21
- @private_key = private_key
22
- @options = options
23
- end
24
-
25
- def private_data
26
- unless File.exist?(@private_key)
27
- raise Tikkie::Api::Exception, "Private key does not exist: #{@private_key}"
28
- end
29
-
30
- OpenSSL::PKey::RSA.new(File.read(@private_key))
31
- end
32
-
33
- def jwt_hashing_algorithm
34
- if @options[:hashing_algorithm]
35
- unless VALID_HASHING_ALGORITHMS.include?(@options[:hashing_algorithm])
36
- raise Tikkie::Api::Exception, "Invalid hashing algorithm provided: #{@options[:hashing_algorithm]} (expected: #{VALID_HASHING_ALGORITHMS.join(', ')})"
37
- end
38
-
39
- @options[:hashing_algorithm]
40
- else
41
- DEFAULT_HASHING_ALGORITHM
42
- end
14
+ @app_token = app_token
15
+ @sandbox = sandbox
43
16
  end
44
17
 
45
18
  def api_url
46
- if @options[:test]
47
- SANDBOX_API_URL
48
- else
49
- PRODUCTION_API_URL
50
- end
51
- end
52
-
53
- def oauth_token_url
54
- if @options[:test]
55
- SANDBOX_OAUTH_TOKEN_URL
56
- else
57
- PRODUCTION_OAUTH_TOKEN_URL
58
- end
19
+ @sandbox ? SANDBOX_API_URL : PRODUCTION_API_URL
59
20
  end
60
21
  end
61
22
  end
@@ -2,20 +2,40 @@
2
2
 
3
3
  module Tikkie
4
4
  module Api
5
- # Tikkie base exception.
6
- class Exception < RuntimeError
5
+ # Generic Tikkie Exception.
6
+ class Exception < StandardError
7
7
  end
8
8
 
9
- # Exception when the authentication fails.
10
- class AuthenticationException < Tikkie::Api::Exception
11
- attr_reader :response, :body
9
+ # Exception when an HTTP request fails.
10
+ class RequestError < Tikkie::Api::Exception
11
+ attr_accessor :response
12
12
 
13
13
  def initialize(response)
14
14
  @response = response
15
- @body = response.body
15
+ end
16
+
17
+ def errors
18
+ response.errors
19
+ end
20
+
21
+ def request_uri
22
+ response.request_uri
23
+ end
24
+
25
+ def http_code
26
+ response.http_code
27
+ end
28
+
29
+ def http_message
30
+ response.http_message
31
+ end
32
+
33
+ def messages
34
+ errors.map(&:message)
35
+ end
16
36
 
17
- message = "Authentication failure at Tikkie"
18
- super(message)
37
+ def to_s
38
+ "#{http_code} #{http_message}: #{messages.join(', ')}"
19
39
  end
20
40
  end
21
41
  end
@@ -2,55 +2,79 @@
2
2
 
3
3
  require 'net/http'
4
4
  require 'uri'
5
+ require 'logger'
5
6
 
6
7
  module Tikkie
7
8
  module Api
8
9
  # Make authenticated HTTP requests to the Tikkie API.
9
10
  class Request
11
+ attr_reader :config
12
+
10
13
  def initialize(config)
11
14
  @config = config
12
15
  end
13
16
 
14
17
  def get(path, params = {})
15
- uri = URI.parse(File.join(@config.api_url, path))
16
- uri.query = URI.encode_www_form(params) unless params.empty?
17
-
18
- request = Net::HTTP::Get.new(uri)
19
- request["Api-Key"] = @config.api_key
20
- request["Authorization"] = "Bearer #{access_token}"
18
+ request(:get, path, params)
19
+ end
21
20
 
22
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
23
- http.request(request)
24
- end
21
+ def post(path, params = {}, body = {})
22
+ request(:post, path, params, body)
23
+ end
25
24
 
26
- response
25
+ def delete(path, params = {})
26
+ request(:delete, path, params)
27
27
  end
28
28
 
29
- def post(path, params = {})
30
- uri = URI.parse(File.join(@config.api_url, path))
29
+ private
31
30
 
32
- request = Net::HTTP::Post.new(uri)
33
- request["Api-Key"] = @config.api_key
34
- request["Authorization"] = "Bearer #{access_token}"
35
- request["Content-Type"] = "application/json"
36
- request.body = params.to_json
31
+ def request(http_method, path, params = {}, body = {})
32
+ uri = URI.join(config.api_url, path)
33
+ uri.query = URI.encode_www_form(params) unless params.empty?
37
34
 
38
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
39
- http.request(request)
35
+ logger.debug("[Tikkie] Request: #{http_method.upcase} #{uri}") if ENV['TIKKIE_DEBUG']
36
+
37
+ case http_method
38
+ when :get
39
+ request = Net::HTTP::Get.new(uri)
40
+ when :post
41
+ request = Net::HTTP::Post.new(uri)
42
+ request.body = body.to_json
43
+ when :delete
44
+ request = Net::HTTP::Delete.new(uri)
45
+ else
46
+ raise Tikkie::Api::Exception, "Invalid HTTP method: #{http_method}"
40
47
  end
41
48
 
42
- response
43
- end
49
+ request["Accept"] = "application/json"
50
+ request["Content-Type"] = "application/json"
51
+ request["Api-Key"] = config.api_key
52
+ request["X-App-Token"] = config.app_token if config.app_token
53
+ request["User-Agent"] = "Ruby tikkie-api/#{Tikkie::Api::VERSION}"
44
54
 
45
- private
55
+ client = Net::HTTP.new(uri.hostname, uri.port)
56
+ client.use_ssl = uri.scheme == "https"
57
+ client.verify_mode = OpenSSL::SSL::VERIFY_PEER
46
58
 
47
- def access_token
48
- if @access_token.nil? || @access_token.expired?
49
- @authentication ||= Tikkie::Api::Authentication.new(@config)
50
- @access_token = @authentication.authenticate
59
+ begin
60
+ response = Tikkie::Api::Response.new(client.request(request))
61
+ # Try to catch some common exceptions Net::HTTP might raise.
62
+ rescue Errno::ETIMEDOUT, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
63
+ IOError, SocketError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::OpenTimeout,
64
+ Net::ProtocolError, Net::ReadTimeout, OpenSSL::SSL::SSLError => e
65
+ raise Tikkie::Api::Exception, e.message
51
66
  end
52
67
 
53
- @access_token.token
68
+ logger.debug("[Tikkie] Response: #{response.http_code}, body: #{response.body}") if ENV['TIKKIE_DEBUG']
69
+
70
+ raise Tikkie::Api::Exception, "Invalid payload" if response.invalid?
71
+ raise Tikkie::Api::RequestError, response if response.error?
72
+
73
+ response
74
+ end
75
+
76
+ def logger
77
+ @logger ||= Logger.new(STDOUT)
54
78
  end
55
79
  end
56
80
  end