paytree 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03f7bf934f81e5f84019e8e2a20026e49db6f363433e7c00ce5b6d6ddbf6d177
4
- data.tar.gz: 6698c7711565529fcd2397120d40fad539e2df905832549f06a8b565f0576a85
3
+ metadata.gz: db5f3b07684338b0a9b63dabf3cc2a00d01adbfcb0c5e8511dd734fc3071d0e7
4
+ data.tar.gz: aa49b4d56b4eba825fa81ef7a5ff9c75708b92b6036ee5e6439fc1b0821a5e15
5
5
  SHA512:
6
- metadata.gz: 3c50a67fe8d0d6b65a6e7268c010097a8c191b991c6bcc5f20ad13cbd227c0621e2fe658d1aec1cb646d1c1e6a95439165b618bf56ed8f6811f37cdfb56c0f5f
7
- data.tar.gz: b4379981cb0ba478550d60de21a74948ca5b7d7ae5d22210cd1cecf6a349a9bfa3bef32fa574d43483bb9bb1b770e432a621315522ac0eb798ef280fe88c9712
6
+ metadata.gz: d56d06d79d80c8a26569cc9c51b3b55eee3b3ec376e69f40ea21a6e37e325e57a2877cf344be727a98b79614b594ca33feffbadc2591e37f3a5f3d3d5d1d788e
7
+ data.tar.gz: d8dade7c6661f6ef505c5b25dbbacbbc744610f2333f9ba8d433bcbc01beea82b694abc7727c9c7a8ecf2d1eccd81e5c69caad380930fc162d3c49ad8f6730b0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paytree (0.2.0)
4
+ paytree (0.3.0)
5
5
  faraday (~> 2.0)
6
6
 
7
7
  GEM
@@ -148,6 +148,7 @@ GEM
148
148
 
149
149
  PLATFORMS
150
150
  x86_64-darwin-24
151
+ x86_64-darwin-25
151
152
  x86_64-linux
152
153
 
153
154
  DEPENDENCIES
data/README.md CHANGED
@@ -18,6 +18,8 @@ Currently supports Kenya's M-Pesa via the Daraja API with plans for additional p
18
18
  - **Convention over Configuration**: One clear setup pattern, opinionated defaults
19
19
  - **Safe Defaults**: Sandbox mode, proper timeouts, comprehensive error handling
20
20
  - **Batteries Included**: STK Push, B2C, B2B, C2B operations out of the box
21
+ - **API Versioning**: Support for both Daraja API v1 and v3 with backward compatibility
22
+ - **Enhanced Reliability**: Automatic token retry with exponential backoff
21
23
  - **Security First**: Credential management, no hardcoded secrets
22
24
 
23
25
  ## Quick Start
