k2-connect-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +75 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +7 -0
  6. data/CHANGELOG.md +0 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +127 -0
  10. data/Guardfile +80 -0
  11. data/LICENSE +21 -0
  12. data/README.md +542 -0
  13. data/Rakefile +8 -0
  14. data/bin/console +14 -0
  15. data/bin/setup +8 -0
  16. data/k2-connect-ruby.gemspec +51 -0
  17. data/lib/k2-connect-ruby.rb +34 -0
  18. data/lib/k2-connect-ruby/k2_errors.rb +77 -0
  19. data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_pay.rb +100 -0
  20. data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_settlement.rb +49 -0
  21. data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_stk.rb +48 -0
  22. data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_transfer.rb +45 -0
  23. data/lib/k2-connect-ruby/k2_financial_entity/entity.rb +6 -0
  24. data/lib/k2-connect-ruby/k2_financial_entity/k2_entity.rb +34 -0
  25. data/lib/k2-connect-ruby/k2_financial_entity/k2_subscribe.rb +42 -0
  26. data/lib/k2-connect-ruby/k2_financial_entity/k2_token.rb +37 -0
  27. data/lib/k2-connect-ruby/k2_services/client/k2_client.rb +24 -0
  28. data/lib/k2-connect-ruby/k2_services/payload_process.rb +14 -0
  29. data/lib/k2-connect-ruby/k2_services/payloads/k2_transaction.rb +48 -0
  30. data/lib/k2-connect-ruby/k2_services/payloads/k2_webhooks.rb +59 -0
  31. data/lib/k2-connect-ruby/k2_services/payloads/transactions/incoming_payment.rb +46 -0
  32. data/lib/k2-connect-ruby/k2_services/payloads/transactions/outgoing_payment.rb +15 -0
  33. data/lib/k2-connect-ruby/k2_services/payloads/transactions/transfer.rb +12 -0
  34. data/lib/k2-connect-ruby/k2_services/payloads/webhooks/b2b_received.rb +10 -0
  35. data/lib/k2-connect-ruby/k2_services/payloads/webhooks/buygoods_received.rb +5 -0
  36. data/lib/k2-connect-ruby/k2_services/payloads/webhooks/buygoods_reversal.rb +5 -0
  37. data/lib/k2-connect-ruby/k2_services/payloads/webhooks/customer_created.rb +14 -0
  38. data/lib/k2-connect-ruby/k2_services/payloads/webhooks/m2m_transaction.rb +8 -0
  39. data/lib/k2-connect-ruby/k2_services/payloads/webhooks/settlement_webhook.rb +36 -0
  40. data/lib/k2-connect-ruby/k2_utilities/config/k2_config.rb +51 -0
  41. data/lib/k2-connect-ruby/k2_utilities/config/k2_config.yml +14 -0
  42. data/lib/k2-connect-ruby/k2_utilities/k2_authorize.rb +14 -0
  43. data/lib/k2-connect-ruby/k2_utilities/k2_connection.rb +38 -0
  44. data/lib/k2-connect-ruby/k2_utilities/k2_process_result.rb +43 -0
  45. data/lib/k2-connect-ruby/k2_utilities/k2_process_webhook.rb +55 -0
  46. data/lib/k2-connect-ruby/k2_utilities/k2_url_parse.rb +7 -0
  47. data/lib/k2-connect-ruby/k2_utilities/k2_validation.rb +126 -0
  48. data/lib/k2-connect-ruby/k2_utilities/spec_modules/spec_config.rb +41 -0
  49. data/lib/k2-connect-ruby/utilities.rb +19 -0
  50. data/lib/k2-connect-ruby/version.rb +3 -0
  51. metadata +265 -0
