zai_payment 1.0.2 → 1.1.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: a3356f8c1b0c4d7806f3d332dd18d4cd9faa2508a867d9bf75c100b80a8b47e4
4
- data.tar.gz: cdf5dc1d7994ec2230d13ce989b489564d9cab6be4756f482e870898c91047e2
3
+ metadata.gz: e3dde823881d82243bdd2f57882c57cfe06ba21d20ab5402b58a876c7c752e8c
4
+ data.tar.gz: 37ce743767afba2f834354a81102cc445c544151c26601e59a0f82eb6b967a92
5
5
  SHA512:
6
- metadata.gz: 7147ac27b27d63eac4c9ba4b51050ca539b4da4aa281a76a3bb569d3642ca691327194640ae0c950f5e7e31acf9f1a1fafb22cd80f27be879dd1a52efc39e2f3
7
- data.tar.gz: bb2ce6f8c53a32ca8a84663aa00f028e7a6adcc9a166fe2137909ebdfff9ee69122bb6340460ac79ac5ff19dec777a8c3716b4afb1ff760d513f73bc232b2462
6
+ metadata.gz: 0b96c6511bc25c2dcbfe44af58957b160c3bd7ac1fb6aa94fb5708ced6a6e2ff6a209f188f664eb6c857ebe4177f5260191eb62aa1e64924adc57d695815ed38
7
+ data.tar.gz: 6e2410984fe931c17097f145de9a23e7c6048af308c068e7f590efab9991a21c838aed6158bb1be56863fa9e86090f46599eaffec1cf415e2610c46861de06e1
data/CHANGELOG.md CHANGED
@@ -1,7 +1,43 @@
1
- ## [Unreleased]
1
+ ## [Released]
2
+
3
+ ## [1.1.0] - 2025-10-22
4
+ ### Added
5
+ - **Webhooks API**: Full CRUD operations for managing Zai webhooks
6
+ - `ZaiPayment.webhooks.list` - List all webhooks with pagination
7
+ - `ZaiPayment.webhooks.show(id)` - Get a specific webhook
8
+ - `ZaiPayment.webhooks.create(...)` - Create a new webhook
9
+ - `ZaiPayment.webhooks.update(id, ...)` - Update an existing webhook
10
+ - `ZaiPayment.webhooks.delete(id)` - Delete a webhook
11
+ - **Base API Client**: Reusable HTTP client for all API requests
12
+ - **Response Wrapper**: Standardized response handling with error management
13
+ - **Enhanced Error Handling**: New error classes for different API scenarios
14
+ - `ValidationError` (400, 422)
15
+ - `UnauthorizedError` (401)
16
+ - `ForbiddenError` (403)
17
+ - `NotFoundError` (404)
18
+ - `RateLimitError` (429)
19
+ - `ServerError` (5xx)
20
+ - `TimeoutError` and `ConnectionError` for network issues
21
+ - Comprehensive test suite for webhook functionality
22
+ - Example code in `examples/webhooks.rb`
23
+
24
+ **Full Changelog**: https://github.com/Sentia/zai-payment/compare/v1.0.2...v1.1.0
25
+
26
+ ## [1.0.2] - 2025-10-22
27
+ - Update gemspec files and readme
28
+
29
+ **Full Changelog**: https://github.com/Sentia/zai-payment/releases/tag/v1.0.2
30
+
31
+
32
+ ##[1.0.1] - 2025-10-21
33
+ - Update readme and versions
34
+
35
+ **Full Changelog**: https://github.com/Sentia/zai-payment/releases/tag/v1.0.1
36
+
2
37
 
3
38
  ## [1.0.0] - 2025-10-21
4
39
 
5
40
  - Initial release: token auth client with in-memory caching (`ZaiPayment.token`, `refresh_token!`, `clear_token!`, `token_type`, `token_expiry`)
6
41
 
7
42
  **Full Changelog**: https://github.com/Sentia/zai-payment/commits/v1.0.0
