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.
@@ -1,145 +1,38 @@
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
- # Query the status of any M-Pesa transaction
29
- class TransactionStatus
6
+ # Query the status of any M-Pesa transaction by ID.
7
+ class TransactionStatus < Client
30
8
  class << self
31
- def query(transaction_id, hash = {})
32
- new(transaction_id, hash).query_status
9
+ def call(transaction_id, **options)
10
+ new(transaction_id, **options).query_status
33
11
  end
34
12
  end
35
13
 
36
- attr_reader :token, :transaction_id, :initiator, :security_credential, :party_a, :result_url, :queue_timeout_url,
37
- :identifier_type
14
+ attr_reader :transaction_id
38
15
 
39
- def initialize(transaction_id, hash = {})
40
- @token = MpesaStk::AccessToken.call(hash['key'], hash['secret'])
16
+ def initialize(transaction_id, **options)
17
+ super(**options)
41
18
  @transaction_id = transaction_id
42
- @initiator = hash['initiator'] || ENV.fetch('initiator', nil)
43
- @security_credential = hash['security_credential'] || ENV.fetch('security_credential', nil)
44
- @party_a = hash['party_a'] || ENV.fetch('business_short_code', nil)
45
- @result_url = hash['result_url'] || ENV.fetch('result_url', nil)
46
- @queue_timeout_url = hash['queue_timeout_url'] || ENV.fetch('queue_timeout_url', nil)
47
- @identifier_type = hash['identifier_type'] || '4'
48
19
  end
49
20
 
50
21
  def query_status
51
- response = HTTParty.post(url, headers: headers, body: body)
52
-
53
- raise StandardError, "Failed to query transaction status: #{response.code} - #{response.body}" unless response.success?
54
-
55
- JSON.parse(response.body)
56
- end
57
-
58
- private
59
-
60
- def url
61
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('transaction_status_url', nil)}"
62
- end
63
-
64
- def headers
65
- {
66
- 'Authorization' => "Bearer #{token}",
67
- 'Content-Type' => 'application/json'
68
- }
69
- end
70
-
71
- def body
72
- {
73
- Initiator: get_initiator,
74
- SecurityCredential: get_security_credential,
75
- CommandID: 'TransactionStatusQuery',
76
- TransactionID: transaction_id,
77
- PartyA: get_party_a,
78
- IdentifierType: identifier_type,
79
- ResultURL: get_result_url,
80
- QueueTimeOutURL: get_queue_timeout_url
81
- }.to_json
82
- end
83
-
84
- def get_initiator
85
- if initiator.nil? || initiator.eql?('')
86
- raise ArgumentError, 'Initiator is not defined' if ENV['initiator'].nil? || ENV['initiator'].eql?('')
87
-
88
- ENV.fetch('initiator', nil)
89
-
90
- else
91
- initiator
92
- end
93
- end
94
-
95
- def get_security_credential
96
- if security_credential.nil? || security_credential.eql?('')
97
- if ENV['security_credential'].nil? || ENV['security_credential'].eql?('')
98
- raise ArgumentError, 'Security Credential is not defined'
99
- end
100
-
101
- ENV.fetch('security_credential', nil)
102
-
103
- else
104
- security_credential
105
- end
106
- end
107
-
108
- def get_party_a
109
- if party_a.nil? || party_a.eql?('')
110
- if ENV['business_short_code'].nil? || ENV['business_short_code'].eql?('')
111
- raise ArgumentError, 'PartyA (Business Short Code) is not defined'
112
- end
113
-
114
- ENV.fetch('business_short_code', nil)
115
-
116
- else
117
- party_a
118
- end
119
- end
120
-
121
- def get_result_url
122
- if result_url.nil? || result_url.eql?('')
123
- raise ArgumentError, 'Result URL is not defined' if ENV['result_url'].nil? || ENV['result_url'].eql?('')
124
-
125
- ENV.fetch('result_url', nil)
126
-
127
- else
128
- result_url
129
- end
130
- end
131
-
132
- def get_queue_timeout_url
133
- if queue_timeout_url.nil? || queue_timeout_url.eql?('')
134
- if ENV['queue_timeout_url'].nil? || ENV['queue_timeout_url'].eql?('')
135
- raise ArgumentError, 'Queue Timeout URL is not defined'
136
- end
137
-
138
- ENV.fetch('queue_timeout_url', nil)
139
-
140
- else
141
- queue_timeout_url
142
- end
22
+ post(
23
+ 'transaction_status_url',
24
+ {
25
+ Initiator: option('initiator'),
26
+ SecurityCredential: option('security_credential'),
27
+ CommandID: 'TransactionStatusQuery',
28
+ TransactionID: transaction_id,
29
+ PartyA: option('business_short_code', :party_a),
30
+ IdentifierType: @options.fetch(:identifier_type, '4'),
31
+ ResultURL: option('result_url'),
32
+ QueueTimeOutURL: option('queue_timeout_url')
33
+ },
34
+ error_message: 'Failed to query transaction status'
35
+ )
143
36
  end
