partner_api 0.11.2

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 (77) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/release.yml +18 -0
  3. data/.github/workflows/ruby.yml +28 -0
  4. data/.gitignore +18 -0
  5. data/.rspec +4 -0
  6. data/CHANGELOG.md +105 -0
  7. data/Gemfile +10 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +85 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +17 -0
  12. data/bin/setup +8 -0
  13. data/docs/anz_api.md +67 -0
  14. data/docs/bnz_api.md +103 -0
  15. data/docs/fab_api.md +206 -0
  16. data/docs/gemini_api.md +184 -0
  17. data/docs/vma_api.md +167 -0
  18. data/docs/westpac_api.md +106 -0
  19. data/lib/anz_api/client.rb +24 -0
  20. data/lib/anz_api/endpoint.rb +29 -0
  21. data/lib/anz_api/endpoints/fetch_jwk.rb +35 -0
  22. data/lib/anz_api/failure_response.rb +33 -0
  23. data/lib/anz_api.rb +15 -0
  24. data/lib/bnz_api/client.rb +45 -0
  25. data/lib/bnz_api/configuration.rb +21 -0
  26. data/lib/bnz_api/endpoint.rb +56 -0
  27. data/lib/bnz_api/endpoints/fetch_id_token.rb +73 -0
  28. data/lib/bnz_api/endpoints/fetch_jwk.rb +35 -0
  29. data/lib/bnz_api/failure_response.rb +33 -0
  30. data/lib/bnz_api/httpx.rb +75 -0
  31. data/lib/bnz_api.rb +18 -0
  32. data/lib/fab_api/client.rb +36 -0
  33. data/lib/fab_api/configuration.rb +26 -0
  34. data/lib/fab_api/endpoint.rb +70 -0
  35. data/lib/fab_api/endpoints/deliver_email.rb +56 -0
  36. data/lib/fab_api/endpoints/deliver_sms.rb +51 -0
  37. data/lib/fab_api/endpoints/exchange_token.rb +49 -0
  38. data/lib/fab_api/endpoints/invalidate_token.rb +53 -0
  39. data/lib/fab_api/endpoints/refresh_token.rb +60 -0
  40. data/lib/fab_api/failure_response.rb +39 -0
  41. data/lib/fab_api/utils/id.rb +13 -0
  42. data/lib/fab_api.rb +18 -0
  43. data/lib/gemini_api/address.rb +23 -0
  44. data/lib/gemini_api/balance.rb +24 -0
  45. data/lib/gemini_api/client.rb +37 -0
  46. data/lib/gemini_api/endpoint.rb +62 -0
  47. data/lib/gemini_api/endpoints/create_address_request.rb +39 -0
  48. data/lib/gemini_api/endpoints/get_available_balances.rb +39 -0
  49. data/lib/gemini_api/endpoints/view_approved_addresses.rb +40 -0
  50. data/lib/gemini_api/endpoints/view_transfers.rb +49 -0
  51. data/lib/gemini_api/endpoints/withdraw_crypto_fund.rb +49 -0
  52. data/lib/gemini_api/failure_response.rb +47 -0
  53. data/lib/gemini_api/transaction.rb +14 -0
  54. data/lib/gemini_api/transfer.rb +44 -0
  55. data/lib/gemini_api.rb +16 -0
  56. data/lib/partner_api/endpoints/base.rb +152 -0
  57. data/lib/partner_api/errors.rb +18 -0
  58. data/lib/partner_api/utils/hash.rb +21 -0
  59. data/lib/partner_api/utils/read_cert.rb +38 -0
  60. data/lib/vma_api/client.rb +33 -0
  61. data/lib/vma_api/configuration.rb +24 -0
  62. data/lib/vma_api/endpoint.rb +29 -0
  63. data/lib/vma_api/endpoints/access_token.rb +60 -0
  64. data/lib/vma_api/endpoints/client_credentials.rb +60 -0
  65. data/lib/vma_api/endpoints/refresh_token.rb +60 -0
  66. data/lib/vma_api/endpoints/revoke_token.rb +55 -0
  67. data/lib/vma_api/failure_response.rb +42 -0
  68. data/lib/vma_api.rb +20 -0
  69. data/lib/westpac_api/client.rb +29 -0
  70. data/lib/westpac_api/configuration.rb +28 -0
  71. data/lib/westpac_api/endpoint.rb +26 -0
  72. data/lib/westpac_api/endpoints/fetch_jwk.rb +33 -0
  73. data/lib/westpac_api/endpoints/fetch_user.rb +96 -0
  74. data/lib/westpac_api/failure_response.rb +33 -0
  75. data/lib/westpac_api.rb +28 -0
  76. data/partner_api.gemspec +31 -0
  77. metadata +191 -0
