zai_payment 2.6.0 → 2.7.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.
@@ -0,0 +1,489 @@
1
+ # Direct API Usage Guide
2
+
3
+ This guide shows you how to use the ZaiPayment client directly to call APIs that haven't been implemented as resource methods yet.
4
+
5
+ ## Overview
6
+
7
+ The `ZaiPayment::Client` class provides low-level HTTP methods (`get`, `post`, `patch`, `delete`) that you can use to call any Zai API endpoint directly.
8
+
9
+ ## Basic Usage
10
+
11
+ ### Creating a Client
12
+
13
+ ```ruby
14
+ # Use the default client (va_base endpoint)
15
+ client = ZaiPayment::Client.new
16
+
17
+ # Or specify a different endpoint
18
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
19
+ ```
20
+
21
+ ### Available Endpoints
22
+
23
+ The client supports different base endpoints configured in your setup:
24
+ - `:va_base` - Virtual Account API base (default)
25
+ - `:core_base` - Core API base (for users, items, etc.)
26
+
27
+ ## HTTP Methods
28
+
29
+ The client provides four HTTP methods:
30
+
31
+ ### GET Request
32
+ ```ruby
33
+ client.get(path, params: {})
34
+ ```
35
+
36
+ ### POST Request
37
+ ```ruby
38
+ client.post(path, body: {})
39
+ ```
40
+
41
+ ### PATCH Request
42
+ ```ruby
43
+ client.patch(path, body: {})
44
+ ```
45
+
46
+ ### DELETE Request
47
+ ```ruby
48
+ client.delete(path)
49
+ ```
50
+
51
+ ## Examples
52
+
53
+ ### Example 1: Get Wallet Account Transactions (Unimplemented)
54
+
55
+ ```ruby
56
+ # If wallet transactions endpoint isn't implemented yet
57
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
58
+
59
+ # Get transactions for a wallet account
60
+ response = client.get("/wallet_accounts/#{wallet_id}/transactions", params: {
61
+ limit: 20,
62
+ offset: 0
63
+ })
64
+
65
+ if response.success?
66
+ transactions = response.data
67
+ transactions.each do |transaction|
68
+ puts "Transaction ID: #{transaction['id']}"
69
+ puts "Amount: #{transaction['amount']} #{transaction['currency']}"
70
+ puts "Type: #{transaction['type']}"
71
+ puts "State: #{transaction['state']}"
72
+ puts "---"
73
+ end
74
+ else
75
+ puts "Error: #{response.status}"
76
+ end
77
+ ```
78
+
79
+ ### Example 2: Create Virtual Account (Unimplemented)
80
+
81
+ ```ruby
82
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
83
+
84
+ # Create a virtual account for a wallet
85
+ response = client.post("/wallet_accounts/#{wallet_id}/virtual_accounts", body: {
86
+ account_name: "My Virtual Account",
87
+ nickname: "main_account"
88
+ })
89
+
90
+ if response.success?
91
+ virtual_account = response.data
92
+ puts "Virtual Account Created!"
93
+ puts "ID: #{virtual_account['id']}"
94
+ puts "Account Number: #{virtual_account['account_number']}"
95
+ puts "BSB: #{virtual_account['bsb']}"
96
+ else
97
+ puts "Error: #{response.status}"
98
+ end
99
+ ```
100
+
101
+ ### Example 3: Get NPP Details (Unimplemented)
102
+
103
+ ```ruby
104
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
105
+
106
+ # Get NPP (New Payments Platform) details for a wallet account
107
+ response = client.get("/wallet_accounts/#{wallet_id}/npp_details")
108
+
109
+ if response.success?
110
+ npp_details = response.data
111
+ puts "NPP PayID: #{npp_details['pay_id']}"
112
+ puts "PayID Type: #{npp_details['pay_id_type']}"
113
+ puts "Status: #{npp_details['status']}"
114
+ end
115
+ ```
116
+
117
+ ### Example 4: Batch Transactions (Unimplemented)
118
+
119
+ ```ruby
120
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
121
+
122
+ # Create a batch transaction
123
+ response = client.post("/batch_transactions", body: {
124
+ account_id: account_id,
125
+ batch_name: "Monthly Payouts",
126
+ transactions: [
127
+ {
128
+ amount: 10000,
129
+ currency: "AUD",
130
+ to_user_id: user_id_1,
131
+ description: "Payout to user 1"
132
+ },
133
+ {
134
+ amount: 15000,
135
+ currency: "AUD",
136
+ to_user_id: user_id_2,
137
+ description: "Payout to user 2"
138
+ }
139
+ ]
140
+ })
141
+
142
+ if response.success?
143
+ batch = response.data
144
+ puts "Batch Transaction Created!"
145
+ puts "Batch ID: #{batch['id']}"
146
+ puts "Status: #{batch['status']}"
147
+ puts "Total Amount: #{batch['total_amount']}"
148
+ end
149
+ ```
150
+
151
+ ### Example 5: Update Item Fee (Unimplemented)
152
+
153
+ ```ruby
154
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
155
+
156
+ # Update a fee for an item
157
+ response = client.patch("/items/#{item_id}/fees/#{fee_id}", body: {
158
+ amount: 500,
159
+ description: "Updated processing fee"
160
+ })
161
+
162
+ if response.success?
163
+ fee = response.data
164
+ puts "Fee Updated!"
165
+ puts "New Amount: #{fee['amount']}"
166
+ end
167
+ ```
168
+
169
+ ### Example 6: List Card Accounts (Unimplemented)
170
+
171
+ ```ruby
172
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
173
+
174
+ # List all card accounts with pagination
175
+ response = client.get("/card_accounts", params: {
176
+ limit: 50,
177
+ offset: 0,
178
+ user_id: user_id # Optional filter
179
+ })
180
+
181
+ if response.success?
182
+ card_accounts = response.data
183
+ card_accounts.each do |card|
184
+ puts "Card ID: #{card['id']}"
185
+ puts "Card Type: #{card['card']['type']}"
186
+ puts "Last 4 Digits: #{card['card']['number']}"
187
+ puts "Active: #{card['active']}"
188
+ puts "---"
189
+ end
190
+
191
+ # Access pagination metadata
192
+ meta = response.meta
193
+ puts "\nTotal: #{meta['total']}"
194
+ puts "Showing #{meta['limit']} items from offset #{meta['offset']}"
195
+ end
196
+ ```
197
+
198
+ ### Example 7: Custom Endpoint with Error Handling
199
+
200
+ ```ruby
201
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
202
+
203
+ begin
204
+ # Call any custom endpoint
205
+ response = client.get("/custom/endpoint/path", params: {
206
+ custom_param: "value"
207
+ })
208
+
209
+ if response.success?
210
+ data = response.data
211
+ puts "Success: #{data.inspect}"
212
+ else
213
+ puts "API Error: #{response.status}"
214
+ end
215
+
216
+ rescue ZaiPayment::Errors::TimeoutError => e
217
+ puts "Request timed out: #{e.message}"
218
+ rescue ZaiPayment::Errors::ConnectionError => e
219
+ puts "Connection failed: #{e.message}"
220
+ rescue ZaiPayment::Errors::UnauthorizedError => e
221
+ puts "Unauthorized: #{e.message}"
222
+ # Maybe refresh token here
223
+ rescue ZaiPayment::Errors::ValidationError => e
224
+ puts "Validation error: #{e.message}"
225
+ rescue ZaiPayment::Errors::ApiError => e
226
+ puts "API error: #{e.message}"
227
+ end
228
+ ```
229
+
230
+ ## Advanced: Using Client in a Custom Resource
231
+
232
+ If you're implementing a new resource that's not yet part of the gem, you can follow this pattern:
233
+
234
+ ```ruby
235
+ module ZaiPayment
236
+ module Resources
237
+ class CustomResource
238
+ attr_reader :client
239
+
240
+ def initialize(client: nil)
241
+ @client = client || Client.new(base_endpoint: :core_base)
242
+ end
243
+
244
+ # List custom resources
245
+ def list(limit: 10, offset: 0)
246
+ params = { limit: limit, offset: offset }
247
+ client.get("/custom_resources", params: params)
248
+ end
249
+
250
+ # Get a custom resource
251
+ def show(resource_id)
252
+ client.get("/custom_resources/#{resource_id}")
253
+ end
254
+
255
+ # Create a custom resource
256
+ def create(name:, description:)
257
+ body = {
258
+ name: name,
259
+ description: description
260
+ }
261
+ client.post("/custom_resources", body: body)
262
+ end
263
+
264
+ # Update a custom resource
265
+ def update(resource_id, **attributes)
266
+ body = build_body(attributes)
267
+ client.patch("/custom_resources/#{resource_id}", body: body)
268
+ end
269
+
270
+ # Delete a custom resource
271
+ def delete(resource_id)
272
+ client.delete("/custom_resources/#{resource_id}")
273
+ end
274
+
275
+ private
276
+
277
+ def build_body(attributes)
278
+ # Filter and transform attributes as needed
279
+ attributes.compact
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ # Usage
286
+ custom = ZaiPayment::Resources::CustomResource.new
287
+ response = custom.list(limit: 20)
288
+ ```
289
+
290
+ ## Response Object
291
+
292
+ All client methods return a `ZaiPayment::Response` object with the following methods:
293
+
294
+ ### Response Methods
295
+
296
+ ```ruby
297
+ response.success? # => true/false (2xx status codes)
298
+ response.client_error? # => true/false (4xx status codes)
299
+ response.server_error? # => true/false (5xx status codes)
300
+ response.status # => HTTP status code (e.g., 200, 404, 500)
301
+ response.data # => Extracted data from response body
302
+ response.body # => Full response body
303
+ response.meta # => Pagination/metadata (if available)
304
+ response.headers # => Response headers
305
+ ```
306
+
307
+ ### Response Data Extraction
308
+
309
+ The `response.data` method automatically extracts the main data from the response:
310
+
311
+ ```ruby
312
+ # For endpoints returning users
313
+ response.data # Automatically extracts response.body['users']
314
+
315
+ # For endpoints returning items
316
+ response.data # Automatically extracts response.body['items']
317
+
318
+ # For endpoints returning wallet_accounts
319
+ response.data # Automatically extracts response.body['wallet_accounts']
320
+
321
+ # For custom endpoints without a recognized key
322
+ response.data # Returns the full response.body
323
+ ```
324
+
325
+ Supported auto-extraction keys:
326
+ - `webhooks`
327
+ - `users`
328
+ - `items`
329
+ - `fees`
330
+ - `transactions`
331
+ - `batch_transactions`
332
+ - `bpay_accounts`
333
+ - `bank_accounts`
334
+ - `card_accounts`
335
+ - `wallet_accounts`
336
+ - `routing_number`
337
+
338
+ ## Error Handling
339
+
340
+ The client automatically handles errors and raises appropriate exceptions:
341
+
342
+ ### Available Error Classes
343
+
344
+ ```ruby
345
+ ZaiPayment::Errors::BadRequestError # 400 Bad Request
346
+ ZaiPayment::Errors::UnauthorizedError # 401 Unauthorized
347
+ ZaiPayment::Errors::ForbiddenError # 403 Forbidden
348
+ ZaiPayment::Errors::NotFoundError # 404 Not Found
349
+ ZaiPayment::Errors::ValidationError # 422 Unprocessable Entity
350
+ ZaiPayment::Errors::RateLimitError # 429 Too Many Requests
351
+ ZaiPayment::Errors::ServerError # 5xx Server Errors
352
+ ZaiPayment::Errors::TimeoutError # Timeout errors
353
+ ZaiPayment::Errors::ConnectionError # Connection errors
354
+ ZaiPayment::Errors::ApiError # Generic API errors
355
+ ```
356
+
357
+ ### Error Handling Example
358
+
359
+ ```ruby
360
+ begin
361
+ response = client.get("/some/endpoint")
362
+ data = response.data
363
+ # Process data...
364
+ rescue ZaiPayment::Errors::NotFoundError => e
365
+ puts "Resource not found: #{e.message}"
366
+ rescue ZaiPayment::Errors::ValidationError => e
367
+ puts "Validation failed: #{e.message}"
368
+ rescue ZaiPayment::Errors::UnauthorizedError => e
369
+ puts "Auth failed, refreshing token..."
370
+ ZaiPayment.refresh_token!
371
+ retry
372
+ rescue ZaiPayment::Errors::ApiError => e
373
+ puts "API error: #{e.message}"
374
+ end
375
+ ```
376
+
377
+ ## Configuration
378
+
379
+ The client uses the global ZaiPayment configuration:
380
+
381
+ ```ruby
382
+ ZaiPayment.configure do |config|
383
+ config.client_id = ENV['ZAI_CLIENT_ID']
384
+ config.client_secret = ENV['ZAI_CLIENT_SECRET']
385
+ config.environment = :prelive # or :production
386
+
387
+ # Optional timeout configuration
388
+ config.timeout = 60 # Overall timeout
389
+ config.open_timeout = 10 # Connection timeout
390
+ config.read_timeout = 50 # Read timeout
391
+ end
392
+ ```
393
+
394
+ ## Best Practices
395
+
396
+ 1. **Use Appropriate Endpoint**: Choose `:core_base` for user/item/transaction APIs, `:va_base` for virtual account APIs
397
+
398
+ 2. **Handle Errors Gracefully**: Always wrap API calls in error handling blocks
399
+
400
+ 3. **Check Response Status**: Always check `response.success?` before accessing data
401
+
402
+ 4. **Use Response.data**: Prefer `response.data` over `response.body` for automatic extraction
403
+
404
+ 5. **Follow API Conventions**: Use the same patterns as existing resource classes when building custom resources
405
+
406
+ 6. **Document Your Usage**: When using direct API calls, document the endpoint and expected responses
407
+
408
+ 7. **Consider Contributing**: If you implement a useful endpoint, consider contributing it back to the gem!
409
+
410
+ ## Need Help?
411
+
412
+ - Check the [Zai API Documentation](https://developer.hellozai.com/)
413
+ - Review existing resource implementations in `lib/zai_payment/resources/`
414
+ - Open an issue on GitHub if you find a commonly-used endpoint that should be added to the gem
415
+
416
+ ## Example: Complete Workflow
417
+
418
+ Here's a complete example workflow using direct API calls:
419
+
420
+ ```ruby
421
+ require 'zai_payment'
422
+
423
+ # Configure the gem
424
+ ZaiPayment.configure do |config|
425
+ config.client_id = ENV['ZAI_CLIENT_ID']
426
+ config.client_secret = ENV['ZAI_CLIENT_SECRET']
427
+ config.environment = :prelive
428
+ end
429
+
430
+ # Initialize client
431
+ client = ZaiPayment::Client.new(base_endpoint: :core_base)
432
+
433
+ begin
434
+ # 1. Get user's wallet account
435
+ wallet_response = ZaiPayment.users.wallet_account('user_123')
436
+ wallet = wallet_response.data
437
+ wallet_id = wallet['id']
438
+
439
+ puts "Wallet Balance: #{wallet['balance']} #{wallet['currency']}"
440
+
441
+ # 2. Get wallet transactions (using direct API call)
442
+ transactions_response = client.get(
443
+ "/wallet_accounts/#{wallet_id}/transactions",
444
+ params: { limit: 10, offset: 0 }
445
+ )
446
+
447
+ if transactions_response.success?
448
+ transactions = transactions_response.data
449
+ puts "\nRecent Transactions:"
450
+ transactions.each do |txn|
451
+ puts "- #{txn['type']}: #{txn['amount']} (#{txn['state']})"
452
+ end
453
+ end
454
+
455
+ # 3. Get NPP details (using direct API call)
456
+ npp_response = client.get("/wallet_accounts/#{wallet_id}/npp_details")
457
+
458
+ if npp_response.success?
459
+ npp = npp_response.data
460
+ puts "\nNPP Details:"
461
+ puts "PayID: #{npp['pay_id']}"
462
+ puts "Status: #{npp['status']}"
463
+ end
464
+
465
+ # 4. Create a virtual account (using direct API call)
466
+ virtual_response = client.post(
467
+ "/wallet_accounts/#{wallet_id}/virtual_accounts",
468
+ body: {
469
+ account_name: "My Virtual Account",
470
+ nickname: "savings"
471
+ }
472
+ )
473
+
474
+ if virtual_response.success?
475
+ virtual = virtual_response.data
476
+ puts "\nVirtual Account Created!"
477
+ puts "Account Number: #{virtual['account_number']}"
478
+ puts "BSB: #{virtual['bsb']}"
479
+ end
480
+
481
+ rescue ZaiPayment::Errors::NotFoundError => e
482
+ puts "Resource not found: #{e.message}"
483
+ rescue ZaiPayment::Errors::ApiError => e
484
+ puts "API error: #{e.message}"
485
+ end
486
+ ```
487
+
488
+ This guide should help you call any Zai API endpoint, even if it hasn't been implemented in the gem yet!
489
+
data/docs/users.md CHANGED
@@ -168,12 +168,51 @@ Show the user's wallet account using a given user ID.
168
168
  ```ruby
169
169
  response = ZaiPayment.users.wallet_account('user_id')
170
170
 
171
+ # Access wallet account details (automatically extracted from response)
171
172
  wallet = response.data
172
- puts wallet['id']
173
- puts wallet['balance']
174
- puts wallet['currency']
173
+ puts "Wallet Account ID: #{wallet['id']}"
174
+ puts "Active: #{wallet['active']}"
175
+ puts "Balance: #{wallet['balance']}"
176
+ puts "Currency: #{wallet['currency']}"
177
+ puts "Created At: #{wallet['created_at']}"
178
+ puts "Updated At: #{wallet['updated_at']}"
179
+
180
+ # Access related resource links
181
+ puts "Self URL: #{wallet['links']['self']}"
182
+ puts "Users URL: #{wallet['links']['users']}"
183
+ puts "Batch Transactions URL: #{wallet['links']['batch_transactions']}"
184
+ puts "Transactions URL: #{wallet['links']['transactions']}"
185
+ puts "BPay Details URL: #{wallet['links']['bpay_details']}"
186
+ puts "NPP Details URL: #{wallet['links']['npp_details']}"
187
+ puts "Virtual Accounts URL: #{wallet['links']['virtual_accounts']}"
175
188
  ```
176
189
 
190
+ **Response Structure:**
191
+
192
+ ```ruby
193
+ {
194
+ "wallet_accounts" => {
195
+ "id" => "5c1c6b10-4c56-0137-8cd7-0242ac110002",
196
+ "active" => true,
197
+ "created_at" => "2019-04-29T02:42:31.536Z",
198
+ "updated_at" => "2020-05-03T12:01:02.254Z",
199
+ "balance" => 663337,
200
+ "currency" => "AUD",
201
+ "links" => {
202
+ "self" => "/transactions/aed45af0-6f63-0138-901c-0a58a9feac03/wallet_accounts",
203
+ "users" => "/wallet_accounts/5c1c6b10-4c56-0137-8cd7-0242ac110002/users",
204
+ "batch_transactions" => "/wallet_accounts/5c1c6b10-4c56-0137-8cd7-0242ac110002/batch_transactions",
205
+ "transactions" => "/wallet_accounts/5c1c6b10-4c56-0137-8cd7-0242ac110002/transactions",
206
+ "bpay_details" => "/wallet_accounts/5c1c6b10-4c56-0137-8cd7-0242ac110002/bpay_details",
207
+ "npp_details" => "/wallet_accounts/5c1c6b10-4c56-0137-8cd7-0242ac110002/npp_details",
208
+ "virtual_accounts" => "/wallet_accounts/5c1c6b10-4c56-0137-8cd7-0242ac110002/virtual_accounts"
209
+ }
210
+ }
211
+ }
212
+ ```
213
+
214
+ **Note:** The `response.data` method automatically extracts the `wallet_accounts` object from the response, so you can access the wallet account fields directly without needing to access `response.data['wallet_accounts']`.
215
+
177
216
  ### List User Items
178
217
 
179
218
  Retrieve an ordered and paginated list of existing items the user is associated with.