mpesa_stk 2.0.0 → 3.0.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.
data/lib/mpesa_stk/c2b.rb CHANGED
@@ -1,127 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018 mboya
4
- #
5
- # MIT License
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the "Software"), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in
15
- # all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- # THE SOFTWARE.
24
-
25
- require 'mpesa_stk/access_token'
3
+ require 'mpesa_stk/client'
26
4
 
27
5
  module MpesaStk
28
- # Customer to Business API - register URLs and simulate C2B payments
29
- class C2B
6
+ # Register C2B URLs and simulate customer-to-business payments.
7
+ class C2B < Client
30
8
  class << self
31
- def register_url(hash = {})
32
- new(hash).register
9
+ def register(**options)
10
+ new(**options).register
33
11
  end
34
12
 
35
- def simulate(amount, phone_number, hash = {})
36
- new(hash).simulate_payment(amount, phone_number)
13
+ def call(amount, phone_number, **options)
14
+ new(**options).simulate_payment(amount, phone_number)
37
15
  end
38
16
  end
39
17
 
40
- attr_reader :token, :short_code, :response_type, :confirmation_url, :validation_url, :command_id, :bill_ref_number
41
-
42
- def initialize(hash = {})
43
- @token = MpesaStk::AccessToken.call(hash['key'], hash['secret'])
44
- @short_code = hash['short_code'] || ENV.fetch('business_short_code', nil)
45
- @response_type = hash['response_type'] || 'Completed'
46
- @confirmation_url = hash['confirmation_url'] || ENV.fetch('confirmation_url', nil)
47
- @validation_url = hash['validation_url']
48
- @command_id = hash['command_id'] || 'CustomerPayBillOnline'
49
- @bill_ref_number = hash['bill_ref_number'] || ''
50
- end
51
-
52
18
  def register
