zai_payment 1.0.2 → 1.2.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: a4b63d2fefc49eb419bc1de0db2cb41c3df25794f6efbf2b9a92ca6091112258
4
+ data.tar.gz: 1ba74f063b3360b79acafe36a25d43c0b92ae09bee3c5d3da2f3ef30bf4fa5ab
5
5
  SHA512:
6
- metadata.gz: 7147ac27b27d63eac4c9ba4b51050ca539b4da4aa281a76a3bb569d3642ca691327194640ae0c950f5e7e31acf9f1a1fafb22cd80f27be879dd1a52efc39e2f3
7
- data.tar.gz: bb2ce6f8c53a32ca8a84663aa00f028e7a6adcc9a166fe2137909ebdfff9ee69122bb6340460ac79ac5ff19dec777a8c3716b4afb1ff760d513f73bc232b2462
6
+ metadata.gz: 209ce54d8809117bb3fe6059c50dc1a6be4a7de974a961506936772b3f0480510b28382445ef113f4bf0ef914b314377bb3ddf5613ee942ad691be818e1d3383
7
+ data.tar.gz: e8efd4e2b0e5d2d42403d389c4218b95735ae4f1dcf60eb3e1e8012d23750bb2a0b8edd5fcc56f13816bc3c3de955de06f477230df5acce534824a15bebbc0ce
data/CHANGELOG.md CHANGED
@@ -1,7 +1,87 @@
1
- ## [Unreleased]
1
+ ## [Released]
2
+
3
+ ## [1.2.0] - 2025-10-22
4
+ ### Added
5
+ - **Webhook Security: Signature Verification** 🔒
6
+ - `create_secret_key(secret_key:)` - Register a secret key with Zai
7
+ - `verify_signature(payload:, signature_header:, secret_key:, tolerance:)` - Verify webhook authenticity
8
+ - `generate_signature(payload, secret_key, timestamp)` - Utility for testing
9
+ - HMAC SHA256 signature verification with Base64 URL-safe encoding
10
+ - Timestamp validation to prevent replay attacks (configurable tolerance)
11
+ - Constant-time comparison to prevent timing attacks
12
+ - Support for multiple signatures (key rotation scenarios)
13
+
14
+ ### Documentation
15
+ - **NEW**: [Authentication Guide](docs/AUTHENTICATION.md) - Comprehensive guide covering:
16
+ - Short way: `ZaiPayment.token` (one-liner approach)
17
+ - Long way: `TokenProvider.new(config:).bearer_token` (advanced control)
18
+ - Token lifecycle and automatic management
19
+ - Multiple configurations, testing, error handling
20
+ - Best practices and troubleshooting
21
+ - **NEW**: [Webhook Security Quick Start](docs/WEBHOOK_SECURITY_QUICKSTART.md) - 5-minute setup guide
22
+ - **NEW**: [Webhook Signature Implementation](docs/WEBHOOK_SIGNATURE.md) - Technical details
23
+ - **NEW**: [Documentation Index](docs/README.md) - Central navigation for all docs
24
+ - **Enhanced**: [Webhook Examples](examples/webhooks.md) - Added 400+ lines of examples:
25
+ - Complete Rails controller implementation
26
+ - Sinatra example
27
+ - Rack middleware example
28
+ - Background job processing pattern
29
+ - Idempotency pattern
30
+ - **Enhanced**: [Webhook Technical Guide](docs/WEBHOOKS.md) - Added 170+ lines on security
31
+ - **Reorganized**: All documentation moved to `docs/` folder for better organization
32
+ - **Updated**: README.md - Now concise with clear links to detailed documentation
33
+
34
+ ### Testing
35
+ - 56 new test cases for webhook signature verification
36
+ - All tests passing: 95/95 ✓
37
+ - Tests cover valid/invalid signatures, expired timestamps, malformed headers, edge cases
38
+
39
+ ### Security
40
+ - ✅ OWASP compliance for webhook security
41
+ - ✅ RFC 2104 (HMAC) implementation
42
+ - ✅ RFC 4648 (Base64url) encoding
43
+ - ✅ Protection against timing attacks, replay attacks, and MITM attacks
44
+
45
+ **Full Changelog**: https://github.com/Sentia/zai-payment/compare/v1.1.0...v1.2.0
46
+
47
+ ## [1.1.0] - 2025-10-22
48
+ ### Added
49
+ - **Webhooks API**: Full CRUD operations for managing Zai webhooks
50
+ - `ZaiPayment.webhooks.list` - List all webhooks with pagination
51
+ - `ZaiPayment.webhooks.show(id)` - Get a specific webhook
52
+ - `ZaiPayment.webhooks.create(...)` - Create a new webhook
53
+ - `ZaiPayment.webhooks.update(id, ...)` - Update an existing webhook
54
+ - `ZaiPayment.webhooks.delete(id)` - Delete a webhook
55
+ - **Base API Client**: Reusable HTTP client for all API requests
56
+ - **Response Wrapper**: Standardized response handling with error management
57
+ - **Enhanced Error Handling**: New error classes for different API scenarios
58
+ - `ValidationError` (400, 422)
59
+ - `UnauthorizedError` (401)
60
+ - `ForbiddenError` (403)
61
+ - `NotFoundError` (404)
62
+ - `RateLimitError` (429)
63
+ - `ServerError` (5xx)
64
+ - `TimeoutError` and `ConnectionError` for network issues
65
+ - Comprehensive test suite for webhook functionality
66
+ - Example code in `examples/webhooks.rb`
67
+
68
+ **Full Changelog**: https://github.com/Sentia/zai-payment/compare/v1.0.2...v1.1.0
69
+
70
+ ## [1.0.2] - 2025-10-22
71
+ - Update gemspec files and readme
72
+
73
+ **Full Changelog**: https://github.com/Sentia/zai-payment/releases/tag/v1.0.2
74
+
75
+
76
+ ##[1.0.1] - 2025-10-21
77
+ - Update readme and versions
78
+
79
+ **Full Changelog**: https://github.com/Sentia/zai-payment/releases/tag/v1.0.1
80
+
2
81
 