144
37
  end
145
38
  end
@@ -1,27 +1,5 @@
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
3
  module MpesaStk
26
- VERSION = '2.0.0'
4
+ VERSION = '3.0.0'
27
5
  end
data/lib/mpesa_stk.rb CHANGED
@@ -1,29 +1,9 @@
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
3
  require 'mpesa_stk/version'
26
- require 'mpesa_stk/push_payment'
4
+ require 'mpesa_stk/config'
5
+ require 'mpesa_stk/client'
6
+ require 'mpesa_stk/access_token'
27
7
  require 'mpesa_stk/push'
28
8
  require 'mpesa_stk/transaction_status'
29
9
  require 'mpesa_stk/stk_push_query'
@@ -36,6 +16,14 @@ require 'mpesa_stk/ratiba'
36
16
  require 'mpesa_stk/iot'
37
17
  require 'mpesa_stk/imsi'
38
18
  require 'mpesa_stk/pull_transactions'
39
- require 'dotenv/load'
19
+
20
+ require 'dotenv/load' unless ENV['MPESA_STK_SKIP_DOTENV'] == 'true'
40
21
  require 'httparty'
41
22
  require 'securerandom'
23
+
24
+ # Ruby client for Safaricom Daraja M-Pesa APIs (STK Push, B2C, B2B, C2B, and related services).
25
+ module MpesaStk
26
+ def self.configure(&block)
27
+ Config.configure(&block)
28
+ end
29
+ end
data/mpesa_stk.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_dependency 'base64', '>= 0.1.0'
33
33
  spec.add_dependency 'csv', '>= 3.0.0'
34
- spec.add_dependency 'httparty', '>= 0.15.6', '< 0.22.0'
34
+ spec.add_dependency 'httparty', '>= 0.15.6', '< 0.25.0'
35
35
  spec.add_dependency 'redis', '>= 4.0'
36
36
  spec.add_dependency 'redis-namespace', '~> 1.5', '>= 1.5.3'
37
37
  spec.add_dependency 'redis-rack', '~> 2.0', '>= 2.0.2'
@@ -40,9 +40,10 @@ Gem::Specification.new do |spec|
40
40
  spec.add_development_dependency 'minitest', '~> 5.20'
41
41
  spec.add_development_dependency 'rake', '>= 12.3.3'
42
42
 
43
- spec.add_development_dependency 'dotenv', '~> 2.8'
44
- spec.add_development_dependency 'pry', '~> 0.12'
45
- spec.add_development_dependency 'pry-nav', '~> 0.3'
43
+ spec.add_development_dependency 'dotenv', '~> 3.0'
44
+ spec.add_development_dependency 'pry', '~> 0.14'
45
+ spec.add_development_dependency 'pry-nav', '~> 1.0'
46
46
  spec.add_development_dependency 'rubocop', '~> 1.0'
47
+ spec.add_development_dependency 'simplecov', '~> 0.22'
47
48
  spec.add_development_dependency 'webmock', '~> 3.18'
48
49
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mpesa_stk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mboya
@@ -47,7 +47,7 @@ dependencies:
47
47
  version: 0.15.6
48
48
  - - "<"
49
49
  - !ruby/object:Gem::Version
50
- version: 0.22.0
50
+ version: 0.25.0
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
@@ -57,7 +57,7 @@ dependencies:
57
57
  version: 0.15.6
58
58
  - - "<"
59
59
  - !ruby/object:Gem::Version
60
- version: 0.22.0
60
+ version: 0.25.0
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: redis
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -160,42 +160,42 @@ dependencies:
160
160
  requirements:
161
161
  - - "~>"
162
162
  - !ruby/object:Gem::Version
163
- version: '2.8'
163
+ version: '3.0'
164
164
  type: :development
165
165
  prerelease: false
166
166
  version_requirements: !ruby/object:Gem::Requirement
167
167
  requirements:
168
168
  - - "~>"
169
169
  - !ruby/object:Gem::Version
170
- version: '2.8'
170
+ version: '3.0'
171
171
  - !ruby/object:Gem::Dependency
172
172
  name: pry
173
173
  requirement: !ruby/object:Gem::Requirement
174
174
  requirements:
175
175
  - - "~>"
176
176
  - !ruby/object:Gem::Version
177
- version: '0.12'
177
+ version: '0.14'
178
178
  type: :development
179
179
  prerelease: false
180
180
  version_requirements: !ruby/object:Gem::Requirement
181
181
  requirements:
182
182
  - - "~>"
183
183
  - !ruby/object:Gem::Version
184
- version: '0.12'
184
+ version: '0.14'
185
185
  - !ruby/object:Gem::Dependency
186
186
  name: pry-nav
187
187
  requirement: !ruby/object:Gem::Requirement
188
188
  requirements:
189
189
  - - "~>"
190
190
  - !ruby/object:Gem::Version
191
- version: '0.3'
191
+ version: '1.0'
192
192
  type: :development
193
193
  prerelease: false
