airvend 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/airvend.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/airvend/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "airvend"
7
+ spec.version = Airvend::VERSION
8
+ spec.authors = ["Uchenna Mba"]
9
+ spec.email = ["hey@uche.io"]
10
+
11
+ spec.summary = "Gem that provides access to bill payment & subscription in Nigeria."
12
+ spec.description = "This gem makes it easy for businesses or individuals to implement vending of Airtime, Data, Electricity, Utilities & Television subscriptions to their application"
13
+ spec.homepage = "https://github.com/urchymanny/airvend-rails"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
+ #
19
+ # spec.metadata["homepage_uri"] = spec.homepage
20
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
21
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ spec.add_development_dependency 'dotenv-rails'
34
+ spec.add_development_dependency 'byebug'
35
+ spec.add_dependency 'httparty', '~> 0.18'
36
+ spec.add_dependency 'json', '~> 2.5'
37
+ spec.add_dependency 'faraday', '~> 1.4'
38
+ spec.add_dependency 'faraday-detailed_logger', '~> 2.3'
39
+ spec.add_dependency 'typhoeus', '~> 1.4'
40
+
41
+ # For more information and examples about making a new gem, checkout our
42
+ # guide at: https://bundler.io/guides/creating_gem.html
43
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "airvend"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ require "byebug"
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
data/lib/airvend.rb ADDED
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require_relative "airvend/version"
4
+ require "dotenv"
5
+ require 'json'
6
+ require 'digest'
7
+ require 'airvend/vend.rb'
8
+ require 'airvend/base_endpoints.rb'
9
+ require 'airvend/errors.rb'
10
+ require 'airvend/upper_case_string.rb'
11
+ require 'airvend/airvend_objects/airtime.rb'
12
+ require 'airvend/airvend_objects/internet.rb'
13
+ require 'airvend/airvend_objects/power.rb'
14
+ require 'airvend/airvend_objects/television.rb'
15
+
16
+ Dotenv.load(File.expand_path("../.env", __FILE__))
17
+
18
+ class Airvend
19
+ attr_accessor :username, :password, :api_key, :production, :url
20
+
21
+ def initialize(username=nil, password=nil, api_key=nil, production=false)
22
+
23
+ @username = username
24
+ @password = password
25
+ @api_key = api_key
26
+ @production = production
27
+ sandbox_url = BASE_ENDPOINTS::SANDBOX_URL
28
+ live_url = BASE_ENDPOINTS::LIVE_URL
29
+
30
+ if (ENV['RAILS_ENV'].nil?)
31
+ @production = production
32
+ warn "Warning: Make sure you set RAILS_ENV to production or development"
33
+ else
34
+ if ENV['RAILS_ENV'] == "production"
35
+ @production=true
36
+ else
37
+ @prodcution=false
38
+ end
39
+ end
40
+
41
+ # set rave url to sandbox or live if we are in production or development
42
+ if production == false
43
+ @url = sandbox_url
44
+ else
45
+ @url = live_url
46
+ end
47
+
48
+ # check if we set our public and secret keys to the environment variable
49
+ if (username.nil? && password.nil?)
50
+ @username = ENV['AIRVEND_USERNAME']
51
+ @password = ENV['AIRVEND_PASSWORD']
52
+ else
53
+ @username = username
54
+ @password = password
55
+ warn "Warning: To ensure your account credentials are safe, It is best to always set your password in the environment variable with AIRVEND_USERNAME & AIRVEND_PASSWORD"
56
+ end
57
+
58
+ # raise this error if no username is passed
59
+ unless !@username.nil?
60
+ raise AirvendBadUserError, "No Username supplied and couldn't find any in environment variables. Make sure to set public key as an environment variable AIRVEND_USERNAME"
61
+ end
62
+
63
+ # raise this error if no password is passed
64
+ unless !@password.nil?
65
+ raise AirvendBadPassError, "No password supplied and couldn't find any in environment variables. Make sure to set secret key as an environment variable AIRVEND_PASSWORD"
66
+ end
67
+
68
+
69
+ if (api_key.nil?)
70
+ @api_key = ENV['AIRVEND_API_KEY']
71
+ else
72
+ @api_key = api_key
73
+ warn "Warning: To ensure your account key is safe, It is best to always set your password in the environment variable with AIRVEND_API_KEY"
74
+ end
75
+
76
+ # raise this error if no username is passed
77
+ unless !@api_key.nil?
78
+ raise AirvendBadKeyError, "No Api Key supplied and couldn't find any in environment variables. Make sure to set public key as an environment variable AIRVEND_USERNAME"
79
+ end
80
+ end
81
+
82
+ # method to return the base url
83
+ def base_url
84
+ return url
85
+ end
86
+
87
+ def headers(hashkey)
88
+ return {
89
+ UpperCaseString.new('Content-Type') => 'application/json',
90
+ UpperCaseString.new('username') => self.username,
91
+ UpperCaseString.new('password') => self.password,
92
+ UpperCaseString.new('hash') => hashkey,
93
+ user_agent: "Airvend-0.1.0"
94
+ }
95
+ end
96
+
97
+ def hash_req(details)
98
+ api_hash = details.to_json+self.api_key
99
+ api_hash = Digest::SHA512.hexdigest api_hash
100
+ return api_hash
101
+ end
102
+
103
+
104
+
105
+ end
@@ -0,0 +1,28 @@
1
+ require "airvend/base/base.rb"
2
+ include Vend
3
+
4
+ class Vend::Airtime < Base
5
+
6
+ def buy(payload)
7
+ params_hash = { 'ref'=> payload[:ref], 'account'=> payload[:phone], 'networkid'=> mno_id(payload[:mno]), 'type'=> "1", 'amount'=> payload[:amount] }
8
+ details = {}
9
+ details.merge!({ 'details'=>params_hash })
10
+ api_hash = @airvendObj.hash_req(details)
11
+ response = vendAdapter(api_hash, details)
12
+ if response.status == 200
13
+ hash = rename_hash(JSON.parse(response.body, { symbolize_names: true }))
14
+ rename_hash(hash[:details])
15
+ hash
16
+ else
17
+ produce_error(response)
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+
24
+ # input = [ref: "usdibisdbsidbsd", mno = "MTN", amount]
25
+ # Dotenv.load(File.expand_path("../.env", __FILE__))
26
+ # a = Airvend.new
27
+ # airtime = Vend::Airtime.new(Airvend.new)
28
+ # payload = { ref: "YOUR-OWN-REF-HERE", account: "08138236694", mno: "mtn", amount: "200"}
@@ -0,0 +1,45 @@
1
+ require "airvend/base/base.rb"
2
+ include Vend
3
+
4
+ class Vend::Internet < Base
5
+
6
+ def buy(payload)
7
+ params_hash = { 'ref'=> payload[:ref], 'account'=> payload[:phone], 'networkid'=> mno_id(payload[:mno]), 'type'=> "2", 'amount'=> payload[:code] }
8
+ details = {}
9
+ details.merge!({ 'details'=>params_hash })
10
+ api_hash = @airvendObj.hash_req(details)
11
+ response = vendAdapter(api_hash, details)
12
+ if response.status == 200
13
+ hash = rename_hash(JSON.parse(response.body, { symbolize_names: true }))
14
+ rename_hash(hash[:details])
15
+ hash
16
+ else
17
+ produce_error(response)
18
+ end
19
+ end
20
+
21
+ def plans(provider)
22
+ data = serialize_plans(get_plans(provider_id(provider), "2")[:details][:message])
23
+ end
24
+
25
+ def serialize_plans(plans)
26
+ plans.sort_by{|e| e[:amount].to_i}.each do |bundle|
27
+ if bundle[:validity] == nil
28
+ bundle[:validity] = "Unlimited"
29
+ elsif bundle[:validity].include? 'Day'
30
+ bundle[:validity].insert(0, " for ")
31
+ bundle[:validity]['Day'] = ' Day'
32
+ elsif bundle[:validity].include? 'Hours'
33
+ bundle[:validity].insert(0, " for ")
34
+ bundle[:validity]['Hours'] = ' Hours'
35
+ elsif bundle[:validity].include? 'Month'
36
+ bundle[:validity].insert(0, " for ")
37
+ bundle[:validity]['Month'] = ' Month'
38
+ elsif bundle[:validity].include? 'Year'
39
+ bundle[:validity].insert(0, " for ")
40
+ bundle[:validity]['Year'] = ' Year'
41
+ end
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,26 @@
1
+ require "airvend/base/base.rb"
2
+ include Vend
3
+
4
+ class Vend::Power < Base
5
+
6
+ def buy(payload)
7
+ params_hash = { 'ref'=> payload[:ref], 'account'=> payload[:account], 'type'=> power_id(payload[:provider], payload[:account_type]), 'amount'=> payload[:amount], 'customernumber'=> payload[:customernumber] }
8
+ details = {}
9
+ details.merge!({ 'details'=>params_hash })
10
+ api_hash = @airvendObj.hash_req(details)
11
+ resp = vendAdapter(api_hash, details)
12
+ if resp.status == 200
13
+ hash = rename_hash(JSON.parse(response.body, { symbolize_names: true }))
14
+ rename_hash(hash[:details])
15
+ hash
16
+ else
17
+ produce_error(response)
18
+ end
19
+ end
20
+
21
+ def verify(payload)
22
+ product_type = power_id(payload[:provider], payload[:account_type])
23
+ verify_customer(product_type, payload[:account])[:details][:message]
24
+ end
25
+
26
+ end
@@ -0,0 +1,29 @@
1
+ require "airvend/base/base.rb"
2
+ include Vend
3
+
4
+ class Vend::Television < Base
5
+
6
+ def buy(payload)
7
+ params_hash = { 'ref'=> payload[:ref], 'account'=> payload[:account], 'type'=> tv_id(payload[:provider]), 'amount'=> payload[:amount], 'customernumber'=> payload[:customernumber], 'invoicePeriod'=>"1" }
8
+ details = {}
9
+ details.merge!({ 'details'=>params_hash })
10
+ api_hash = @airvendObj.hash_req(details)
11
+ resp = vendAdapter(api_hash, details)
12
+ if resp.status == 200
13
+ hash = rename_hash(JSON.parse(response.body, { symbolize_names: true }))
14
+ rename_hash(hash[:details])
15
+ hash
16
+ else
17
+ produce_error(response)
18
+ end
19
+ end
20
+
21
+ def verify(payload)
22
+ verify_customer(tv_id(payload[:provider]), payload[:account])[:details][:message]
23
+ end
24
+
25
+ def plans(provider)
26
+ plans = get_plans("", tv_id(provider))[:details][:message]
27
+ new_plans = plans.each { |p| p.delete(:descrition); p[:description] = p.delete :name; p[:amount] = p[:amount].to_s}
28
+ end
29
+ end
@@ -0,0 +1,180 @@
1
+ require 'faraday'
2
+ require 'faraday/detailed_logger'
3
+ require 'typhoeus'
4
+ class Base
5
+ def initialize(airvendObj)
6
+ @airvendObj = airvendObj
7
+ end
8
+
9
+ def connect(api_hash)
10
+ conn = Faraday.new(:url => @airvendObj.base_url, :headers => @airvendObj.headers(api_hash)) do |faraday|
11
+ faraday.request :url_encoded
12
+ faraday.response :detailed_logger
13
+ faraday.adapter :typhoeus
14
+ end
15
+ return conn
16
+ end
17
+
18
+
19
+ def produce_error(response)
20
+ status = response.status
21
+ if status == 400
22
+ raise AirvendBadRequestError, "There's something wrong with this request - #{response.status}"
23
+ elsif status == 401
24
+ raise AirvendUnauthorizedError, "You're not authorized to access this resource - #{response.status}"
25
+ elsif status == 404
26
+ raise AirvendNotFoundError, "The resource could not be found - #{response.status}"
27
+ elsif status == 409
28
+ raise AirvendConflictError, "The Reference you provided already exists, please use unique reference IDs for every transaction - #{response.status}"
29
+ elsif status == 417
30
+ raise AirvendIncorrectPayload, "Please confirm that your payload data is correct and standard - #{response.status}"
31
+ elsif (400..451).member?(status)
32
+ raise AirvendUnknownClientError, "Please send an email to hey@uche.io explaining how you got this error - #{response.status}"
33
+ elsif (500..511).member?(status)
34
+ raise AirvendServerError.new(response.body)
35
+ end
36
+ end
37
+
38
+ def rename_hash(hash)
39
+ hash.keys.each do |a|
40
+ hash[underscorelize(a.to_s).to_sym] = hash.delete a
41
+ end
42
+ hash
43
+ end
44
+
45
+ def underscorelize(text)
46
+ text.gsub(/::/, '/').
47
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
48
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
49
+ tr("-", "_").
50
+ downcase
51
+ end
52
+
53
+ def get_plans(provider, product_type)
54
+ params_hash = { 'networkid'=> provider, 'type'=> product_type }
55
+ details = {}
56
+ details.merge!({ 'details'=>params_hash })
57
+ api_hash = @airvendObj.hash_req(details)
58
+ begin
59
+ response = productAdapter(api_hash, details)
60
+ rescue
61
+ return response
62
+ else
63
+ hash = rename_hash(JSON.parse(response.body, { symbolize_names: true }))
64
+ details = rename_hash(hash[:details])
65
+ details[:message].each do |d|
66
+ rename_hash(d)
67
+ end
68
+ hash
69
+ end
70
+ end
71
+
72
+ def verify_customer(product_type, account_id)
73
+ params_hash = { 'type'=> product_type, 'account'=> account_id }
74
+ details = {}
75
+ details.merge!({ 'details'=>params_hash })
76
+ api_hash = @airvendObj.hash_req(details)
77
+ begin
78
+ response = verifyAdapter(api_hash, details)
79
+ rescue
80
+ return response
81
+ else
82
+ if response.status == 200
83
+ hash = rename_hash(JSON.parse(response.body, { symbolize_names: true }))
84
+ rename_hash(hash[:details])
85
+ hash
86
+ else
87
+ produce_error(response)
88
+ end
89
+ end
90
+ end
91
+
92
+ def mno_id(mno)
93
+ case mno.downcase
94
+ when "mtn"
95
+ 2
96
+ when "airtel"
97
+ 1
98
+ when "glo"
99
+ 3
100
+ when "9mobile"
101
+ 4
102
+ else
103
+ raise AirvendInvalidProvider, "Invalid Mobile Network Operator, mno can only be 'mtn', 'glo', 'airtel' or '9mobile'"
104
+ end
105
+ end
106
+
107
+ def provider_id(mno)
108
+ case mno.downcase
109
+ when "mtn"
110
+ 2
111
+ when "airtel"
112
+ 1
113
+ when "glo"
114
+ 3
115
+ when "9mobile"
116
+ 4
117
+ else
118
+ raise AirvendInvalidProvider, "Invalid Mobile Network Operator, mno can only be 'mtn', 'glo', 'airtel' or '9mobile'"
119
+ end
120
+ end
121
+
122
+ def power_id(account, account_type)
123
+ x = account.upcase
124
+ y = account_type.upcase
125
+ if x == "IE"
126
+ if y == "POSTPAID"
127
+ return "10"
128
+ elsif y == "PREPAID"
129
+ return "11"
130
+ end
131
+ elsif x == "EKO"
132
+ if y == "POSTPAID"
133
+ return "13"
134
+ elsif y == "PREPAID"
135
+ return "14"
136
+ end
137
+ elsif x == "PHED"
138
+ if y == "POSTPAID"
139
+ return "15"
140
+ elsif y == "PREPAID"
141
+ return "16"
142
+ end
143
+ elsif x == "EEDC"
144
+ if y == "POSTPAID"
145
+ return "22"
146
+ elsif y == "PREPAID"
147
+ return "21"
148
+ end
149
+ elsif x == "KEDCO"
150
+ return "20"
151
+ elsif x == "AEDC"
152
+ if y == "POSTPAID"
153
+ return "25"
154
+ elsif y == "PREPAID"
155
+ return "24"
156
+ end
157
+ elsif x == "IBEDC"
158
+ if y == "POSTPAID"
159
+ return "12"
160
+ elsif y == "PREPAID"
161
+ return "11"
162
+ end
163
+ else
164
+ raise AirvendInvalidProvider, "Invalid Power Provider, see documentation: https://github.com/urchymanny/airvend-ruby#electricity-vending"
165
+ end
166
+ end
167
+
168
+ def tv_id(provider)
169
+ case provider.downcase
170
+ when "dstv"
171
+ 30
172
+ when "gotv"
173
+ 40
174
+ when "startimes"
175
+ 70
176
+ else
177
+ raise AirvendInvalidProvider, "Invalid Power Provider, see documentation: https://github.com/urchymanny/airvend-ruby#electricity-vending"
178
+ end
179
+ end
180
+ end