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 +4 -4
- data/CHANGELOG.md +81 -1
- data/IMPLEMENTATION.md +201 -0
- data/README.md +81 -13
- data/docs/ARCHITECTURE.md +232 -0
- data/docs/AUTHENTICATION.md +647 -0
- data/docs/README.md +81 -0
- data/docs/WEBHOOKS.md +417 -0
- data/docs/WEBHOOK_SECURITY_QUICKSTART.md +141 -0
- data/docs/WEBHOOK_SIGNATURE.md +244 -0
- data/examples/webhooks.md +635 -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 +331 -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 +40 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4b63d2fefc49eb419bc1de0db2cb41c3df25794f6efbf2b9a92ca6091112258
|
|
4
|
+
data.tar.gz: 1ba74f063b3360b79acafe36a25d43c0b92ae09bee3c5d3da2f3ef30bf4fa5ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 209ce54d8809117bb3fe6059c50dc1a6be4a7de974a961506936772b3f0480510b28382445ef113f4bf0ef914b314377bb3ddf5613ee942ad691be818e1d3383
|
|
7
|
+
data.tar.gz: e8efd4e2b0e5d2d42403d389c4218b95735ae4f1dcf60eb3e1e8012d23750bb2a0b8edd5fcc56f13816bc3c3de955de06f477230df5acce534824a15bebbc0ce
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,87 @@
|
|
|
1
|
-
## [
|
|
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
|
-
## 🚀
|
|
47
|
+
## 🚀 Quick Start
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
### Authentication
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Get an OAuth2 token with automatic caching and refresh:
|
|
52
52
|
|
|
53
|
-
|
|
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
|
-
|
|
68
|
+
The gem handles OAuth2 Client Credentials flow automatically - tokens are cached and refreshed before expiration.
|
|
56
69
|
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
- [
|
|
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
|
+
|