@@ -0,0 +1,6 @@
1
+ require 'k2-connect-ruby/k2_financial_entity/k2_token'
2
+ require 'k2-connect-ruby/k2_financial_entity/k2_subscribe'
3
+ require 'k2-connect-ruby/k2_financial_entity/entities/k2_stk'
4
+ require 'k2-connect-ruby/k2_financial_entity/entities/k2_pay'
5
+ require 'k2-connect-ruby/k2_financial_entity/entities/k2_transfer'
6
+ require 'k2-connect-ruby/k2_financial_entity/entities/k2_settlement'
@@ -0,0 +1,34 @@
1
+ # Common Class Behaviours for stk, pay and transfers
2
+ class K2Entity
3
+ attr_accessor :access_token, :the_array
4
+ attr_reader :k2_response_body, :query_hash, :location_url
5
+ include K2Validation, K2Utilities
6
+
7
+ # Initialize with access token from Subscriber Class
8
+ def initialize(access_token)
9
+ @access_token = access_token
10
+ @threads = []
11
+ @exception_array = %w[authenticity_token]
12
+ end
13
+
14
+ # Query/Check the status of a previously initiated request
15
+ def query_status(class_type, path_url)
16
+ query(class_type, path_url)
17
+ end
18
+
19
+ # Query Location URL
20
+ def query_resource(class_type, url)
21
+ query(class_type, url)
22
+ end
23
+
24
+ def query(class_type, path_url)
25
+ # TODO: Add back the validation to ensure only https location urls are returned
26
+ # path_url = validate_url(@location_url)
27
+ query_hash = make_hash(path_url, 'get', @access_token, class_type, nil)
28
+ @threads << Thread.new do
29
+ sleep 0.25
30
+ @k2_response_body = K2Connect.make_request(query_hash)
31
+ end
32
+ @threads.each(&:join)
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ # Class for Subscription Service
2
+ class K2Subscribe
3
+ include K2Validation, K2Utilities
4
+ attr_reader :location_url, :k2_response_body
5
+ attr_accessor :access_token, :webhook_secret
6
+
7
+ # Initialize with the event_type
8
+ def initialize(access_token)
9
+ raise ArgumentError, 'Nil or Empty Access Token Given!' if access_token.blank?
10
+ @threads = []
11
+ @access_token = access_token
12
+ end
13
+
14
+ # Implemented a Case condition that minimises repetition
15
+ def webhook_subscribe(params)
16
+ params = validate_webhook_input(params)
17
+ validate_webhook(params)
18
+ k2_request_body = {
19
+ event_type: params[:event_type],
20
+ url: params[:url],
21
+ scope: params[:scope],
22
+ scope_reference: params[:scope_reference]
23
+ }
24
+ subscribe_hash = make_hash(K2Config.path_url('webhook_subscriptions'), 'post', @access_token,'Subscription', k2_request_body)
25
+ @location_url = K2Connect.make_request(subscribe_hash)
26
+ end
27
+
28
+ # Query Recent Webhook
29
+ def query_webhook(location_url = @location_url)
30
+ query_hash = make_hash(location_url, 'get', @access_token, 'Subscription', nil)
31
+ @threads << Thread.new do
32
+ sleep 0.25
33
+ @k2_response_body = K2Connect.make_request(query_hash)
34
+ end
35
+ @threads.each(&:join)
36
+ end
37
+
38
+ # Query Specific Webhook URL
39
+ def query_resource_url(url)
40
+ query_webhook(url)
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ # Class for Gaining Access Token
2
+ class K2AccessToken
3
+ attr_reader :access_token
4
+
5
+ def initialize(client_id, client_secret)
6
+ validate_client_credentials(client_id, client_secret)
7
+ @client_id = client_id
8
+ @client_secret = client_secret
9
+ end
10
+
11
+ # Method for sending the request to K2 sandbox or Mock Server (Receives the access_token)
12
+ def request_token
13
+ token_params = {
14
+ client_id: @client_id,
15
+ client_secret: @client_secret,
16
+ grant_type: 'client_credentials'
17
+ }
18
+ token_hash = K2AccessToken.make_hash(K2Config.path_url('oauth_token'), 'post', 'Access Token', token_params)
19
+ @access_token = K2Connect.make_request(token_hash)
20
+ end
21
+
22
+ def validate_client_credentials(client_id, client_secret)
23
+ raise ArgumentError, 'Nil or Empty Client Id or Secret!' if client_id.blank? || client_secret.blank?
24
+ end
25
+
26
+ class << self
27
+ # Create a Hash containing important details accessible for K2Connect
28
+ def make_hash(path_url, request, class_type, body)
29
+ {
30
+ path_url: path_url,
31
+ request_type: request,
32
+ class_type: class_type,
33
+ params: body
34
+ }.with_indifferent_access
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # TODO: Uncomment the checking of the request scheme
2
+ class K2Client
3
+ attr_accessor :api_secret_key,
4
+ :hash_body,
5
+ :k2_signature
6
+
7
+ # Initialize method
8
+ def initialize(api_secret_key)
9
+ raise ArgumentError, 'No Secret Key Given!' if api_secret_key.blank?
10
+ @api_secret_key = api_secret_key
11
+ end
12
+
13
+ # Method for parsing the Entire Request. Come back to it later to trim. L8r call it set_client_variables
14
+ def parse_request(the_request)
15
+ raise ArgumentError, 'Nil Request Parameter Input!' if the_request.blank?
16
+
17
+ # The Response Body.
18
+ @hash_body = Yajl::Parser.parse(the_request.body.read.as_json)
19
+ # The Response Header
20
+ hash_header = Yajl::Parser.parse(the_request.env.select { |k, _| k =~ /^HTTP_/ }.to_json)
21
+ # The K2 Signature
22
+ @k2_signature = hash_header['HTTP_X_KOPOKOPO_SIGNATURE']
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ require 'k2-connect-ruby/k2_services/client/k2_client'
2
+
3
+ require 'k2-connect-ruby/k2_services/payloads/k2_webhooks'
4
+ require 'k2-connect-ruby/k2_services/payloads/webhooks/b2b_received'
5
+ require 'k2-connect-ruby/k2_services/payloads/webhooks/m2m_transaction'
6
+ require 'k2-connect-ruby/k2_services/payloads/webhooks/customer_created'
7
+ require 'k2-connect-ruby/k2_services/payloads/webhooks/buygoods_received'
8
+ require 'k2-connect-ruby/k2_services/payloads/webhooks/buygoods_reversal'
9
+ require 'k2-connect-ruby/k2_services/payloads/webhooks/settlement_webhook'
10
+
11
+ require 'k2-connect-ruby/k2_services/payloads/k2_transaction'
12
+ require 'k2-connect-ruby/k2_services/payloads/transactions/transfer'
13
+ require 'k2-connect-ruby/k2_services/payloads/transactions/incoming_payment'
14
+ require 'k2-connect-ruby/k2_services/payloads/transactions/outgoing_payment'
@@ -0,0 +1,48 @@
1
+ class K2Transaction
2
+ attr_reader :id,
3
+ :type,
4
+ :metadata,
5
+ :links_self,
6
+ :callback_url
7
+
8
+ def initialize(payload)
9
+ @id = payload.dig('data', 'id')
10
+ @type = payload.dig('data', 'type')
11
+ @metadata = payload.dig('data', 'attributes', 'metadata')
12
+ @links_self = payload.dig('data', 'attributes', '_links', 'self')
13
+ @callback_url = payload.dig('data', 'attributes', '_links', 'callback_url')
14
+ end
15
+ end
16
+
17
+ class CommonPayment < K2Transaction
18
+ include ActiveModel::Validations
19
+
20
+ attr_reader :status,
21
+ :initiation_time
22
+
23
+ validate :valid_payment_type
24
+
25
+ def initialize(payload)
26
+ super
27
+ @status = payload.dig('data', 'attributes', 'status')
28
+ @initiation_time = payload.dig('data', 'attributes', 'initiation_time') if @type.eql?('incoming_payment')
29
+ end
30
+
31
+ private
32
+
33
+ def valid_payment_type; end
34
+ end
35
+
36
+ class OutgoingTransaction < CommonPayment
37
+ attr_reader :created_at,
38
+ :transfer_batches,
39
+ :total_value
40
+
41
+ def initialize(payload)
42
+ super
43
+ @created_at = payload.dig('data', 'attributes', 'created_at')
44
+ @currency = payload.dig('data', 'attributes', 'amount', 'currency')
45
+ @total_value = payload.dig('data', 'attributes', 'amount', 'value')
46
+ @transfer_batches = payload.dig('data', 'attributes', 'transfer_batches')
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ class Webhook
2
+ attr_reader :id,
3
+ :topic,
4
+ :created_at,
5
+ :links_self,
6
+ :event_type,
7
+ :links_resource,
8
+ :event_resource,
9
+ :resource_id
10
+
11
+ def initialize(payload)
12
+ @id = payload.dig('id')
13
+ @topic = payload.dig('topic')
14
+ @created_at = payload.dig('created_at')
15
+ # Event
16
+ @event_type = payload.dig('event', 'type')
17
+ @resource_id = payload.dig('event', 'resource', 'id') unless @event_type.eql?('Customer Created')
18
+ # Links
19
+ @links_self = payload.dig('_links', 'self')
20
+ @links_resource = payload.dig('_links', 'resource')
21
+ end
22
+ end
23
+
24
+ class K2CommonEvents < Webhook
25
+ REFERENCE_EXCEPTIONS = ["Merchant to Merchant Transaction", "Settlement Transfer"]
26
+
27
+ attr_reader :reference,
28
+ :origination_time,
29
+ :amount,
30
+ :currency,
31
+ :status
32
+
33
+ def initialize(payload)
34
+ super
35
+ @reference = payload.dig('event', 'resource', 'reference') unless REFERENCE_EXCEPTIONS.include?(@event_type)
36
+ @origination_time = payload.dig('event', 'resource', 'origination_time')
37
+ @amount = payload.dig('event', 'resource', 'amount')
38
+ @currency = payload.dig('event', 'resource', 'currency')
39
+ @status = payload.dig('event', 'resource', 'status')
40
+ end
41
+
42
+ end
43
+
44
+ class Buygoods < K2CommonEvents
45
+ attr_reader :system,
46
+ :till_number,
47
+ :sender_phone_number,
48
+ :sender_first_name,
49
+ :sender_last_name
50
+
51
+ def initialize(payload)
52
+ super
53
+ @system = payload.dig('event', 'resource', 'system')
54
+ @till_number = payload.dig('event', 'resource', 'till_number')
55
+ @sender_phone_number = payload.dig('event', 'resource', 'sender_phone_number')
56
+ @sender_first_name = payload.dig('event', 'resource', 'sender_first_name')
57
+ @sender_last_name = payload.dig('event', 'resource', 'sender_last_name')
58
+ end
59
+ end
@@ -0,0 +1,46 @@
1
+ class IncomingPayments < CommonPayment
2
+ attr_reader :event,
3
+ :event_type,
4
+ :transaction_reference,
5
+ :origination_time,
6
+ :sender_phone_number,
7
+ :amount,
8
+ :currency,
9
+ :till_number,
10
+ :system,
11
+ :resource_id,
12
+ :resource_status,
13
+ :sender_first_name,
14
+ :sender_middle_name,
15
+ :sender_last_name,
16
+ :errors
17
+
18
+ def initialize(payload)
19
+ super
20
+ # Event details
21
+ @event = payload.dig('data', 'attributes', 'event')
22
+ @event_type = payload.dig('data', 'attributes', 'event', 'type')
23
+ # Resource details
24
+ @resource_id = payload.dig('data', 'attributes', 'event', 'resource', 'id')
25
+ @transaction_reference = payload.dig('data', 'attributes', 'event', 'resource', 'reference')
26
+ @origination_time = payload.dig('data', 'attributes', 'event', 'resource', 'origination_time')
27
+ @sender_phone_number = payload.dig('data', 'attributes', 'event', 'resource', 'sender_phone_number')
28
+ @amount = payload.dig('data', 'attributes', 'event', 'resource', 'amount')
29
+ @currency = payload.dig('data', 'attributes', 'event', 'resource', 'currency')
30
+ @till_number = payload.dig('data', 'attributes', 'event', 'resource', 'till_number')
31
+ @system = payload.dig('data', 'attributes', 'event', 'resource', 'system')
32
+ @resource_status = payload.dig('data', 'attributes', 'event', 'resource', 'status')
33
+ @sender_first_name = payload.dig('data', 'attributes', 'event', 'resource', 'sender_first_name')
34
+ @sender_middle_name = payload.dig('data', 'attributes', 'event', 'resource', 'sender_middle_name')
35
+ @sender_last_name = payload.dig('data', 'attributes', 'event', 'resource', 'sender_last_name')
36
+ # Errors
37
+ @errors = payload.dig('data', 'attributes', 'event', 'errors')
38
+ end
39
+
40
+ private
41
+
42
+ def valid_payment_type
43
+ raise ArgumentError, "Wrong Payment Type" unless @type.eql?("incoming_payment")
44
+ end
45
+
46
+ end
@@ -0,0 +1,15 @@
1
+ class OutgoingPayment < OutgoingTransaction
2
+ attr_reader :transaction_reference,
3
+ :destination
4
+
5
+ def initialize(payload)
6
+ super
7
+ end
8
+
9
+ private
10
+
11
+ def valid_payment_type
12
+ raise ArgumentError, "Wrong Payment Type" unless @type.eql?("payment")
13
+ end
14
+
15
+ end
@@ -0,0 +1,12 @@
1
+ class Transfer < OutgoingTransaction
2
+
3
+ def initialize(payload)
4
+ super
5
+ end
6
+
7
+ private
8
+
9
+ def valid_payment_type
10
+ raise ArgumentError, "Wrong Payment Type" unless @type.eql?("settlement_transfer")
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ class B2b < K2CommonEvents
2
+ attr_reader :sending_till,
3
+ :till_number
4
+
5
+ def initialize(payload)
6
+ super
7
+ @till_number = payload.dig('event', 'resource', 'till_number')
8
+ @sending_till = payload.dig('event', 'resource', 'sending_till')
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class BuygoodsTransactionReceived < Buygoods
2
+ def initialize(payload)
3
+ super
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class BuygoodsTransactionReversed < Buygoods
2
+ def initialize(payload)
3
+ super
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ class CustomerCreated < Webhook
2
+ attr_reader :resource_first_name,
3
+ :resource_middle_name,
4
+ :resource_last_name,
5
+ :resource_phone_number
6
+
7
+ def initialize(payload)
8
+ super
9
+ @resource_first_name = payload.dig('event', 'resource', 'first_name')
10
+ @resource_middle_name = payload.dig('event', 'resource', 'middle_name')
11
+ @resource_last_name = payload.dig('event', 'resource', 'last_name')
12
+ @resource_phone_number = payload.dig('event', 'resource', 'phone_number')
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ class MerchantToMerchant < K2CommonEvents
2
+ attr_reader :resource_sending_merchant
3
+
4
+ def initialize(payload)
5
+ super
6
+ @resource_sending_merchant = payload.dig('event', 'resource', 'sending_merchant')
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ class SettlementWebhook < K2CommonEvents
2
+ attr_reader :disbursements,
3
+ :destination_type,
4
+ :destination_network,
5
+ :destination_reference,
6
+ :destination_last_name,
7
+ :destination_first_name,
8
+ :destination_account_name,
9
+ :destination_phone_number,
10
+ :destination_account_number,
11
+ :destination_bank_branch_ref,
12
+ :destination_settlement_method
13
+
14
+ def initialize(payload)
15
+ super
16
+ # Destination
17
+ @disbursements = payload.dig('event', 'resource', 'disbursements')
18
+ @destination_type = payload.dig('event', 'resource', 'destination', 'type')
19
+ @destination_reference = payload.dig('event', 'resource', 'destination', 'resource', 'reference')
20
+ destination_assets(payload)
21
+ end
22
+
23
+ def destination_assets(payload)
24
+ if @destination_type.eql?('Mobile Wallet')
25
+ @destination_network = payload.dig('event', 'resource', 'destination', 'resource', 'network')
26
+ @destination_last_name = payload.dig('event', 'resource', 'destination', 'resource', 'last_name')
27
+ @destination_first_name = payload.dig('event', 'resource', 'destination', 'resource', 'first_name')
28
+ @destination_phone_number = payload.dig('event', 'resource', 'destination', 'resource', 'phone_number')
29
+ elsif @destination_type.eql?('Bank Account')
30
+ @destination_account_name = payload.dig('event', 'resource', 'destination', 'resource', 'account_name')
31
+ @destination_account_number = payload.dig('event', 'resource', 'destination', 'resource', 'account_number')
32
+ @destination_bank_branch_ref = payload.dig('event', 'resource', 'destination', 'resource', 'bank_branch_ref')
33
+ @destination_settlement_method = payload.dig('event', 'resource', 'destination', 'resource', 'settlement_method')
34
+ end
35
+ end
36
+ end