zai_payment 1.2.0 → 1.3.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 +64 -0
- data/CONTRIBUTING.md +383 -0
- data/IMPLEMENTATION.md +280 -177
- data/IMPLEMENTATION_SUMMARY.md +195 -0
- data/README.md +85 -9
- data/badges/.gitkeep +2 -0
- data/badges/coverage.json +1 -0
- data/docs/USERS.md +414 -0
- data/docs/USER_ID_FIELD.md +284 -0
- data/docs/USER_QUICK_REFERENCE.md +230 -0
- data/docs/WEBHOOK_SECURITY_QUICKSTART.md +2 -7
- data/examples/users.md +746 -0
- data/lib/zai_payment/client.rb +10 -3
- data/lib/zai_payment/resources/user.rb +383 -0
- data/lib/zai_payment/response.rb +1 -1
- data/lib/zai_payment/version.rb +1 -1
- data/lib/zai_payment.rb +6 -0
- metadata +10 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# User ID Field in Zai Payment API
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
The `id` field in Zai's Create User API is **optional** in practice, despite being marked as "required" in some documentation.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
### Option 1: Auto-Generated ID (Default) ✅ **Recommended**
|
|
10
|
+
|
|
11
|
+
**Don't provide an `id` field** - Zai will automatically generate one:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Zai will generate an ID like "user-1556506027"
|
|
15
|
+
response = ZaiPayment.users.create(
|
|
16
|
+
email: 'buyer@example.com',
|
|
17
|
+
first_name: 'John',
|
|
18
|
+
last_name: 'Doe',
|
|
19
|
+
country: 'USA'
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
user_id = response.data['id'] # => "user-1556506027" (auto-generated)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Option 2: Custom ID
|
|
26
|
+
|
|
27
|
+
**Provide your own `id`** to map to your existing system:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# Use your own ID
|
|
31
|
+
response = ZaiPayment.users.create(
|
|
32
|
+
id: "buyer-#{your_database_user_id}", # Your custom ID
|
|
33
|
+
email: 'buyer@example.com',
|
|
34
|
+
first_name: 'John',
|
|
35
|
+
last_name: 'Doe',
|
|
36
|
+
country: 'USA'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
user_id = response.data['id'] # => "buyer-123" (your custom ID)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## ID Validation Rules
|
|
43
|
+
|
|
44
|
+
If you provide a custom ID, it must:
|
|
45
|
+
|
|
46
|
+
1. ✅ **Not contain the `.` (dot) character**
|
|
47
|
+
2. ✅ **Not be blank/empty**
|
|
48
|
+
3. ✅ **Be unique** across all your users
|
|
49
|
+
|
|
50
|
+
### Valid IDs:
|
|
51
|
+
```ruby
|
|
52
|
+
✅ "user-123"
|
|
53
|
+
✅ "buyer_456"
|
|
54
|
+
✅ "seller-abc-xyz"
|
|
55
|
+
✅ "merchant:789"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Invalid IDs:
|
|
59
|
+
```ruby
|
|
60
|
+
❌ "user.123" # Contains dot character
|
|
61
|
+
❌ " " # Blank/empty
|
|
62
|
+
❌ "" # Empty string
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Use Cases
|
|
66
|
+
|
|
67
|
+
### When to Use Auto-Generated IDs
|
|
68
|
+
|
|
69
|
+
Use Zai's auto-generated IDs when:
|
|
70
|
+
- You're building a new system without existing user IDs
|
|
71
|
+
- You want simplicity and don't need ID mapping
|
|
72
|
+
- You're prototyping or testing
|
|
73
|
+
|
|
74
|
+
**Example:**
|
|
75
|
+
```ruby
|
|
76
|
+
response = ZaiPayment.users.create(
|
|
77
|
+
email: 'user@example.com',
|
|
78
|
+
first_name: 'Alice',
|
|
79
|
+
last_name: 'Smith',
|
|
80
|
+
country: 'USA'
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Store Zai's generated ID in your database
|
|
84
|
+
your_user.update(zai_user_id: response.data['id'])
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### When to Use Custom IDs
|
|
88
|
+
|
|
89
|
+
Use custom IDs when:
|
|
90
|
+
- You have existing user IDs in your system
|
|
91
|
+
- You want to easily map between your database and Zai
|
|
92
|
+
- You need predictable ID formats for integration
|
|
93
|
+
|
|
94
|
+
**Example:**
|
|
95
|
+
```ruby
|
|
96
|
+
# Your Rails user has ID 123
|
|
97
|
+
response = ZaiPayment.users.create(
|
|
98
|
+
id: "user-#{current_user.id}", # "user-123"
|
|
99
|
+
email: current_user.email,
|
|
100
|
+
first_name: current_user.first_name,
|
|
101
|
+
last_name: current_user.last_name,
|
|
102
|
+
country: current_user.country_code
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Easy to find later: just use "user-#{user.id}"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Complete Examples
|
|
109
|
+
|
|
110
|
+
### Example 1: Simple Auto-Generated ID
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# Let Zai generate the ID
|
|
114
|
+
response = ZaiPayment.users.create(
|
|
115
|
+
email: 'simple@example.com',
|
|
116
|
+
first_name: 'Bob',
|
|
117
|
+
last_name: 'Builder',
|
|
118
|
+
country: 'AUS'
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
puts "Created user: #{response.data['id']}"
|
|
122
|
+
# => "Created user: user-1698765432"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Example 2: Custom ID with Your Database
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# In your Rails model
|
|
129
|
+
class User < ApplicationRecord
|
|
130
|
+
after_create :create_zai_user
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def create_zai_user
|
|
135
|
+
response = ZaiPayment.users.create(
|
|
136
|
+
id: "platform-user-#{id}", # Use your DB ID
|
|
137
|
+
email: email,
|
|
138
|
+
first_name: first_name,
|
|
139
|
+
last_name: last_name,
|
|
140
|
+
country: country_code
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Store for reference (though you can reconstruct it)
|
|
144
|
+
update_column(:zai_user_id, response.data['id'])
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def zai_user_id_computed
|
|
148
|
+
"platform-user-#{id}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Example 3: UUID-Based Custom IDs
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
# Using UUIDs from your system
|
|
157
|
+
user_uuid = SecureRandom.uuid
|
|
158
|
+
|
|
159
|
+
response = ZaiPayment.users.create(
|
|
160
|
+
id: "user-#{user_uuid}",
|
|
161
|
+
email: 'uuid@example.com',
|
|
162
|
+
first_name: 'Charlie',
|
|
163
|
+
last_name: 'UUID',
|
|
164
|
+
country: 'USA'
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
puts response.data['id']
|
|
168
|
+
# => "user-550e8400-e29b-41d4-a716-446655440000"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Error Handling
|
|
172
|
+
|
|
173
|
+
### Invalid ID with Dot Character
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
begin
|
|
177
|
+
ZaiPayment.users.create(
|
|
178
|
+
id: 'user.123', # Invalid - contains dot
|
|
179
|
+
email: 'test@example.com',
|
|
180
|
+
first_name: 'Test',
|
|
181
|
+
last_name: 'User',
|
|
182
|
+
country: 'USA'
|
|
183
|
+
)
|
|
184
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
185
|
+
puts e.message
|
|
186
|
+
# => "id cannot contain '.' character"
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Blank ID
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
begin
|
|
194
|
+
ZaiPayment.users.create(
|
|
195
|
+
id: ' ', # Invalid - blank
|
|
196
|
+
email: 'test@example.com',
|
|
197
|
+
first_name: 'Test',
|
|
198
|
+
last_name: 'User',
|
|
199
|
+
country: 'USA'
|
|
200
|
+
)
|
|
201
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
202
|
+
puts e.message
|
|
203
|
+
# => "id cannot be blank if provided"
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Duplicate ID
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
# First user
|
|
211
|
+
ZaiPayment.users.create(
|
|
212
|
+
id: 'duplicate-123',
|
|
213
|
+
email: 'first@example.com',
|
|
214
|
+
first_name: 'First',
|
|
215
|
+
last_name: 'User',
|
|
216
|
+
country: 'USA'
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Second user with same ID
|
|
220
|
+
begin
|
|
221
|
+
ZaiPayment.users.create(
|
|
222
|
+
id: 'duplicate-123', # Same ID
|
|
223
|
+
email: 'second@example.com',
|
|
224
|
+
first_name: 'Second',
|
|
225
|
+
last_name: 'User',
|
|
226
|
+
country: 'USA'
|
|
227
|
+
)
|
|
228
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
229
|
+
puts "Duplicate ID error from Zai API"
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Best Practices
|
|
234
|
+
|
|
235
|
+
### ✅ DO:
|
|
236
|
+
|
|
237
|
+
1. **Use auto-generated IDs for simplicity** - Let Zai handle it
|
|
238
|
+
2. **Use custom IDs with clear prefixes** - e.g., `buyer-123`, `seller-456`
|
|
239
|
+
3. **Store the generated ID** in your database for reference
|
|
240
|
+
4. **Use consistent ID patterns** across your application
|
|
241
|
+
|
|
242
|
+
### ❌ DON'T:
|
|
243
|
+
|
|
244
|
+
1. **Don't use dots in custom IDs** - Use hyphens or underscores instead
|
|
245
|
+
2. **Don't reuse IDs** - Each user must have a unique ID
|
|
246
|
+
3. **Don't use special characters** that might cause issues
|
|
247
|
+
4. **Don't rely solely on custom IDs** - Always store what Zai returns
|
|
248
|
+
|
|
249
|
+
## Migration Strategy
|
|
250
|
+
|
|
251
|
+
If you're migrating from auto-generated to custom IDs (or vice versa):
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
# You can't change a user's ID after creation
|
|
255
|
+
# Instead, you'll need to:
|
|
256
|
+
|
|
257
|
+
# 1. Create new users with custom IDs
|
|
258
|
+
new_response = ZaiPayment.users.create(
|
|
259
|
+
id: "migrated-user-#{old_user.id}",
|
|
260
|
+
email: old_user.email,
|
|
261
|
+
# ... other attributes
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# 2. Update your database records
|
|
265
|
+
old_user.update(zai_user_id: new_response.data['id'])
|
|
266
|
+
|
|
267
|
+
# 3. Migrate any related data (transactions, etc.)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## References
|
|
271
|
+
|
|
272
|
+
- [Zai Developer Documentation](https://developer.hellozai.com/docs/onboarding-a-payin-user)
|
|
273
|
+
- [Zai API Reference](https://developer.hellozai.com/reference/createuser)
|
|
274
|
+
- [User Management Guide](USERS.md)
|
|
275
|
+
|
|
276
|
+
## Summary
|
|
277
|
+
|
|
278
|
+
**The `id` field is OPTIONAL:**
|
|
279
|
+
|
|
280
|
+
- ✅ **Don't provide it** → Zai auto-generates (simplest)
|
|
281
|
+
- ✅ **Provide it** → Use your own custom ID (for mapping)
|
|
282
|
+
|
|
283
|
+
Choose based on your use case. When in doubt, **let Zai generate the ID** - it's simpler and works perfectly! 🎉
|
|
284
|
+
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# User Management Quick Reference
|
|
2
|
+
|
|
3
|
+
Quick reference guide for the ZaiPayment User Management API.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require 'zai_payment'
|
|
9
|
+
|
|
10
|
+
# Configure
|
|
11
|
+
ZaiPayment.configure do |config|
|
|
12
|
+
config.environment = :prelive
|
|
13
|
+
config.client_id = ENV['ZAI_CLIENT_ID']
|
|
14
|
+
config.client_secret = ENV['ZAI_CLIENT_SECRET']
|
|
15
|
+
config.scope = ENV['ZAI_SCOPE']
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## CRUD Operations
|
|
20
|
+
|
|
21
|
+
### List Users
|
|
22
|
+
```ruby
|
|
23
|
+
response = ZaiPayment.users.list(limit: 10, offset: 0)
|
|
24
|
+
users = response.data
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Show User
|
|
28
|
+
```ruby
|
|
29
|
+
response = ZaiPayment.users.show('user_id')
|
|
30
|
+
user = response.data
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Create Payin User
|
|
34
|
+
```ruby
|
|
35
|
+
response = ZaiPayment.users.create(
|
|
36
|
+
email: 'buyer@example.com',
|
|
37
|
+
first_name: 'John',
|
|
38
|
+
last_name: 'Doe',
|
|
39
|
+
country: 'USA'
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Create Payout User
|
|
44
|
+
```ruby
|
|
45
|
+
response = ZaiPayment.users.create(
|
|
46
|
+
email: 'seller@example.com',
|
|
47
|
+
first_name: 'Jane',
|
|
48
|
+
last_name: 'Smith',
|
|
49
|
+
country: 'AUS',
|
|
50
|
+
dob: '19900101',
|
|
51
|
+
address_line1: '123 Main St',
|
|
52
|
+
city: 'Sydney',
|
|
53
|
+
state: 'NSW',
|
|
54
|
+
zip: '2000'
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Update User
|
|
59
|
+
```ruby
|
|
60
|
+
response = ZaiPayment.users.update(
|
|
61
|
+
'user_id',
|
|
62
|
+
mobile: '+1234567890',
|
|
63
|
+
city: 'New York'
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Required Fields
|
|
68
|
+
|
|
69
|
+
### Payin User (Buyer)
|
|
70
|
+
| Field | Type | Required |
|
|
71
|
+
|-------|------|----------|
|
|
72
|
+
| email | String | ✓ |
|
|
73
|
+
| first_name | String | ✓ |
|
|
74
|
+
| last_name | String | ✓ |
|
|
75
|
+
| country | String (ISO 3166-1 alpha-3) | ✓ |
|
|
76
|
+
| device_id | String | When charging* |
|
|
77
|
+
| ip_address | String | When charging* |
|
|
78
|
+
|
|
79
|
+
### Payout User (Seller/Merchant)
|
|
80
|
+
| Field | Type | Required |
|
|
81
|
+
|-------|------|----------|
|
|
82
|
+
| email | String | ✓ |
|
|
83
|
+
| first_name | String | ✓ |
|
|
84
|
+
| last_name | String | ✓ |
|
|
85
|
+
| country | String (ISO 3166-1 alpha-3) | ✓ |
|
|
86
|
+
| dob | String (YYYYMMDD) | ✓ |
|
|
87
|
+
| address_line1 | String | ✓ |
|
|
88
|
+
| city | String | ✓ |
|
|
89
|
+
| state | String | ✓ |
|
|
90
|
+
| zip | String | ✓ |
|
|
91
|
+
|
|
92
|
+
\* Required when an item is created and a card is charged
|
|
93
|
+
|
|
94
|
+
## Validation Formats
|
|
95
|
+
|
|
96
|
+
### Email
|
|
97
|
+
```ruby
|
|
98
|
+
email: 'user@example.com'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Country Code (ISO 3166-1 alpha-3)
|
|
102
|
+
```ruby
|
|
103
|
+
country: 'USA' # United States
|
|
104
|
+
country: 'AUS' # Australia
|
|
105
|
+
country: 'GBR' # United Kingdom
|
|
106
|
+
country: 'CAN' # Canada
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Date of Birth (YYYYMMDD)
|
|
110
|
+
```ruby
|
|
111
|
+
dob: '19900101' # January 1, 1990
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error Handling
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
begin
|
|
118
|
+
response = ZaiPayment.users.create(...)
|
|
119
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
120
|
+
# Handle validation errors (400, 422)
|
|
121
|
+
rescue ZaiPayment::Errors::UnauthorizedError => e
|
|
122
|
+
# Handle auth errors (401)
|
|
123
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
124
|
+
# Handle not found (404)
|
|
125
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
126
|
+
# Handle general API errors
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Common Patterns
|
|
131
|
+
|
|
132
|
+
### Progressive Profile
|
|
133
|
+
```ruby
|
|
134
|
+
# 1. Quick signup
|
|
135
|
+
response = ZaiPayment.users.create(
|
|
136
|
+
email: 'user@example.com',
|
|
137
|
+
first_name: 'John',
|
|
138
|
+
last_name: 'Doe',
|
|
139
|
+
country: 'USA'
|
|
140
|
+
)
|
|
141
|
+
user_id = response.data['id']
|
|
142
|
+
|
|
143
|
+
# 2. Add details later
|
|
144
|
+
ZaiPayment.users.update(
|
|
145
|
+
user_id,
|
|
146
|
+
address_line1: '123 Main St',
|
|
147
|
+
city: 'New York',
|
|
148
|
+
state: 'NY',
|
|
149
|
+
zip: '10001'
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Batch Creation
|
|
154
|
+
```ruby
|
|
155
|
+
users_data.each do |data|
|
|
156
|
+
begin
|
|
157
|
+
ZaiPayment.users.create(**data)
|
|
158
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
159
|
+
# Log error and continue
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Response Structure
|
|
165
|
+
|
|
166
|
+
### Success Response
|
|
167
|
+
```ruby
|
|
168
|
+
response.success? # => true
|
|
169
|
+
response.status # => 200 or 201
|
|
170
|
+
response.data # => User hash
|
|
171
|
+
response.meta # => Metadata (for list)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### User Object
|
|
175
|
+
```ruby
|
|
176
|
+
{
|
|
177
|
+
"id" => "user_123",
|
|
178
|
+
"email" => "user@example.com",
|
|
179
|
+
"first_name" => "John",
|
|
180
|
+
"last_name" => "Doe",
|
|
181
|
+
"country" => "USA",
|
|
182
|
+
"created_at" => "2025-01-01T00:00:00Z",
|
|
183
|
+
...
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Country Codes Reference
|
|
188
|
+
|
|
189
|
+
Common ISO 3166-1 alpha-3 country codes:
|
|
190
|
+
|
|
191
|
+
| Country | Code |
|
|
192
|
+
|---------|------|
|
|
193
|
+
| United States | USA |
|
|
194
|
+
| Australia | AUS |
|
|
195
|
+
| United Kingdom | GBR |
|
|
196
|
+
| Canada | CAN |
|
|
197
|
+
| New Zealand | NZL |
|
|
198
|
+
| Germany | DEU |
|
|
199
|
+
| France | FRA |
|
|
200
|
+
| Japan | JPN |
|
|
201
|
+
| Singapore | SGP |
|
|
202
|
+
|
|
203
|
+
[Full list](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3)
|
|
204
|
+
|
|
205
|
+
## Testing
|
|
206
|
+
|
|
207
|
+
### Run Tests
|
|
208
|
+
```bash
|
|
209
|
+
bundle exec rspec spec/zai_payment/resources/user_spec.rb
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Run Demo
|
|
213
|
+
```bash
|
|
214
|
+
ruby examples/user_demo.rb
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Documentation Links
|
|
218
|
+
|
|
219
|
+
- [Full User Guide](USERS.md)
|
|
220
|
+
- [Usage Examples](../examples/users.md)
|
|
221
|
+
- [Zai: Payin User](https://developer.hellozai.com/docs/onboarding-a-payin-user)
|
|
222
|
+
- [Zai: Payout User](https://developer.hellozai.com/docs/onboarding-a-payout-user)
|
|
223
|
+
|
|
224
|
+
## Support
|
|
225
|
+
|
|
226
|
+
For issues or questions:
|
|
227
|
+
1. Check the [User Management Guide](USERS.md)
|
|
228
|
+
2. Review [Examples](../examples/users.md)
|
|
229
|
+
3. Visit [Zai Developer Portal](https://developer.hellozai.com/)
|
|
230
|
+
|
|
@@ -123,8 +123,8 @@ end
|
|
|
123
123
|
|
|
124
124
|
## 📚 Full Documentation
|
|
125
125
|
|
|
126
|
-
- [Complete Setup Guide](
|
|
127
|
-
- [More Examples](examples/webhooks.md#webhook-security-complete-setup-guide)
|
|
126
|
+
- [Complete Setup Guide](WEBHOOKS.md#webhook-security-signature-verification)
|
|
127
|
+
- [More Examples](../examples/webhooks.md#webhook-security-complete-setup-guide)
|
|
128
128
|
- [Zai Official Docs](https://developer.hellozai.com/docs/verify-webhook-signatures)
|
|
129
129
|
|
|
130
130
|
## 💡 Pro Tips
|
|
@@ -134,8 +134,3 @@ end
|
|
|
134
134
|
3. **Add Rate Limiting**: Protect against DoS attacks
|
|
135
135
|
4. **Log Everything**: Monitor for suspicious activity
|
|
136
136
|
5. **Test Replay Attacks**: Ensure old webhooks are rejected
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
**Need Help?** See the [full implementation guide](WEBHOOK_SIGNATURE_IMPLEMENTATION.md)
|
|
141
|
-
|