43
+
data/IMPLEMENTATION.md ADDED
@@ -0,0 +1,201 @@
1
+ # Implementation Summary: Zai Payment Webhooks
2
+
3
+ ## ✅ What Was Implemented
4
+
5
+ ### 1. Core Infrastructure (New Files)
6
+
7
+ #### `/lib/zai_payment/client.rb`
8
+ - Base HTTP client for all API requests
9
+ - Handles authentication automatically
10
+ - Supports GET, POST, PATCH, DELETE methods
11
+ - Proper error handling and connection management
12
+ - Thread-safe and reusable
13
+
14
+ #### `/lib/zai_payment/response.rb`
15
+ - Response wrapper class
16
+ - Convenience methods: `success?`, `client_error?`, `server_error?`
17
+ - Automatic error raising based on HTTP status
18
+ - Clean data extraction from response body
19
+
20
+ #### `/lib/zai_payment/resources/webhook.rb`
21
+ - Complete CRUD operations for webhooks:
22
+ - `list(limit:, offset:)` - List all webhooks with pagination
23
+ - `show(webhook_id)` - Get specific webhook details
24
+ - `create(url:, object_type:, enabled:, description:)` - Create new webhook
25
+ - `update(webhook_id, ...)` - Update existing webhook
26
+ - `delete(webhook_id)` - Delete webhook
27
+ - Full input validation
28
+ - URL format validation
29
+ - Comprehensive error messages
30
+
31
+ ### 2. Enhanced Error Handling
32
+
33
+ #### `/lib/zai_payment/errors.rb` (Updated)
34
+ Added new error classes:
35
+ - `ApiError` - Base API error
36
+ - `BadRequestError` (400)
37
+ - `UnauthorizedError` (401)
38
+ - `ForbiddenError` (403)
39
+ - `NotFoundError` (404)
40
+ - `ValidationError` (422)
41
+ - `RateLimitError` (429)
42
+ - `ServerError` (5xx)
43
+ - `TimeoutError` - Network timeout
44
+ - `ConnectionError` - Connection failed
45
+
46
+ ### 3. Main Module Integration
47
+
48
+ #### `/lib/zai_payment.rb` (Updated)
49
+ - Added `require` statements for new components
50
+ - Added `webhooks` method that returns a singleton instance
51
+ - Usage: `ZaiPayment.webhooks.list`
52
+
53
+ ### 4. Testing
54
+
55
+ #### `/spec/zai_payment/resources/webhook_spec.rb` (New)
56
+ Comprehensive test suite covering:
57
+ - List webhooks (success, pagination, unauthorized)
58
+ - Show webhook (success, not found, validation)
59
+ - Create webhook (success, validation errors, API errors)
60
+ - Update webhook (success, not found, validation)
61
+ - Delete webhook (success, not found, validation)
62
+ - Edge cases and error scenarios
63
+
64
+ ### 5. Documentation
65
+
66
+ #### `/examples/webhooks.rb` (New)
67
+ - Complete usage examples
68
+ - All CRUD operations
69
+ - Error handling patterns
70
+ - Pagination examples
71
+ - Custom client instances
72
+
73
+ #### `/docs/WEBHOOKS.md` (New)
74
+ - Architecture overview
75
+ - API method documentation
76
+ - Error handling guide
77
+ - Best practices
78
+ - Testing instructions
79
+ - Future enhancements
80
+
81
+ #### `/README.md` (Updated)
82
+ - Added webhook usage section
83
+ - Error handling examples
84
+ - Updated roadmap (Webhooks: Done ✅)
85
+
86
+ #### `/CHANGELOG.md` (Updated)
87
+ - Added v1.1.0 release notes
88
+ - Documented all new features
89
+ - Listed all new error classes
90
+
91
+ ### 6. Version
92
+
93
+ #### `/lib/zai_payment/version.rb` (Updated)
94
+ - Bumped version to 1.1.0
95
+
96
+ ## 📁 File Structure
97
+
98
+ ```
99
+ lib/
100
+ ├── zai_payment/
101
+ │ ├── auth/ # Authentication (existing)
102
+ │ ├── client.rb # ✨ NEW: Base HTTP client
103
+ │ ├── response.rb # ✨ NEW: Response wrapper
104
+ │ ├── resources/
105
+ │ │ └── webhook.rb # ✨ NEW: Webhook CRUD operations
106
+ │ ├── config.rb # (existing)
107
+ │ ├── errors.rb # ✅ UPDATED: Added API error classes
108
+ │ └── version.rb # ✅ UPDATED: v1.1.0
109
+ └── zai_payment.rb # ✅ UPDATED: Added webhooks accessor
110
+
111
+ spec/
112
+ └── zai_payment/
113
+ └── resources/
114
+ └── webhook_spec.rb # ✨ NEW: Comprehensive tests
115
+
116
+ examples/
117
+ └── webhooks.rb # ✨ NEW: Usage examples
118
+
119
+ docs/
120
+ └── WEBHOOKS.md # ✨ NEW: Complete documentation
121
+ ```
122
+
123
+ ## 🎯 Key Features
124
+
125
+ 1. **Clean API**: `ZaiPayment.webhooks.list`, `.show`, `.create`, `.update`, `.delete`
126
+ 2. **Automatic Authentication**: Uses existing TokenProvider
127
+ 3. **Comprehensive Validation**: URL format, required fields, etc.
128
+ 4. **Rich Error Handling**: Specific errors for each scenario
129
+ 5. **Pagination Support**: Built-in pagination for list operations
130
+ 6. **Thread-Safe**: Reuses existing thread-safe authentication
131
+ 7. **Well-Tested**: Full RSpec test coverage
132
+ 8. **Documented**: Inline docs, examples, and guides
133
+
134
+ ## 🚀 Usage
135
+
136
+ ```ruby
137
+ # Configure once
138
+ ZaiPayment.configure do |config|
139
+ config.environment = :prelive
140
+ config.client_id = ENV['ZAI_CLIENT_ID']
141
+ config.client_secret = ENV['ZAI_CLIENT_SECRET']
142
+ config.scope = ENV['ZAI_SCOPE']
143
+ end
144
+
145
+ # Use webhooks
146
+ response = ZaiPayment.webhooks.list
147
+ webhooks = response.data
148
+
149
+ response = ZaiPayment.webhooks.create(
150
+ url: 'https://example.com/webhook',
151
+ object_type: 'transactions',
152
+ enabled: true
153
+ )
154
+ ```
155
+
156
+ ## ✨ Best Practices Applied
157
+
158
+ 1. **Single Responsibility Principle**: Each class has one clear purpose
159
+ 2. **DRY**: Reusable Client and Response classes
160
+ 3. **Open/Closed**: Easy to extend for new resources (Users, Items, etc.)
161
+ 4. **Dependency Injection**: Client accepts custom config and token provider
162
+ 5. **Fail Fast**: Validation before API calls
163
+ 6. **Clear Error Messages**: Descriptive validation errors
164
+ 7. **RESTful Design**: Standard HTTP methods and status codes
165
+ 8. **Comprehensive Testing**: Unit tests for all scenarios
166
+ 9. **Documentation**: Examples, inline docs, and guides
167
+ 10. **Version Control**: Semantic versioning with changelog
168
+
169
+ ## 🔄 Ready for Extension
170
+
171
+ The infrastructure is now in place to easily add more resources:
172
+
173
+ ```ruby
174
+ # Future resources can follow the same pattern:
175
+ lib/zai_payment/resources/
176
+ ├── webhook.rb # ✅ Done
177
+ ├── user.rb # Coming soon
178
+ ├── item.rb # Coming soon
179
+ ├── transaction.rb # Coming soon
180
+ └── wallet.rb # Coming soon
181
+ ```
182
+
183
+ Each resource can reuse:
184
+ - `ZaiPayment::Client` for HTTP requests
185
+ - `ZaiPayment::Response` for response handling
186
+ - Error classes for consistent error handling
187
+ - Same authentication mechanism
188
+ - Same configuration
189
+ - Same testing patterns
190
+
191
+ ## 🎉 Summary
192
+
193
+ Successfully implemented a complete, production-ready webhook management system for the Zai Payment gem with:
194
+ - ✅ Full CRUD operations
195
+ - ✅ Comprehensive testing
196
+ - ✅ Rich error handling
197
+ - ✅ Complete documentation
198
+ - ✅ Clean, maintainable code
199
+ - ✅ Following Ruby and Rails best practices
200
+ - ✅ Ready for production use
201
+
data/README.md CHANGED
@@ -67,14 +67,78 @@ Or, more easily, you can get a token with the convenience one-liner:
67
67
  ZaiPayment.token
