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
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module Resources
6
+ # Resource for a Sandbox App.
7
+ class SandboxApp < Base
8
+ def app_token
9
+ body[:appToken]
10
+ end
11
+
12
+ private
13
+
14
+ def create_resource(attributes)
15
+ request.post("sandboxapps", options, attributes)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Tikkie
7
+ module Api
8
+ # Parses and wraps the response from the Tikkie API.
9
+ class Response
10
+ attr_reader :response, :body
11
+
12
+ def initialize(response)
13
+ @response = response
14
+ @body = response.body ? parse_body(response.body) : {}
15
+ end
16
+
17
+ def success?
18
+ http_code == 200 || http_code == 201 || http_code == 204
19
+ end
20
+
21
+ def error?
22
+ !success?
23
+ end
24
+
25
+ def invalid?
26
+ body.nil?
27
+ end
28
+
29
+ def request_uri
30
+ response.uri
31
+ end
32
+
33
+ def http_code
34
+ response.code.to_i
35
+ end
36
+
37
+ def http_message
38
+ response.message
39
+ end
40
+
41
+ def errors
42
+ @errors ||= begin
43
+ errors = []
44
+
45
+ if body[:errors]
46
+ body[:errors].each do |error|
47
+ errors << Tikkie::Api::Resources::Error.new(error)
48
+ end
49
+ end
50
+
51
+ errors
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def parse_body(body)
58
+ JSON.parse(body, symbolize_names: true)
59
+ rescue JSON::ParserError
60
+ nil
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module V1
6
+ # Access token that can be used to make API calls.
7
+ class AccessToken
8
+ attr_accessor :token
9
+
10
+ def initialize(token, expires_in)
11
+ @token = token
12
+ @expires_at = Time.now + expires_in.to_i
13
+ end
14
+
15
+ def expired?
16
+ Time.now > @expires_at
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'jwt'
5
+ require 'net/http'
6
+ require 'uri'
7
+
8
+ module Tikkie
9
+ module Api
10
+ module V1
11
+ # Provides authentication for the ABN AMRO OAuth API.
12
+ # see https://developer.abnamro.com/get-started#authentication
13
+ class Authentication
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ def authenticate
19
+ uri = URI.parse(File.join(@config.api_url, "/oauth/token"))
20
+
21
+ request = Net::HTTP::Post.new(uri)
22
+ request["Api-Key"] = @config.api_key
23
+
24
+ request.set_form_data(
25
+ client_assertion: jwt_token,
26
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
27
+ grant_type: "client_credentials",
28
+ scope: "tikkie"
29
+ )
30
+
31
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
32
+ http.request(request)
33
+ end
34
+
35
+ if response.is_a?(Net::HTTPSuccess)
36
+ json = JSON.parse(response.body, symbolize_names: true)
37
+
38
+ Tikkie::Api::V1::AccessToken.new(json[:access_token], json[:expires_in])
39
+ else
40
+ raise Tikkie::Api::V1::AuthenticationException, response
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def jwt_token
47
+ now = Time.now.to_i
48
+
49
+ payload = {
50
+ nbf: now - 120,
51
+ exp: now + 120, # Token is valid for 2 minutes
52
+ iss: "Ruby Tikkie client",
53
+ sub: @config.api_key,
54
+ aud: @config.oauth_token_url
55
+ }
56
+
57
+ # Send header typ as String, not symbol (JWT version 1.x adds "typ" as String by default).
58
+ headers = {
59
+ "typ" => "JWT"
60
+ }
61
+
62
+ JWT.encode(payload, @config.private_data, @config.jwt_hashing_algorithm, headers)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module V1
6
+ # Tikkie API client.
7
+ class Client
8
+ def initialize(config)
9
+ @request = Tikkie::Api::V1::Request.new(config)
10
+ end
11
+
12
+ def platforms
13
+ Tikkie::Api::V1::Requests::Platforms.new(@request)
14
+ end
15
+
16
+ def users
17
+ Tikkie::Api::V1::Requests::Users.new(@request)
18
+ end
19
+
20
+ def payment_requests
21
+ Tikkie::Api::V1::Requests::PaymentRequests.new(@request)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module V1
6
+ # Tikkie API Configuration. An API Key and private key are mandatory.
7
+ # see https://developer.abnamro.com/get-started
8
+ class Configuration
9
+ SANDBOX_API_URL = "https://api-sandbox.abnamro.com/v1/"
10
+ PRODUCTION_API_URL = "https://api.abnamro.com/v1/"
11
+
12
+ SANDBOX_OAUTH_TOKEN_URL = "https://auth-sandbox.abnamro.com/oauth/token"
13
+ PRODUCTION_OAUTH_TOKEN_URL = "https://auth.abnamro.com/oauth/token"
14
+
15
+ DEFAULT_HASHING_ALGORITHM = "RS256"
16
+ VALID_HASHING_ALGORITHMS = %w[RS256 RS384 RS512].freeze
17
+
18
+ attr_reader :api_key, :private_key, :options
19
+
20
+ def initialize(api_key, private_key, options = {})
21
+ @api_key = api_key
22
+ @private_key = private_key
23
+ @options = options
24
+ end
25
+
26
+ def private_data
27
+ unless File.exist?(@private_key)
28
+ raise Tikkie::Api::V1::Exception, "Private key does not exist: #{@private_key}"
29
+ end
30
+
31
+ OpenSSL::PKey::RSA.new(File.read(@private_key))
32
+ end
33
+
34
+ def jwt_hashing_algorithm
35
+ if @options[:hashing_algorithm]
36
+ unless VALID_HASHING_ALGORITHMS.include?(@options[:hashing_algorithm])
37
+ raise Tikkie::Api::V1::Exception, "Invalid hashing algorithm provided: #{@options[:hashing_algorithm]} (expected: #{VALID_HASHING_ALGORITHMS.join(', ')})"
38
+ end
39
+
40
+ @options[:hashing_algorithm]
41
+ else
42
+ DEFAULT_HASHING_ALGORITHM
43
+ end
44
+ end
45
+
46
+ def api_url
47
+ if @options[:test]
48
+ SANDBOX_API_URL
49
+ else
50
+ PRODUCTION_API_URL
51
+ end
52
+ end
53
+
54
+ def oauth_token_url
55
+ if @options[:test]
56
+ SANDBOX_OAUTH_TOKEN_URL
57
+ else
58
+ PRODUCTION_OAUTH_TOKEN_URL
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module V1
6
+ # Tikkie base exception.
7
+ class Exception < RuntimeError
8
+ end
9
+
10
+ # Exception when the authentication fails.
11
+ class AuthenticationException < Tikkie::Api::V1::Exception
12
+ attr_reader :response, :body
13
+
14
+ def initialize(response)
15
+ @response = response
16
+ @body = response.body
17
+
18
+ message = "Authentication failure at Tikkie"
19
+ super(message)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Tikkie
7
+ module Api
8
+ module V1
9
+ # Make authenticated HTTP requests to the Tikkie API.
10
+ class Request
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def get(path, params = {})
16
+ uri = URI.parse(File.join(@config.api_url, path))
17
+ uri.query = URI.encode_www_form(params) unless params.empty?
18
+
19
+ request = Net::HTTP::Get.new(uri)
20
+ request["Api-Key"] = @config.api_key
21
+ request["Authorization"] = "Bearer #{access_token}"
22
+
23
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
24
+ http.request(request)
25
+ end
26
+
27
+ response
28
+ end
29
+
30
+ def post(path, params = {})
31
+ uri = URI.parse(File.join(@config.api_url, path))
32
+
33
+ request = Net::HTTP::Post.new(uri)
34
+ request["Api-Key"] = @config.api_key
35
+ request["Authorization"] = "Bearer #{access_token}"
36
+ request["Content-Type"] = "application/json"
37
+ request.body = params.to_json
38
+
39
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
40
+ http.request(request)
41
+ end
42
+
43
+ response
44
+ end
45
+
46
+ private
47
+
48
+ def access_token
49
+ if @access_token.nil? || @access_token.expired?
50
+ @authentication ||= Tikkie::Api::V1::Authentication.new(@config)
51
+ @access_token = @authentication.authenticate
52
+ end
53
+
54
+ @access_token.token
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module Tikkie
6
+ module Api
7
+ module V1
8
+ module Requests
9
+ # Payment requests operations at Tikkie.
10
+ class PaymentRequests
11
+ def initialize(request)
12
+ @request = request
13
+ end
14
+
15
+ def list(platform_token, user_token, options = {})
16
+ offset = options[:offset] || 0
17
+ limit = options[:limit] || 20
18
+ from_date = options[:from_date]
19
+ to_date = options[:to_date]
20
+
21
+ params = { offset: offset, limit: limit }
22
+ params[:fromDate] = from_date.respond_to?(:utc) ? from_date.utc.iso8601 : from_date if from_date
23
+ params[:toDate] = to_date.respond_to?(:utc) ? to_date.utc.iso8601 : to_date if to_date
24
+
25
+ response = @request.get("/tikkie/platforms/#{platform_token}/users/#{user_token}/paymentrequests", params)
26
+ Tikkie::Api::V1::Responses::PaymentRequests.new(response, offset: offset, limit: limit)
27
+ end
28
+
29
+ def get(platform_token, user_token, payment_request_token)
30
+ response = @request.get("/tikkie/platforms/#{platform_token}/users/#{user_token}/paymentrequests/#{payment_request_token}")
31
+
32
+ Tikkie::Api::V1::Responses::PaymentRequest.new(response)
33
+ end
34
+
35
+ def create(platform_token, user_token, bank_account_token, options = {})
36
+ params = {
37
+ currency: options.fetch(:currency),
38
+ description: options.fetch(:description)
39
+ }
40
+ params[:amountInCents] = to_cents(options[:amount]) if options.key?(:amount)
41
+ params[:externalId] = options[:external_id] if options.key?(:external_id)
42
+
43
+ response = @request.post("/tikkie/platforms/#{platform_token}/users/#{user_token}/bankaccounts/#{bank_account_token}/paymentrequests", params)
44
+
45
+ Tikkie::Api::V1::Responses::PaymentRequestCreated.new(response)
46
+ end
47
+
48
+ private
49
+
50
+ def to_cents(amount)
51
+ decimal = BigDecimal(amount.to_s)
52
+ decimal *= 100 # to cents
53
+ decimal.to_i
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tikkie
4
+ module Api
5
+ module V1
6
+ module Requests
7
+ # Platforms operations at Tikkie.
8
+ class Platforms
9
+ def initialize(request)
10
+ @request = request
11
+ end
12
+
13
+ def list
14
+ response = @request.get("/tikkie/platforms")
15
+ Tikkie::Api::V1::Responses::Platforms.new(response)
16
+ end
17
+
18
+ def create(options = {})
19
+ params = {
20
+ name: options.fetch(:name),
21
+ phoneNumber: options.fetch(:phone_number),
22
+ platformUsage: options.fetch(:platform_usage),
23
+ email: options[:email],
24
+ notificationUrl: options[:notification_url]
25
+ }
26
+ response = @request.post("/tikkie/platforms", params)
27
+
28
+ Tikkie::Api::V1::Responses::Platform.new(response)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end