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.
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Zai Payment Ruby Library
2
2
 
3
+ ![GitHub License](https://img.shields.io/github/license/Sentia/zai-payment)
4
+ [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-MIT-blue.svg)](./CODE_OF_CONDUCT.md)
5
+ [![Gem Version](https://badge.fury.io/rb/zai_payment.svg)](https://badge.fury.io/rb/zai_payment)
6
+ [![GitHub release](https://img.shields.io/github/release/Sentia/zai-payment.svg)](https://github.com/Sentia/zai-payment/releases)
7
+ [![Gem](https://img.shields.io/gem/dt/zai_payment.svg)](https://rubygems.org/gems/zai_payment)
3
8
  [![CI](https://github.com/Sentia/zai-payment/actions/workflows/ci.yml/badge.svg)](https://github.com/Sentia/zai-payment/actions/workflows/ci.yml)
9
+ ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FSentia%2Fzai-payment%2Fmain%2Fbadges%2Fcoverage.json)
10
+ ![GitHub top language](https://img.shields.io/github/languages/top/Sentia/zai-payment)
11
+ [![Documentation](https://img.shields.io/badge/docs-rubydoc.info-blue.svg)](https://rubydoc.info/gems/zai_payment)
12
+ [![Contributing](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](./CONTRIBUTING.md)
4
13
 
5
14
  A lightweight and extensible Ruby client for the **Zai (AssemblyPay)** API — starting with secure OAuth2 authentication, and ready for Payments, Virtual Accounts, Webhooks, and more.
6
15
 
@@ -8,22 +17,23 @@ A lightweight and extensible Ruby client for the **Zai (AssemblyPay)** API — s
8
17
 
9
18
  ## ✨ Features
10
19
 
11
- - 🔐 OAuth2 Client Credentials authentication with automatic token management
12
- - 🧠 Smart token caching and refresh
13
- - ⚙️ Environment-aware (Pre-live / Production)
14
- - 🧱 Modular structure: easy to extend to Payments, Wallets, Webhooks, etc.
15
- - 🧩 Thread-safe in-memory store (Redis support coming soon)
16
- - 🧰 Simple Ruby API, no heavy dependencies
20
+ - 🔐 **OAuth2 Authentication** - Client Credentials flow with automatic token management
21
+ - 🧠 **Smart Token Caching** - Auto-refresh before expiration, thread-safe storage
22
+ - 👥 **User Management** - Create and manage payin (buyers) & payout (sellers) users
23
+ - 🪝 **Webhooks** - Full CRUD + secure signature verification (HMAC SHA256)
24
+ - ⚙️ **Environment-Aware** - Seamless Pre-live / Production switching
25
+ - 🧱 **Modular & Extensible** - Clean resource-based architecture
26
+ - 🧰 **Zero Heavy Dependencies** - Lightweight, fast, and reliable
27
+ - 📦 **Production Ready** - 88%+ test coverage, RuboCop compliant
17
28
 
18
29
  ---
19
30
 
20
31
  ## 🧭 Installation
21
32
 
22
- ### From GitHub (private repo)
23
33
  Add this line to your Gemfile:
24
34
 
25
35
  ```ruby
26
- gem 'zai_payment', '~> 1.0', '>= 1.0.2'
36
+ gem 'zai_payment'
27
37
  ```
28
38
 
29
39
  Then install
@@ -69,6 +79,67 @@ The gem handles OAuth2 Client Credentials flow automatically - tokens are cached
69
79
 
70
80
  📖 **[Complete Authentication Guide](docs/AUTHENTICATION.md)** - Two approaches, examples, and best practices
71
81
 
82
+ ### Users
83
+
84
+ Manage payin (buyer) and payout (seller/merchant) users:
85
+
86
+ ```ruby
87
+ # Create a payin user (buyer)
88
+ response = ZaiPayment.users.create(
89
+ email: 'buyer@example.com',
90
+ first_name: 'John',
91
+ last_name: 'Doe',
92
+ country: 'USA',
93
+ mobile: '+1234567890'
94
+ )
95
+
96
+ # Create a payout user (seller/merchant)
97
+ response = ZaiPayment.users.create(
98
+ email: 'seller@example.com',
99
+ first_name: 'Jane',
100
+ last_name: 'Smith',
101
+ country: 'AUS',
102
+ dob: '19900101',
103
+ address_line1: '456 Market St',
104
+ city: 'Sydney',
105
+ state: 'NSW',
106
+ zip: '2000'
107
+ )
108
+
109
+ # Create a business user with company details
110
+ response = ZaiPayment.users.create(
111
+ email: 'director@company.com',
112
+ first_name: 'John',
113
+ last_name: 'Director',
114
+ country: 'AUS',
115
+ mobile: '+61412345678',
116
+ authorized_signer_title: 'Director',
117
+ company: {
118
+ name: 'My Company',
119
+ legal_name: 'My Company Pty Ltd',
120
+ tax_number: '123456789',
121
+ business_email: 'admin@company.com',
122
+ country: 'AUS',
123
+ charge_tax: true
124
+ }
125
+ )
126
+
127
+ # List users
128
+ response = ZaiPayment.users.list(limit: 10, offset: 0)
129
+
130
+ # Get user details
131
+ response = ZaiPayment.users.show('user_id')
132
+
133
+ # Update user
134
+ response = ZaiPayment.users.update('user_id', mobile: '+9876543210')
135
+ ```
136
+
137
+ **📚 Documentation:**
138
+ - 📖 [User Management Guide](docs/USERS.md) - Complete guide for payin and payout users
139
+ - 💡 [User Examples](examples/users.md) - Real-world usage patterns and Rails integration
140
+ - 🔗 [Zai: Onboarding a Payin User](https://developer.hellozai.com/docs/onboarding-a-payin-user)
141
+ - 🔗 [Zai: Onboarding a Payout User](https://developer.hellozai.com/docs/onboarding-a-payout-user)
142
+
72
143
  ### Webhooks
73
144
 
74
145
  Manage webhook endpoints:
@@ -127,9 +198,9 @@ end
127
198
  | ------------------------------- | --------------------------------- | -------------- |
128
199
  | ✅ Authentication | OAuth2 Client Credentials flow | Done |
129
200
  | ✅ Webhooks | CRUD for webhook endpoints | Done |
201
+ | ✅ Users | Manage PayIn / PayOut users | Done |
130
202
  | 💳 Payments | Single and recurring payments | 🚧 In progress |
131
203
  | 🏦 Virtual Accounts (VA / PIPU) | Manage virtual accounts & PayTo | ⏳ Planned |
132
- | 👤 Users | Manage PayIn / PayOut users | ⏳ Planned |
133
204
  | 💼 Wallets | Create and manage wallet accounts | ⏳ Planned |
134
205
 
135
206
  ## 🧪 Development
@@ -184,9 +255,14 @@ Everyone interacting in the ZaiPayment project's codebases, issue trackers, chat
184
255
 
185
256
  ### Getting Started
186
257
  - [**Authentication Guide**](docs/AUTHENTICATION.md) - Two approaches to getting tokens, automatic management
258
+ - [**User Management Guide**](docs/USERS.md) - Managing payin and payout users
187
259
  - [**Webhook Examples**](examples/webhooks.md) - Complete webhook usage guide
188
260
  - [**Documentation Index**](docs/README.md) - Full documentation navigation
189
261
 
262
+ ### Examples & Patterns
263
+ - [User Examples](examples/users.md) - Real-world user management patterns
264
+ - [Webhook Examples](examples/webhooks.md) - Webhook integration patterns
265
+
190
266
  ### Technical Guides
191
267
  - [Webhook Architecture](docs/WEBHOOKS.md) - Technical implementation details
192
268
  - [Architecture Overview](docs/ARCHITECTURE.md) - System architecture and design
data/badges/.gitkeep ADDED
@@ -0,0 +1,2 @@
1
+ # This directory stores coverage badge JSON
2
+
@@ -0,0 +1 @@
1
+ {"schemaVersion": 1, "label": "coverage", "message": "95.18%", "color": "brightgreen"}
data/docs/USERS.md ADDED
@@ -0,0 +1,414 @@
1
+ # User Management
2
+
3
+ The User resource provides methods for managing Zai users (both payin and payout users).
4
+
5
+ ## Overview
6
+
7
+ Zai supports two types of user onboarding:
8
+ - **Payin User (Buyer)**: A user who makes payments
9
+ - **Payout User (Seller/Merchant)**: A user who receives payments
10
+
11
+ Both user types use the same endpoints but have different required information based on their role and verification requirements.
12
+
13
+ ## References
14
+
15
+ - [Onboarding a Payin User](https://developer.hellozai.com/docs/onboarding-a-payin-user)
16
+ - [Onboarding a Payout User](https://developer.hellozai.com/docs/onboarding-a-payout-user)
17
+
18
+ ## Usage
19
+
20
+ ### Initialize the User Resource
21
+
22
+ ```ruby
23
+ # Using the singleton instance
24
+ users = ZaiPayment.users
25
+
26
+ # Or create a new instance
27
+ users = ZaiPayment::Resources::User.new
28
+ ```
29
+
30
+ ## Methods
31
+
32
+ ### List Users
33
+
34
+ Retrieve a list of all users with pagination support.
35
+
36
+ ```ruby
37
+ # List users with default pagination (limit: 10, offset: 0)
38
+ response = ZaiPayment.users.list
39
+
40
+ # List users with custom pagination
41
+ response = ZaiPayment.users.list(limit: 20, offset: 10)
42
+
43
+ # Access the data
44
+ response.data # => Array of user objects
45
+ response.meta # => Pagination metadata
46
+ ```
47
+
48
+ ### Show User
49
+
50
+ Get details of a specific user by ID.
51
+
52
+ ```ruby
53
+ response = ZaiPayment.users.show('user_id')
54
+
55
+ # Access user details
56
+ user = response.data
57
+ puts user['email']
58
+ puts user['first_name']
59
+ puts user['last_name']
60
+ ```
61
+
62
+ ### Create Payin User (Buyer)
63
+
64
+ Create a new payin user who will make payments on your platform.
65
+
66
+ #### Required Fields for Payin Users
67
+
68
+ - `email` - User's email address
69
+ - `first_name` - User's first name
70
+ - `last_name` - User's last name
71
+ - `country` - Country code (ISO 3166-1 alpha-3, e.g., USA, AUS, GBR)
72
+ - `device_id` - Required when an item is created and card is charged
73
+ - `ip_address` - Required when an item is created and card is charged
74
+
75
+ #### Recommended Fields
76
+
77
+ - `address_line1` - Street address
78
+ - `city` - City
79
+ - `state` - State/Province
80
+ - `zip` - Postal/ZIP code
81
+ - `mobile` - Mobile phone number
82
+ - `dob` - Date of birth (YYYYMMDD format)
83
+
84
+ #### Example
85
+
86
+ ```ruby
87
+ response = ZaiPayment.users.create(
88
+ email: 'buyer@example.com',
89
+ first_name: 'John',
90
+ last_name: 'Doe',
91
+ country: 'USA',
92
+ mobile: '+1234567890',
93
+ address_line1: '123 Main St',
94
+ city: 'New York',
95
+ state: 'NY',
96
+ zip: '10001',
97
+ device_id: 'device_12345',
98
+ ip_address: '192.168.1.1'
99
+ )
100
+
101
+ user = response.data
102
+ puts user['id'] # => "user_payin_123"
103
+ ```
104
+
105
+ ### Create Payout User (Seller/Merchant)
106
+
107
+ Create a new payout user who will receive payments. Payout users must undergo verification and provide more detailed information.
108
+
109
+ #### Required Fields for Payout Users (Individuals)
110
+
111
+ - `email` - User's email address
112
+ - `first_name` - User's first name
113
+ - `last_name` - User's last name
114
+ - `address_line1` - Street address (Required for payout)
115
+ - `city` - City (Required for payout)
116
+ - `state` - State/Province (Required for payout)
117
+ - `zip` - Postal/ZIP code (Required for payout)
118
+ - `country` - Country code (ISO 3166-1 alpha-3)
119
+ - `dob` - Date of birth (YYYYMMDD format, Required for payout)
120
+
121
+ #### Example
122
+
123
+ ```ruby
124
+ response = ZaiPayment.users.create(
125
+ email: 'seller@example.com',
126
+ first_name: 'Jane',
127
+ last_name: 'Smith',
128
+ country: 'AUS',
129
+ dob: '19900101',
130
+ address_line1: '456 Market St',
131
+ city: 'Sydney',
132
+ state: 'NSW',
133
+ zip: '2000',
134
+ mobile: '+61412345678',
135
+ government_number: 'TFN123456789'
136
+ )
137
+
138
+ user = response.data
139
+ puts user['id'] # => "user_payout_456"
140
+ puts user['verification_state'] # => "pending" or "approved"
141
+ ```
142
+
143
+ ### Update User
144
+
145
+ Update an existing user's information.
146
+
147
+ ```ruby
148
+ response = ZaiPayment.users.update(
149
+ 'user_id',
150
+ mobile: '+9876543210',
151
+ address_line1: '789 New St',
152
+ city: 'Los Angeles',
153
+ state: 'CA',
154
+ zip: '90001'
155
+ )
156
+
157
+ updated_user = response.data
158
+ ```
159
+
160
+ ### Create Business User with Company
161
+
162
+ Create a payout user representing a business entity with full company details. This is useful for merchants, marketplace sellers, or any business that needs to receive payments.
163
+
164
+ #### Required Company Fields
165
+
166
+ When the `company` parameter is provided, the following fields are required:
167
+ - `name` - Company name
168
+ - `legal_name` - Legal business name
169
+ - `tax_number` - Tax/ABN/TFN number
170
+ - `business_email` - Business email address
171
+ - `country` - Country code (ISO 3166-1 alpha-3)
172
+
173
+ #### Example
174
+
175
+ ```ruby
176
+ response = ZaiPayment.users.create(
177
+ # Personal details (authorized signer)
178
+ email: 'john.director@example.com',
179
+ first_name: 'John',
180
+ last_name: 'Smith',
181
+ country: 'AUS',
182
+ mobile: '+61412345678',
183
+
184
+ # Job title (required for AMEX merchants)
185
+ authorized_signer_title: 'Director',
186
+
187
+ # Company details
188
+ company: {
189
+ name: 'Smith Trading Co',
190
+ legal_name: 'Smith Trading Company Pty Ltd',
191
+ tax_number: '53004085616', # ABN for Australian companies
192
+ business_email: 'accounts@smithtrading.com',
193
+ country: 'AUS',
194
+ charge_tax: true, # GST registered
195
+
196
+ # Optional company fields
197
+ address_line1: '123 Business Street',
198
+ address_line2: 'Suite 5',
199
+ city: 'Melbourne',
200
+ state: 'VIC',
201
+ zip: '3000',
202
+ phone: '+61398765432'
203
+ }
204
+ )
205
+
206
+ user = response.data
207
+ puts "Business user created: #{user['id']}"
208
+ puts "Company: #{user['company']['name']}"
209
+ ```
210
+
211
+ ## Field Reference
212
+
213
+ ### All User Fields
214
+
215
+ | Field | Type | Description | Payin Required | Payout Required |
216
+ |-------|------|-------------|----------------|-----------------|
217
+ | `email` | String | User's email address | ✓ | ✓ |
218
+ | `first_name` | String | User's first name | ✓ | ✓ |
219
+ | `last_name` | String | User's last name | ✓ | ✓ |
220
+ | `country` | String | ISO 3166-1 alpha-3 country code | ✓ | ✓ |
221
+ | `address_line1` | String | Street address | Recommended | ✓ |
222
+ | `address_line2` | String | Additional address info | Optional | Optional |
223
+ | `city` | String | City | Recommended | ✓ |
224
+ | `state` | String | State/Province | Recommended | ✓ |
225
+ | `zip` | String | Postal/ZIP code | Recommended | ✓ |
226
+ | `mobile` | String | Mobile phone number (international format) | Recommended | Recommended |
227
+ | `phone` | String | Phone number | Optional | Optional |
228
+ | `dob` | String | Date of birth (DD/MM/YYYY) | Recommended | ✓ |
229
+ | `government_number` | String | Tax/Government ID (SSN, TFN, etc.) | Optional | Recommended |
230
+ | `drivers_license_number` | String | Driving license number | Optional | Optional |
231
+ | `drivers_license_state` | String | State section of driving license | Optional | Optional |
232
+ | `logo_url` | String | URL link to logo | Optional | Optional |
233
+ | `color_1` | String | Color code number 1 | Optional | Optional |
234
+ | `color_2` | String | Color code number 2 | Optional | Optional |
235
+ | `custom_descriptor` | String | Custom text for bank statements | Optional | Optional |
236
+ | `authorized_signer_title` | String | Job title (e.g., Director) - Required for AMEX | Optional | AMEX Required |
237
+ | `company` | Object | Company details (see below) | Optional | Optional |
238
+ | `device_id` | String | Device ID for fraud prevention | When charging* | N/A |
239
+ | `ip_address` | String | IP address for fraud prevention | When charging* | N/A |
240
+ | `user_type` | String | 'payin' or 'payout' | Optional | Optional |
241
+
242
+ \* Required when an item is created and a card is charged
243
+
244
+ ### Company Object Fields
245
+
246
+ When creating a business user, you can provide a `company` object with the following fields:
247
+
248
+ | Field | Type | Description | Required |
249
+ |-------|------|-------------|----------|
250
+ | `name` | String | Company name | ✓ |
251
+ | `legal_name` | String | Legal business name | ✓ |
252
+ | `tax_number` | String | ABN/TFN/Tax number | ✓ |
253
+ | `business_email` | String | Business email address | ✓ |
254
+ | `country` | String | Country code (ISO 3166-1 alpha-3) | ✓ |
255
+ | `charge_tax` | Boolean | Charge GST/tax? (true/false) | Optional |
256
+ | `address_line1` | String | Business address line 1 | Optional |
257
+ | `address_line2` | String | Business address line 2 | Optional |
258
+ | `city` | String | Business city | Optional |
259
+ | `state` | String | Business state | Optional |
260
+ | `zip` | String | Business postal code | Optional |
261
+ | `phone` | String | Business phone number | Optional |
262
+
263
+ ## Error Handling
264
+
265
+ The User resource will raise validation errors for:
266
+
267
+ - Missing required fields
268
+ - Invalid email format
269
+ - Invalid country code (must be ISO 3166-1 alpha-3)
270
+ - Invalid date of birth format (must be YYYYMMDD)
271
+ - Invalid user type (must be 'payin' or 'payout')
272
+
273
+ ```ruby
274
+ begin
275
+ response = ZaiPayment.users.create(
276
+ email: 'invalid-email',
277
+ first_name: 'John',
278
+ last_name: 'Doe',
279
+ country: 'USA'
280
+ )
281
+ rescue ZaiPayment::Errors::ValidationError => e
282
+ puts "Validation error: #{e.message}"
283
+ rescue ZaiPayment::Errors::ApiError => e
284
+ puts "API error: #{e.message}"
285
+ end
286
+ ```
287
+
288
+ ## Best Practices
289
+
290
+ ### For Payin Users
291
+
292
+ 1. **Collect information progressively**: You can create a payin user with minimal information and update it later as needed.
293
+ 2. **Capture device information**: Use Hosted Forms and Hosted Fields to capture device ID and IP address when processing payments.
294
+ 3. **Store device_id and ip_address**: These are required when creating items and charging cards for fraud prevention.
295
+
296
+ ### For Payout Users
297
+
298
+ 1. **Collect complete information upfront**: Payout users require more detailed information for verification and underwriting.
299
+ 2. **Verify date of birth format**: Ensure DOB is in YYYYMMDD format (e.g., 19900101).
300
+ 3. **Provide accurate address**: Complete address information is required for payout users to pass verification.
301
+ 4. **Handle verification states**: Payout users go through verification (`pending`, `pending_check`, `approved`, etc.).
302
+
303
+ ## Response Structure
304
+
305
+ ### Successful Response
306
+
307
+ ```ruby
308
+ response.success? # => true
309
+ response.status # => 200 or 201
310
+ response.data # => User object hash
311
+ response.meta # => Pagination metadata (for list)
312
+ ```
313
+
314
+ ### User Object
315
+
316
+ ```ruby
317
+ {
318
+ "id" => "user_123",
319
+ "email" => "user@example.com",
320
+ "first_name" => "John",
321
+ "last_name" => "Doe",
322
+ "country" => "USA",
323
+ "address_line1" => "123 Main St",
324
+ "city" => "New York",
325
+ "state" => "NY",
326
+ "zip" => "10001",
327
+ "mobile" => "+1234567890",
328
+ "dob" => "19900101",
329
+ "verification_state" => "approved",
330
+ "created_at" => "2025-01-01T00:00:00Z",
331
+ "updated_at" => "2025-01-01T00:00:00Z"
332
+ }
333
+ ```
334
+
335
+ ## Complete Examples
336
+
337
+ ### Example 1: Create and Update a Payin User
338
+
339
+ ```ruby
340
+ # Step 1: Create a payin user with minimal info
341
+ response = ZaiPayment.users.create(
342
+ email: 'buyer@example.com',
343
+ first_name: 'John',
344
+ last_name: 'Doe',
345
+ country: 'USA'
346
+ )
347
+
348
+ user_id = response.data['id']
349
+
350
+ # Step 2: Update with additional info later
351
+ ZaiPayment.users.update(
352
+ user_id,
353
+ address_line1: '123 Main St',
354
+ city: 'New York',
355
+ state: 'NY',
356
+ zip: '10001',
357
+ mobile: '+1234567890'
358
+ )
359
+ ```
360
+
361
+ ### Example 2: Create a Payout User with Complete Information
362
+
363
+ ```ruby
364
+ response = ZaiPayment.users.create(
365
+ # Required fields
366
+ email: 'seller@example.com',
367
+ first_name: 'Jane',
368
+ last_name: 'Smith',
369
+ country: 'AUS',
370
+ dob: '19900101',
371
+ address_line1: '456 Market St',
372
+ city: 'Sydney',
373
+ state: 'NSW',
374
+ zip: '2000',
375
+
376
+ # Additional recommended fields
377
+ mobile: '+61412345678',
378
+ government_number: 'TFN123456789',
379
+ user_type: 'payout'
380
+ )
381
+
382
+ user = response.data
383
+ puts "Created payout user: #{user['id']}"
384
+ puts "Verification state: #{user['verification_state']}"
385
+ ```
386
+
387
+ ### Example 3: List and Filter Users
388
+
389
+ ```ruby
390
+ # Get first page of users
391
+ response = ZaiPayment.users.list(limit: 10, offset: 0)
392
+
393
+ response.data.each do |user|
394
+ puts "#{user['email']} - #{user['first_name']} #{user['last_name']}"
395
+ end
396
+
397
+ # Get next page
398
+ next_response = ZaiPayment.users.list(limit: 10, offset: 10)
399
+ ```
400
+
401
+ ## Testing
402
+
403
+ The User resource includes comprehensive test coverage. Run the tests with:
404
+
405
+ ```bash
406
+ bundle exec rspec spec/zai_payment/resources/user_spec.rb
407
+ ```
408
+
409
+ ## See Also
410
+
411
+ - [Webhook Documentation](WEBHOOKS.md)
412
+ - [Authentication Documentation](AUTHENTICATION.md)
413
+ - [Architecture Documentation](ARCHITECTURE.md)
414
+