@@ -89,12 +91,59 @@ Paytree.configure_mpesa(
89
91
  # shortcode: "YOUR_PRODUCTION_SHORTCODE",
90
92
  # passkey: Rails.application.credentials.mpesa[:passkey],
91
93
  # sandbox: false,
92
- # retryable_errors: ["429.001.01", "500.001.02", "503.001.01"] # Optional: errors to retry
94
+ # api_version: "v1", # Optional: "v1" (default) or "v3"
95
+ # retryable_errors: ["429.001.01", "500.001.02", "503.001.01"] # Optional: errors to retry
93
96
  # )
94
97
  ```
95
98
 
96
99
  ---
97
100
 
101
+ ## API Version Support
102
+
103
+ Paytree supports both M-Pesa Daraja API v1 and v3 endpoints. The API version can be configured globally or via environment variables.
104
+
105
+ ### Configuration Options
106
+
107
+ ```ruby
108
+ # Use v1 API (default - backward compatible)
109
+ Paytree.configure_mpesa(
110
+ key: "YOUR_KEY",
111
+ secret: "YOUR_SECRET",
112
+ api_version: "v1" # Default
113
+ )
114
+
115
+ # Use v3 API (latest features)
116
+ Paytree.configure_mpesa(
117
+ key: "YOUR_KEY",
118
+ secret: "YOUR_SECRET",
119
+ api_version: "v3"
120
+ )
121
+
122
+ # Or via environment variable
123
+ ENV['MPESA_API_VERSION'] = 'v3'
124
+ Paytree.configure_mpesa(
125
+ key: "YOUR_KEY",
126
+ secret: "YOUR_SECRET"
127
+ # api_version automatically picked up from ENV
128
+ )
129
+ ```
130
+
131
+ ### Differences Between v1 and v3
132
+
133
+ | Feature | v1 | v3 |
134
+ |---------|----|----|
135
+ | **Endpoints** | `/mpesa/b2c/v1/paymentrequest` | `/mpesa/b2c/v3/paymentrequest` |
136
+ | **OriginatorConversationID** | Not required | Auto-generated UUID |
137
+ | **Reliability** | Standard | Enhanced with better tracking |
138
+
139
+
140
+ **Backward Compatibility:**
141
+ - Existing code continues to work unchanged (defaults to v1)
142
+ - No breaking changes when upgrading Paytree versions
143
+ - Can switch between v1/v3 by changing configuration only
144
+
145
+ ---
146
+
98
147
  ## Usage Examples
99
148
 
100
149
  ### STK Push (Customer Payment)
@@ -168,7 +217,7 @@ end
168
217
 
169
218
  Send funds directly to a customer’s M-Pesa wallet via the B2C API.
170
219
 
171
- ### Example
220
+ ### Basic Example
172
221
  ```ruby
173
222
  response = Paytree::Mpesa::B2C.call(
174
223
  phone_number: "254712345678",
@@ -186,6 +235,43 @@ else
186
235
  end
187
236
  ```
188
237
 
238
+ ### v3 API Features
239
+
240
+ When using `api_version: "v3"`, B2C calls automatically include an `OriginatorConversationID` for enhanced tracking:
241
+
242
+ ```ruby
243
+ # Configure for v3 API
244
+ Paytree.configure_mpesa(
245
+ key: "YOUR_KEY",
246
+ secret: "YOUR_SECRET",
247
+ api_version: "v3"
248
+ )
249
+
250
+ # Same call, but now uses v3 endpoint with auto-generated OriginatorConversationID
251
+ response = Paytree::Mpesa::B2C.call(
252
+ phone_number: "254712345678",
253
+ amount: 100
254
+ )
255
+
256
+ # v3 response includes additional tracking data
257
+ if response.success?
258
+ puts "Conversation ID: #{response.data["ConversationID"]}"
259
+ puts "Originator ID: #{response.data["OriginatorConversationID"]}" # Auto-generated UUID
260
+ end
261
+ ```
262
+
263
+ ### Custom OriginatorConversationID (v3 only)
264
+
265
+ You can provide your own tracking ID for v3 API calls:
266
+
267
+ ```ruby
268
+ response = Paytree::Mpesa::B2C.call(
269
+ phone_number: "254712345678",
270
+ amount: 100,
271
+ originator_conversation_id: "CUSTOM-TRACK-#{Time.now.to_i}"
272
+ )
273
+ ```
274
+
189
275
  ---
190
276
 
191
277
  ## C2B (Customer to Business)
@@ -321,6 +407,9 @@ Paytree allows you to configure which error codes should be considered retryable
321
407
  - `"429.001.01"` - Rate limit exceeded
322
408
  - `"500.001.02"` - Temporary server error
323
409
  - `"503.001.01"` - Service temporarily unavailable
410
+ - `"timeout.connection"` - Network connection timeout (Net::OpenTimeout)
411
+ - `"timeout.read"` - Network read timeout (Net::ReadTimeout)
412
+ - `"timeout.request"` - HTTP request timeout (Faraday::TimeoutError)
324
413
 
325
414
  Configure retryable errors during setup:
326
415
 
@@ -328,7 +417,14 @@ Configure retryable errors during setup:
328
417
  Paytree.configure_mpesa(
329
418
  key: "YOUR_KEY",
330
419
  secret: "YOUR_SECRET",
331
- retryable_errors: ["429.001.01", "500.001.02", "503.001.01"]
420
+ retryable_errors: [
421
+ "429.001.01", # Rate limit
422
+ "500.001.02", # Server error
423
+ "503.001.01", # Service unavailable
424
+ "timeout.connection", # Connection timeout
425
+ "timeout.read", # Read timeout
426
+ "timeout.request" # Request timeout
427
+ ]
332
428
  )
333
429
  ```
334
430
 
@@ -5,7 +5,7 @@ module Paytree
5
5
  class Mpesa
6
6
  attr_accessor :key, :secret, :shortcode, :passkey, :adapter,
7
7
  :initiator_name, :initiator_password, :sandbox,
8
- :extras, :timeout, :retryable_errors
8
+ :extras, :timeout, :retryable_errors, :api_version
9
9
 
10
10
  def initialize
11
11
  @extras = {}
@@ -13,6 +13,7 @@ module Paytree
13
13
  @mutex = Mutex.new
14
14
  @timeout = 30 # Default 30 second timeout
15
15
  @retryable_errors = [] # Default empty array
16
+ @api_version = "v1" # Default to v1 for backward compatibility
16
17
  end
17
18
 
18
19
  def base_url
@@ -5,7 +5,9 @@ module Paytree
5
5
  module Adapters
6
6
  module Daraja
7
7
  class B2B < Base
8
- ENDPOINT = "/mpesa/b2b/v1/paymentrequest"
8
+ def self.endpoint
9
+ "/mpesa/b2b/#{config.api_version}/paymentrequest"
10
+ end
9
11
 
10
12
  class << self
11
13
  def call(short_code:, receiver_shortcode:, amount:, account_reference:, **opts)
@@ -28,7 +30,7 @@ module Paytree
28
30
  ResultURL: config.extras[:result_url]
29
31
  }.compact
30
32
 
31
- post_to_mpesa(:b2b, ENDPOINT, payload)
33
+ post_to_mpesa(:b2b, endpoint, payload)
32
34
  end
33
35
  end
34
36
  end
@@ -5,7 +5,9 @@ module Paytree
5
5
  module Adapters
6
6
  module Daraja
7
7
  class B2C < Base
8
- ENDPOINT = "/mpesa/b2c/v1/paymentrequest"
8
+ def self.endpoint
9
+ "/mpesa/b2c/#{config.api_version}/paymentrequest"
10
+ end
9
11
 
10
12
  class << self
11
13
  def call(phone_number:, amount:, **opts)
@@ -23,9 +25,14 @@ module Paytree
23
25
  CommandID: opts[:command_id] || "BusinessPayment",
24
26
  Remarks: opts[:remarks] || "OK",
25
27
  Occasion: opts[:occasion] || "Payment"
26
- }.compact
28
+ }
29
+
30
+ # Add OriginatorConversationID for v3
31
+ if config.api_version == "v3"
32
+ payload[:OriginatorConversationID] = opts[:originator_conversation_id] || generate_conversation_id
33
+ end
27
34
 
