airvend 0.1.0
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.
- checksums.yaml +7 -0
- data/.byebug_history +19 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +148 -0
- data/LICENSE.txt +21 -0
- data/README.md +346 -0
- data/Rakefile +12 -0
- data/airvend.gemspec +43 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/airvend.rb +105 -0
- data/lib/airvend/airvend_objects/airtime.rb +28 -0
- data/lib/airvend/airvend_objects/internet.rb +45 -0
- data/lib/airvend/airvend_objects/power.rb +26 -0
- data/lib/airvend/airvend_objects/television.rb +29 -0
- data/lib/airvend/base/base.rb +180 -0
- data/lib/airvend/base_endpoints.rb +21 -0
- data/lib/airvend/errors.rb +37 -0
- data/lib/airvend/upper_case_string.rb +5 -0
- data/lib/airvend/vend.rb +43 -0
- data/lib/airvend/version.rb +5 -0
- metadata +166 -0
data/Rakefile
ADDED
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
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
|