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
data/examples/users.md
ADDED
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
# User Management Examples
|
|
2
|
+
|
|
3
|
+
This document provides practical examples for managing users in Zai Payment.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Setup](#setup)
|
|
8
|
+
- [Payin User Examples](#payin-user-examples)
|
|
9
|
+
- [Payout User Examples](#payout-user-examples)
|
|
10
|
+
- [Advanced Usage](#advanced-usage)
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
require 'zai_payment'
|
|
16
|
+
|
|
17
|
+
# Configure ZaiPayment
|
|
18
|
+
ZaiPayment.configure do |config|
|
|
19
|
+
config.environment = :prelive # or :production
|
|
20
|
+
config.client_id = ENV['ZAI_CLIENT_ID']
|
|
21
|
+
config.client_secret = ENV['ZAI_CLIENT_SECRET']
|
|
22
|
+
config.scope = ENV['ZAI_SCOPE']
|
|
23
|
+
end
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Payin User Examples
|
|
27
|
+
|
|
28
|
+
### Example 1: Basic Payin User (Buyer)
|
|
29
|
+
|
|
30
|
+
Create a buyer with minimal required information.
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
# Create a basic payin user
|
|
34
|
+
response = ZaiPayment.users.create(
|
|
35
|
+
email: 'buyer@example.com',
|
|
36
|
+
first_name: 'John',
|
|
37
|
+
last_name: 'Doe',
|
|
38
|
+
country: 'USA'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if response.success?
|
|
42
|
+
user = response.data
|
|
43
|
+
puts "Payin user created successfully!"
|
|
44
|
+
puts "User ID: #{user['id']}"
|
|
45
|
+
puts "Email: #{user['email']}"
|
|
46
|
+
else
|
|
47
|
+
puts "Failed to create user"
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Example 2: Payin User with Complete Profile
|
|
52
|
+
|
|
53
|
+
Create a buyer with all recommended information for better fraud prevention.
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
response = ZaiPayment.users.create(
|
|
57
|
+
# Required fields
|
|
58
|
+
email: 'john.buyer@example.com',
|
|
59
|
+
first_name: 'John',
|
|
60
|
+
last_name: 'Doe',
|
|
61
|
+
country: 'USA',
|
|
62
|
+
|
|
63
|
+
# Recommended fields
|
|
64
|
+
address_line1: '123 Main Street',
|
|
65
|
+
address_line2: 'Apt 4B',
|
|
66
|
+
city: 'New York',
|
|
67
|
+
state: 'NY',
|
|
68
|
+
zip: '10001',
|
|
69
|
+
mobile: '+1234567890',
|
|
70
|
+
dob: '15/01/1990',
|
|
71
|
+
|
|
72
|
+
# For fraud prevention (required when charging)
|
|
73
|
+
device_id: 'device_abc123xyz',
|
|
74
|
+
ip_address: '192.168.1.1'
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
user = response.data
|
|
78
|
+
puts "Complete payin user profile created: #{user['id']}"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Example 3: Progressive Profile Building
|
|
82
|
+
|
|
83
|
+
Create a user quickly, then update with additional information later.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
# Step 1: Quick user creation during signup
|
|
87
|
+
response = ZaiPayment.users.create(
|
|
88
|
+
email: 'quicksignup@example.com',
|
|
89
|
+
first_name: 'Jane',
|
|
90
|
+
last_name: 'Smith',
|
|
91
|
+
country: 'AUS'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
user_id = response.data['id']
|
|
95
|
+
puts "User created quickly: #{user_id}"
|
|
96
|
+
|
|
97
|
+
# Step 2: Update with shipping address during checkout
|
|
98
|
+
ZaiPayment.users.update(
|
|
99
|
+
user_id,
|
|
100
|
+
address_line1: '456 Collins Street',
|
|
101
|
+
city: 'Melbourne',
|
|
102
|
+
state: 'VIC',
|
|
103
|
+
zip: '3000',
|
|
104
|
+
mobile: '+61412345678'
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
puts "User profile updated with shipping address"
|
|
108
|
+
|
|
109
|
+
# Step 3: Add device info before payment
|
|
110
|
+
ZaiPayment.users.update(
|
|
111
|
+
user_id,
|
|
112
|
+
device_id: 'device_xyz789',
|
|
113
|
+
ip_address: '203.0.113.42'
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
puts "Device information added for payment"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Payout User Examples
|
|
120
|
+
|
|
121
|
+
### Example 4: Individual Payout User (Seller)
|
|
122
|
+
|
|
123
|
+
Create a seller who will receive payments. All required fields must be provided.
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
response = ZaiPayment.users.create(
|
|
127
|
+
# Required for payout users
|
|
128
|
+
email: 'seller@example.com',
|
|
129
|
+
first_name: 'Alice',
|
|
130
|
+
last_name: 'Johnson',
|
|
131
|
+
country: 'USA',
|
|
132
|
+
dob: '20/03/1985',
|
|
133
|
+
address_line1: '789 Market Street',
|
|
134
|
+
city: 'San Francisco',
|
|
135
|
+
state: 'CA',
|
|
136
|
+
zip: '94103',
|
|
137
|
+
|
|
138
|
+
# Recommended
|
|
139
|
+
mobile: '+14155551234',
|
|
140
|
+
government_number: '123456789', # SSN or Tax ID
|
|
141
|
+
user_type: 'payout'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
seller = response.data
|
|
145
|
+
puts "Payout user created: #{seller['id']}"
|
|
146
|
+
puts "Verification state: #{seller['verification_state']}"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Example 5: Australian Payout User
|
|
150
|
+
|
|
151
|
+
Create an Australian seller with appropriate details.
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
response = ZaiPayment.users.create(
|
|
155
|
+
email: 'aussie.seller@example.com',
|
|
156
|
+
first_name: 'Bruce',
|
|
157
|
+
last_name: 'Williams',
|
|
158
|
+
country: 'AUS',
|
|
159
|
+
dob: '10/07/1980',
|
|
160
|
+
|
|
161
|
+
# Australian address
|
|
162
|
+
address_line1: '123 George Street',
|
|
163
|
+
city: 'Sydney',
|
|
164
|
+
state: 'NSW',
|
|
165
|
+
zip: '2000',
|
|
166
|
+
|
|
167
|
+
mobile: '+61298765432',
|
|
168
|
+
government_number: '123456789', # TFN (Tax File Number)
|
|
169
|
+
user_type: 'payout'
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if response.success?
|
|
173
|
+
seller = response.data
|
|
174
|
+
puts "Australian seller created: #{seller['id']}"
|
|
175
|
+
puts "Ready for bank account setup"
|
|
176
|
+
end
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Example 6: UK Payout User
|
|
180
|
+
|
|
181
|
+
Create a UK-based seller.
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
response = ZaiPayment.users.create(
|
|
185
|
+
email: 'uk.merchant@example.com',
|
|
186
|
+
first_name: 'Oliver',
|
|
187
|
+
last_name: 'Brown',
|
|
188
|
+
country: 'GBR', # ISO 3166-1 alpha-3 code for United Kingdom
|
|
189
|
+
dob: '05/05/1992',
|
|
190
|
+
|
|
191
|
+
# UK address
|
|
192
|
+
address_line1: '10 Downing Street',
|
|
193
|
+
city: 'London',
|
|
194
|
+
state: 'England',
|
|
195
|
+
zip: 'SW1A 2AA',
|
|
196
|
+
|
|
197
|
+
mobile: '+447700900123',
|
|
198
|
+
government_number: 'AB123456C', # National Insurance Number
|
|
199
|
+
user_type: 'payout'
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
merchant = response.data
|
|
203
|
+
puts "UK merchant created: #{merchant['id']}"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Advanced Usage
|
|
207
|
+
|
|
208
|
+
### Example 7: List and Search Users
|
|
209
|
+
|
|
210
|
+
Retrieve and paginate through your users.
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
# Get first page of users
|
|
214
|
+
page1 = ZaiPayment.users.list(limit: 10, offset: 0)
|
|
215
|
+
|
|
216
|
+
puts "Total users: #{page1.meta['total']}"
|
|
217
|
+
puts "Showing: #{page1.data.length} users"
|
|
218
|
+
|
|
219
|
+
page1.data.each_with_index do |user, index|
|
|
220
|
+
puts "#{index + 1}. #{user['email']} - #{user['first_name']} #{user['last_name']}"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Get next page
|
|
224
|
+
page2 = ZaiPayment.users.list(limit: 10, offset: 10)
|
|
225
|
+
puts "\nNext page has #{page2.data.length} users"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Example 8: Get User Details
|
|
229
|
+
|
|
230
|
+
Retrieve complete details for a specific user.
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
user_id = 'user_abc123'
|
|
234
|
+
|
|
235
|
+
response = ZaiPayment.users.show(user_id)
|
|
236
|
+
|
|
237
|
+
if response.success?
|
|
238
|
+
user = response.data
|
|
239
|
+
|
|
240
|
+
puts "User Details:"
|
|
241
|
+
puts " ID: #{user['id']}"
|
|
242
|
+
puts " Email: #{user['email']}"
|
|
243
|
+
puts " Name: #{user['first_name']} #{user['last_name']}"
|
|
244
|
+
puts " Country: #{user['country']}"
|
|
245
|
+
puts " City: #{user['city']}, #{user['state']}"
|
|
246
|
+
puts " Created: #{user['created_at']}"
|
|
247
|
+
puts " Verification: #{user['verification_state']}"
|
|
248
|
+
end
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Example 9: Update Multiple Fields
|
|
252
|
+
|
|
253
|
+
Update several user fields at once.
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
user_id = 'user_abc123'
|
|
257
|
+
|
|
258
|
+
response = ZaiPayment.users.update(
|
|
259
|
+
user_id,
|
|
260
|
+
email: 'newemail@example.com',
|
|
261
|
+
mobile: '+1555123456',
|
|
262
|
+
address_line1: '999 Updated Street',
|
|
263
|
+
city: 'Boston',
|
|
264
|
+
state: 'MA',
|
|
265
|
+
zip: '02101'
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
updated_user = response.data
|
|
269
|
+
puts "User #{user_id} updated successfully"
|
|
270
|
+
puts "New email: #{updated_user['email']}"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Example 10: Error Handling
|
|
274
|
+
|
|
275
|
+
Properly handle validation and API errors.
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
begin
|
|
279
|
+
response = ZaiPayment.users.create(
|
|
280
|
+
email: 'invalid-email', # Invalid format
|
|
281
|
+
first_name: 'Test',
|
|
282
|
+
last_name: 'User',
|
|
283
|
+
country: 'US' # Invalid: should be 3 letters
|
|
284
|
+
)
|
|
285
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
286
|
+
puts "Validation failed: #{e.message}"
|
|
287
|
+
# Handle validation errors (e.g., show to user)
|
|
288
|
+
|
|
289
|
+
rescue ZaiPayment::Errors::UnauthorizedError => e
|
|
290
|
+
puts "Authentication failed: #{e.message}"
|
|
291
|
+
# Refresh token and retry
|
|
292
|
+
ZaiPayment.refresh_token!
|
|
293
|
+
retry
|
|
294
|
+
|
|
295
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
296
|
+
puts "Resource not found: #{e.message}"
|
|
297
|
+
|
|
298
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
299
|
+
puts "API error occurred: #{e.message}"
|
|
300
|
+
# Log error for debugging
|
|
301
|
+
|
|
302
|
+
rescue ZaiPayment::Errors::ConnectionError => e
|
|
303
|
+
puts "Connection failed: #{e.message}"
|
|
304
|
+
# Retry with exponential backoff
|
|
305
|
+
|
|
306
|
+
rescue ZaiPayment::Errors::TimeoutError => e
|
|
307
|
+
puts "Request timed out: #{e.message}"
|
|
308
|
+
# Retry request
|
|
309
|
+
end
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Example 11: Batch User Creation
|
|
313
|
+
|
|
314
|
+
Create multiple users efficiently.
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
users_to_create = [
|
|
318
|
+
{
|
|
319
|
+
email: 'buyer1@example.com',
|
|
320
|
+
first_name: 'Alice',
|
|
321
|
+
last_name: 'Anderson',
|
|
322
|
+
country: 'USA'
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
email: 'buyer2@example.com',
|
|
326
|
+
first_name: 'Bob',
|
|
327
|
+
last_name: 'Brown',
|
|
328
|
+
country: 'USA'
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
email: 'seller1@example.com',
|
|
332
|
+
first_name: 'Charlie',
|
|
333
|
+
last_name: 'Chen',
|
|
334
|
+
country: 'AUS',
|
|
335
|
+
dob: '01/01/1990',
|
|
336
|
+
address_line1: '123 Test St',
|
|
337
|
+
city: 'Sydney',
|
|
338
|
+
state: 'NSW',
|
|
339
|
+
zip: '2000',
|
|
340
|
+
user_type: 'payout'
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
|
|
344
|
+
created_users = []
|
|
345
|
+
failed_users = []
|
|
346
|
+
|
|
347
|
+
users_to_create.each do |user_data|
|
|
348
|
+
begin
|
|
349
|
+
response = ZaiPayment.users.create(**user_data)
|
|
350
|
+
created_users << response.data
|
|
351
|
+
puts "✓ Created: #{user_data[:email]}"
|
|
352
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
353
|
+
failed_users << { user: user_data, error: e.message }
|
|
354
|
+
puts "✗ Failed: #{user_data[:email]} - #{e.message}"
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
puts "\nSummary:"
|
|
359
|
+
puts "Created: #{created_users.length} users"
|
|
360
|
+
puts "Failed: #{failed_users.length} users"
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Example 12: User Profile Validator
|
|
364
|
+
|
|
365
|
+
Create a helper to validate user data before API call.
|
|
366
|
+
|
|
367
|
+
```ruby
|
|
368
|
+
class UserValidator
|
|
369
|
+
def self.validate_payin(attributes)
|
|
370
|
+
errors = []
|
|
371
|
+
|
|
372
|
+
errors << "Email is required" unless attributes[:email]
|
|
373
|
+
errors << "First name is required" unless attributes[:first_name]
|
|
374
|
+
errors << "Last name is required" unless attributes[:last_name]
|
|
375
|
+
errors << "Country is required" unless attributes[:country]
|
|
376
|
+
|
|
377
|
+
if attributes[:email] && !valid_email?(attributes[:email])
|
|
378
|
+
errors << "Email format is invalid"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
if attributes[:country] && attributes[:country].length != 3
|
|
382
|
+
errors << "Country must be 3-letter ISO code"
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
if attributes[:dob] && !valid_dob?(attributes[:dob])
|
|
386
|
+
errors << "DOB must be in DD/MM/YYYY format"
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
errors
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def self.validate_payout(attributes)
|
|
393
|
+
errors = validate_payin(attributes)
|
|
394
|
+
|
|
395
|
+
# Additional required fields for payout users
|
|
396
|
+
errors << "Address is required for payout users" unless attributes[:address_line1]
|
|
397
|
+
errors << "City is required for payout users" unless attributes[:city]
|
|
398
|
+
errors << "State is required for payout users" unless attributes[:state]
|
|
399
|
+
errors << "Zip is required for payout users" unless attributes[:zip]
|
|
400
|
+
errors << "DOB is required for payout users" unless attributes[:dob]
|
|
401
|
+
|
|
402
|
+
errors
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
private
|
|
406
|
+
|
|
407
|
+
def self.valid_email?(email)
|
|
408
|
+
email.match?(/\A[^@\s]+@[^@\s]+\.[^@\s]+\z/)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def self.valid_dob?(dob)
|
|
412
|
+
dob.to_s.match?(%r{\A\d{2}/\d{2}/\d{4}\z})
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Usage
|
|
417
|
+
user_data = {
|
|
418
|
+
email: 'test@example.com',
|
|
419
|
+
first_name: 'Test',
|
|
420
|
+
last_name: 'User',
|
|
421
|
+
country: 'USA'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
errors = UserValidator.validate_payin(user_data)
|
|
425
|
+
|
|
426
|
+
if errors.empty?
|
|
427
|
+
response = ZaiPayment.users.create(**user_data)
|
|
428
|
+
puts "User created: #{response.data['id']}"
|
|
429
|
+
else
|
|
430
|
+
puts "Validation errors:"
|
|
431
|
+
errors.each { |error| puts " - #{error}" }
|
|
432
|
+
end
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Example 13: Rails Integration
|
|
436
|
+
|
|
437
|
+
Example of integrating with a Rails application.
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
# app/models/user.rb
|
|
441
|
+
class User < ApplicationRecord
|
|
442
|
+
after_create :create_zai_user
|
|
443
|
+
|
|
444
|
+
def create_zai_user
|
|
445
|
+
return if zai_user_id.present?
|
|
446
|
+
|
|
447
|
+
response = ZaiPayment.users.create(
|
|
448
|
+
email: email,
|
|
449
|
+
first_name: first_name,
|
|
450
|
+
last_name: last_name,
|
|
451
|
+
country: country_code,
|
|
452
|
+
user_type: user_type
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
update(zai_user_id: response.data['id'])
|
|
456
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
457
|
+
Rails.logger.error "Failed to create Zai user: #{e.message}"
|
|
458
|
+
# Handle error appropriately
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def sync_to_zai
|
|
462
|
+
return unless zai_user_id
|
|
463
|
+
|
|
464
|
+
ZaiPayment.users.update(
|
|
465
|
+
zai_user_id,
|
|
466
|
+
email: email,
|
|
467
|
+
first_name: first_name,
|
|
468
|
+
last_name: last_name,
|
|
469
|
+
mobile: phone_number,
|
|
470
|
+
address_line1: address,
|
|
471
|
+
city: city,
|
|
472
|
+
state: state,
|
|
473
|
+
zip: zip_code
|
|
474
|
+
)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def fetch_from_zai
|
|
478
|
+
return unless zai_user_id
|
|
479
|
+
|
|
480
|
+
response = ZaiPayment.users.show(zai_user_id)
|
|
481
|
+
response.data
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# app/controllers/users_controller.rb
|
|
486
|
+
class UsersController < ApplicationController
|
|
487
|
+
def create
|
|
488
|
+
@user = User.new(user_params)
|
|
489
|
+
|
|
490
|
+
if @user.save
|
|
491
|
+
# Zai user is created automatically via after_create callback
|
|
492
|
+
redirect_to @user, notice: 'User created successfully'
|
|
493
|
+
else
|
|
494
|
+
render :new
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def sync_zai_profile
|
|
499
|
+
@user = User.find(params[:id])
|
|
500
|
+
@user.sync_to_zai
|
|
501
|
+
redirect_to @user, notice: 'Profile synced with Zai'
|
|
502
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
503
|
+
redirect_to @user, alert: "Sync failed: #{e.message}"
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
private
|
|
507
|
+
|
|
508
|
+
def user_params
|
|
509
|
+
params.require(:user).permit(
|
|
510
|
+
:email, :first_name, :last_name, :country_code, :user_type
|
|
511
|
+
)
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## Testing Examples
|
|
517
|
+
|
|
518
|
+
### Example 14: RSpec Integration Tests
|
|
519
|
+
|
|
520
|
+
```ruby
|
|
521
|
+
# spec/models/user_spec.rb
|
|
522
|
+
require 'rails_helper'
|
|
523
|
+
|
|
524
|
+
RSpec.describe User, type: :model do
|
|
525
|
+
describe '#create_zai_user' do
|
|
526
|
+
let(:user) { build(:user, email: 'test@example.com') }
|
|
527
|
+
|
|
528
|
+
before do
|
|
529
|
+
stub_request(:post, %r{/users})
|
|
530
|
+
.to_return(
|
|
531
|
+
status: 201,
|
|
532
|
+
body: { id: 'zai_user_123', email: 'test@example.com' }.to_json,
|
|
533
|
+
headers: { 'Content-Type' => 'application/json' }
|
|
534
|
+
)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
it 'creates a Zai user after user creation' do
|
|
538
|
+
expect { user.save }.to change { user.zai_user_id }.from(nil).to('zai_user_123')
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
## Common Patterns
|
|
545
|
+
|
|
546
|
+
### Pattern 1: Two-Step User Creation
|
|
547
|
+
|
|
548
|
+
```ruby
|
|
549
|
+
# Step 1: Create user during signup (minimal info)
|
|
550
|
+
def create_initial_user(email:, name_parts:)
|
|
551
|
+
ZaiPayment.users.create(
|
|
552
|
+
email: email,
|
|
553
|
+
first_name: name_parts[:first],
|
|
554
|
+
last_name: name_parts[:last],
|
|
555
|
+
country: 'USA' # Default or from IP geolocation
|
|
556
|
+
)
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Step 2: Complete profile later
|
|
560
|
+
def complete_user_profile(user_id:, profile_data:)
|
|
561
|
+
ZaiPayment.users.update(user_id, **profile_data)
|
|
562
|
+
end
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Pattern 2: Smart Retry Logic
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
def create_user_with_retry(attributes, max_retries: 3)
|
|
569
|
+
retries = 0
|
|
570
|
+
|
|
571
|
+
begin
|
|
572
|
+
ZaiPayment.users.create(**attributes)
|
|
573
|
+
rescue ZaiPayment::Errors::TimeoutError, ZaiPayment::Errors::ConnectionError => e
|
|
574
|
+
retries += 1
|
|
575
|
+
if retries < max_retries
|
|
576
|
+
sleep(2 ** retries) # Exponential backoff
|
|
577
|
+
retry
|
|
578
|
+
else
|
|
579
|
+
raise e
|
|
580
|
+
end
|
|
581
|
+
rescue ZaiPayment::Errors::UnauthorizedError
|
|
582
|
+
ZaiPayment.refresh_token!
|
|
583
|
+
retry
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Pattern 3: Business User with Company
|
|
589
|
+
|
|
590
|
+
Create a user representing a business entity with full company details.
|
|
591
|
+
|
|
592
|
+
```ruby
|
|
593
|
+
# Example: Create a merchant user with company information
|
|
594
|
+
response = ZaiPayment.users.create(
|
|
595
|
+
# Personal details
|
|
596
|
+
email: 'john.director@example.com',
|
|
597
|
+
first_name: 'John',
|
|
598
|
+
last_name: 'Smith',
|
|
599
|
+
country: 'AUS',
|
|
600
|
+
mobile: '+61412345678',
|
|
601
|
+
|
|
602
|
+
# Business role
|
|
603
|
+
authorized_signer_title: 'Director',
|
|
604
|
+
|
|
605
|
+
# Company details
|
|
606
|
+
company: {
|
|
607
|
+
name: 'Smith Trading Co',
|
|
608
|
+
legal_name: 'Smith Trading Company Pty Ltd',
|
|
609
|
+
tax_number: '53004085616', # ABN for Australian companies
|
|
610
|
+
business_email: 'accounts@smithtrading.com',
|
|
611
|
+
country: 'AUS',
|
|
612
|
+
charge_tax: true, # GST registered
|
|
613
|
+
|
|
614
|
+
# Business address
|
|
615
|
+
address_line1: '123 Business Street',
|
|
616
|
+
address_line2: 'Suite 5',
|
|
617
|
+
city: 'Melbourne',
|
|
618
|
+
state: 'VIC',
|
|
619
|
+
zip: '3000',
|
|
620
|
+
phone: '+61398765432'
|
|
621
|
+
}
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
if response.success?
|
|
625
|
+
user = response.data
|
|
626
|
+
puts "Business user created: #{user['id']}"
|
|
627
|
+
puts "Company: #{user['company']['name']}"
|
|
628
|
+
end
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Pattern 4: Enhanced Fraud Prevention
|
|
632
|
+
|
|
633
|
+
Create users with additional verification data for enhanced security.
|
|
634
|
+
|
|
635
|
+
```ruby
|
|
636
|
+
# Example: Payin user with driver's license and IP tracking
|
|
637
|
+
response = ZaiPayment.users.create(
|
|
638
|
+
# Required fields
|
|
639
|
+
email: 'secure.buyer@example.com',
|
|
640
|
+
first_name: 'Sarah',
|
|
641
|
+
last_name: 'Johnson',
|
|
642
|
+
country: 'USA',
|
|
643
|
+
|
|
644
|
+
# Enhanced verification
|
|
645
|
+
dob: '15/01/1990',
|
|
646
|
+
government_number: '123-45-6789', # SSN for US
|
|
647
|
+
drivers_license_number: 'D1234567',
|
|
648
|
+
drivers_license_state: 'CA',
|
|
649
|
+
|
|
650
|
+
# Fraud prevention
|
|
651
|
+
ip_address: '192.168.1.100',
|
|
652
|
+
device_id: 'device_abc123xyz',
|
|
653
|
+
|
|
654
|
+
# Contact and address
|
|
655
|
+
mobile: '+14155551234',
|
|
656
|
+
address_line1: '456 Market Street',
|
|
657
|
+
address_line2: 'Apt 12',
|
|
658
|
+
city: 'San Francisco',
|
|
659
|
+
state: 'CA',
|
|
660
|
+
zip: '94103'
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
puts "Secure user created with enhanced verification"
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Pattern 5: Custom Branding for Merchants
|
|
667
|
+
|
|
668
|
+
Create a merchant user with custom branding for statements and payment pages.
|
|
669
|
+
|
|
670
|
+
```ruby
|
|
671
|
+
# Example: Merchant with custom branding
|
|
672
|
+
response = ZaiPayment.users.create(
|
|
673
|
+
email: 'merchant@brandedstore.com',
|
|
674
|
+
first_name: 'Alex',
|
|
675
|
+
last_name: 'Merchant',
|
|
676
|
+
country: 'AUS',
|
|
677
|
+
mobile: '+61411222333',
|
|
678
|
+
dob: '10/05/1985',
|
|
679
|
+
|
|
680
|
+
# Branding
|
|
681
|
+
logo_url: 'https://example.com/logo.png',
|
|
682
|
+
color_1: '#FF5733', # Primary brand color
|
|
683
|
+
color_2: '#C70039', # Secondary brand color
|
|
684
|
+
custom_descriptor: 'BRANDED STORE', # Shows on bank statements
|
|
685
|
+
|
|
686
|
+
# Address
|
|
687
|
+
address_line1: '789 Retail Plaza',
|
|
688
|
+
city: 'Brisbane',
|
|
689
|
+
state: 'QLD',
|
|
690
|
+
zip: '4000'
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
merchant = response.data
|
|
694
|
+
puts "Branded merchant created: #{merchant['id']}"
|
|
695
|
+
puts "Custom descriptor: #{merchant['custom_descriptor']}"
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### Pattern 6: AMEX Merchant Setup
|
|
699
|
+
|
|
700
|
+
Create a merchant specifically configured for American Express transactions.
|
|
701
|
+
|
|
702
|
+
```ruby
|
|
703
|
+
# Example: AMEX merchant with required fields
|
|
704
|
+
response = ZaiPayment.users.create(
|
|
705
|
+
email: 'director@amexshop.com',
|
|
706
|
+
first_name: 'Michael',
|
|
707
|
+
last_name: 'Director',
|
|
708
|
+
country: 'AUS',
|
|
709
|
+
mobile: '+61400111222',
|
|
710
|
+
dob: '20/03/1980',
|
|
711
|
+
|
|
712
|
+
# AMEX requirement: Must specify authorized signer title
|
|
713
|
+
authorized_signer_title: 'Managing Director',
|
|
714
|
+
|
|
715
|
+
# Business details
|
|
716
|
+
address_line1: '100 Corporate Drive',
|
|
717
|
+
city: 'Sydney',
|
|
718
|
+
state: 'NSW',
|
|
719
|
+
zip: '2000',
|
|
720
|
+
|
|
721
|
+
# Company for AMEX merchants
|
|
722
|
+
company: {
|
|
723
|
+
name: 'AMEX Shop',
|
|
724
|
+
legal_name: 'AMEX Shop Pty Limited',
|
|
725
|
+
tax_number: '51824753556',
|
|
726
|
+
business_email: 'finance@amexshop.com',
|
|
727
|
+
country: 'AUS',
|
|
728
|
+
charge_tax: true,
|
|
729
|
+
address_line1: '100 Corporate Drive',
|
|
730
|
+
city: 'Sydney',
|
|
731
|
+
state: 'NSW',
|
|
732
|
+
zip: '2000',
|
|
733
|
+
phone: '+61299887766'
|
|
734
|
+
}
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
puts "AMEX-ready merchant created: #{response.data['id']}"
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
## See Also
|
|
741
|
+
|
|
742
|
+
- [User Management Documentation](../docs/USERS.md)
|
|
743
|
+
- [Webhook Examples](webhooks.md)
|
|
744
|
+
- [Zai API Reference](https://developer.hellozai.com/reference)
|
|
745
|
+
|
|
746
|
+
|