k2-connect-ruby 0.0.1

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 (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
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = %w(--color --format documentation nested)
6
+ end
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'k2-connect-ruby'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,51 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'k2-connect-ruby/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'k2-connect-ruby'
7
+ spec.version = K2ConnectRuby::VERSION
8
+ spec.authors = ['DavidKar1uk1']
9
+ spec.email = ['David.mwangi@kopokopo.com']
10
+
11
+ spec.summary = 'Ruby SDK for connection to the Kopo Kopo API.'
12
+ spec.description = 'Ruby SDK for connection to the Kopo Kopo API, with webhook handling and JSON request parsing with the Ruby on Rails framework.'
13
+ spec.homepage = 'https://github.com/kopokopo/k2-connect-ruby.git'
14
+ spec.license = 'MIT'
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = 'https://github.com/kopokopo/k2-connect-ruby.git'
23
+ spec.metadata['changelog_uri'] = 'https://github.com/kopokopo/k2-connect-ruby.git/CHANGELOG.MD'
24
+ else
25
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
26
+ 'public gem pushes.'
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+ spec.bindir = 'exe'
35
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ['lib']
37
+
38
+
39
+ spec.add_dependency 'activesupport', '~> 6.1.3.1'
40
+ spec.add_dependency 'activemodel', '~> 6.1.3.1'
41
+ spec.add_development_dependency 'bundler', '~> 2.0'
42
+ spec.add_development_dependency "guard", '~> 2.16.2'
43
+ spec.add_development_dependency "guard-rspec", '~> 4.7.3'
44
+ spec.add_dependency 'rest-client', '~> 2.1.0'
45
+ spec.add_development_dependency 'rake', '~> 13.0.3'
46
+ spec.add_development_dependency 'rspec', '~> 3.10.0'
47
+ spec.add_development_dependency "rspec-nc", '~> 0.3.0'
48
+ spec.add_development_dependency 'vcr', '~> 6.0.0'
49
+ spec.add_development_dependency 'webmock', '~> 3.12.2'
50
+ spec.add_dependency 'yajl-ruby', '~> 1.4.1'
51
+ end
@@ -0,0 +1,34 @@
1
+ # Base
2
+ require 'k2-connect-ruby/k2_errors'
3
+ require 'k2-connect-ruby/version'
4
+ require 'active_model'
5
+ require 'active_support/json'
6
+ # For the blank? check
7
+ require 'active_support/dependencies/autoload'
8
+ require 'active_support/core_ext'
9
+
10
+ # Utilities
11
+ require 'k2-connect-ruby/utilities'
12
+
13
+ # Services
14
+ require 'k2-connect-ruby/k2_services/payload_process'
15
+
16
+ # Entity
17
+ require 'k2-connect-ruby/k2_financial_entity/k2_entity'
18
+ require 'k2-connect-ruby/k2_financial_entity/entity'
19
+
20
+ # ActiveSupport
21
+ require 'active_support/core_ext/hash/indifferent_access'
22
+
23
+ # YAJL
24
+ require 'yajl'
25
+
26
+ # YAML
27
+ require 'yaml'
28
+
29
+ # Rest Client
30
+ require 'rest-client'
31
+
32
+ # JSON
33
+ require 'json'
34
+
@@ -0,0 +1,77 @@
1
+ # Standard K2Error
2
+ class K2Errors < StandardError
3
+ attr_reader :status, :message
4
+ def initialize(msg = @message)
5
+ super(msg)
6
+ end
7
+ end
8
+
9
+ # For errors concerning the Status code returned from Kopo Kopo
10
+ class K2ConnectionError < K2Errors
11
+ def initialize(error)
12
+ @error = error
13
+ end
14
+
15
+ def message
16
+ case @error
17
+ when 400.to_s
18
+ STDERR.puts "Bad Request.\n\tYour request is Invalid"
19
+ when 401.to_s
20
+ STDERR.puts "Unauthorized.\n Your API key is wrong"
21
+ when 403.to_s
22
+ STDERR.puts "Forbidden.\n The resource requested cannot be accessed"
23
+ when 404.to_s
24
+ STDERR.puts "Not Found.\n\tThe specified resource could not be found"
25
+ when 405.to_s
26
+ STDERR.puts "Method Not Allowed.\n You tried to access a resource with an invalid method"
27
+ when 406.to_s
28
+ STDERR.puts "Not Acceptable.\n You requested a format that isn't valid json"
29
+ when 410.to_s
30
+ STDERR.puts "Gone.\n The resource requested has been moved"
31
+ when 429.to_s
32
+ STDERR.puts "Too Many Requests.\n Request threshold has been exceeded"
33
+ when 500.to_s
34
+ STDERR.puts "Internal Server Error.\n We had a problem with our server. Try again later"
35
+ when 503.to_s
36
+ STDERR.puts "Service Unavailable.\n We're temporarily offline for maintenance. Please try again later"
37
+ else
38
+ STDERR.puts 'Undefined Kopo Kopo Server Response.'
39
+ end
40
+ end
41
+ end
42
+
43
+ # Errors concerning the Validation module
44
+ class K2ValidateErrors < K2Errors
45
+ attr_reader :the_keys
46
+
47
+ def initialize(the_keys)
48
+ super
49
+ @the_keys = the_keys
50
+ @status = :bad_request
51
+ end
52
+
53
+ def loop_keys
54
+ STDERR.puts @message
55
+ @the_keys.each(&method(:puts))
56
+ end
57
+
58
+ def message
59
+ loop_keys
60
+ end
61
+ end
62
+
63
+ # Hash / Params has Empty Values within it
64
+ class K2EmptyParams < K2ValidateErrors
65
+ def initialize(the_keys)
66
+ @message = "Invalid Hash Object!\n The Following Parameter(s) are Empty: "
67
+ super
68
+ end
69
+ end
70
+
71
+ # Error for Incorrect Hash Key Symbols (K2Entity validate => input)
72
+ class K2IncorrectParams < K2ValidateErrors
73
+ def initialize(the_keys)
74
+ @message = "Incorrect Hash/Parameters Object!\n The Following Parameter(s) are Incorrect: "
75
+ super
76
+ end
77
+ end
@@ -0,0 +1,100 @@
1
+ # For PAY/ Send Money to others
2
+ # TODO: Add K2Config configuration for the callback URL
3
+ class K2Pay < K2Entity
4
+ attr_reader :recipients_location_url, :payments_location_url
5
+
6
+ # Adding PAY Recipients with either mobile_wallets or bank_accounts as destination of your payments.
7
+ def add_recipient(params)
8
+ params = params.with_indifferent_access
9
+ @exception_array += %w[type]
10
+ # In the case of mobile pay recipient
11
+ if params[:type].eql?('mobile_wallet')
12
+ params = validate_input(params, @exception_array += %w[first_name last_name phone_number email network])
13
+ k2_request_pay_recipient = {
14
+ first_name: params[:first_name],
15
+ last_name: params[:last_name],
16
+ phone_number: validate_phone(params[:phone_number]),
17
+ email: validate_email(params[:email]),
18
+ network: params[:network]
19
+ }
20
+ # In the case of bank pay recipient
21
+ elsif params[:type].eql?('bank_account')
22
+ params = validate_input(params, @exception_array += %w[account_name account_number bank_branch_ref settlement_method])
23
+ k2_request_pay_recipient = {
24
+ account_name: params[:account_name],
25
+ account_number: params[:account_number],
26
+ bank_branch_ref: params[:bank_branch_ref],
27
+ settlement_method: params[:settlement_method]
28
+ }
29
+ # In the case of till pay recipient
30
+ elsif params[:type].eql?('till')
31
+ params = validate_input(params, @exception_array += %w[till_name till_number])
32
+ k2_request_pay_recipient = {
33
+ till_name: params[:till_name],
34
+ till_number: params[:till_number]
35
+ }
36
+ # In the case of bank pay recipient
37
+ elsif params[:type].eql?('kopo_kopo_merchant')
38
+ params = validate_input(params, @exception_array += %w[alias_name till_number])
39
+ k2_request_pay_recipient = {
40
+ alias_name: params[:alias_name],
41
+ till_number: params[:till_number]
42
+ }
43
+ else
44
+ raise ArgumentError, 'Undefined Payment Method.'
45
+ end
46
+ recipients_body = {
47
+ type: params[:type],
48
+ #type: params['pay_type'],
49
+ pay_recipient: k2_request_pay_recipient
50
+ }
51
+ pay_recipient_hash = make_hash(K2Config.path_url('pay_recipient'), 'post', @access_token, 'PAY', recipients_body)
52
+ @threads << Thread.new do
53
+ sleep 0.25
54
+ @recipients_location_url = K2Connect.make_request(pay_recipient_hash)
55
+ end
56
+ @threads.each(&:join)
57
+ end
58
+
59
+ # Create an outgoing Payment to a third party.
60
+ def create_payment(params)
61
+ # Validation
62
+ params = validate_input(params, @exception_array += %w[destination_reference destination_type currency value callback_url metadata])
63
+ # The Request Body Parameters
64
+ k2_request_pay_amount = {
65
+ currency: params[:currency],
66
+ value: params[:value]
67
+ }
68
+ k2_request_pay_metadata = params[:metadata]
69
+ k2_request_links = {
70
+ callback_url: params[:callback_url]
71
+ }
72
+ create_payment_body = {
73
+ destination_reference: params[:destination_reference],
74
+ destination_type: params[:destination_type],
75
+ amount: k2_request_pay_amount,
76
+ meta_data: k2_request_pay_metadata,
77
+ _links: k2_request_links
78
+ }
79
+ create_payment_hash = make_hash(K2Config.path_url('payments'), 'post', @access_token, 'PAY', create_payment_body)
80
+ @threads << Thread.new do
81
+ sleep 0.25
82
+ @payments_location_url = K2Connect.make_request(create_payment_hash)
83
+ end
84
+ @threads.each(&:join)
85
+ end
86
+
87
+ # Query/Check the status of a previously initiated PAY Payment request
88
+ def query_status(method_type)
89
+ if method_type.eql?('recipients')
90
+ super('PAY', @recipients_location_url)
91
+ elsif method_type.eql?('payments')
92
+ super('PAY', @payments_location_url)
93
+ end
94
+ end
95
+
96
+ # Query Location URL
97
+ def query_resource(url)
98
+ super('PAY', url)
99
+ end
100
+ end
@@ -0,0 +1,49 @@
1
+ # For Creating pre-approved and owned settlement accounts
2
+ class K2Settlement < K2Entity
3
+ # Create a Verified Settlement Account via API (Mobile Wallet and Bank Account)
4
+ def add_settlement_account(params)
5
+ params=params.with_indifferent_access
6
+ the_path_url = ''
7
+ settlement_body = {}
8
+ @exception_array += %w[type]
9
+ # The Request Body Parameters
10
+ if params[:type].eql?('merchant_wallet')
11
+ params = validate_input(params, @exception_array += %w[first_name last_name phone_number network])
12
+ settlement_body = {
13
+ first_name: params[:first_name],
14
+ last_name: params[:last_name],
15
+ phone_number: validate_phone(params[:phone_number]),
16
+ network: params[:network]
17
+ }
18
+ the_path_url = K2Config.path_url('settlement_mobile_wallet')
19
+ elsif params[:type].eql?('merchant_bank_account')
20
+ params = validate_input(params, @exception_array += %w[account_name bank_ref bank_branch_ref account_number currency value, settlement_method])
21
+ settlement_body = {
22
+ account_name: params[:account_name],
23
+ bank_branch_ref: params[:bank_branch_ref],
24
+ account_number: params[:account_number],
25
+ settlement_method: params[:settlement_method]
26
+ }
27
+ the_path_url = K2Config.path_url('settlement_bank_account')
28
+ else
29
+ raise ArgumentError, 'Unknown Settlement Account'
30
+ end
31
+
32
+ settlement_hash = make_hash(the_path_url, 'post', @access_token, 'Transfer', settlement_body)
33
+ @threads << Thread.new do
34
+ sleep 0.25
35
+ @location_url = K2Connect.make_request(settlement_hash)
36
+ end
37
+ @threads.each(&:join)
38
+ end
39
+
40
+ # Check the status of a prior initiated Transfer. Make sure to add the id to the url
41
+ def query_status
42
+ super('Settlement', path_url=@location_url)
43
+ end
44
+
45
+ # Query Location URL
46
+ def query_resource(url)
47
+ super('Settlement', url)
48
+ end
49
+ end
@@ -0,0 +1,48 @@
1
+ # For STK Push/Receive MPESA Payments from merchant's customers
2
+ class K2Stk < K2Entity
3
+ # Receive payments from M-PESA users.
4
+ def receive_mpesa_payments(params)
5
+ # Validation
6
+ params = validate_input(params, @exception_array += %w[payment_channel till_number first_name last_name phone_number email currency value metadata callback_url])
7
+ # The Request Body Parameters
8
+ k2_request_subscriber = {
9
+ first_name: params[:first_name],
10
+ middle_name: params[:middle_name],
11
+ last_name: params[:last_name],
12
+ phone_number: validate_phone(params[:phone_number]),
13
+ email: validate_email(params[:email])
14
+ }
15
+ k2_request_amount = {
16
+ currency: 'KES',
17
+ value: params[:value]
18
+ }
19
+ k2_request_metadata = params[:metadata]
20
+ k2_request_links = {
21
+ callback_url: params[:callback_url]
22
+ }
23
+ receive_body = {
24
+ payment_channel: params[:payment_channel],
25
+ till_number: validate_till_number_prefix(params[:till_number]),
26
+ subscriber: k2_request_subscriber,
27
+ amount: k2_request_amount,
28
+ meta_data: k2_request_metadata,
29
+ _links: k2_request_links
30
+ }
31
+ receive_hash = make_hash(K2Config.path_url('incoming_payments'), 'post', @access_token, 'STK', receive_body)
32
+ @threads << Thread.new do
33
+ sleep 0.25
34
+ @location_url = K2Connect.make_request(receive_hash)
35
+ end
36
+ @threads.each(&:join)
37
+ end
38
+
39
+ # Query/Check STK Payment Request Status
40
+ def query_status
41
+ super('STK', path_url=@location_url)
42
+ end
43
+
44
+ # Query Location URL
45
+ def query_resource(url)
46
+ super('STK', url)
47
+ end
48
+ end
@@ -0,0 +1,45 @@
1
+ # For Transferring funds to pre-approved and owned settlement accounts
2
+ class K2Transfer < K2Entity
3
+ # Create a either a 'blind' transfer, for when destination is specified, and a 'targeted' transfer which has a specified destination.
4
+ def transfer_funds(params)
5
+ # Validation
6
+ unless params["destination_reference"].blank? && params["destination_type"].blank?
7
+ params = validate_input(params, @exception_array += %w[destination_reference destination_type currency value callback_url metadata])
8
+ end
9
+ params = params.with_indifferent_access
10
+ # The Request Body Parameters
11
+ k2_request_transfer = {
12
+ destination_reference: params[:destination_reference],
13
+ destination_type: params[:destination_type],
14
+ amount: {
15
+ currency: params[:currency],
16
+ value: params[:value]
17
+ }
18
+ }
19
+ metadata = params[:metadata]
20
+ transfer_body = k2_request_transfer.merge(
21
+ {
22
+ _links:
23
+ {
24
+ callback_url: params[:callback_url]
25
+ },
26
+ metadata: metadata
27
+ })
28
+ transfer_hash = make_hash(K2Config.path_url('transfers'), 'post', @access_token, 'Transfer', transfer_body)
29
+ @threads << Thread.new do
30
+ sleep 0.25
31
+ @location_url = K2Connect.make_request(transfer_hash)
32
+ end
33
+ @threads.each(&:join)
34
+ end
35
+
36
+ # Check the status of a prior initiated Transfer. Make sure to add the id to the url
37
+ def query_status
38
+ super('Transfer', path_url=@location_url)
39
+ end
40
+
41
+ # Query Location URL
42
+ def query_resource(url)
43
+ super('Transfer', url)
44
+ end
45
+ end