3
82
  ## [1.0.0] - 2025-10-21
4
83
 
5
84
  - Initial release: token auth client with in-memory caching (`ZaiPayment.token`, `refresh_token!`, `clear_token!`, `token_type`, `token_expiry`)
6
85
 
7
86
  **Full Changelog**: https://github.com/Sentia/zai-payment/commits/v1.0.0
87
+
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
@@ -44,27 +44,81 @@ ZaiPayment.configure do |c|
44
44
  end
45
45
  ```
46
46
 
47
- ## 🚀 Authentication
47
+ ## 🚀 Quick Start
48
48
 
49
- The Zai Payment gem implements OAuth2 Client Credentials flow for secure authentication with the Zai API. The gem intelligently manages your authentication tokens behind the scenes, so you don't have to worry about token expiration or manual refreshes.
49
+ ### Authentication
50
50
 
51
- When you request a token, the gem automatically caches it and reuses it for subsequent requests. Since Zai tokens expire after 60 minutes, the gem monitors the token lifetime and seamlessly refreshes it before expiration — ensuring your API calls never fail due to stale credentials.
51
+ Get an OAuth2 token with automatic caching and refresh:
52
52
 
53
- This automatic token management means you can focus on building your integration while the gem handles all the authentication complexity for you. Simply configure your credentials once, and the gem takes care of the rest.
53
+ ```ruby
54
+ # Simple one-liner (recommended)
55
+ token = ZaiPayment.token
56
+
57
+ # Or with full control (advanced)
58
+ config = ZaiPayment::Config.new
59
+ config.environment = :prelive
60
+ config.client_id = 'your_client_id'
61
+ config.client_secret = 'your_client_secret'
62
+ config.scope = 'your_scope'
63
+
64
+ token_provider = ZaiPayment::Auth::TokenProvider.new(config: config)
65
+ token = token_provider.bearer_token
66
+ ```
54
67
 
55
- For more details about Zai's OAuth2 authentication, see the [official documentation](https://developer.hellozai.com/reference/overview#authentication).
68
+ The gem handles OAuth2 Client Credentials flow automatically - tokens are cached and refreshed before expiration.
56
69
 
57
- ```ruby
58
- client = ZaiPayment::Auth::TokenProvider.new(config: ZaiPayment.config)
70
+ 📖 **[Complete Authentication Guide](docs/AUTHENTICATION.md)** - Two approaches, examples, and best practices
59
71
 
60
- client.bearer_token
72
+ ### Webhooks
73
+
74
+ Manage webhook endpoints:
75
+
76
+ ```ruby
77
+ # List webhooks
78
+ response = ZaiPayment.webhooks.list
79
+ webhooks = response.data
80
+
81
+ # Create a webhook
82
+ response = ZaiPayment.webhooks.create(
83
+ url: 'https://example.com/webhooks/zai',
84
+ object_type: 'transactions',
85
+ enabled: true
86
+ )
87
+
88
+ # Secure your webhooks with signature verification
89
+ secret_key = SecureRandom.alphanumeric(32)
90
+ ZaiPayment.webhooks.create_secret_key(secret_key: secret_key)
61
91
  ```