53
- response = HTTParty.post(register_url_endpoint, headers: headers, body: register_body)
54
-
55
- raise StandardError, "Failed to register C2B URL: #{response.code} - #{response.body}" unless response.success?
56
-
57
- JSON.parse(response.body)
58
- end
59
-
60
- def simulate_payment(amount, phone_number)
61
- response = HTTParty.post(simulate_url_endpoint, headers: headers, body: simulate_body(amount, phone_number))
62
-
63
- raise StandardError, "Failed to simulate C2B payment: #{response.code} - #{response.body}" unless response.success?
64
-
65
- JSON.parse(response.body)
66
- end
67
-
68
- private
69
-
70
- def register_url_endpoint
71
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('c2b_register_url', nil)}"
72
- end
73
-
74
- def simulate_url_endpoint
75
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('c2b_simulate_url', nil)}"
76
- end
77
-
78
- def headers
79
- {
80
- 'Authorization' => "Bearer #{token}",
81
- 'Content-Type' => 'application/json'
82
- }
83
- end
84
-
85
- def register_body
86
- body_hash = {
87
- ShortCode: get_short_code,
88
- ResponseType: response_type,
89
- ConfirmationURL: get_confirmation_url
19
+ body = {
20
+ ShortCode: option('business_short_code', :short_code),
21
+ ResponseType: @options.fetch(:response_type, 'Completed'),
22
+ ConfirmationURL: option('confirmation_url')
90
23
  }
91
- body_hash[:ValidationURL] = validation_url if validation_url
92
- body_hash.to_json
93
- end
24
+ validation_url = optional_option('validation_url')
25
+ body[:ValidationURL] = validation_url if validation_url
94
26
 
95
- def simulate_body(amount, phone_number)
96
- {
97
- ShortCode: get_short_code,
98
- CommandID: command_id,
99
- Amount: amount.to_s,
100
- Msisdn: phone_number,
101
- BillRefNumber: bill_ref_number
102
- }.to_json
27
+ post('c2b_register_url', body, error_message: 'Failed to register C2B URL')
103
28
  end
104
29
 
105
- def get_short_code
106
- if short_code.nil? || short_code.eql?('')
107
- raise ArgumentError, 'Short Code is not defined' if ENV['business_short_code'].nil? || ENV['business_short_code'].eql?('')
108
-
109
- ENV.fetch('business_short_code', nil)
110
-
111
- else
112
- short_code
113
- end
114
- end
115
-
116
- def get_confirmation_url
117
- if confirmation_url.nil? || confirmation_url.eql?('')
118
- raise ArgumentError, 'Confirmation URL is not defined' if ENV['confirmation_url'].nil? || ENV['confirmation_url'].eql?('')
119
-
120
- ENV.fetch('confirmation_url', nil)
121
-
122
- else
123
- confirmation_url
124
- end
30
+ def simulate_payment(amount, phone_number)
31
+ post(
32
+ 'c2b_simulate_url',
33
+ {
34
+ ShortCode: option('business_short_code', :short_code),
35
+ CommandID: @options.fetch(:command_id, 'CustomerPayBillOnline'),
36
+ Amount: amount.to_s,
37
+ Msisdn: phone_number,
38
+ BillRefNumber: @options.fetch(:bill_ref_number, '')
39
+ },
40
+ error_message: 'Failed to simulate C2B payment'
41
+ )
125
42
  end
126
43
  end
127
44
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'date'
5
+ require 'json'
6
+ require 'mpesa_stk/config'
7
+ require 'mpesa_stk/access_token'
8
+
9
+ module MpesaStk
10
+ # Shared OAuth, HTTP, and configuration helpers for Daraja API clients
11
+ class Client
12
+ AUTH_KEYS = %i[key secret].freeze
13
+
14
+ def initialize(key: nil, secret: nil, **options)
15
+ @key = key
16
+ @secret = secret
17
+ @options = options
18
+ end
19
+
20
+ def token
21
+ @token ||= AccessToken.call(@key, @secret)
22
+ end
23
+
24
+ def post(path_env_key, body, error_message: 'Request failed')
25
+ url = "#{Config.fetch('base_url')}#{Config.fetch(path_env_key)}"
26
+ response = HTTParty.post(url, headers: json_headers, body: body.to_json)
27
+
28
+ raise StandardError, "#{error_message}: #{response.code} - #{response.body}" unless response.success?
29
+
30
+ JSON.parse(response.body)
31
+ end
32
+
33
+ def json_headers
34
+ {
35
+ 'Authorization' => "Bearer #{token}",
36
+ 'Content-Type' => 'application/json'
37
+ }
38
+ end
39
+
40
+ def stk_timestamp
41
+ DateTime.now.strftime('%Y%m%d%H%M%S').to_i
42
+ end
43
+
44
+ def stk_password(short_code, passkey, timestamp = stk_timestamp)
45
+ Base64.encode64("#{short_code}#{passkey}#{timestamp}").delete("\n")
46
+ end
47
+
48
+ def random_reference(length = 5)
49
+ charset = Array('A'..'Z') + Array('a'..'z')
50
+ Array.new(length) { charset.sample }.join
51
+ end
52
+
53
+ def option(env_key, keyword_key = nil)
54
+ keyword_key ||= env_key.to_s.tr('-', '_').to_sym
55
+ Config.fetch(env_key.to_s, @options[keyword_key])
56
+ end
57
+
58
+ def optional_option(env_key, keyword_key = nil)
59
+ keyword_key ||= env_key.to_s.tr('-', '_').to_sym
60
+ Config.env(env_key.to_s, @options[keyword_key])
61
+ end
62
+
63
+ def self.extract_auth_options(kwargs)
64
+ kwargs.slice(*AUTH_KEYS)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MpesaStk
4
+ # Central configuration: ENV defaults with optional overrides via MpesaStk.configure
5
+ class Config
6
+ class << self
7
+ attr_writer :settings
8
+
9
+ def settings
10
+ @settings ||= {}
11
+ end
12
+
13
+ def configure
14
+ yield(settings)
15
+ end
16
+
17
+ def env(name, override = nil)
18
+ override = nil if override.respond_to?(:empty?) && override.empty?
19
+
20
+ value = settings[name.to_sym]
21
+ value = settings[name.to_s] if value.nil? && settings.key?(name.to_s)
22
+ value = override unless override.nil?
23
+ value = ENV.fetch(name.to_s, nil) if value.nil? || (value.respond_to?(:empty?) && value.empty?)
24
+
25
+ value
26
+ end
27
+
28
+ def fetch(name, override = nil, label: nil)
29
+ value = env(name, override)
30
+ raise ArgumentError, "#{label || name} is not defined" if value.nil? || value.to_s.empty?
31
+
32
+ value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,70 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018 mboya
4
- #
5
- # MIT License
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the "Software"), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in
15
- # all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- # THE SOFTWARE.
24
-
25
- require 'mpesa_stk/access_token'
3
+ require 'mpesa_stk/client'
26
4
 
27
5
  module MpesaStk
28
- # IMSI/SWAP Operations - query IMSI and SIM swap information for fraud prevention
29
- class IMSI
6
+ # Check customer account type indicator (ATI) via IMSI API.
7
+ class IMSI < Client
30
8
  class << self
