airvend 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|