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
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