zai_payment 1.0.1 → 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 +4 -4
- data/CHANGELOG.md +37 -1
- data/IMPLEMENTATION.md +201 -0
- data/README.md +66 -4
- data/docs/ARCHITECTURE.md +232 -0
- data/docs/WEBHOOKS.md +157 -0
- data/examples/webhooks.md +146 -0
- data/lib/zai_payment/client.rb +116 -0
- data/lib/zai_payment/config.rb +2 -0
- data/lib/zai_payment/errors.rb +19 -0
- data/lib/zai_payment/resources/webhook.rb +157 -0
- data/lib/zai_payment/response.rb +77 -0
- data/lib/zai_payment/version.rb +1 -1
- data/lib/zai_payment.rb +10 -0
- metadata +13 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3dde823881d82243bdd2f57882c57cfe06ba21d20ab5402b58a876c7c752e8c
|
|
4
|
+
data.tar.gz: 37ce743767afba2f834354a81102cc445c544151c26601e59a0f82eb6b967a92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b96c6511bc25c2dcbfe44af58957b160c3bd7ac1fb6aa94fb5708ced6a6e2ff6a209f188f664eb6c857ebe4177f5260191eb62aa1e64924adc57d695815ed38
|
|
7
|
+
data.tar.gz: 6e2410984fe931c17097f145de9a23e7c6048af308c068e7f590efab9991a21c838aed6158bb1be56863fa9e86090f46599eaffec1cf415e2610c46861de06e1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,43 @@
|
|
|
1
|
-
## [
|
|
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
|
@@ -23,9 +23,7 @@ A lightweight and extensible Ruby client for the **Zai (AssemblyPay)** API — s
|
|
|
23
23
|
Add this line to your Gemfile:
|
|
24
24
|
|
|
25
25
|
```ruby
|
|
26
|
-
gem
|
|
27
|
-
git: "https://github.com/Sentia/zai-payment.git",
|
|
28
|
-
branch: "main"
|
|
26
|
+
gem 'zai_payment', '~> 1.0', '>= 1.0.2'
|
|
29
27
|
```
|
|
30
28
|
|
|
31
29
|
Then install
|
|
@@ -69,14 +67,78 @@ Or, more easily, you can get a token with the convenience one-liner:
|
|
|
69
67
|
ZaiPayment.token
|
|
70
68
|
```
|
|
71
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
|
+
|
|
72
134
|
## 🧩 Roadmap
|
|
73
135
|
|
|
74
136
|
| Area | Description | Status |
|
|
75
137
|
| ------------------------------- | --------------------------------- | -------------- |
|
|
76
138
|
| ✅ Authentication | OAuth2 Client Credentials flow | Done |
|
|
139
|
+
| ✅ Webhooks | CRUD for webhook endpoints | Done |
|
|
77
140
|
| 💳 Payments | Single and recurring payments | 🚧 In progress |
|
|
78
141
|
| 🏦 Virtual Accounts (VA / PIPU) | Manage virtual accounts & PayTo | ⏳ Planned |
|
|
79
|
-
| 🧾 Webhooks | CRUD for webhook endpoints | ⏳ Planned |
|
|
80
142
|
| 👤 Users | Manage PayIn / PayOut users | ⏳ Planned |
|
|
81
143
|
| 💼 Wallets | Create and manage wallet accounts | ⏳ Planned |
|
|
82
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
|
+
|