k2-connect-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +75 -0
- data/.gitmodules +6 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +127 -0
- data/Guardfile +80 -0
- data/LICENSE +21 -0
- data/README.md +542 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/k2-connect-ruby.gemspec +51 -0
- data/lib/k2-connect-ruby.rb +34 -0
- data/lib/k2-connect-ruby/k2_errors.rb +77 -0
- data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_pay.rb +100 -0
- data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_settlement.rb +49 -0
- data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_stk.rb +48 -0
- data/lib/k2-connect-ruby/k2_financial_entity/entities/k2_transfer.rb +45 -0
- data/lib/k2-connect-ruby/k2_financial_entity/entity.rb +6 -0
- data/lib/k2-connect-ruby/k2_financial_entity/k2_entity.rb +34 -0
- data/lib/k2-connect-ruby/k2_financial_entity/k2_subscribe.rb +42 -0
- data/lib/k2-connect-ruby/k2_financial_entity/k2_token.rb +37 -0
- data/lib/k2-connect-ruby/k2_services/client/k2_client.rb +24 -0
- data/lib/k2-connect-ruby/k2_services/payload_process.rb +14 -0
- data/lib/k2-connect-ruby/k2_services/payloads/k2_transaction.rb +48 -0
- data/lib/k2-connect-ruby/k2_services/payloads/k2_webhooks.rb +59 -0
- data/lib/k2-connect-ruby/k2_services/payloads/transactions/incoming_payment.rb +46 -0
- data/lib/k2-connect-ruby/k2_services/payloads/transactions/outgoing_payment.rb +15 -0
- data/lib/k2-connect-ruby/k2_services/payloads/transactions/transfer.rb +12 -0
- data/lib/k2-connect-ruby/k2_services/payloads/webhooks/b2b_received.rb +10 -0
- data/lib/k2-connect-ruby/k2_services/payloads/webhooks/buygoods_received.rb +5 -0
- data/lib/k2-connect-ruby/k2_services/payloads/webhooks/buygoods_reversal.rb +5 -0
- data/lib/k2-connect-ruby/k2_services/payloads/webhooks/customer_created.rb +14 -0
- data/lib/k2-connect-ruby/k2_services/payloads/webhooks/m2m_transaction.rb +8 -0
- data/lib/k2-connect-ruby/k2_services/payloads/webhooks/settlement_webhook.rb +36 -0
- data/lib/k2-connect-ruby/k2_utilities/config/k2_config.rb +51 -0
- data/lib/k2-connect-ruby/k2_utilities/config/k2_config.yml +14 -0
- data/lib/k2-connect-ruby/k2_utilities/k2_authorize.rb +14 -0
- data/lib/k2-connect-ruby/k2_utilities/k2_connection.rb +38 -0
- data/lib/k2-connect-ruby/k2_utilities/k2_process_result.rb +43 -0
- data/lib/k2-connect-ruby/k2_utilities/k2_process_webhook.rb +55 -0
- data/lib/k2-connect-ruby/k2_utilities/k2_url_parse.rb +7 -0
- data/lib/k2-connect-ruby/k2_utilities/k2_validation.rb +126 -0
- data/lib/k2-connect-ruby/k2_utilities/spec_modules/spec_config.rb +41 -0
- data/lib/k2-connect-ruby/utilities.rb +19 -0
- data/lib/k2-connect-ruby/version.rb +3 -0
- 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,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,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
|