31
- def check_ati(customer_number, hash = {}, version: 'v1')
32
- new(hash).check_ati(customer_number, version)
9
+ def call(customer_number, version: 'v1', **options)
10
+ new(**options).check_ati(customer_number, version)
33
11
  end
34
12
  end
35
13
 
36
- attr_reader :token
37
-
38
- def initialize(hash = {})
39
- @token = MpesaStk::AccessToken.call(hash['key'], hash['secret'])
40
- end
41
-
42
14
  def check_ati(customer_number, version = 'v1')
43
- endpoint = version == 'v2' ? '/imsi/v2/checkATI' : '/imsi/v1/checkATI'
44
- response = HTTParty.post(url(endpoint), headers: headers, body: body(customer_number))
15
+ endpoint = version.to_s == 'v2' ? '/imsi/v2/checkATI' : '/imsi/v1/checkATI'
16
+ url = "#{Config.fetch('base_url')}#{endpoint}"
17
+ response = HTTParty.post(
18
+ url,
19
+ headers: json_headers,
20
+ body: { customerNumber: customer_number }.to_json
21
+ )
45
22
 
46
23
  raise StandardError, "Failed to check ATI: #{response.code} - #{response.body}" unless response.success?
47
24
 
48
25
  JSON.parse(response.body)
49
26
  end
50
-
51
- private
52
-
53
- def url(endpoint)
54
- "#{ENV.fetch('base_url', nil)}#{endpoint}"
55
- end
56
-
57
- def headers
58
- {
59
- 'Authorization' => "Bearer #{token}",
60
- 'Content-Type' => 'application/json'
61
- }
62
- end
63
-
64
- def body(customer_number)
65
- {
66
- customerNumber: customer_number
67
- }.to_json
68
- end
69
27
  end
70
28
  end
data/lib/mpesa_stk/iot.rb CHANGED
@@ -1,53 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018 mboya
4
- #
5
- # MIT License
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the "Software"), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in
15
- # all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- # THE SOFTWARE.
24
-
25
- require 'mpesa_stk/access_token'
3
+ require 'mpesa_stk/client'
26
4
  require 'securerandom'
27
5
 
28
6
  module MpesaStk
29
- # IoT SIM Management & Messaging - manage SIM cards and send/receive messages for IoT devices
30
- class IoT
7
+ # IoT SIM management and messaging via Safaricom SIM portal API.
8
+ class IoT < Client
31
9
  class << self
32
- def sims(hash = {})
33
- new(hash)
10
+ def call(action, *args, **options)
11
+ new(**options).public_send(action, *args)
34
12
  end
35
13
 
36
- def messaging(hash = {})
37
- new(hash)
14
+ def list_sims(**options)
15
+ new(**options).get_all_sims(
16
+ start_at_index: options.fetch(:start_at_index, 0),
17
+ page_size: options.fetch(:page_size, 10)
18
+ )
19
+ end
20
+
21
+ def send_message(msisdn, message, **options)
22
+ new(**options).send_single_message(msisdn, message)
38
23
  end
39
24
  end
40
25
 
41
- attr_reader :token, :api_key, :vpn_group, :username
26
+ attr_reader :api_key, :vpn_group, :username
42
27
 
43
- def initialize(hash = {})
44
- @token = MpesaStk::AccessToken.call(hash['key'], hash['secret'])
45
- @api_key = hash['api_key'] || ENV['iot_api_key'] || 'Yl4S3KEcr173mbeUdYdjf147IuG3rJ824ArMkP6Z'
46
- @vpn_group = hash['vpn_group'] || ENV['vpn_group'] || ''
47
- @username = hash['username'] || ENV['username'] || ''
28
+ def initialize(key: nil, secret: nil, api_key: nil, vpn_group: nil, username: nil, **options)
29
+ super(key: key, secret: secret, **options)
30
+ @api_key = Config.env('iot_api_key', api_key) || 'Yl4S3KEcr173mbeUdYdjf147IuG3rJ824ArMkP6Z'
31
+ @vpn_group = Config.env('vpn_group', vpn_group) || ''
32
+ @username = Config.env('username', username) || ''
48
33
  end
49
34
 
50
- # SIM Operations
51
35
  def get_all_sims(start_at_index: 0, page_size: 10)