194
194
  version_requirements: !ruby/object:Gem::Requirement
195
195
  requirements:
196
196
  - - "~>"
197
197
  - !ruby/object:Gem::Version
198
- version: '0.3'
198
+ version: '1.0'
199
199
  - !ruby/object:Gem::Dependency
200
200
  name: rubocop
201
201
  requirement: !ruby/object:Gem::Requirement
@@ -210,6 +210,20 @@ dependencies:
210
210
  - - "~>"
211
211
  - !ruby/object:Gem::Version
212
212
  version: '1.0'
213
+ - !ruby/object:Gem::Dependency
214
+ name: simplecov
215
+ requirement: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - "~>"
218
+ - !ruby/object:Gem::Version
219
+ version: '0.22'
220
+ type: :development
221
+ prerelease: false
222
+ version_requirements: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - "~>"
225
+ - !ruby/object:Gem::Version
226
+ version: '0.22'
213
227
  - !ruby/object:Gem::Dependency
214
228
  name: webmock
215
229
  requirement: !ruby/object:Gem::Requirement
@@ -242,6 +256,7 @@ files:
242
256
  - Gemfile
243
257
  - Gemfile.lock
244
258
  - LICENSE.txt
259
+ - MIGRATION.md
245
260
  - README.md
246
261
  - Rakefile
247
262
  - SECURITY.md
@@ -254,11 +269,12 @@ files:
254
269
  - lib/mpesa_stk/b2b.rb
255
270
  - lib/mpesa_stk/b2c.rb
256
271
  - lib/mpesa_stk/c2b.rb
272
+ - lib/mpesa_stk/client.rb
273
+ - lib/mpesa_stk/config.rb
257
274
  - lib/mpesa_stk/imsi.rb
258
275
  - lib/mpesa_stk/iot.rb
259
276
  - lib/mpesa_stk/pull_transactions.rb
260
277
  - lib/mpesa_stk/push.rb
261
- - lib/mpesa_stk/push_payment.rb
262
278
  - lib/mpesa_stk/ratiba.rb
263
279
  - lib/mpesa_stk/reversal.rb
264
280
  - lib/mpesa_stk/stk_push_query.rb
@@ -287,7 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
287
303
  - !ruby/object:Gem::Version
288
304
  version: '0'
289
305
  requirements: []
290
- rubygems_version: 4.0.1
306
+ rubygems_version: 4.0.9
291
307
  specification_version: 4
292
308
  summary: Lipa na M-Pesa Online Payment.
293
309
  test_files: []
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
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 'date'
26
- require 'mpesa_stk/access_token'
27
-
28
- module MpesaStk
29
- # Initiates STK Push payment for a single application using ENV variables
30
- class PushPayment
31
- class << self
32
- def call(amount, phone_number)
33
- new(amount, phone_number).push_payment
34
- end
35
- end
36
-
37
- attr_reader :token, :amount, :phone_number
38
-
39
- def initialize(amount, phone_number)
40
- @token = MpesaStk::AccessToken.call
41
- @amount = amount
42
- @phone_number = phone_number
43
- end
44
-
45
- def push_payment
46
- response = HTTParty.post(url, headers: headers, body: body)
47
-
48
- raise StandardError, "Failed to push payment: #{response.code} - #{response.body}" unless response.success?
49
-
50
- JSON.parse(response.body)
51
- end
52
-
53
- private
54
-
55
- def url
56
- "#{ENV.fetch('base_url', nil)}#{ENV.fetch('process_request_url', nil)}"
57
- end
58
-
59
- def headers
60
- {
61
- 'Authorization' => "Bearer #{token}",
62
- 'Content-Type' => 'application/json'
63
- }
64
- end
65
-
66
- def body
67
- {
68
- BusinessShortCode: ENV.fetch('business_short_code', nil).to_s,
69
- Password: generate_password,
70
- Timestamp: timestamp.to_s,
71
- TransactionType: 'CustomerPayBillOnline',
72
- Amount: amount.to_s,
73
- PartyA: phone_number.to_s,
74
- PartyB: ENV.fetch('business_short_code', nil).to_s,
75
- PhoneNumber: phone_number.to_s,
76
- CallBackURL: ENV.fetch('callback_url', nil).to_s,
77
- AccountReference: generate_bill_reference_number(5),
78
- TransactionDesc: generate_bill_reference_number(5)
79
- }.to_json
80
- end
81
-
82
- def generate_bill_reference_number(number)
83
- charset = Array('A'..'Z') + Array('a'..'z')
84
- Array.new(number) { charset.sample }.join
85
- end
86
-
87
- def timestamp
88
- DateTime.now.strftime('%Y%m%d%H%M%S').to_i
89
- end
90
-
91
- # shortcode
92
- # passkey
93
- # timestamp
94
- def generate_password
95
- key = "#{ENV.fetch('business_short_code', nil)}#{ENV.fetch('business_passkey', nil)}#{timestamp}"
96
- Base64.encode64(key).split("\n").join
97
- end
98
- end
99
- end