28
- post_to_mpesa(:b2c, ENDPOINT, payload)
35
+ post_to_mpesa(:b2c, endpoint, payload.compact)
29
36
  end
30
37
  end
31
38
  end
@@ -1,4 +1,5 @@
1
1
  require "base64"
2
+ require "securerandom"
2
3
  require_relative "response_helpers"
3
4
  require_relative "../../../utils/error_handling"
4
5
 
@@ -106,6 +107,10 @@ module Paytree
106
107
  end
107
108
  end
108
109
 
110
+ def generate_conversation_id
111
+ SecureRandom.uuid
112
+ end
113
+
109
114
  private
110
115
 
111
116
  def fetch_token
@@ -7,7 +7,7 @@ module Paytree
7
7
  Paytree::Errors::Base => e
8
8
  emit_error(e, context)
9
9
  raise
10
- rescue Faraday::TimeoutError => e
10
+ rescue Faraday::TimeoutError, Net::OpenTimeout, Net::ReadTimeout => e
11
11
  handle_faraday_error(
12
12
  e,
13
13
  context,
@@ -54,7 +54,11 @@ module Paytree
54
54
  code = info[:code]
55
55
  else
56
56
  message = error.message
57
- code = nil
57
+ code = case error
58
+ when Net::OpenTimeout then "timeout.connection"
59
+ when Net::ReadTimeout then "timeout.read"
60
+ when Faraday::TimeoutError then "timeout.request"
61
+ end
58
62
  end
59
63
 
60
64
  wrap_and_raise(
@@ -1,3 +1,3 @@
1
1
  module Paytree
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/paytree.rb CHANGED
@@ -51,7 +51,8 @@ module Paytree
51
51
  passkey: "MPESA_PASSKEY",
52
52
  initiator_name: "MPESA_INITIATOR_NAME",
53
53
  initiator_password: "MPESA_INITIATOR_PASSWORD",
54
- sandbox: "MPESA_SANDBOX"
54
+ sandbox: "MPESA_SANDBOX",
55
+ api_version: "MPESA_API_VERSION"
55
56
  }
56
57
 
57
58
  config = {}
data/paytree.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.files = Dir.chdir(__dir__) do
29
29
  `git ls-files -z`.split("\x0").reject do |f|
30
- f.match(%r{^(test|spec|features|bin|exe)/}) || f.include?(".git")
30
+ f.match(%r{^(test|spec|features|bin|exe)/}) || f.include?(".git") || f.end_with?(".gem")
31
31
  end
32
32
  end
33
33
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paytree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Chuck
@@ -123,7 +123,6 @@ files:
123
123
  - lib/paytree/response.rb
124
124
  - lib/paytree/utils/error_handling.rb
125
125
  - lib/paytree/version.rb
126
- - paytree-0.2.0.gem
127
126
  - paytree.gemspec
128
127
  - sig/paytree.rbs
129
128
  homepage: https://github.com/mundanecodes/paytree
@@ -154,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
153
  - !ruby/object:Gem::Version
155
154
  version: '0'
156
155
  requirements: []
157
- rubygems_version: 3.6.9
156
+ rubygems_version: 3.6.7
158
157
  specification_version: 4
159
158
  summary: A Ruby wrapper for the Mpesa API in Kenya.
160
159
  test_files: []
data/paytree-0.2.0.gem DELETED
Binary file