paystack_sdk 0.0.3 → 0.0.4
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/.standard.yml +2 -0
- data/README.md +274 -21
- data/lib/paystack_sdk/client.rb +10 -5
- data/lib/paystack_sdk/resources/base.rb +48 -5
- data/lib/paystack_sdk/resources/transactions.rb +205 -17
- data/lib/paystack_sdk/validations.rb +199 -0
- data/lib/paystack_sdk/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcb48b6fccd1a9125723781ac1f6d7adcf116459890587132dd12c516325a4aa
|
4
|
+
data.tar.gz: 175a50985ee81851b541efbc24c8c8075f8ecdf082e12b14a7736a248fbbca02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a49b817e05714ee17fc0f950385cb9af46209fd3fc7b58f69d012a08bcaf2540c1b0fa45a5d7398bd4d38eb1abd56e65bbec815dbc5e8ea1e53ebecb1799977
|
7
|
+
data.tar.gz: d0b9948e467e520ee7e1b636c9ca0865a6ee0f8ff804dac7445ba7e939964b657bc7dab66a7fedba633212b5ab39c52fba69ea28a3a351796bca225f11042389
|
data/.standard.yml
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,30 @@
|
|
2
2
|
|
3
3
|
The `paystack_sdk` gem provides a simple and intuitive interface for interacting with Paystack's payment gateway API. It allows developers to easily integrate Paystack's payment processing features into their Ruby applications. With support for various endpoints, this SDK simplifies tasks such as initiating transactions, verifying payments, managing customers, and more.
|
4
4
|
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
- [Installation](#installation)
|
8
|
+
- [Quick Start](#quick-start)
|
9
|
+
- [Usage](#usage)
|
10
|
+
- [Client Initialization](#client-initialization)
|
11
|
+
- [Transactions](#transactions)
|
12
|
+
- [Initialize a Transaction](#initialize-a-transaction)
|
13
|
+
- [Verify a Transaction](#verify-a-transaction)
|
14
|
+
- [List Transactions](#list-transactions)
|
15
|
+
- [Fetch a Transaction](#fetch-a-transaction)
|
16
|
+
- [Get Transaction Totals](#get-transaction-totals)
|
17
|
+
- [Response Handling](#response-handling)
|
18
|
+
- [Working with Response Objects](#working-with-response-objects)
|
19
|
+
- [Accessing the Original Response](#accessing-the-original-response)
|
20
|
+
- [Error Handling](#error-handling)
|
21
|
+
- [Advanced Usage](#advanced-usage)
|
22
|
+
- [Environment Variables](#environment-variables)
|
23
|
+
- [Direct Resource Instantiation](#direct-resource-instantiation)
|
24
|
+
- [Development](#development)
|
25
|
+
- [Contributing](#contributing)
|
26
|
+
- [License](#license)
|
27
|
+
- [Code of Conduct](#code-of-conduct)
|
28
|
+
|
5
29
|
## Installation
|
6
30
|
|
7
31
|
Add this line to your application's Gemfile:
|
@@ -22,52 +46,281 @@ Or install it yourself as:
|
|
22
46
|
gem install paystack_sdk
|
23
47
|
```
|
24
48
|
|
49
|
+
## Quick Start
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'paystack_sdk'
|
53
|
+
|
54
|
+
# Initialize the client with your secret key
|
55
|
+
paystack = PaystackSdk::Client.new(secret_key: "sk_test_xxx")
|
56
|
+
|
57
|
+
# Initialize a transaction
|
58
|
+
params = {
|
59
|
+
email: "customer@email.com",
|
60
|
+
amount: 2300, # Amount in the smallest currency unit (kobo for NGN)
|
61
|
+
currency: "NGN"
|
62
|
+
}
|
63
|
+
|
64
|
+
response = paystack.transactions.initiate(params)
|
65
|
+
|
66
|
+
if response.success?
|
67
|
+
puts "Visit this URL to complete payment: #{response.authorization_url}"
|
68
|
+
else
|
69
|
+
puts "Error: #{response.error_message}"
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
25
73
|
## Usage
|
26
74
|
|
27
|
-
|
75
|
+
### Client Initialization
|
28
76
|
|
29
77
|
```ruby
|
30
|
-
|
78
|
+
# Initialize with your Paystack secret key
|
79
|
+
paystack = PaystackSdk::Client.new(secret_key: "sk_test_xxx")
|
80
|
+
|
81
|
+
# You can access the connection directly if needed
|
82
|
+
connection = paystack.connection
|
83
|
+
```
|
84
|
+
|
85
|
+
### Transactions
|
31
86
|
|
32
|
-
|
33
|
-
paystack = PaystackSdk::Client.new(api_key: "sk_test_xxx")
|
87
|
+
The SDK provides comprehensive support for Paystack's Transaction API.
|
34
88
|
|
35
|
-
|
36
|
-
|
37
|
-
|
89
|
+
#### Initialize a Transaction
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# Prepare transaction parameters
|
93
|
+
params = {
|
94
|
+
email: "customer@example.com",
|
95
|
+
amount: 10000, # Amount in the smallest currency unit (e.g., kobo, pesewas, cents)
|
96
|
+
currency: "GHS",
|
97
|
+
callback_url: "https://example.com/callback"
|
98
|
+
}
|
99
|
+
|
100
|
+
# Initialize the transaction
|
101
|
+
response = paystack.transactions.initiate(params)
|
38
102
|
|
39
103
|
if response.success?
|
40
|
-
puts
|
104
|
+
puts "Transaction initialized successfully!"
|
105
|
+
puts "Authorization URL: #{response.authorization_url}"
|
106
|
+
puts "Access Code: #{response.access_code}"
|
107
|
+
puts "Reference: #{response.reference}"
|
41
108
|
else
|
42
|
-
puts
|
109
|
+
puts "Error: #{response.error_message}"
|
43
110
|
end
|
111
|
+
```
|
112
|
+
|
113
|
+
#### Verify a Transaction
|
44
114
|
|
45
|
-
|
115
|
+
```ruby
|
116
|
+
# Verify using transaction reference
|
46
117
|
response = paystack.transactions.verify(reference: "transaction_reference")
|
118
|
+
|
119
|
+
if response.success?
|
120
|
+
transaction = response.data
|
121
|
+
puts "Transaction verified successfully!"
|
122
|
+
puts "Status: #{transaction.status}"
|
123
|
+
puts "Amount: #{transaction.amount}"
|
124
|
+
puts "Currency: #{transaction.currency}"
|
125
|
+
puts "Customer Email: #{transaction.customer.email}"
|
126
|
+
|
127
|
+
# Check specific transaction status
|
128
|
+
case transaction.status
|
129
|
+
when "success"
|
130
|
+
puts "Payment successful!"
|
131
|
+
when "pending"
|
132
|
+
puts "Payment is pending."
|
133
|
+
else
|
134
|
+
puts "Current status: #{transaction.status}"
|
135
|
+
end
|
136
|
+
else
|
137
|
+
puts "Verification failed: #{response.error_message}"
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
#### List Transactions
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
# Get all transactions (default pagination: 50 per page)
|
145
|
+
response = paystack.transactions.list
|
146
|
+
|
147
|
+
# With custom pagination
|
148
|
+
response = paystack.transactions.list(per_page: 20, page: 2)
|
149
|
+
|
150
|
+
# With additional filters
|
151
|
+
response = paystack.transactions.list(
|
152
|
+
per_page: 10,
|
153
|
+
page: 1,
|
154
|
+
from: "2025-01-01",
|
155
|
+
to: "2025-04-30",
|
156
|
+
status: "success"
|
157
|
+
)
|
158
|
+
|
159
|
+
if response.success?
|
160
|
+
puts "Total transactions: #{response.count}" # response.size is another way
|
161
|
+
|
162
|
+
response.data.each do |transaction|
|
163
|
+
puts "ID: #{transaction.id}"
|
164
|
+
puts "Reference: #{transaction.reference}"
|
165
|
+
puts "Amount: #{transaction.amount}"
|
166
|
+
puts "----------------"
|
167
|
+
end
|
168
|
+
|
169
|
+
# Get the first transaction
|
170
|
+
first_transaction = response.data.first
|
171
|
+
puts "First transaction reference: #{first_transaction.reference}"
|
172
|
+
|
173
|
+
# Get the last transaction
|
174
|
+
last_transaction = response.data.last
|
175
|
+
puts "Last transaction amount: #{last_transaction.amount}"
|
176
|
+
else
|
177
|
+
puts "Error: #{response.error_message}"
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
#### Fetch a Transaction
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# Fetch a specific transaction by ID
|
185
|
+
transaction_id = "12345"
|
186
|
+
response = paystack.transactions.fetch(transaction_id)
|
187
|
+
|
188
|
+
if response.success?
|
189
|
+
transaction = response.data
|
190
|
+
puts "Transaction details:"
|
191
|
+
puts "ID: #{transaction.id}"
|
192
|
+
puts "Reference: #{transaction.reference}"
|
193
|
+
puts "Amount: #{transaction.amount}"
|
194
|
+
puts "Status: #{transaction.status}"
|
195
|
+
|
196
|
+
# Access customer information
|
197
|
+
puts "Customer Email: #{transaction.customer.email}"
|
198
|
+
puts "Customer Name: #{transaction.customer.name}"
|
199
|
+
else
|
200
|
+
puts "Error: #{response.error_message}"
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
#### Get Transaction Totals
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
# Get transaction volume and success metrics
|
208
|
+
response = paystack.transactions.totals
|
209
|
+
|
47
210
|
if response.success?
|
48
|
-
puts "
|
211
|
+
puts "Total Transactions: #{response.data.total_transactions}"
|
212
|
+
puts "Total Volume: #{response.data.total_volume}"
|
213
|
+
puts "Pending Transfers: #{response.data.pending_transfers}"
|
49
214
|
else
|
50
|
-
puts "
|
215
|
+
puts "Error: #{response.error_message}"
|
51
216
|
end
|
52
217
|
```
|
53
218
|
|
54
|
-
###
|
219
|
+
### Response Handling
|
220
|
+
|
221
|
+
#### Working with Response Objects
|
222
|
+
|
223
|
+
All API requests return a `PaystackSdk::Response` object that provides easy access to the response data.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
response = paystack.transactions.initiate(params)
|
227
|
+
|
228
|
+
# Check if the request was successful
|
229
|
+
response.success? # => true or false
|
230
|
+
|
231
|
+
# Access response message
|
232
|
+
response.api_message # => "Authorization URL created"
|
233
|
+
|
234
|
+
# Access data using dot notation
|
235
|
+
response.data.authorization_url
|
236
|
+
response.data.access_code
|
55
237
|
|
56
|
-
|
57
|
-
|
238
|
+
# Access data directly from the response
|
239
|
+
response.authorization_url # Same as response.data.authorization_url
|
58
240
|
|
59
|
-
|
60
|
-
|
241
|
+
# Access nested data
|
242
|
+
response.data.customer.email
|
243
|
+
|
244
|
+
# For arrays, use array methods
|
245
|
+
response.data.first # First item in an array
|
246
|
+
response.data.last # Last item in an array
|
247
|
+
response.data.size # Size of the array
|
248
|
+
|
249
|
+
# Iterate through array data
|
250
|
+
response.data.each do |item|
|
251
|
+
puts item.id
|
252
|
+
end
|
253
|
+
```
|
254
|
+
|
255
|
+
#### Accessing the Original Response
|
256
|
+
|
257
|
+
Sometimes you may need access to the original API response:
|
61
258
|
|
62
|
-
For example
|
63
259
|
```ruby
|
64
|
-
response =
|
260
|
+
response = paystack.transactions.list
|
261
|
+
|
262
|
+
# Access the original response body
|
263
|
+
original = response.original_response
|
264
|
+
|
265
|
+
# Access metadata from the original response
|
266
|
+
total_count = original.dig("meta", "total")
|
267
|
+
current_page = original.dig("meta", "page")
|
268
|
+
```
|
269
|
+
|
270
|
+
#### Error Handling
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
response = paystack.transactions.verify(reference: "invalid_reference")
|
274
|
+
|
275
|
+
unless response.success?
|
276
|
+
puts "Error: #{response.error_message}"
|
277
|
+
|
278
|
+
# Take action based on the error
|
279
|
+
if response.error_message.include?("not found")
|
280
|
+
puts "The transaction reference was not found."
|
281
|
+
elsif response.error_message.include?("Invalid key")
|
282
|
+
puts "API authentication failed. Check your API key."
|
283
|
+
end
|
284
|
+
end
|
285
|
+
```
|
286
|
+
|
287
|
+
## Advanced Usage
|
288
|
+
|
289
|
+
### Environment Variables
|
290
|
+
|
291
|
+
You can use environment variables to configure the SDK:
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# Set the PAYSTACK_SECRET_KEY environment variable
|
295
|
+
ENV["PAYSTACK_SECRET_KEY"] = "sk_test_xxx"
|
296
|
+
|
297
|
+
# Then initialize resources without specifying the key
|
298
|
+
transactions = PaystackSdk::Resources::Transactions.new
|
299
|
+
```
|
300
|
+
|
301
|
+
### Direct Resource Instantiation
|
302
|
+
|
303
|
+
For more advanced usage, you can instantiate resource classes directly:
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
# With a secret key
|
307
|
+
transactions = PaystackSdk::Resources::Transactions.new(secret_key: "sk_test_xxx")
|
308
|
+
|
309
|
+
# With an existing Faraday connection
|
310
|
+
connection = Faraday.new(url: "https://api.paystack.co") do |conn|
|
311
|
+
# Configure the connection
|
312
|
+
end
|
65
313
|
|
66
|
-
|
314
|
+
# The secret key can be omitted if set in an environment
|
315
|
+
transactions = PaystackSdk::Resources::Transactions.new(connection, secret_key:)
|
67
316
|
```
|
68
317
|
|
318
|
+
For more detailed documentation on specific resources, please refer to the following guides:
|
69
319
|
|
70
|
-
|
320
|
+
- [Transactions](https://paystack.com/docs/api/transaction/)
|
321
|
+
- [Customers](https://paystack.com/docs/api/customer/)
|
322
|
+
- [Plans](https://paystack.com/docs/api/plan/)
|
323
|
+
- [Subscriptions](https://paystack.com/docs/api/subscription/)
|
71
324
|
|
72
325
|
## Development
|
73
326
|
|
data/lib/paystack_sdk/client.rb
CHANGED
@@ -9,17 +9,20 @@ module PaystackSdk
|
|
9
9
|
# The base URL for the Paystack API.
|
10
10
|
BASE_URL = "https://api.paystack.co"
|
11
11
|
|
12
|
+
# @return [Faraday::Connection] The Faraday connection object used for API requests
|
13
|
+
attr_reader :connection
|
14
|
+
|
12
15
|
# Initializes a new `Client` instance.
|
13
16
|
#
|
14
|
-
# @param
|
17
|
+
# @param secret_key [String] The secret API key for authenticating with the Paystack API.
|
15
18
|
#
|
16
19
|
# @example
|
17
|
-
# client = PaystackSdk::Client.new(
|
18
|
-
def initialize(
|
20
|
+
# client = PaystackSdk::Client.new(secret_key: "sk_test_xxx")
|
21
|
+
def initialize(secret_key:)
|
19
22
|
@connection = Faraday.new(url: BASE_URL) do |conn|
|
20
23
|
conn.request :json
|
21
24
|
conn.response :json, content_type: /\bjson$/
|
22
|
-
conn.headers["Authorization"] = "Bearer #{
|
25
|
+
conn.headers["Authorization"] = "Bearer #{secret_key}"
|
23
26
|
conn.headers["Content-Type"] = "application/json"
|
24
27
|
conn.headers["User-Agent"] = "paystack_sdk/#{PaystackSdk::VERSION}"
|
25
28
|
conn.adapter Faraday.default_adapter
|
@@ -32,8 +35,10 @@ module PaystackSdk
|
|
32
35
|
# `Transactions` resource.
|
33
36
|
#
|
34
37
|
# @example
|
38
|
+
# ```ruby
|
35
39
|
# transactions = client.transactions
|
36
|
-
# response = transactions.
|
40
|
+
# response = transactions.initiate(params)
|
41
|
+
# ```
|
37
42
|
def transactions
|
38
43
|
@transactions ||= Resources::Transactions.new(@connection)
|
39
44
|
end
|
@@ -1,25 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "../response"
|
4
|
+
require_relative "../client"
|
5
|
+
require_relative "../validations"
|
4
6
|
|
5
7
|
module PaystackSdk
|
6
8
|
module Resources
|
7
9
|
# The `Base` class serves as a parent class for all resource classes in the SDK.
|
8
10
|
# It provides shared functionality, such as handling API responses.
|
9
11
|
class Base
|
12
|
+
# Include validation methods
|
13
|
+
include PaystackSdk::Validations
|
14
|
+
|
10
15
|
# Initializes a new `Base` instance.
|
11
16
|
#
|
12
|
-
# @param connection [Faraday::Connection] The Faraday connection object used for API requests.
|
13
|
-
|
14
|
-
|
17
|
+
# @param connection [Faraday::Connection, nil] The Faraday connection object used for API requests.
|
18
|
+
# If nil, a new connection will be created using the default API key.
|
19
|
+
# @param secret_key [String, nil] Optional API key to use for creating a new connection.
|
20
|
+
# Only used if connection is nil.
|
21
|
+
#
|
22
|
+
# @example With an existing connection
|
23
|
+
# connection = Faraday.new(...)
|
24
|
+
# resource = PaystackSdk::Resources::SomeResource.new(connection)
|
25
|
+
#
|
26
|
+
# @example With an API key
|
27
|
+
# resource = PaystackSdk::Resources::SomeResource.new(secret_key: "sk_test_xxx")
|
28
|
+
#
|
29
|
+
# @example With default connection (requires PAYSTACK_SECRET_KEY environment variable)
|
30
|
+
# resource = PaystackSdk::Resources::SomeResource.new
|
31
|
+
def initialize(connection = nil, secret_key: nil)
|
32
|
+
@connection = if connection
|
33
|
+
connection
|
34
|
+
elsif secret_key
|
35
|
+
create_connection(secret_key:)
|
36
|
+
else
|
37
|
+
# Try to get API key from environment variable
|
38
|
+
env_secret_key = ENV["PAYSTACK_SECRET_KEY"]
|
39
|
+
raise PaystackSdk::Error, "No connection or API key provided" unless env_secret_key
|
40
|
+
|
41
|
+
create_connection(secret_key: env_secret_key)
|
42
|
+
end
|
15
43
|
end
|
16
44
|
|
17
45
|
private
|
18
46
|
|
19
|
-
#
|
47
|
+
# Creates a new Faraday connection with the Paystack API.
|
48
|
+
#
|
49
|
+
# @param secret_key [String] The secret API key for authenticating with the Paystack API.
|
50
|
+
# @return [Faraday::Connection] A configured Faraday connection.
|
51
|
+
def create_connection(secret_key:)
|
52
|
+
Faraday.new(url: PaystackSdk::Client::BASE_URL) do |conn|
|
53
|
+
conn.request :json
|
54
|
+
conn.response :json, content_type: /\bjson$/
|
55
|
+
conn.headers["Authorization"] = "Bearer #{secret_key}"
|
56
|
+
conn.headers["Content-Type"] = "application/json"
|
57
|
+
conn.headers["User-Agent"] = "paystack_sdk/#{PaystackSdk::VERSION}"
|
58
|
+
conn.adapter Faraday.default_adapter
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Handles the API response, wrapping it in a Response object.
|
20
63
|
#
|
21
64
|
# @param response [Faraday::Response] The response object returned by the Faraday connection.
|
22
|
-
# @return [PaystackSdk::Response] The parsed response body wrapped in a `PaystackSdk::Response` object
|
65
|
+
# @return [PaystackSdk::Response] The parsed response body wrapped in a `PaystackSdk::Response` object.
|
23
66
|
# @raise [PaystackSdk::Error] If the response indicates an error.
|
24
67
|
def handle_response(response)
|
25
68
|
PaystackSdk::Response.new(response)
|
@@ -12,20 +12,25 @@ module PaystackSdk
|
|
12
12
|
#
|
13
13
|
# Example usage:
|
14
14
|
# ```ruby
|
15
|
-
# transactions = PaystackSdk::Resources::Transactions.new(
|
15
|
+
# transactions = PaystackSdk::Resources::Transactions.new(secret_key:)
|
16
16
|
#
|
17
17
|
# # Initialize a transaction
|
18
18
|
# payload = { email: "customer@email.com", amount: 10000, currency: "GHS" }
|
19
|
-
# response = transactions.
|
19
|
+
# response = transactions.initiate(payload)
|
20
20
|
# if response.success?
|
21
21
|
# puts "Transaction initialized successfully."
|
22
|
-
# puts "Authorization URL: #{response.
|
22
|
+
# puts "Authorization URL: #{response.authorization_url}"
|
23
23
|
# else
|
24
24
|
# puts "Error initializing transaction: #{response.error_message}"
|
25
25
|
# end
|
26
26
|
#
|
27
27
|
# # Verify a transaction
|
28
28
|
# response = transactions.verify(reference: "transaction_reference")
|
29
|
+
# if response.status == "success"
|
30
|
+
# puts "The payment with reference '#{response.reference}' is verified"
|
31
|
+
# else
|
32
|
+
# puts "Current status: #{response.status}"
|
33
|
+
# end
|
29
34
|
#
|
30
35
|
# # List transactions
|
31
36
|
# response = transactions.list(per_page: 50, page: 1)
|
@@ -40,14 +45,26 @@ module PaystackSdk
|
|
40
45
|
# Initializes a new transaction.
|
41
46
|
#
|
42
47
|
# @param payload [Hash] The payload containing transaction details (e.g., email, amount, currency).
|
43
|
-
# @return [
|
48
|
+
# @return [PaystackSdk::Response] The response from the Paystack API.
|
44
49
|
# @raise [PaystackSdk::Error] If the payload is invalid or the API request fails.
|
45
50
|
#
|
46
51
|
# @example
|
52
|
+
# ```ruby
|
47
53
|
# payload = { email: "customer@email.com", amount: 10000, currency: "GHS" }
|
48
|
-
# response = transactions.
|
54
|
+
# response = transactions.initiate(payload)
|
55
|
+
# ```
|
49
56
|
def initiate(payload)
|
50
|
-
|
57
|
+
validate_fields!(
|
58
|
+
payload: payload,
|
59
|
+
validations: {
|
60
|
+
email: {type: :email, required: true},
|
61
|
+
amount: {type: :positive_integer, required: true},
|
62
|
+
currency: {type: :currency, required: false},
|
63
|
+
reference: {type: :reference, required: false},
|
64
|
+
callback_url: {required: false}
|
65
|
+
}
|
66
|
+
)
|
67
|
+
|
51
68
|
response = @connection.post("/transaction/initialize", payload)
|
52
69
|
handle_response(response)
|
53
70
|
end
|
@@ -55,12 +72,14 @@ module PaystackSdk
|
|
55
72
|
# Verifies a transaction using its reference.
|
56
73
|
#
|
57
74
|
# @param reference [String] The unique reference for the transaction.
|
58
|
-
# @return [
|
75
|
+
# @return [PaystackSdk::Response] The response from the Paystack API.
|
59
76
|
# @raise [PaystackSdk::Error] If the API request fails.
|
60
77
|
#
|
61
78
|
# @example
|
62
|
-
# response = transactions.verify("transaction_reference")
|
79
|
+
# response = transactions.verify(reference: "transaction_reference")
|
63
80
|
def verify(reference:)
|
81
|
+
validate_presence!(value: reference, name: "Reference")
|
82
|
+
|
64
83
|
response = @connection.get("/transaction/verify/#{reference}")
|
65
84
|
handle_response(response)
|
66
85
|
end
|
@@ -69,39 +88,208 @@ module PaystackSdk
|
|
69
88
|
#
|
70
89
|
# @param per_page [Integer] Number of records per page (default: 50)
|
71
90
|
# @param page [Integer] Page number to retrieve (default: 1)
|
91
|
+
# @param from [String] A timestamp from which to start listing transactions e.g. 2016-09-24T00:00:05.000Z, 2016-09-21
|
92
|
+
# @param to [String] A timestamp at which to stop listing transactions e.g. 2016-09-24T00:00:05.000Z, 2016-09-21
|
93
|
+
# @param status [String] Filter transactions by status ('failed', 'success', 'abandoned')
|
94
|
+
# @param customer [Integer] Specify an ID for the customer whose transactions you want to retrieve
|
95
|
+
# @param currency [String] Specify the transaction currency to filter
|
96
|
+
# @param amount [Integer] Filter by transaction amount
|
72
97
|
# @return [PaystackSdk::Response] The response from the Paystack API containing a
|
73
98
|
# list of transactions.
|
74
99
|
# @raise [PaystackSdk::Error] If the API request fails.
|
75
100
|
#
|
76
101
|
# @example
|
77
102
|
# response = transactions.list(per_page: 20, page: 2)
|
78
|
-
|
79
|
-
|
103
|
+
# # With filters
|
104
|
+
# response = transactions.list(per_page: 10, from: "2023-01-01", to: "2023-12-31", status: "success")
|
105
|
+
def list(per_page: 50, page: 1, **params)
|
106
|
+
# Create a combined parameter hash for validation
|
107
|
+
all_params = {per_page: per_page, page: page}.merge(params)
|
108
|
+
|
109
|
+
# Validate parameters
|
110
|
+
validate_fields!(
|
111
|
+
payload: all_params,
|
112
|
+
validations: {
|
113
|
+
per_page: {type: :positive_integer, required: false},
|
114
|
+
page: {type: :positive_integer, required: false},
|
115
|
+
from: {type: :date, required: false},
|
116
|
+
to: {type: :date, required: false},
|
117
|
+
status: {type: :inclusion, allowed_values: %w[failed success abandoned], required: false},
|
118
|
+
customer: {type: :positive_integer, required: false},
|
119
|
+
amount: {type: :positive_integer, required: false},
|
120
|
+
currency: {type: :currency, required: false}
|
121
|
+
}
|
122
|
+
)
|
123
|
+
|
124
|
+
# Prepare request parameters
|
125
|
+
request_params = {perPage: per_page, page: page}.merge(params)
|
126
|
+
response = @connection.get("/transaction", request_params)
|
80
127
|
handle_response(response)
|
81
128
|
end
|
82
129
|
|
83
130
|
# Fetches details of a single transaction by its ID.
|
84
131
|
#
|
85
|
-
# @param transaction_id [Integer] The ID of the transaction to fetch.
|
86
|
-
# @return [
|
132
|
+
# @param transaction_id [String, Integer] The ID of the transaction to fetch.
|
133
|
+
# @return [PaystackSdk::Response] The response from the Paystack API containing transaction details.
|
87
134
|
# @raise [PaystackSdk::Error] If the API request fails.
|
88
135
|
#
|
89
136
|
# @example
|
90
|
-
# response = transactions.fetch("
|
137
|
+
# response = transactions.fetch("12345")
|
91
138
|
def fetch(transaction_id)
|
139
|
+
validate_presence!(value: transaction_id, name: "Transaction ID")
|
140
|
+
|
92
141
|
response = @connection.get("/transaction/#{transaction_id}")
|
93
142
|
handle_response(response)
|
94
143
|
end
|
95
144
|
|
96
145
|
# Fetches the totals of all transactions.
|
97
146
|
#
|
98
|
-
# @
|
147
|
+
# @param from [String] A timestamp from which to start listing transaction totals e.g. 2016-09-24T00:00:05.000Z, 2016-09-21
|
148
|
+
# @param to [String] A timestamp at which to stop listing transaction totals e.g. 2016-09-24T00:00:05.000Z, 2016-09-21
|
149
|
+
# @return [PaystackSdk::Response] The response from the Paystack API containing transaction totals.
|
150
|
+
# @raise [PaystackSdk::Error] If the API request fails.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# response = transactions.totals
|
154
|
+
# # With date filters
|
155
|
+
# response = transactions.totals(from: "2023-01-01", to: "2023-12-31")
|
156
|
+
def totals(**params)
|
157
|
+
validate_fields!(
|
158
|
+
payload: params,
|
159
|
+
validations: {
|
160
|
+
from: {type: :date, required: false},
|
161
|
+
to: {type: :date, required: false}
|
162
|
+
}
|
163
|
+
)
|
164
|
+
|
165
|
+
response = @connection.get("/transaction/totals", params)
|
166
|
+
handle_response(response)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Exports transactions as a downloadable file.
|
170
|
+
#
|
171
|
+
# @param from [String] A timestamp from which to start the export e.g. 2016-09-24T00:00:05.000Z, 2016-09-21
|
172
|
+
# @param to [String] A timestamp at which to stop the export e.g. 2016-09-24T00:00:05.000Z, 2016-09-21
|
173
|
+
# @param status [String] Export only transactions with a specific status ('failed', 'success', 'abandoned')
|
174
|
+
# @param currency [String] Specify the transaction currency to export
|
175
|
+
# @param amount [Integer] Filter by transaction amount
|
176
|
+
# @param settled [Boolean] Set to true to export only settled transactions
|
177
|
+
# @param payment_page [Integer] Specify a payment page ID to export only transactions conducted through the page
|
178
|
+
# @param customer [Integer] Specify an ID for the customer whose transactions you want to export
|
179
|
+
# @param settlement [Integer] Specify a settlement ID to export only transactions in the settlement
|
180
|
+
# @return [PaystackSdk::Response] The response from the Paystack API containing the export details.
|
181
|
+
# @raise [PaystackSdk::Error] If the API request fails.
|
182
|
+
#
|
183
|
+
# @example
|
184
|
+
# response = transactions.export
|
185
|
+
# # With filters
|
186
|
+
# response = transactions.export(from: "2023-01-01", to: "2023-12-31", status: "success", currency: "NGN")
|
187
|
+
def export(**params)
|
188
|
+
validate_fields!(
|
189
|
+
payload: params,
|
190
|
+
validations: {
|
191
|
+
from: {type: :date, required: false},
|
192
|
+
to: {type: :date, required: false},
|
193
|
+
status: {type: :inclusion, allowed_values: %w[failed success abandoned], required: false},
|
194
|
+
currency: {type: :currency, required: false},
|
195
|
+
amount: {type: :positive_integer, required: false},
|
196
|
+
payment_page: {type: :positive_integer, required: false},
|
197
|
+
customer: {type: :positive_integer, required: false},
|
198
|
+
settlement: {type: :positive_integer, required: false}
|
199
|
+
}
|
200
|
+
)
|
201
|
+
|
202
|
+
response = @connection.get("/transaction/export", params)
|
203
|
+
handle_response(response)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Charges an authorization code for subsequent payments.
|
207
|
+
#
|
208
|
+
# @param payload [Hash] The payload containing charge details.
|
209
|
+
# @option payload [String] :authorization_code Authorization code for the transaction (required)
|
210
|
+
# @option payload [String] :email Customer's email address (required)
|
211
|
+
# @option payload [Integer] :amount Amount in kobo, pesewas, or cents to charge (required)
|
212
|
+
# @option payload [String] :reference Unique transaction reference. Only -, ., = and alphanumeric characters allowed
|
213
|
+
# @option payload [String] :currency Currency in which amount should be charged (default: NGN)
|
214
|
+
# @option payload [Hash] :metadata Additional transaction information
|
215
|
+
# @option payload [Array<Hash>] :split_code Split payment among multiple accounts
|
216
|
+
# @return [PaystackSdk::Response] The response from the Paystack API.
|
217
|
+
# @raise [PaystackSdk::Error] If the payload is invalid or the API request fails.
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# payload = {
|
221
|
+
# authorization_code: "AUTH_72btv547",
|
222
|
+
# email: "customer@email.com",
|
223
|
+
# amount: 10000
|
224
|
+
# }
|
225
|
+
# response = transactions.charge_authorization(payload)
|
226
|
+
def charge_authorization(payload)
|
227
|
+
validate_fields!(
|
228
|
+
payload: payload,
|
229
|
+
validations: {
|
230
|
+
authorization_code: {required: true},
|
231
|
+
email: {type: :email, required: true},
|
232
|
+
amount: {type: :positive_integer, required: true},
|
233
|
+
reference: {type: :reference, required: false},
|
234
|
+
currency: {type: :currency, required: false}
|
235
|
+
}
|
236
|
+
)
|
237
|
+
|
238
|
+
response = @connection.post("/transaction/charge_authorization", payload)
|
239
|
+
handle_response(response)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Performs a partial debit on a customer's account.
|
243
|
+
#
|
244
|
+
# @param payload [Hash] The payload containing partial debit details.
|
245
|
+
# @option payload [String] :authorization_code Authorization code for the transaction (required)
|
246
|
+
# @option payload [String] :currency Currency in which amount should be charged (required)
|
247
|
+
# @option payload [Integer] :amount Amount in kobo, pesewas, or cents to charge (required)
|
248
|
+
# @option payload [String] :email Customer's email address (required)
|
249
|
+
# @option payload [String] :reference Unique transaction reference. Only -, ., = and alphanumeric characters allowed
|
250
|
+
# @option payload [Hash] :metadata Additional transaction information
|
251
|
+
# @option payload [Array<Hash>] :split_code Split payment among multiple accounts
|
252
|
+
# @return [PaystackSdk::Response] The response from the Paystack API.
|
253
|
+
# @raise [PaystackSdk::Error] If the payload is invalid or the API request fails.
|
254
|
+
#
|
255
|
+
# @example
|
256
|
+
# payload = {
|
257
|
+
# authorization_code: "AUTH_72btv547",
|
258
|
+
# currency: "NGN",
|
259
|
+
# amount: 10000,
|
260
|
+
# email: "customer@email.com"
|
261
|
+
# }
|
262
|
+
# response = transactions.partial_debit(payload)
|
263
|
+
def partial_debit(payload)
|
264
|
+
validate_fields!(
|
265
|
+
payload: payload,
|
266
|
+
validations: {
|
267
|
+
authorization_code: {required: true},
|
268
|
+
currency: {type: :currency, required: true},
|
269
|
+
amount: {type: :positive_integer, required: true},
|
270
|
+
email: {type: :email, required: true},
|
271
|
+
reference: {type: :reference, required: false}
|
272
|
+
}
|
273
|
+
)
|
274
|
+
|
275
|
+
response = @connection.post("/transaction/partial_debit", payload)
|
276
|
+
handle_response(response)
|
277
|
+
end
|
278
|
+
|
279
|
+
# View the timeline of a transaction
|
280
|
+
#
|
281
|
+
# @param id_or_reference [String] The ID or reference of the transaction
|
282
|
+
# @return [PaystackSdk::Response] The response from the Paystack API containing timeline details.
|
99
283
|
# @raise [PaystackSdk::Error] If the API request fails.
|
100
284
|
#
|
101
285
|
# @example
|
102
|
-
#
|
103
|
-
|
104
|
-
|
286
|
+
# response = transactions.timeline("12345")
|
287
|
+
# # OR
|
288
|
+
# response = transactions.timeline("ref_123456789")
|
289
|
+
def timeline(id_or_reference)
|
290
|
+
validate_presence!(value: id_or_reference, name: "Transaction ID or Reference")
|
291
|
+
|
292
|
+
response = @connection.get("/transaction/timeline/#{id_or_reference}")
|
105
293
|
handle_response(response)
|
106
294
|
end
|
107
295
|
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaystackSdk
|
4
|
+
# The Validations module provides shared validation methods for Paystack SDK resources.
|
5
|
+
# It includes methods for validating common parameters like references, amounts, dates, etc.
|
6
|
+
#
|
7
|
+
# This module is intended to be included in resource classes to provide consistent
|
8
|
+
# parameter validation before making API calls.
|
9
|
+
module Validations
|
10
|
+
# Validates that input is a hash.
|
11
|
+
#
|
12
|
+
# @param input [Object] The input to validate
|
13
|
+
# @param name [String] Name of the parameter for error messages
|
14
|
+
# @raise [PaystackSdk::Error] If input is not a hash
|
15
|
+
def validate_hash!(input:, name: "Payload")
|
16
|
+
raise PaystackSdk::Error, "#{name} must be a hash" unless input.is_a?(Hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Validates that required parameters are present in a payload.
|
20
|
+
#
|
21
|
+
# @param payload [Hash] The payload to validate
|
22
|
+
# @param required_params [Array<Symbol>] List of required parameter keys
|
23
|
+
# @param operation_name [String] Name of the operation for error messages
|
24
|
+
# @raise [PaystackSdk::Error] If any required parameters are missing
|
25
|
+
def validate_required_params!(payload:, required_params:, operation_name: "Operation")
|
26
|
+
missing_params = required_params.select do |param|
|
27
|
+
!payload.key?(param) && !payload.key?(param.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
unless missing_params.empty?
|
31
|
+
missing_list = missing_params.map(&:to_s).join(", ")
|
32
|
+
raise PaystackSdk::Error, "#{operation_name} requires these missing parameter(s): #{missing_list}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Validates that a value is present (not nil or empty).
|
37
|
+
#
|
38
|
+
# @param value [Object] The value to validate
|
39
|
+
# @param name [String] Name of the parameter for error messages
|
40
|
+
# @raise [PaystackSdk::Error] If value is nil or empty
|
41
|
+
def validate_presence!(value:, name: "Parameter")
|
42
|
+
if value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
43
|
+
raise PaystackSdk::Error, "#{name} cannot be empty"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Validates that a number is a positive integer.
|
48
|
+
#
|
49
|
+
# @param value [Object] The value to validate
|
50
|
+
# @param name [String] Name of the parameter for error messages
|
51
|
+
# @param allow_nil [Boolean] Whether nil values are allowed
|
52
|
+
# @raise [PaystackSdk::Error] If value is not a positive integer (and not nil if allow_nil is true)
|
53
|
+
def validate_positive_integer!(value:, name: "Parameter", allow_nil: true)
|
54
|
+
if value.nil?
|
55
|
+
raise PaystackSdk::Error, "#{name} cannot be nil" unless allow_nil
|
56
|
+
elsif !value.is_a?(Integer) || value < 1
|
57
|
+
raise PaystackSdk::Error, "#{name} must be a positive integer"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Validates a transaction reference format.
|
62
|
+
#
|
63
|
+
# @param reference [String] The reference to validate
|
64
|
+
# @param name [String] Name of the parameter for error messages
|
65
|
+
# @raise [PaystackSdk::Error] If reference format is invalid
|
66
|
+
def validate_reference_format!(reference:, name: "Reference")
|
67
|
+
unless reference.to_s.match?(/^[a-zA-Z0-9._=-]+$/)
|
68
|
+
raise PaystackSdk::Error, "#{name} can only contain alphanumeric characters and the following: -, ., ="
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Validates a date string format.
|
73
|
+
#
|
74
|
+
# @param date_str [String] The date string to validate
|
75
|
+
# @param name [String] Name of the parameter for error messages
|
76
|
+
# @param allow_nil [Boolean] Whether nil values are allowed
|
77
|
+
# @raise [PaystackSdk::Error] If date format is invalid
|
78
|
+
def validate_date_format!(date_str:, name: "Date", allow_nil: true)
|
79
|
+
if date_str.nil?
|
80
|
+
raise PaystackSdk::Error, "#{name} cannot be nil" unless allow_nil
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
Date.parse(date_str.to_s)
|
86
|
+
rescue Date::Error
|
87
|
+
raise PaystackSdk::Error, "Invalid #{name.downcase} format. Use format: YYYY-MM-DD or ISO8601"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Validates that a value is within an allowed set of values.
|
92
|
+
#
|
93
|
+
# @param value [Object] The value to validate
|
94
|
+
# @param allowed_values [Array] List of allowed values
|
95
|
+
# @param name [String] Name of the parameter for error messages
|
96
|
+
# @param allow_nil [Boolean] Whether nil values are allowed
|
97
|
+
# @param case_sensitive [Boolean] Whether the comparison is case-sensitive
|
98
|
+
# @raise [PaystackSdk::Error] If value is not in the allowed set
|
99
|
+
def validate_inclusion!(value:, allowed_values:, name: "Parameter", allow_nil: true, case_sensitive: false)
|
100
|
+
if value.nil?
|
101
|
+
raise PaystackSdk::Error, "#{name} cannot be nil" unless allow_nil
|
102
|
+
return
|
103
|
+
end
|
104
|
+
|
105
|
+
check_value = case_sensitive ? value.to_s : value.to_s.downcase
|
106
|
+
check_allowed = case_sensitive ? allowed_values : allowed_values.map(&:downcase)
|
107
|
+
|
108
|
+
unless check_allowed.include?(check_value)
|
109
|
+
allowed_list = allowed_values.join("', '")
|
110
|
+
raise PaystackSdk::Error, "#{name} must be one of: '#{allowed_list}'"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Validates an email format.
|
115
|
+
#
|
116
|
+
# @param email [String] The email to validate
|
117
|
+
# @param name [String] Name of the parameter for error messages
|
118
|
+
# @param allow_nil [Boolean] Whether nil values are allowed
|
119
|
+
# @raise [PaystackSdk::Error] If email format is invalid
|
120
|
+
def validate_email!(email:, name: "Email", allow_nil: false)
|
121
|
+
if email.nil?
|
122
|
+
raise PaystackSdk::Error, "#{name} cannot be nil" unless allow_nil
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
126
|
+
unless email.to_s.match?(/\A[^@\s]+@[^@\s]+\.[^@\s]+\z/)
|
127
|
+
raise PaystackSdk::Error, "Invalid #{name.downcase} format"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Validates a currency code format.
|
132
|
+
#
|
133
|
+
# @param currency [String] The currency code to validate
|
134
|
+
# @param name [String] Name of the parameter for error messages
|
135
|
+
# @param allow_nil [Boolean] Whether nil values are allowed
|
136
|
+
# @raise [PaystackSdk::Error] If currency format is invalid
|
137
|
+
def validate_currency!(currency:, name: "Currency", allow_nil: true)
|
138
|
+
if currency.nil?
|
139
|
+
raise PaystackSdk::Error, "#{name} cannot be nil" unless allow_nil
|
140
|
+
return
|
141
|
+
end
|
142
|
+
|
143
|
+
unless currency.to_s.match?(/\A[A-Z]{3}\z/)
|
144
|
+
raise PaystackSdk::Error, "#{name} must be a 3-letter ISO code (e.g., NGN, USD, GHS)"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Validates multiple fields at once.
|
149
|
+
#
|
150
|
+
# @param payload [Hash] The payload containing fields to validate
|
151
|
+
# @param validations [Hash] Mapping of field names to validation options
|
152
|
+
# @raise [PaystackSdk::Error] If any validation fails
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# validate_fields!(
|
156
|
+
# payload: params,
|
157
|
+
# validations: {
|
158
|
+
# email: { type: :email, required: true },
|
159
|
+
# amount: { type: :positive_integer, required: true },
|
160
|
+
# currency: { type: :currency, required: false },
|
161
|
+
# reference: { type: :reference, required: false }
|
162
|
+
# }
|
163
|
+
# )
|
164
|
+
def validate_fields!(payload:, validations:)
|
165
|
+
# First check required fields
|
166
|
+
required_fields = validations.select { |_, opts| opts[:required] }.keys
|
167
|
+
validate_required_params!(payload: payload, required_params: required_fields) unless required_fields.empty?
|
168
|
+
|
169
|
+
# Then validate each field
|
170
|
+
validations.each do |field, options|
|
171
|
+
value = payload[field] || payload[field.to_s]
|
172
|
+
next if value.nil? && !options[:required]
|
173
|
+
|
174
|
+
case options[:type]
|
175
|
+
when :email
|
176
|
+
validate_email!(email: value, name: field.to_s.capitalize, allow_nil: !options[:required])
|
177
|
+
when :positive_integer
|
178
|
+
validate_positive_integer!(value: value, name: field.to_s, allow_nil: !options[:required])
|
179
|
+
when :reference
|
180
|
+
validate_reference_format!(reference: value, name: field.to_s) if value
|
181
|
+
when :date
|
182
|
+
validate_date_format!(date_str: value, name: field.to_s, allow_nil: !options[:required])
|
183
|
+
when :currency
|
184
|
+
validate_currency!(currency: value, name: field.to_s, allow_nil: !options[:required])
|
185
|
+
when :inclusion
|
186
|
+
if value
|
187
|
+
validate_inclusion!(
|
188
|
+
value: value,
|
189
|
+
allowed_values: options[:allowed_values],
|
190
|
+
name: field.to_s,
|
191
|
+
allow_nil: !options[:required],
|
192
|
+
case_sensitive: options[:case_sensitive]
|
193
|
+
)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
data/lib/paystack_sdk/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paystack_sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maxwell Nana Forson (theLazyProgrammer)
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -119,6 +119,7 @@ files:
|
|
119
119
|
- lib/paystack_sdk/resources/base.rb
|
120
120
|
- lib/paystack_sdk/resources/transactions.rb
|
121
121
|
- lib/paystack_sdk/response.rb
|
122
|
+
- lib/paystack_sdk/validations.rb
|
122
123
|
- lib/paystack_sdk/version.rb
|
123
124
|
- mise.toml
|
124
125
|
- sig/paystack_sdk.rbs
|