@@ -0,0 +1,49 @@
1
+ require 'fab_api/endpoint'
2
+ require 'fab_api/utils/id'
3
+
4
+ module FabApi
5
+ module Endpoints
6
+ class ExchangeToken < Endpoint
7
+ prepend PartnerApi::Endpoints::Initializer
8
+
9
+ def initialize(access_token:, system:, transaction_id: Utils::Id.generate)
10
+ @access_token = access_token
11
+ @system = system
12
+ @transaction_id = transaction_id
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :access_token, :system, :transaction_id
18
+
19
+ def method
20
+ "POST"
21
+ end
22
+
23
+ def path
24
+ "/security/v1/verifytoken"
25
+ end
26
+
27
+ def parameters
28
+ {
29
+ "applicationArea": {
30
+ "transactionId": transaction_id,
31
+ },
32
+ "dataArea": {
33
+ system: system,
34
+ accessCode: access_token,
35
+ grantType: 'authorization_code'
36
+ }
37
+ }
38
+ end
39
+
40
+ def decode(response)
41
+ response.parse.dig('dataArea', 'token')
42
+ end
43
+
44
+ def logging_params
45
+ { transaction_id: transaction_id }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ require 'fab_api/endpoint'
2
+ require 'fab_api/utils/id'
3
+
4
+ module FabApi
5
+ module Endpoints
6
+ class InvalidateToken < Endpoint
7
+ prepend PartnerApi::Endpoints::Initializer
8
+
9
+ def initialize(
10
+ partner_user_id:,
11
+ access_token:,
12
+ system:,
13
+ transaction_id: Utils::Id.generate
14
+ )
15
+ @partner_user_id = partner_user_id
16
+ @access_token = access_token
17
+ @system = system
18
+ @transaction_id = transaction_id
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :partner_user_id, :access_token, :system, :transaction_id
24
+
25
+ def method
26
+ "POST"
27
+ end
28
+
29
+ def path
30
+ "/security/v1/invalidatetoken"
31
+ end
32
+
33
+ def parameters
34
+ {
35
+ "applicationArea": {
36
+ "transactionId": transaction_id,
37
+ },
38
+ "dataArea": {
39
+ system: system,
40
+ accessCode: access_token,
41
+ userId: partner_user_id
42
+ }
43
+ }
44
+ end
45
+
46
+ def decode(_response); end
47
+
48
+ def logging_params
49
+ { transaction_id: transaction_id, user_id: partner_user_id }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ require 'fab_api/endpoint'
2
+ require 'fab_api/utils/id'
3
+
4
+ module FabApi
5
+ module Endpoints
6
+ class RefreshToken < Endpoint
7
+ prepend PartnerApi::Endpoints::Initializer
8
+
9
+ def initialize(
10
+ access_token:,
11
+ refresh_token:,
12
+ system:,
13
+ transaction_id: Utils::Id.generate,
14
+ new_token_flag: true
15
+ )
16
+ @access_token = access_token
17
+ @refresh_token = refresh_token
18
+ @system = system
19
+ @transaction_id = transaction_id
20
+ @new_token_flag = new_token_flag
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :access_token, :refresh_token, :system, :transaction_id,
26
+ :new_token_flag
27
+
28
+ def method
29
+ "POST"
30
+ end
31
+
32
+ def path
33
+ "/security/v1/verifytoken"
34
+ end
35
+
36
+ def parameters
37
+ {
38
+ "applicationArea": {
39
+ "transactionId": transaction_id,
40
+ },
41
+ "dataArea": {
42
+ system: system,
43
+ accessCode: access_token,
44
+ refreshCode: refresh_token,
45
+ grantType: 'refresh_token',
46
+ newTokenRequired: new_token_flag
47
+ }
48
+ }
49
+ end
50
+
51
+ def decode(response)
52
+ response.parse.dig('dataArea', 'token')
53
+ end
54
+
55
+ def logging_params
56
+ { transaction_id: transaction_id }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,39 @@
1
+ require 'partner_api/errors'
2
+
3
+ module FabApi
4
+ class FailureResponse
5
+ def initialize(response)
6
+ @response = response
7
+ end
8
+
9
+ def errors
10
+ if response.status.success?
11
+ response.parse['responseStatus']['errorDetails'].map do |error|
12
+ PartnerApi::Errors::RequestError.new(code: error['errorCode'], message: error['errorDesc'])
13
+ end
14
+ else
15
+ [PartnerApi::Errors::RequestError.new(message: "Invalid Response: #{body}")]
16
+ end
17
+ end
18
+
19
+ def error
20
+ errors.first
21
+ end
22
+
23
+ def status
24
+ response.status
25
+ end
26
+
27
+ def body
28
+ response.body
29
+ end
30
+
31
+ def headers
32
+ response.headers.to_h
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :response
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ require 'securerandom'
2
+
3
+ module FabApi
4
+ module Utils
5
+ module Id
6
+ # Make sure identifier start with AS,
7
+ # followed by any unique characters and max length of 24 characters.
8
+ def self.generate
9
+ "AS#{SecureRandom.hex(11)}"
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/fab_api.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'forwardable'
2
+ require 'concurrent-ruby'
3
+
4
+ require 'fab_api/client'
5
+ require 'fab_api/configuration'
6
+
7
+ module FabApi
8
+ extend self
9
+ extend Forwardable
10
+
11
+ @configuration = Concurrent::Hash.new
12
+
13
+ def_delegators 'configuration(:default)', :config, :configure
14
+
15
+ def configuration(key)
16
+ @configuration[key] ||= Configuration.new
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ module GeminiApi
2
+ Address = Struct.new(
3
+ :network,
4
+ :scope,
5
+ :label,
6
+ :status,
7
+ :created_at,
8
+ :address,
9
+ :raw_data,
10
+ keyword_init: true
11
+ ) do
12
+ def self.build(raw_data)
13
+ new(
14
+ network: raw_data['network'],
15
+ scope: raw_data['scope'],
16
+ label: raw_data['label'],
17
+ status: raw_data['status'],
18
+ created_at: Time.at(raw_data['createdAt'].to_f / 1000),
19
+ address: raw_data['address']
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ require 'bigdecimal'
2
+
3
+ module GeminiApi
4
+ Balance = Struct.new(
5
+ :type,
6
+ :currency,
7
+ :amount,
8
+ :available,
9
+ :available_for_withdrawal,
10
+ :raw_data,
11
+ keyword_init: true
12
+ ) do
13
+ def self.build(raw_data)
14
+ new(
15
+ type: raw_data['type'],
16
+ currency: raw_data['currency'],
17
+ amount: BigDecimal(raw_data['amount']),
18
+ available: BigDecimal(raw_data['available']),
19
+ available_for_withdrawal: BigDecimal(raw_data['availableForWithdrawal']),
20
+ raw_data: raw_data
21
+ )
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hanami/utils/string'
4
+ require 'hanami/utils/class'
5
+
6
+ require 'gemini_api/endpoints/create_address_request'
7
+ require 'gemini_api/endpoints/view_approved_addresses'
8
+ require 'gemini_api/endpoints/withdraw_crypto_fund'
9
+ require 'gemini_api/endpoints/get_available_balances'
10
+ require 'gemini_api/endpoints/view_transfers'
11
+
12
+ module GeminiApi
13
+ class Client
14
+ def self.endpoint(name)
15
+ define_method(name) do |**args|
16
+ klass_name = Hanami::Utils::String.classify(name)
17
+ endpoint_klass = Hanami::Utils::Class.load!("GeminiApi::Endpoints::#{klass_name}")
18
+
19
+ endpoint_klass.(config, **args)
20
+ end
21
+ end
22
+
23
+ endpoint :create_address_request
24
+ endpoint :view_approved_addresses
25
+ endpoint :withdraw_crypto_fund
26
+ endpoint :get_available_balances
27
+ endpoint :view_transfers
28
+
29
+ def initialize(config: GeminiApi.config)
30
+ @config = config
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :config
36
+ end
37
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'partner_api/endpoints/base'
4
+ require 'gemini_api/failure_response'
5
+
6
+ module GeminiApi
7
+ class Endpoint < PartnerApi::Endpoints::Base
8
+ HMAC_ALGORITHM = 'SHA384'
9
+
10
+ private
11
+
12
+ def request_options
13
+ { headers: combined_headers }
14
+ end
15
+
16
+ def connection_options
17
+ {}
18
+ end
19
+
20
+ def default_headers
21
+ {
22
+ 'Content-Type' => 'text/plain',
23
+ 'Content-Length' => '0',
24
+ 'Cache-Control' => 'no-cache',
25
+ 'X-GEMINI-APIKEY' => config.api_key,
26
+ 'X-GEMINI-PAYLOAD' => base64_payload,
27
+ 'X-GEMINI-SIGNATURE' => signature
28
+ }
29
+ end
30
+
31
+ def default_parameters
32
+ {
33
+ 'request' => path,
34
+ 'nonce' => Time.now.to_i
35
+ }
36
+ end
37
+
38
+ def base64_payload
39
+ Base64.urlsafe_encode64(combined_payload.to_json)
40
+ end
41
+
42
+ def signature
43
+ OpenSSL::HMAC.hexdigest(HMAC_ALGORITHM, config.api_secret, base64_payload)
44
+ end
45
+
46
+ def successful?(response)
47
+ response.status.success?
48
+ end
49
+
50
+ def decode(response)
51
+ response.parse
52
+ end
53
+
54
+ def decode_error(response)
55
+ FailureResponse.new(response)
56
+ end
57
+
58
+ def logging_params
59
+ {}
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gemini_api/endpoint'
4
+
5
+ module GeminiApi
6
+ module Endpoints
7
+ class CreateAddressRequest < Endpoint
8
+ prepend PartnerApi::Endpoints::Initializer
9
+
10
+ # See https://docs.gemini.com/rest-api/#create-an-address-request
11
+ def initialize(network:, address:, label:, account: nil)
12
+ @network = network
13
+ @address = address
14
+ @label = label
15
+ @account = account
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :network, :address, :label, :account
21
+
22
+ def method
23
+ 'POST'
24
+ end
25
+
26
+ def path
27
+ "/v1/approvedAddresses/#{network}/request"
28
+ end
29
+
30
+ def parameters
31
+ {
32
+ address: address,
33
+ label: label,
34
+ account: account
35
+ }.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gemini_api/endpoint'
4
+ require 'gemini_api/balance'
5
+
6
+ module GeminiApi
7
+ module Endpoints
8
+ class GetAvailableBalances < Endpoint
9
+ prepend PartnerApi::Endpoints::Initializer
10
+
11
+ # See https://docs.gemini.com/rest-api/#get-available-balances
12
+ def initialize(account: nil)
13
+ @account = account
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :account
19
+
20
+ def method
21
+ 'POST'
22
+ end
23
+
24
+ def path
25
+ "/v1/balances"
26
+ end
27
+
28
+ def parameters
29
+ { account: account }.compact
30
+ end
31
+
32
+ def decode(response)
33
+ response.parse.map do |balance|
34
+ Balance.build(balance)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gemini_api/endpoint'
4
+ require 'gemini_api/address'
5
+
6
+ module GeminiApi
7
+ module Endpoints
8
+ class ViewApprovedAddresses < Endpoint
9
+ prepend PartnerApi::Endpoints::Initializer
10
+
11
+ # See https://docs.gemini.com/rest-api/#view-approved-addresses
12
+ def initialize(network: :gusd, account: nil)
13
+ @network = network
14
+ @account = account
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :network, :account
20
+
21
+ def method
22
+ 'POST'
23
+ end
24
+
25
+ def path
26
+ "/v1/approvedAddresses/account/#{network}"
27
+ end
28
+
29
+ def parameters
30
+ { account: account }.compact
31
+ end
32
+
33
+ def decode(response)
34
+ response.parse['approvedAddresses'].map do |address|
35
+ Address.build(address)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gemini_api/endpoint'
4
+ require 'gemini_api/transfer'
5
+
6
+ module GeminiApi
7
+ module Endpoints
8
+ class ViewTransfers < Endpoint
9
+ prepend PartnerApi::Endpoints::Initializer
10
+
11
+ # See: https://docs.gemini.com/rest-api/#transfers
12
+ def initialize(currency:, timestamp:, limit_transfers: 50, account: nil, show_completed_deposit_advances: false)
13
+ @currency = currency
14
+ @timestamp = timestamp
15
+ @limit_transfers = limit_transfers
16
+ @account = account
17
+ @show_completed_deposit_advances = show_completed_deposit_advances
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :currency, :timestamp, :limit_transfers, :account, :show_completed_deposit_advances
23
+
24
+ def method
25
+ 'POST'
26
+ end
27
+
28
+ def path
29
+ '/v1/transfers'
30
+ end
31
+
32
+ def parameters
33
+ {
34
+ currency: currency,
35
+ timestamp: timestamp,
36
+ limit_transfers: limit_transfers,
37
+ account: account,
38
+ show_completed_deposit_advances: show_completed_deposit_advances
39
+ }.compact
40
+ end
41
+
42
+ def decode(response)
43
+ response.parse.map do |transfer|
44
+ Transfer.build(transfer)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gemini_api/endpoint'
4
+ require 'gemini_api/transaction'
5
+
6
+ module GeminiApi
7
+ module Endpoints
8
+ class WithdrawCryptoFund < Endpoint
9
+ prepend PartnerApi::Endpoints::Initializer
10
+
11
+ # By default, we allow withdrawing USD as GUSD
12
+ # See: https://docs.gemini.com/rest-api/#withdraw-usd-as-gusd
13
+ # For other currency, see
14
+ # https://docs.gemini.com/rest-api/#withdraw-crypto-funds
15
+ def initialize(address:, amount:, currency: :usd, account: nil, client_transfer_id: nil)
16
+ @currency = currency.to_sym
17
+ @address = address
18
+ @amount = amount
19
+ @account = account
20
+ @client_transfer_id = client_transfer_id
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :address, :amount, :account, :currency, :client_transfer_id
26
+
27
+ def method
28
+ 'POST'
29
+ end
30
+
31
+ def path
32
+ "/v1/withdraw/#{currency}"
33
+ end
34
+
35
+ def parameters
36
+ {
37
+ address: address,
38
+ amount: amount,
39
+ account: account,
40
+ clientTransferId: client_transfer_id
41
+ }.compact
42
+ end
43
+
44
+ def decode(response)
45
+ Transaction.build(response.parse)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeminiApi
4
+ class FailureResponse
5
+ def initialize(response)
6
+ @response = response
7
+ end
8
+
9
+ def errors
10
+ if json?
11
+ parsed_response = response.parse
12
+ [
13
+ PartnerApi::Errors::RequestError.new(
14
+ code: parsed_response['reason'],
15
+ message: parsed_response['message']
16
+ )
17
+ ]
18
+ else
19
+ [PartnerApi::Errors::RequestError.new(message: "Invalid Response: #{body}")]
20
+ end
21
+ end
22
+
23
+ def error
24
+ errors.first
25
+ end
26
+
27
+ def status
28
+ response.status
29
+ end
30
+
31
+ def body
32
+ response.body
33
+ end
34
+
35
+ def headers
36
+ response.headers.to_h
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :response
42
+
43
+ def json?
44
+ response.content_type.mime_type == 'application/json'
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeminiApi
4
+ Transaction = Struct.new(:address, :amount, :withdrawal_id, :raw_data, keyword_init: true) do
5
+ def self.build(raw_data)
6
+ new(
7
+ address: raw_data['address'],
8
+ amount: raw_data['amount'].to_f,
9
+ withdrawal_id: raw_data['withdrawalId'],
10
+ raw_data: raw_data
11
+ )
12
+ end
13
+ end
14
+ end