paytree 0.2.0 → 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 +4 -4
- data/Gemfile.lock +2 -1
- data/README.md +112 -6
- data/lib/paytree/configs/mpesa.rb +2 -1
- data/lib/paytree/mpesa/adapters/daraja/b2b.rb +4 -2
- data/lib/paytree/mpesa/adapters/daraja/b2c.rb +10 -3
- data/lib/paytree/mpesa/adapters/daraja/base.rb +5 -0
- data/lib/paytree/utils/error_handling.rb +6 -2
- data/lib/paytree/version.rb +1 -1
- data/lib/paytree.rb +2 -1
- data/paytree.gemspec +7 -3
- metadata +12 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db5f3b07684338b0a9b63dabf3cc2a00d01adbfcb0c5e8511dd734fc3071d0e7
|
4
|
+
data.tar.gz: aa49b4d56b4eba825fa81ef7a5ff9c75708b92b6036ee5e6439fc1b0821a5e15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d56d06d79d80c8a26569cc9c51b3b55eee3b3ec376e69f40ea21a6e37e325e57a2877cf344be727a98b79614b594ca33feffbadc2591e37f3a5f3d3d5d1d788e
|
7
|
+
data.tar.gz: d8dade7c6661f6ef505c5b25dbbacbbc744610f2333f9ba8d433bcbc01beea82b694abc7727c9c7a8ecf2d1eccd81e5c69caad380930fc162d3c49ad8f6730b0
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
<p align="center">
|
2
|
+
<img src="https://badge.fury.io/rb/paytree.svg" />
|
3
|
+
<img src="https://img.shields.io/badge/license-MIT-green.svg" />
|
4
|
+
<img src="https://img.shields.io/badge/ruby-3.2+-red" />
|
5
|
+
<a href="https://github.com/mundanecodes/paytree/actions">
|
6
|
+
<img src="https://github.com/mundanecodes/paytree/actions/workflows/ci.yml/badge.svg" />
|
7
|
+
</a>
|
8
|
+
</p>
|
9
|
+
|
1
10
|
# Paytree
|
2
11
|
|
3
|
-
A simple, highly opinionated Rails-optional Ruby gem for mobile money integrations.
|
12
|
+
A simple, highly opinionated Rails-optional Ruby gem for mobile money integrations.
|
13
|
+
Currently supports Kenya's M-Pesa via the Daraja API with plans for additional providers.
|
4
14
|
|
5
15
|
## Features
|
6
16
|
|
@@ -8,6 +18,8 @@ A simple, highly opinionated Rails-optional Ruby gem for mobile money integratio
|
|
8
18
|
- **Convention over Configuration**: One clear setup pattern, opinionated defaults
|
9
19
|
- **Safe Defaults**: Sandbox mode, proper timeouts, comprehensive error handling
|
10
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
|
11
23
|
- **Security First**: Credential management, no hardcoded secrets
|
12
24
|
|
13
25
|
## Quick Start
|
@@ -79,12 +91,59 @@ Paytree.configure_mpesa(
|
|
79
91
|
# shortcode: "YOUR_PRODUCTION_SHORTCODE",
|
80
92
|
# passkey: Rails.application.credentials.mpesa[:passkey],
|
81
93
|
# sandbox: false,
|
82
|
-
#
|
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
|
83
96
|
# )
|
84
97
|
```
|
85
98
|
|
86
99
|
---
|
87
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
|
+
|
88
147
|
## Usage Examples
|
89
148
|
|
90
149
|
### STK Push (Customer Payment)
|
@@ -158,7 +217,7 @@ end
|
|
158
217
|
|
159
218
|
Send funds directly to a customer’s M-Pesa wallet via the B2C API.
|
160
219
|
|
161
|
-
### Example
|
220
|
+
### Basic Example
|
162
221
|
```ruby
|
163
222
|
response = Paytree::Mpesa::B2C.call(
|
164
223
|
phone_number: "254712345678",
|
@@ -176,6 +235,43 @@ else
|
|
176
235
|
end
|
177
236
|
```
|
178
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
|
+
|
179
275
|
---
|
180
276
|
|
181
277
|
## C2B (Customer to Business)
|
@@ -268,10 +364,10 @@ unless response.success?
|
|
268
364
|
puts response.code # "404.001.03" (if available)
|
269
365
|
puts response.data # {
|
270
366
|
# "requestId" => "",
|
271
|
-
# "errorCode" => "404.001.03",
|
367
|
+
# "errorCode" => "404.001.03",
|
272
368
|
# "errorMessage" => "Invalid Access Token"
|
273
369
|
# }
|
274
|
-
|
370
|
+
|
275
371
|
# Check if error is retryable (based on configuration)
|
276
372
|
if response.retryable?
|
277
373
|
puts "This error can be retried"
|
@@ -311,6 +407,9 @@ Paytree allows you to configure which error codes should be considered retryable
|
|
311
407
|
- `"429.001.01"` - Rate limit exceeded
|
312
408
|
- `"500.001.02"` - Temporary server error
|
313
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)
|
314
413
|
|
315
414
|
Configure retryable errors during setup:
|
316
415
|
|
@@ -318,7 +417,14 @@ Configure retryable errors during setup:
|
|
318
417
|
Paytree.configure_mpesa(
|
319
418
|
key: "YOUR_KEY",
|
320
419
|
secret: "YOUR_SECRET",
|
321
|
-
retryable_errors: [
|
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
|
+
]
|
322
428
|
)
|
323
429
|
```
|
324
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
|
-
|
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,
|
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
|
-
|
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
|
-
}
|
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,
|
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 =
|
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(
|
data/lib/paytree/version.rb
CHANGED
data/lib/paytree.rb
CHANGED
data/paytree.gemspec
CHANGED
@@ -6,8 +6,10 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ["Charles Chuck"]
|
7
7
|
spec.email = ["chalcchuck@gmail.com"]
|
8
8
|
|
9
|
-
spec.summary = "
|
10
|
-
spec.description =
|
9
|
+
spec.summary = "A Ruby wrapper for the Mpesa API in Kenya."
|
10
|
+
spec.description = <<~DESC
|
11
|
+
Paytree is a lightweight Ruby wrapper for the full Mpesa API suite in Kenya - including B2C, C2B, STK Push and more. It supports certificate encryption, clean facades, and a pluggable adapter system (e.g. Daraja, Airtel..). Built for Rails and pure Ruby apps.
|
12
|
+
DESC
|
11
13
|
spec.homepage = "https://github.com/mundanecodes/paytree"
|
12
14
|
spec.license = "MIT"
|
13
15
|
spec.required_ruby_version = ">= 3.2.0"
|
@@ -20,10 +22,12 @@ Gem::Specification.new do |spec|
|
|
20
22
|
spec.metadata["wiki_uri"] = "https://github.com/mundanecodes/paytree/wiki"
|
21
23
|
spec.metadata["mailing_list_uri"] = "https://github.com/mundanecodes/paytree/discussions"
|
22
24
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
25
|
+
spec.metadata["keywords"] = "mpesa,mpesa-api,b2c,stk-push,mobile-money,payments,daraja"
|
26
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
23
27
|
|
24
28
|
spec.files = Dir.chdir(__dir__) do
|
25
29
|
`git ls-files -z`.split("\x0").reject do |f|
|
26
|
-
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")
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paytree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Chuck
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: faraday
|
@@ -80,8 +79,12 @@ dependencies:
|
|
80
79
|
- - ">="
|
81
80
|
- !ruby/object:Gem::Version
|
82
81
|
version: '0'
|
83
|
-
description:
|
84
|
-
|
82
|
+
description: 'Paytree is a lightweight Ruby wrapper for the full Mpesa API suite in
|
83
|
+
Kenya - including B2C, C2B, STK Push and more. It supports certificate encryption,
|
84
|
+
clean facades, and a pluggable adapter system (e.g. Daraja, Airtel..). Built for
|
85
|
+
Rails and pure Ruby apps.
|
86
|
+
|
87
|
+
'
|
85
88
|
email:
|
86
89
|
- chalcchuck@gmail.com
|
87
90
|
executables: []
|
@@ -134,7 +137,8 @@ metadata:
|
|
134
137
|
wiki_uri: https://github.com/mundanecodes/paytree/wiki
|
135
138
|
mailing_list_uri: https://github.com/mundanecodes/paytree/discussions
|
136
139
|
allowed_push_host: https://rubygems.org
|
137
|
-
|
140
|
+
keywords: mpesa,mpesa-api,b2c,stk-push,mobile-money,payments,daraja
|
141
|
+
rubygems_mfa_required: 'true'
|
138
142
|
rdoc_options: []
|
139
143
|
require_paths:
|
140
144
|
- lib
|
@@ -149,8 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
153
|
- !ruby/object:Gem::Version
|
150
154
|
version: '0'
|
151
155
|
requirements: []
|
152
|
-
rubygems_version: 3.
|
153
|
-
signing_key:
|
156
|
+
rubygems_version: 3.6.7
|
154
157
|
specification_version: 4
|
155
|
-
summary:
|
158
|
+
summary: A Ruby wrapper for the Mpesa API in Kenya.
|
156
159
|
test_files: []
|