62
92
 
63
- Or, more easily, you can get a token with the convenience one-liner:
93
+ **📚 Documentation:**
94
+ - 📖 [Webhook Examples & Complete Guide](examples/webhooks.md) - Full CRUD operations and patterns
95
+ - 🔒 [Security Quick Start](docs/WEBHOOK_SECURITY_QUICKSTART.md) - 5-minute webhook security setup
96
+ - 🏗️ [Architecture & Implementation](docs/WEBHOOKS.md) - Detailed technical documentation
97
+ - 🔐 [Signature Verification Details](docs/WEBHOOK_SIGNATURE.md) - Security implementation specs
64
98
 
99
+ ### Error Handling
100
+
101
+ The gem provides specific error classes for different scenarios:
65
102
 
66
103
  ```ruby
67
- ZaiPayment.token
104
+ begin
105
+ response = ZaiPayment.webhooks.create(
106
+ url: 'https://example.com/webhook',
107
+ object_type: 'transactions'
108
+ )
109
+ rescue ZaiPayment::Errors::ValidationError => e
110
+ # Handle validation errors (400, 422)
111
+ puts "Validation error: #{e.message}"
112
+ rescue ZaiPayment::Errors::UnauthorizedError => e
113
+ # Handle authentication errors (401)
114
+ puts "Authentication failed: #{e.message}"
115
+ rescue ZaiPayment::Errors::NotFoundError => e
116
+ # Handle not found errors (404)
117
+ puts "Resource not found: #{e.message}"
118
+ rescue ZaiPayment::Errors::ApiError => e
119
+ # Handle other API errors
120
+ puts "API error: #{e.message}"
121
+ end
68
122
  ```
69
123
 
70
124
  ## 🧩 Roadmap
@@ -72,9 +126,9 @@ ZaiPayment.token
72
126
  | Area | Description | Status |
73
127
  | ------------------------------- | --------------------------------- | -------------- |
74
128
  | ✅ Authentication | OAuth2 Client Credentials flow | Done |
129
+ | ✅ Webhooks | CRUD for webhook endpoints | Done |
75
130
  | 💳 Payments | Single and recurring payments | 🚧 In progress |
76
131
  | 🏦 Virtual Accounts (VA / PIPU) | Manage virtual accounts & PayTo | ⏳ Planned |
77
- | 🧾 Webhooks | CRUD for webhook endpoints | ⏳ Planned |
78
132
  | 👤 Users | Manage PayIn / PayOut users | ⏳ Planned |
79
133
  | 💼 Wallets | Create and manage wallet accounts | ⏳ Planned |
80
134
 
@@ -126,8 +180,22 @@ The gem is available as open source under the terms of the [MIT License](https:/
126
180
 
127
181
  Everyone interacting in the ZaiPayment project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Sentia/zai-payment/blob/main/CODE_OF_CONDUCT.md).
128
182
 
129
- ## 🔗 Resources
183
+ ## 📚 Documentation
184
+
185
+ ### Getting Started
186
+ - [**Authentication Guide**](docs/AUTHENTICATION.md) - Two approaches to getting tokens, automatic management
187
+ - [**Webhook Examples**](examples/webhooks.md) - Complete webhook usage guide
188
+ - [**Documentation Index**](docs/README.md) - Full documentation navigation
189
+
190
+ ### Technical Guides
191
+ - [Webhook Architecture](docs/WEBHOOKS.md) - Technical implementation details
192
+ - [Architecture Overview](docs/ARCHITECTURE.md) - System architecture and design
193
+
194
+ ### Security
195
+ - [Webhook Security Quick Start](docs/WEBHOOK_SECURITY_QUICKSTART.md) - 5-minute setup guide
196
+ - [Signature Verification](docs/WEBHOOK_SIGNATURE.md) - Implementation details
130
197
 
198
+ ### External Resources
131
199
  - [Zai Developer Portal](https://developer.hellozai.com/)
132
200
  - [Zai API Reference](https://developer.hellozai.com/reference)
133
- - [AssemblyPay Auth Documentation](https://developer.hellozai.com/docs/introduction)
201
+ - [Zai OAuth Documentation](https://developer.hellozai.com/docs/introduction)
@@ -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
+