zai_payment 1.2.0 → 1.3.1

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.
@@ -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-pay-in-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-pay-in-user)
222
+ - [Zai: Payout User](https://developer.hellozai.com/docs/onboarding-a-pay-out-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](docs/WEBHOOKS.md#webhook-security-signature-verification)
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
-