68
68
  ```
69
69
 
70
+ ## 🚀 Usage
71
+
72
+ ### Webhooks
73
+
74
+ The gem provides a comprehensive interface for managing Zai webhooks:
75
+
76
+ ```ruby
77
+ # List all webhooks
78
+ response = ZaiPayment.webhooks.list
79
+ webhooks = response.data
80
+
81
+ # List with pagination
82
+ response = ZaiPayment.webhooks.list(limit: 20, offset: 10)
83
+
84
+ # Get a specific webhook
85
+ response = ZaiPayment.webhooks.show('webhook_id')
86
+ webhook = response.data
87
+
88
+ # Create a webhook
89
+ response = ZaiPayment.webhooks.create(
90
+ url: 'https://example.com/webhooks/zai',
91
+ object_type: 'transactions',
92
+ enabled: true,
93
+ description: 'Production webhook for transactions'
94
+ )
95
+
96
+ # Update a webhook
97
+ response = ZaiPayment.webhooks.update(
98
+ 'webhook_id',
99
+ enabled: false,
100
+ description: 'Temporarily disabled'
101
+ )
102
+
103
+ # Delete a webhook
104
+ response = ZaiPayment.webhooks.delete('webhook_id')
105
+ ```
106
+
107
+ For more examples, see [examples/webhooks.md](examples/webhooks.md).
108
+
109
+ ### Error Handling
110
+
111
+ The gem provides specific error classes for different scenarios:
112
+
113
+ ```ruby
114
+ begin
115
+ response = ZaiPayment.webhooks.create(
116
+ url: 'https://example.com/webhook',
117
+ object_type: 'transactions'
118
+ )
119
+ rescue ZaiPayment::Errors::ValidationError => e
120
+ # Handle validation errors (400, 422)
121
+ puts "Validation error: #{e.message}"
122
+ rescue ZaiPayment::Errors::UnauthorizedError => e
123
+ # Handle authentication errors (401)
124
+ puts "Authentication failed: #{e.message}"
125
+ rescue ZaiPayment::Errors::NotFoundError => e
126
+ # Handle not found errors (404)
127
+ puts "Resource not found: #{e.message}"
128
+ rescue ZaiPayment::Errors::ApiError => e
129
+ # Handle other API errors
130
+ puts "API error: #{e.message}"
131
+ end
132
+ ```
133
+
70
134
  ## 🧩 Roadmap
71
135
 
72
136
  | Area | Description | Status |
73
137
  | ------------------------------- | --------------------------------- | -------------- |
74
138
  | ✅ Authentication | OAuth2 Client Credentials flow | Done |
139
+ | ✅ Webhooks | CRUD for webhook endpoints | Done |
75
140
  | 💳 Payments | Single and recurring payments | 🚧 In progress |
76
141
  | 🏦 Virtual Accounts (VA / PIPU) | Manage virtual accounts & PayTo | ⏳ Planned |
77
- | 🧾 Webhooks | CRUD for webhook endpoints | ⏳ Planned |
78
142
  | 👤 Users | Manage PayIn / PayOut users | ⏳ Planned |
79
143
  | 💼 Wallets | Create and manage wallet accounts | ⏳ Planned |
80
144
 
@@ -0,0 +1,232 @@
1
+ # Zai Payment Webhook Architecture
2
+
3
+ ```
4
+ ┌─────────────────────────────────────────────────────────────────┐
5
+ │ Client Application │
6
+ └────────────────────────┬────────────────────────────────────────┘
7
+
8
+ │ ZaiPayment.webhooks.list()
9
+ │ ZaiPayment.webhooks.create(...)
10
+ │ ZaiPayment.webhooks.update(...)
11
+ │ ZaiPayment.webhooks.delete(...)
12
+
13
+
14
+ ┌─────────────────────────────────────────────────────────────────┐
15
+ │ ZaiPayment (Module) │
16
+ │ ┌───────────────────────────────────────────────────────────┐ │
17
+ │ │ config() - Configuration singleton │ │
18
+ │ │ auth() - TokenProvider singleton │ │
19
+ │ │ webhooks() - Webhook resource singleton │ │
20
+ │ └───────────────────────────────────────────────────────────┘ │
21
+ └────────────────────────┬────────────────────────────────────────┘
22
+
23
+ ┌───────────────┴───────────────┐
24
+ │ │
25
+ ▼ ▼
26
+ ┌──────────────────┐ ┌──────────────────────┐
27
+ │ Config │ │ Auth::TokenProvider │
28
+ │ ───────────── │ │ ────────────────── │
29
+ │ - environment │◄─────────│ Uses config │
30
+ │ - client_id │ │ - bearer_token() │
31
+ │ - client_secret │ │ - refresh_token() │
32
+ │ - scope │ │ - clear_token() │
33
+ │ - endpoints() │ │ │
34
+ └──────────────────┘ └──────────────────────┘
35
+
36
+
37
+
38
+ ┌──────────────────────┐
39
+ │ TokenStore │
40
+ │ ────────────────── │
41
+ │ (MemoryStore) │
42
+ │ - fetch() │
43
+ │ - write() │
44
+ │ - clear() │
45
+ └──────────────────────┘
46
+
47
+
48
+ ┌─────────────────────────────────────────────────────────────────┐
49
+ │ Resources::Webhook (Resource Layer) │
50
+ │ ┌───────────────────────────────────────────────────────────┐ │
51
+ │ │ list(limit:, offset:) │ │
52
+ │ │ show(webhook_id) │ │
53
+ │ │ create(url:, object_type:, enabled:, description:) │ │
54
+ │ │ update(webhook_id, ...) │ │
55
+ │ │ delete(webhook_id) │ │
56
+ │ │ │ │
57
+ │ │ Private validation methods: │ │
58
+ │ │ - validate_id!() │ │
59
+ │ │ - validate_presence!() │ │
60
+ │ │ - validate_url!() │ │
61
+ │ └───────────────────────────────────────────────────────────┘ │
62
+ └────────────────────────┬────────────────────────────────────────┘
63
+
64
+
65
+ ┌─────────────────────────────────────────────────────────────────┐
66
+ │ Client (HTTP Layer) │
67
+ │ ┌───────────────────────────────────────────────────────────┐ │
68
+ │ │ get(path, params:) │ │
69
+ │ │ post(path, body:) │ │
70
+ │ │ patch(path, body:) │ │
71
+ │ │ delete(path) │ │
72
+ │ │ │ │
73
+ │ │ Private: │ │
74
+ │ │ - connection() - Faraday with auth headers │ │
75
+ │ │ - handle_faraday_error() │ │
76
+ │ └───────────────────────────────────────────────────────────┘ │
77
+ └────────────────────────┬────────────────────────────────────────┘
78
+
79
+
80
+ ┌─────────────────────────────────────────────────────────────────┐
81
+ │ Faraday (HTTP Client) │
82
+ │ ┌───────────────────────────────────────────────────────────┐ │
83
+ │ │ Authorization: Bearer <token> │ │
84
+ │ │ Content-Type: application/json │ │
85
+ │ │ Accept: application/json │ │
86
+ │ └───────────────────────────────────────────────────────────┘ │
87
+ └────────────────────────┬────────────────────────────────────────┘
88
+
89
+
90
+ ┌─────────────────────────────────────────────────────────────────┐
91
+ │ Zai API │
92
+ │ sandbox.au-0000.api.assemblypay.com/webhooks │
93
+ └────────────────────────┬────────────────────────────────────────┘
94
+
95
+ │ HTTP Response
96
+
97
+ ┌─────────────────────────────────────────────────────────────────┐
98
+ │ Response (Wrapper) │
99
+ │ ┌───────────────────────────────────────────────────────────┐ │
100
+ │ │ status - HTTP status code │ │
101
+ │ │ body - Raw response body │ │
102
+ │ │ headers - Response headers │ │
103
+ │ │ data() - Extracted data │ │
104
+ │ │ meta() - Pagination metadata │ │
105
+ │ │ success?() - 2xx status check │ │
106
+ │ │ client_error?()- 4xx status check │ │
107
+ │ │ server_error?()- 5xx status check │ │
108
+ │ │ │ │
109
+ │ │ Private: │ │
110
+ │ │ - check_for_errors!() - Raises specific errors │ │
111
+ │ └───────────────────────────────────────────────────────────┘ │
112
+ └────────────────────────┬────────────────────────────────────────┘
113
+
114
+
115
+ ┌─────────────────────────────────────────────────────────────────┐
116
+ │ Error Hierarchy │
117
+ │ ┌───────────────────────────────────────────────────────────┐ │
118
+ │ │ Error (Base) │ │
119
+ │ │ ├── AuthError │ │
120
+ │ │ ├── ConfigurationError │ │
121
+ │ │ ├── ApiError │ │
122
+ │ │ │ ├── BadRequestError (400) │ │
123
+ │ │ │ ├── UnauthorizedError (401) │ │
124
+ │ │ │ ├── ForbiddenError (403) │ │
125
+ │ │ │ ├── NotFoundError (404) │ │
126
+ │ │ │ ├── ValidationError (422) │ │
127
+ │ │ │ ├── RateLimitError (429) │ │
128
+ │ │ │ └── ServerError (5xx) │ │
129
+ │ │ ├── TimeoutError │ │
130
+ │ │ └── ConnectionError │ │
131
+ │ └───────────────────────────────────────────────────────────┘ │
132
+ └─────────────────────────────────────────────────────────────────┘
133
+ ```
134
+
135
+ ## Request Flow
136
+
137
+ 1. **Client calls** `ZaiPayment.webhooks.list()`
138
+ 2. **Module** returns singleton `Resources::Webhook` instance
139
+ 3. **Webhook resource** validates input and calls `client.get('/webhooks', params: {...})`
140
+ 4. **Client** prepares HTTP request with authentication
141
+ 5. **TokenProvider** provides valid bearer token (auto-refresh if expired)
142
+ 6. **Faraday** makes HTTP request to Zai API
143
+ 7. **Response** wraps Faraday response
144
+ 8. **Response** checks status and raises error if needed
145
+ 9. **Response** returns to client with `data()` and `meta()` methods
146
+ 10. **Client application** receives response and processes data
147
+
148
+ ## Key Design Decisions
149
+
150
+ ### 1. Singleton Pattern for Resources
151
+ ```ruby
152
+ ZaiPayment.webhooks # Always returns same instance
153
+ ```
154
+ - Reduces object creation overhead
155
+ - Consistent configuration across application
156
+ - Easy to use in any context
157
+
158
+ ### 2. Dependency Injection
159
+ ```ruby
160
+ Webhook.new(client: custom_client)
161
+ ```
162
+ - Testable (can inject mock client)
163
+ - Flexible (can use different configs)
164
+ - Follows SOLID principles
165
+
166
+ ### 3. Response Wrapper
167
+ ```ruby
168
+ response = webhooks.list
169
+ response.success? # Boolean check
170
+ response.data # Extracted data
171
+ response.meta # Pagination info
172
+ ```
173
+ - Consistent interface across all resources
174
+ - Rich API for checking status
175
+ - Automatic error handling
176
+
177
+ ### 4. Fail Fast Validation
178
+ ```ruby
179
+ validate_url!(url) # Before API call
180
+ ```
181
+ - Catches errors early
182
+ - Better error messages
183
+ - Reduces unnecessary API calls
184
+
185
+ ### 5. Resource-Based Organization
186
+ ```ruby
187
+ lib/zai_payment/resources/
188
+ ├── webhook.rb
189
+ ├── user.rb # Future
190
+ └── item.rb # Future
191
+ ```
192
+ - Easy to extend
193
+ - Clear separation of concerns
194
+ - Follows REST principles
195
+
196
+ ## Thread Safety
197
+
198
+ - ✅ **TokenProvider**: Uses Mutex for thread-safe token refresh
199
+ - ✅ **MemoryStore**: Thread-safe token storage
200
+ - ✅ **Client**: Creates new Faraday connection per instance
201
+ - ✅ **Webhook**: Stateless, no shared mutable state
202
+
203
+ ## Extension Points
204
+
205
+ Add new resources by following the same pattern:
206
+
207
+ ```ruby
208
+ # lib/zai_payment/resources/user.rb
209
+ module ZaiPayment
210
+ module Resources
211
+ class User
212
+ def initialize(client: nil)
213
+ @client = client || Client.new
214
+ end
215
+
216
+ def list
217
+ client.get('/users')
218
+ end
219
+
220
+ def show(user_id)
221
+ client.get("/users/#{user_id}")
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ # lib/zai_payment.rb
228
+ def users
229
+ @users ||= Resources::User.new
230
+ end
231
+ ```
232
+
data/docs/WEBHOOKS.md ADDED
@@ -0,0 +1,157 @@
1
+ # Zai Payment Webhook Implementation
2
+
3
+ ## Overview
4
+ This document provides a summary of the webhook implementation in the zai_payment gem.
5
+
6
+ ## Architecture
7
+
8
+ ### Core Components
9
+
10
+ 1. **Client** (`lib/zai_payment/client.rb`)
11
+ - Base HTTP client for making API requests
12
+ - Handles authentication automatically via TokenProvider
13
+ - Supports GET, POST, PATCH, DELETE methods
14
+ - Manages connection with proper headers and JSON encoding/decoding
15
+
16
+ 2. **Response** (`lib/zai_payment/response.rb`)
17
+ - Wraps Faraday responses
18
+ - Provides convenient methods: `success?`, `client_error?`, `server_error?`
19
+ - Automatically raises appropriate errors based on HTTP status
20
+ - Extracts data and metadata from response body
21
+
22
+ 3. **Webhook Resource** (`lib/zai_payment/resources/webhook.rb`)
23
+ - Implements all CRUD operations for webhooks
24
+ - Full input validation
25
+ - Clean, documented API
26
+
27
+ 4. **Enhanced Error Handling** (`lib/zai_payment/errors.rb`)
28
+ - Specific error classes for different scenarios
29
+ - Makes debugging and error handling easier
30
+
31
+ ## API Methods
32
+
33
+ ### List Webhooks
34
+ ```ruby
35
+ ZaiPayment.webhooks.list(limit: 10, offset: 0)
36
+ ```
37
+ - Returns paginated list of webhooks
38
+ - Response includes `data` (array of webhooks) and `meta` (pagination info)
39
+
40
+ ### Show Webhook
41
+ ```ruby
42
+ ZaiPayment.webhooks.show(webhook_id)
43
+ ```
44
+ - Returns details of a specific webhook
45
+ - Raises `NotFoundError` if webhook doesn't exist
46
+
47
+ ### Create Webhook
48
+ ```ruby
49
+ ZaiPayment.webhooks.create(
50
+ url: 'https://example.com/webhook',
51
+ object_type: 'transactions',
52
+ enabled: true,
53
+ description: 'Optional description'
54
+ )
55
+ ```
56
+ - Validates URL format
57
+ - Validates required fields
58
+ - Returns created webhook with ID
59
+
60
+ ### Update Webhook
61
+ ```ruby
62
+ ZaiPayment.webhooks.update(
63
+ webhook_id,
64
+ url: 'https://example.com/new-webhook',
65
+ enabled: false
66
+ )
67
+ ```
68
+ - All fields are optional
69
+ - Only updates provided fields
70
+ - Validates URL format if URL is provided
71
+
72
+ ### Delete Webhook
73
+ ```ruby
74
+ ZaiPayment.webhooks.delete(webhook_id)
75
+ ```
76
+ - Permanently deletes the webhook
77
+ - Returns 204 No Content on success
78
+
79
+ ## Error Handling
80
+
81
+ The gem provides specific error classes:
82
+
83
+ | Error Class | HTTP Status | Description |
84
+ |------------|-------------|-------------|
85
+ | `ValidationError` | 400, 422 | Invalid input data |
86
+ | `UnauthorizedError` | 401 | Authentication failed |
87
+ | `ForbiddenError` | 403 | Access denied |
88
+ | `NotFoundError` | 404 | Resource not found |
89
+ | `RateLimitError` | 429 | Too many requests |
90
+ | `ServerError` | 5xx | Server-side error |
91
+ | `TimeoutError` | - | Request timeout |
92
+ | `ConnectionError` | - | Connection failed |
93
+
94
+ Example:
95
+ ```ruby
96
+ begin
97
+ response = ZaiPayment.webhooks.create(...)
98
+ rescue ZaiPayment::Errors::ValidationError => e
99
+ puts "Validation failed: #{e.message}"
100
+ rescue ZaiPayment::Errors::UnauthorizedError => e
101
+ puts "Authentication failed: #{e.message}"
102
+ end
103
+ ```
104
+
105
+ ## Best Practices Implemented
106
+
107
+ 1. **Single Responsibility**: Each class has a clear, focused purpose
108
+ 2. **DRY (Don't Repeat Yourself)**: Client and Response classes are reusable
109
+ 3. **Error Handling**: Comprehensive error handling with specific error classes
110
+ 4. **Input Validation**: All inputs are validated before making API calls
111
+ 5. **Documentation**: Inline documentation with examples
112
+ 6. **Testing**: Comprehensive test coverage using RSpec
113
+ 7. **Thread Safety**: TokenProvider uses mutex for thread-safe token refresh
114
+ 8. **Configuration**: Centralized configuration management
115
+ 9. **RESTful Design**: Follows REST principles for resource management
116
+ 10. **Response Wrapping**: Consistent response format across all methods
117
+
118
+ ## Usage Examples
119
+
120
+ See `examples/webhooks.rb` for complete examples including:
121
+ - Basic CRUD operations
122
+ - Pagination
123
+ - Error handling
124
+ - Custom client instances
125
+
126
+ ## Testing
127
+
128
+ Run the webhook tests:
129
+ ```bash
130
+ bundle exec rspec spec/zai_payment/resources/webhook_spec.rb
131
+ ```
132
+
133
+ The test suite covers:
134
+ - All CRUD operations
135
+ - Success and error scenarios
136
+ - Input validation
137
+ - Error handling
138
+ - Edge cases
139
+
140
+ ## Future Enhancements
141
+
142
+ Potential improvements for future versions:
143
+ 1. Webhook job management (list jobs, show job details)
144
+ 2. Webhook signature verification
145
+ 3. Webhook retry logic
146
+ 4. Bulk operations
147
+ 5. Async webhook operations
148
+
149
+ ## API Reference
150
+
151
+ For the official Zai API documentation, see:
152
+ - [List Webhooks](https://developer.hellozai.com/reference/getallwebhooks)
153
+ - [Show Webhook](https://developer.hellozai.com/reference/getwebhookbyid)
154
+ - [Create Webhook](https://developer.hellozai.com/reference/createwebhook)
155
+ - [Update Webhook](https://developer.hellozai.com/reference/updatewebhook)
156
+ - [Delete Webhook](https://developer.hellozai.com/reference/deletewebhookbyid)
157
+
@@ -0,0 +1,146 @@
1
+ # Webhook Examples
2
+
3
+ This file demonstrates how to use the ZaiPayment webhook functionality.
4
+
5
+ ## Setup
6
+
7
+ ```ruby
8
+ require 'zai_payment'
9
+
10
+ # Configure the gem
11
+ ZaiPayment.configure do |config|
12
+ config.environment = :prelive # or :production
13
+ config.client_id = 'your_client_id'
14
+ config.client_secret = 'your_client_secret'
15
+ config.scope = 'your_scope'
16
+ end
17
+ ```
18
+
19
+ ## List Webhooks
20
+
21
+ ```ruby
22
+ # Get all webhooks
23
+ response = ZaiPayment.webhooks.list
24
+ puts response.data # Array of webhooks
25
+ puts response.meta # Pagination metadata
26
+
27
+ # With pagination
28
+ response = ZaiPayment.webhooks.list(limit: 20, offset: 10)
29
+ ```
30
+
31
+ ## Show a Specific Webhook
32
+
33
+ ```ruby
34
+ webhook_id = 'webhook_123'
35
+ response = ZaiPayment.webhooks.show(webhook_id)
36
+
37
+ webhook = response.data
38
+ puts webhook['id']
39
+ puts webhook['url']
40
+ puts webhook['object_type']
41
+ puts webhook['enabled']
42
+ ```
43
+
44
+ ## Create a Webhook
45
+
46
+ ```ruby
47
+ response = ZaiPayment.webhooks.create(
48
+ url: 'https://example.com/webhooks/zai',
49
+ object_type: 'transactions',
50
+ enabled: true,
51
+ description: 'Production webhook for transactions'
52
+ )
53
+
54
+ new_webhook = response.data
55
+ puts "Created webhook with ID: #{new_webhook['id']}"
56
+ ```
57
+
58
+ ## Update a Webhook
59
+
60
+ ```ruby
61
+ webhook_id = 'webhook_123'
62
+
63
+ # Update specific fields
64
+ response = ZaiPayment.webhooks.update(
65
+ webhook_id,
66
+ enabled: false,
67
+ description: 'Temporarily disabled'
68
+ )
69
+
70
+ # Or update multiple fields
71
+ response = ZaiPayment.webhooks.update(
72
+ webhook_id,
73
+ url: 'https://example.com/webhooks/zai-v2',
74
+ object_type: 'items',
75
+ enabled: true
76
+ )
77
+ ```
78
+
79
+ ## Delete a Webhook
80
+
81
+ ```ruby
82
+ webhook_id = 'webhook_123'
83
+ response = ZaiPayment.webhooks.delete(webhook_id)
84
+
85
+ if response.success?
86
+ puts "Webhook deleted successfully"
87
+ end
88
+ ```
89
+
90
+ ## Error Handling
91
+
92
+ ```ruby
93
+ begin
94
+ response = ZaiPayment.webhooks.create(
95
+ url: 'https://example.com/webhook',
96
+ object_type: 'transactions'
97
+ )
98
+ rescue ZaiPayment::Errors::ValidationError => e
99
+ puts "Validation error: #{e.message}"
100
+ rescue ZaiPayment::Errors::UnauthorizedError => e
101
+ puts "Authentication failed: #{e.message}"
102
+ rescue ZaiPayment::Errors::NotFoundError => e
103
+ puts "Resource not found: #{e.message}"
104
+ rescue ZaiPayment::Errors::ApiError => e
105
+ puts "API error: #{e.message}"
106
+ end
107
+ ```
108
+
109
+ ## Using Custom Client Instance
110
+
111
+ If you need more control, you can create your own client instance:
112
+
113
+ ```ruby
114
+ config = ZaiPayment::Config.new
115
+ config.environment = :prelive
116
+ config.client_id = 'your_client_id'
117
+ config.client_secret = 'your_client_secret'
118
+ config.scope = 'your_scope'
119
+
120
+ token_provider = ZaiPayment::Auth::TokenProvider.new(config: config)
121
+ client = ZaiPayment::Client.new(config: config, token_provider: token_provider)
122
+
123
+ webhooks = ZaiPayment::Resources::Webhook.new(client: client)
124
+ response = webhooks.list
125
+ ```
126
+
127
+ ## Response Object
128
+
129
+ All webhook methods return a `ZaiPayment::Response` object with the following methods:
130
+
131
+ ```ruby
132
+ response = ZaiPayment.webhooks.list
133
+
134
+ # Check status
135
+ response.success? # => true/false (2xx status)
136
+ response.client_error? # => true/false (4xx status)
137
+ response.server_error? # => true/false (5xx status)
138
+
139
+ # Access data
140
+ response.data # => Main response data (array or hash)
141
+ response.meta # => Pagination metadata (if available)
142
+ response.body # => Raw response body
143
+ response.headers # => Response headers
144
+ response.status # => HTTP status code
145
+ ```
146
+
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+
5
+ module ZaiPayment
6
+ # Base API client that handles HTTP requests to Zai API
7
+ class Client
8
+ attr_reader :config, :token_provider
9
+
10
+ def initialize(config: nil, token_provider: nil)
11
+ @config = config || ZaiPayment.config
12
+ @token_provider = token_provider || ZaiPayment.auth
13
+ end
14
+
15
+ # Perform a GET request
16
+ #
17
+ # @param path [String] the API endpoint path
18
+ # @param params [Hash] query parameters
19
+ # @return [Response] the API response
20
+ def get(path, params: {})
21
+ request(:get, path, params: params)
22
+ end
23
+
24
+ # Perform a POST request
25
+ #
26
+ # @param path [String] the API endpoint path
27
+ # @param body [Hash] request body
28
+ # @return [Response] the API response
29
+ def post(path, body: {})
30
+ request(:post, path, body: body)
31
+ end
32
+
33
+ # Perform a PATCH request
34
+ #
35
+ # @param path [String] the API endpoint path
36
+ # @param body [Hash] request body
37
+ # @return [Response] the API response
38
+ def patch(path, body: {})
39
+ request(:patch, path, body: body)
40
+ end
41
+
42
+ # Perform a DELETE request
43
+ #
44
+ # @param path [String] the API endpoint path
45
+ # @return [Response] the API response
46
+ def delete(path)
47
+ request(:delete, path)
48
+ end
49
+
50
+ private
51
+
52
+ def request(method, path, params: {}, body: {})
53
+ response = connection.public_send(method) do |req|
54
+ req.url path
55
+ req.params = params if params.any?
56
+ req.body = body if body.any?
57
+ end
58
+
59
+ Response.new(response)
60
+ rescue Faraday::Error => e
61
+ handle_faraday_error(e)
62
+ end
63
+
64
+ def connection
65
+ @connection ||= build_connection
66
+ end
67
+
68
+ def build_connection
69
+ Faraday.new do |faraday|
70
+ configure_connection(faraday)
71
+ end
72
+ end
73
+
74
+ def configure_connection(faraday)
75
+ faraday.url_prefix = base_url
76
+ apply_headers(faraday)
77
+ apply_middleware(faraday)
78
+ apply_timeouts(faraday)
79
+ faraday.adapter Faraday.default_adapter
80
+ end
81
+
82
+ def apply_headers(faraday)
83
+ faraday.headers['Authorization'] = token_provider.bearer_token
84
+ faraday.headers['Content-Type'] = 'application/json'
85
+ faraday.headers['Accept'] = 'application/json'
86
+ end
87
+
88
+ def apply_middleware(faraday)
89
+ faraday.request :json
90
+ faraday.response :json, content_type: /\bjson$/
91
+ end
92
+
93
+ def apply_timeouts(faraday)
94
+ faraday.options.timeout = config.timeout if config.timeout
95
+ faraday.options.open_timeout = config.open_timeout if config.open_timeout
96
+ end
97
+
98
+ def base_url
99
+ # Webhooks API uses va_base endpoint
100
+ config.endpoints[:va_base]
101
+ end
102
+
103
+ def handle_faraday_error(error)
104
+ case error
105
+ when Faraday::TimeoutError
106
+ raise Errors::TimeoutError, "Request timed out: #{error.message}"
107
+ when Faraday::ConnectionFailed
108
+ raise Errors::ConnectionError, "Connection failed: #{error.message}"
109
+ when Faraday::ClientError
110
+ raise Errors::ApiError, "Client error: #{error.message}"
111
+ else
112
+ raise Errors::ApiError, "Request failed: #{error.message}"
113
+ end
114
+ end
115
+ end
116
+ end
@@ -10,6 +10,8 @@ module ZaiPayment
10
10
  @client_id = nil
11
11
  @client_secret = nil
12
12
  @scope = nil
13
+ @timeout = 10
14
+ @open_timeout = 10
13
15
  end
14
16
 
15
17
  def validate!
@@ -2,8 +2,27 @@
2
2
 
3
3
  module ZaiPayment
4
4
  module Errors
5
+ # Base error class
5
6
  class Error < StandardError; end
7
+
8
+ # Authentication errors
6
9
  class AuthError < Error; end
10
+
11
+ # Configuration errors
7
12
  class ConfigurationError < Error; end
13
+
14
+ # API errors
15
+ class ApiError < Error; end
16
+ class BadRequestError < ApiError; end
17
+ class UnauthorizedError < ApiError; end
18
+ class ForbiddenError < ApiError; end
19
+ class NotFoundError < ApiError; end
20
+ class ValidationError < ApiError; end
21
+ class RateLimitError < ApiError; end
22
+ class ServerError < ApiError; end
23
+
24
+ # Network errors
25
+ class TimeoutError < Error; end
26
+ class ConnectionError < Error; end
8
27
  end
9
28
  end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZaiPayment
4
+ module Resources
5
+ # Webhook resource for managing Zai webhooks
6
+ #
7
+ # @see https://developer.hellozai.com/reference/getallwebhooks
8
+ class Webhook
9
+ attr_reader :client
10
+
11
+ def initialize(client: nil)
12
+ @client = client || Client.new
13
+ end
14
+
15
+ # List all webhooks
16
+ #
17
+ # @param limit [Integer] number of records to return (default: 10)
18
+ # @param offset [Integer] number of records to skip (default: 0)
19
+ # @return [Response] the API response containing webhooks array
20
+ #
21
+ # @example
22
+ # webhooks = ZaiPayment::Resources::Webhook.new
23
+ # response = webhooks.list
24
+ # response.data # => [{"id" => "...", "url" => "..."}, ...]
25
+ #
26
+ # @see https://developer.hellozai.com/reference/getallwebhooks
27
+ def list(limit: 10, offset: 0)
28
+ params = {
29
+ limit: limit,
30
+ offset: offset
31
+ }
32
+
33
+ client.get('/webhooks', params: params)
34
+ end
35
+
36
+ # Get a specific webhook by ID
37
+ #
38
+ # @param webhook_id [String] the webhook ID
39
+ # @return [Response] the API response containing webhook details
40
+ #
41
+ # @example
42
+ # webhooks = ZaiPayment::Resources::Webhook.new
43
+ # response = webhooks.show("webhook_id")
44
+ # response.data # => {"id" => "webhook_id", "url" => "...", ...}
45
+ #
46
+ # @see https://developer.hellozai.com/reference/getwebhookbyid
47
+ def show(webhook_id)
48
+ validate_id!(webhook_id, 'webhook_id')
49
+ client.get("/webhooks/#{webhook_id}")
50
+ end
51
+
52
+ # Create a new webhook
53
+ #
54
+ # @param url [String] the webhook URL to receive notifications
55
+ # @param object_type [String] the type of object to watch (e.g., 'transactions', 'items')
56
+ # @param enabled [Boolean] whether the webhook is enabled (default: true)
57
+ # @param description [String] optional description of the webhook
58
+ # @return [Response] the API response containing created webhook
59
+ #
60
+ # @example
61
+ # webhooks = ZaiPayment::Resources::Webhook.new
62
+ # response = webhooks.create(
63
+ # url: "https://example.com/webhooks",
64
+ # object_type: "transactions",
65
+ # enabled: true
66
+ # )
67
+ #
68
+ # @see https://developer.hellozai.com/reference/createwebhook
69
+ def create(url: nil, object_type: nil, enabled: true, description: nil)
70
+ validate_presence!(url, 'url')
71
+ validate_presence!(object_type, 'object_type')
72
+ validate_url!(url)
73
+
74
+ body = {
75
+ url: url,
76
+ object_type: object_type,
77
+ enabled: enabled
78
+ }
79
+
80
+ body[:description] = description if description
81
+
82
+ client.post('/webhooks', body: body)
83
+ end
84
+
85
+ # Update an existing webhook
86
+ #
87
+ # @param webhook_id [String] the webhook ID
88
+ # @param url [String] optional new webhook URL
89
+ # @param object_type [String] optional new object type
90
+ # @param enabled [Boolean] optional enabled status
91
+ # @param description [String] optional description
92
+ # @return [Response] the API response containing updated webhook
93
+ #
94
+ # @example
95
+ # webhooks = ZaiPayment::Resources::Webhook.new
96
+ # response = webhooks.update(
97
+ # "webhook_id",
98
+ # enabled: false
99
+ # )
100
+ #
101
+ # @see https://developer.hellozai.com/reference/updatewebhook
102
+ def update(webhook_id, url: nil, object_type: nil, enabled: nil, description: nil)
103
+ validate_id!(webhook_id, 'webhook_id')
104
+
105
+ body = {}
106
+ body[:url] = url if url
107
+ body[:object_type] = object_type if object_type
108
+ body[:enabled] = enabled unless enabled.nil?
109
+ body[:description] = description if description
110
+
111
+ validate_url!(url) if url
112
+
113
+ raise Errors::ValidationError, 'At least one attribute must be provided for update' if body.empty?
114
+
115
+ client.patch("/webhooks/#{webhook_id}", body: body)
116
+ end
117
+
118
+ # Delete a webhook
119
+ #
120
+ # @param webhook_id [String] the webhook ID
121
+ # @return [Response] the API response
122
+ #
123
+ # @example
124
+ # webhooks = ZaiPayment::Resources::Webhook.new
125
+ # response = webhooks.delete("webhook_id")
126
+ #
127
+ # @see https://developer.hellozai.com/reference/deletewebhook
128
+ def delete(webhook_id)
129
+ validate_id!(webhook_id, 'webhook_id')
130
+ client.delete("/webhooks/#{webhook_id}")
131
+ end
132
+
133
+ private
134
+
135
+ def validate_id!(value, field_name)
136
+ return unless value.nil? || value.to_s.strip.empty?
137
+
138
+ raise Errors::ValidationError, "#{field_name} is required and cannot be blank"
139
+ end
140
+
141
+ def validate_presence!(value, field_name)
142
+ return unless value.nil? || value.to_s.strip.empty?
143
+
144
+ raise Errors::ValidationError, "#{field_name} is required and cannot be blank"
145
+ end
146
+
147
+ def validate_url!(url)
148
+ uri = URI.parse(url)
149
+ unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
150
+ raise Errors::ValidationError, 'url must be a valid HTTP or HTTPS URL'
151
+ end
152
+ rescue URI::InvalidURIError
153
+ raise Errors::ValidationError, 'url must be a valid URL'
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZaiPayment
4
+ # Wrapper for API responses
5
+ class Response
6
+ attr_reader :status, :body, :headers, :raw_response
7
+
8
+ def initialize(faraday_response)
9
+ @raw_response = faraday_response
10
+ @status = faraday_response.status
11
+ @body = faraday_response.body
12
+ @headers = faraday_response.headers
13
+
14
+ check_for_errors!
15
+ end
16
+
17
+ # Check if the response was successful (2xx status)
18
+ def success?
19
+ (200..299).cover?(status)
20
+ end
21
+
22
+ # Check if the response was a client error (4xx status)
23
+ def client_error?
24
+ (400..499).cover?(status)
25
+ end
26
+
27
+ # Check if the response was a server error (5xx status)
28
+ def server_error?
29
+ (500..599).cover?(status)
30
+ end
31
+
32
+ # Get the data from the response body
33
+ def data
34
+ body.is_a?(Hash) ? body['webhooks'] || body : body
35
+ end
36
+
37
+ # Get pagination or metadata info
38
+ def meta
39
+ body.is_a?(Hash) ? body['meta'] : nil
40
+ end
41
+
42
+ ERROR_STATUS_MAP = {
43
+ 400 => Errors::BadRequestError,
44
+ 401 => Errors::UnauthorizedError,
45
+ 403 => Errors::ForbiddenError,
46
+ 404 => Errors::NotFoundError,
47
+ 422 => Errors::ValidationError,
48
+ 429 => Errors::RateLimitError
49
+ }.merge((500..599).to_h { |code| [code, Errors::ServerError] }).freeze
50
+
51
+ private
52
+
53
+ def check_for_errors!
54
+ return if success?
55
+
56
+ raise_appropriate_error
57
+ end
58
+
59
+ def raise_appropriate_error
60
+ error_message = extract_error_message
61
+ error_class = error_class_for_status
62
+ raise error_class, error_message
63
+ end
64
+
65
+ def error_class_for_status
66
+ ERROR_STATUS_MAP.fetch(status, Errors::ApiError)
67
+ end
68
+
69
+ def extract_error_message
70
+ if body.is_a?(Hash)
71
+ body['error'] || body['message'] || body['errors']&.join(', ') || "HTTP #{status}"
72
+ else
73
+ "HTTP #{status}: #{body}"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ZaiPayment
4
- VERSION = '1.0.2'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/zai_payment.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'faraday'
4
+ require 'uri'
4
5
  require_relative 'zai_payment/version'
5
6
  require_relative 'zai_payment/config'
6
7
  require_relative 'zai_payment/errors'
7
8
  require_relative 'zai_payment/auth/token_provider'
8
9
  require_relative 'zai_payment/auth/token_store'
9
10
  require_relative 'zai_payment/auth/token_stores/memory_store'
11
+ require_relative 'zai_payment/client'
12
+ require_relative 'zai_payment/response'
13
+ require_relative 'zai_payment/resources/webhook'
10
14
 
11
15
  module ZaiPayment
12
16
  class << self
@@ -29,5 +33,11 @@ module ZaiPayment
29
33
  def clear_token! = auth.clear_token
30
34
  def token_expiry = auth.token_expiry
31
35
  def token_type = auth.token_type
36
+
37
+ # --- Resource accessors ---
38
+ # @return [ZaiPayment::Resources::Webhook] webhook resource instance
39
+ def webhooks
40
+ @webhooks ||= Resources::Webhook.new
41
+ end
32
42
  end
33
43
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zai_payment
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eddy Jaga
@@ -32,15 +32,22 @@ extra_rdoc_files: []
32
32
  files:
33
33
  - CHANGELOG.md
34
34
  - CODE_OF_CONDUCT.md
35
+ - IMPLEMENTATION.md
35
36
  - LICENSE.txt
36
37
  - README.md
37
38
  - Rakefile
39
+ - docs/ARCHITECTURE.md
40
+ - docs/WEBHOOKS.md
41
+ - examples/webhooks.md
38
42
  - lib/zai_payment.rb
39
43
  - lib/zai_payment/auth/token_provider.rb
40
44
  - lib/zai_payment/auth/token_store.rb
41
45
  - lib/zai_payment/auth/token_stores/memory_store.rb
46
+ - lib/zai_payment/client.rb
42
47
  - lib/zai_payment/config.rb
43
48
  - lib/zai_payment/errors.rb
49
+ - lib/zai_payment/resources/webhook.rb
50
+ - lib/zai_payment/response.rb
44
51
  - lib/zai_payment/version.rb
45
52
  - sig/zai_payment.rbs
46
53
  homepage: https://github.com/Sentia/zai-payment