52
36
  post_request('/allsims', {
53
37
  vpnGroup: [vpn_group],
@@ -58,27 +42,15 @@ module MpesaStk
58
42
  end
59
43
 
60
44
  def query_lifecycle_status(msisdn)
61
- post_request('/queryLifeCycleStatus', {
62
- msisdn: msisdn,
63
- vpnGroup: vpn_group,
64
- username: username
65
- })
45
+ post_request('/queryLifeCycleStatus', { msisdn: msisdn, vpnGroup: vpn_group, username: username })
66
46
  end
67
47
 
68
48
  def query_customer_info(msisdn)
69
- post_request('/querycustomerinfo', {
70
- msisdn: msisdn,
71
- vpnGroup: vpn_group,
72
- username: username
73
- })
49
+ post_request('/querycustomerinfo', { msisdn: msisdn, vpnGroup: vpn_group, username: username })
74
50
  end
75
51
 
76
52
  def sim_activation(msisdn)
77
- post_request('/simactivation', {
78
- msisdn: msisdn,
79
- vpnGroup: vpn_group,
80
- username: username
81
- })
53
+ post_request('/simactivation', { msisdn: msisdn, vpnGroup: vpn_group, username: username })
82
54
  end
83
55
 
84
56
  def get_activation_trends(start_date:, stop_date:)
@@ -100,11 +72,7 @@ module MpesaStk
100
72
  end
101
73
 
102
74
  def get_location_info(msisdn)
103
- post_request('/getlocationinfo', {
104
- msisdn: msisdn,
105
- vpnGroup: vpn_group,
106
- username: username
107
- })
75
+ post_request('/getlocationinfo', { msisdn: msisdn, vpnGroup: vpn_group, username: username })
108
76
  end
109
77
 
110
78
  def suspend_unsuspend_sub(msisdn, product, operation)
@@ -117,11 +85,8 @@ module MpesaStk
117
85
  })
118
86
  end
119
87
 
120
- # Messaging Operations
121
88
  def get_all_messages(page_no: 1, page_size: 10)
122
- get_request("/getallmessages?pageNo=#{page_no}&pageSize=#{page_size}", {
123
- vpnGroup: vpn_group
124
- })
89
+ get_request("/getallmessages?pageNo=#{page_no}&pageSize=#{page_size}", { vpnGroup: vpn_group })
125
90
  end
126
91
 
127
92
  def search_messages(search_value, page_no: 1, page_size: 5)
@@ -152,28 +117,20 @@ module MpesaStk
152
117
  end
153
118
 
154
119
  def delete_message(message_id)
155
- post_request('/deletemessage', {
156
- id: message_id,
157
- vpnGroup: vpn_group,
158
- username: username
159
- })
120
+ post_request('/deletemessage', { id: message_id, vpnGroup: vpn_group, username: username })
160
121
  end
161
122
 
162
123
  def delete_message_thread(msisdn)
163
- post_request('/deleteMessageThread', {
164
- msisdn: msisdn,
165
- vpnGroup: vpn_group,
166
- username: username
167
- })
124
+ post_request('/deleteMessageThread', { msisdn: msisdn, vpnGroup: vpn_group, username: username })
168
125
  end
169
126
 
170
127
  private
171
128
 
172
129
  def base_url
173
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('iot_base_url', nil)}"
130
+ "#{Config.fetch('base_url')}#{Config.fetch('iot_base_url')}"
174
131
  end
175
132
 
176
- def headers(msisdn: nil)
133
+ def iot_headers(msisdn: nil)
177
134
  headers_hash = {
178
135
  'Authorization' => "Bearer #{token}",
179
136
  'Content-Type' => 'application/json',
@@ -188,7 +145,11 @@ module MpesaStk
188
145
  end
189
146
 
190
147
  def post_request(endpoint, body)
191
- response = HTTParty.post("#{base_url}#{endpoint}", headers: headers(msisdn: body[:msisdn]), body: body.to_json)
148
+ response = HTTParty.post(
149
+ "#{base_url}#{endpoint}",
150
+ headers: iot_headers(msisdn: body[:msisdn]),
151
+ body: body.to_json
152
+ )
192
153
 
193
154
  raise StandardError, "Failed IoT request: #{response.code} - #{response.body}" unless response.success?
194
155
 
@@ -196,7 +157,7 @@ module MpesaStk
196
157
  end
197
158
 
198
159
  def get_request(endpoint, body)
199
- response = HTTParty.post("#{base_url}#{endpoint}", headers: headers, body: body.to_json)
160
+ response = HTTParty.post("#{base_url}#{endpoint}", headers: iot_headers, body: body.to_json)
200
161
 
201
162
  raise StandardError, "Failed IoT request: #{response.code} - #{response.body}" unless response.success?
202
163
 
@@ -1,126 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018 mboya
4
- #
5
- # MIT License
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the "Software"), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in
15
- # all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- # THE SOFTWARE.
24
-
25
- require 'mpesa_stk/access_token'
3
+ require 'mpesa_stk/client'
26
4
 
27
5
  module MpesaStk
28
- # Pull Transactions API - register URLs and query historical transaction data
29
- class PullTransactions
6
+ # Register and query pull transaction callbacks.
7
+ class PullTransactions < Client
30
8
  class << self
31
- def register(hash = {})
32
- new(hash).register_url
9
+ def register(**options)
10
+ new(**options).register_url
33
11
  end
34
12
 
35
- def query(start_date, end_date, hash = {})
36
- new(hash).query_transactions(start_date, end_date)
13
+ def call(start_date, end_date, **options)
14
+ new(**options).query_transactions(start_date, end_date)
37
15
  end
38
16
  end
39
17
 
40
- attr_reader :token, :short_code, :request_type, :nominated_number, :callback_url, :offset_value
41
-
42
- def initialize(hash = {})
43
- @token = MpesaStk::AccessToken.call(hash['key'], hash['secret'])
44
- @short_code = hash['short_code'] || ENV.fetch('business_short_code', nil)
45
- @request_type = hash['request_type'] || ''
46
- @nominated_number = hash['nominated_number'] || ''
47
- @callback_url = hash['callback_url'] || ENV.fetch('callback_url', nil)
48
- @offset_value = hash['offset_value'] || '0'
49
- end
50
-
51
18
  def register_url
52
- response = HTTParty.post(register_endpoint, headers: headers, body: register_body)
53
-
54
- unless response.success?
55
- raise StandardError, "Failed to register pull transactions URL: #{response.code} - #{response.body}"
56
- end
57
-
58
- JSON.parse(response.body)
19
+ post(
20
+ 'pull_transactions_register_url',
21
+ {
22
+ ShortCode: option('business_short_code', :short_code),
23
+ RequestType: @options.fetch(:request_type, ''),
24
+ NominatedNumber: @options.fetch(:nominated_number, ''),
25
+ CallBackURL: option('callback_url')
26
+ },
27
+ error_message: 'Failed to register pull transactions URL'
28
+ )
59
29
  end
60
30
 
61
31
  def query_transactions(start_date, end_date)
62
- response = HTTParty.post(query_endpoint, headers: headers, body: query_body(start_date, end_date))
63
-
64
- raise StandardError, "Failed to query pull transactions: #{response.code} - #{response.body}" unless response.success?
65
-
66
- JSON.parse(response.body)
67
- end
68
-
69
- private
70
-
71
- def register_endpoint
72
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('pull_transactions_register_url', nil)}"
73
- end
74
-
75
- def query_endpoint
76
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('pull_transactions_query_url', nil)}"
77
- end
78
-
79
- def headers
80
- {
81
- 'Authorization' => "Bearer #{token}",
82
- 'Content-Type' => 'application/json'
83
- }
84
- end
85
-
86
- def register_body
87
- {
88
- ShortCode: get_short_code,
89
- RequestType: request_type,
90
- NominatedNumber: nominated_number,
91
- CallBackURL: get_callback_url
92
- }.to_json
93
- end
94
-
95
- def query_body(start_date, end_date)
96
- {
97
- ShortCode: get_short_code,
98
- StartDate: start_date,
99
- EndDate: end_date,
100
- OffSetValue: offset_value
101
- }.to_json
102
- end
103
-
104
- def get_short_code
105
- if short_code.nil? || short_code.eql?('')
106
- raise ArgumentError, 'Short Code is not defined' if ENV['business_short_code'].nil? || ENV['business_short_code'].eql?('')
107
-
108
- ENV.fetch('business_short_code', nil)
109
-
110
- else
111
- short_code
112
- end
113
- end
114
-
115
- def get_callback_url
116
- if callback_url.nil? || callback_url.eql?('')
117
- raise ArgumentError, 'Callback URL is not defined' if ENV['callback_url'].nil? || ENV['callback_url'].eql?('')
118
-
119
- ENV.fetch('callback_url', nil)
120
-
121
- else
122
- callback_url
123
- end
32
+ post(
33
+ 'pull_transactions_query_url',
34
+ {
35
+ ShortCode: option('business_short_code', :short_code),
36
+ StartDate: start_date,
37
+ EndDate: end_date,
38
+ OffSetValue: @options.fetch(:offset_value, '0')
39
+ },
40
+ error_message: 'Failed to query pull transactions'
41
+ )
124
42
  end
125
43
  end
126
44
  end