zai_payment 2.6.1 → 2.8.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/docs/pay_ids.md ADDED
@@ -0,0 +1,777 @@
1
+ # PayID Management
2
+
3
+ The PayId resource provides methods for registering and managing Zai PayIDs for virtual accounts.
4
+
5
+ ## Overview
6
+
7
+ PayIDs are easy-to-remember identifiers (like email addresses) that can be used instead of BSB and account numbers for receiving payments in Australia's New Payments Platform (NPP). They provide a more user-friendly way for customers to make payments.
8
+
9
+ PayIDs are particularly useful for:
10
+ - Simplifying payment collection with memorable identifiers
11
+ - Enabling fast payments through the New Payments Platform
12
+ - Providing customers with an alternative to BSB and account numbers
13
+ - Enhancing user experience with familiar email-based identifiers
14
+
15
+ ## Key Features
16
+
17
+ - **Email-based PayIDs**: Register email addresses as payment identifiers
18
+ - **Linked to Virtual Accounts**: Each PayID is associated with a virtual account
19
+ - **Status Tracking**: Monitor PayID status (pending_activation, active, etc.)
20
+ - **Secure Registration**: Validated registration process with proper error handling
21
+
22
+ ## References
23
+
24
+ - [PayID API](https://developer.hellozai.com/reference/registerpayid)
25
+ - [Zai API Documentation](https://developer.hellozai.com/docs)
26
+
27
+ ## Usage
28
+
29
+ ### Initialize the PayId Resource
30
+
31
+ ```ruby
32
+ # Using a new instance
33
+ pay_ids = ZaiPayment::Resources::PayId.new
34
+
35
+ # Or use with custom client
36
+ client = ZaiPayment::Client.new(base_endpoint: :va_base)
37
+ pay_ids = ZaiPayment::Resources::PayId.new(client: client)
38
+ ```
39
+
40
+ ## Methods
41
+
42
+ ### Register PayID
43
+
44
+ Register a PayID for a given Virtual Account. This creates a PayID that customers can use to send payments to the virtual account.
45
+
46
+ #### Parameters
47
+
48
+ - `virtual_account_id` (required) - The virtual account ID
49
+ - `pay_id` (required) - The PayID being registered (max 256 characters)
50
+ - `type` (required) - The type of PayID (currently only 'EMAIL' is supported)
51
+ - `details` (required) - Hash containing additional details:
52
+ - `pay_id_name` (optional) - Name to identify the entity (1-140 characters)
53
+ - `owner_legal_name` (optional) - Full legal account name (1-140 characters)
54
+
55
+ #### Example
56
+
57
+ ```ruby
58
+ # Register an EMAIL PayID
59
+ pay_ids = ZaiPayment::Resources::PayId.new
60
+
61
+ response = pay_ids.create(
62
+ '46deb476-c1a6-41eb-8eb7-26a695bbe5bc',
63
+ pay_id: 'jsmith@mydomain.com',
64
+ type: 'EMAIL',
65
+ details: {
66
+ pay_id_name: 'J Smith',
67
+ owner_legal_name: 'Mr John Smith'
68
+ }
69
+ )
70
+
71
+ # Access PayID details
72
+ if response.success?
73
+ pay_id = response.data
74
+
75
+ puts "PayID: #{pay_id['pay_id']}"
76
+ puts "Type: #{pay_id['type']}"
77
+ puts "Status: #{pay_id['status']}"
78
+ puts "PayID Name: #{pay_id['details']['pay_id_name']}"
79
+ puts "Owner Legal Name: #{pay_id['details']['owner_legal_name']}"
80
+ end
81
+ ```
82
+
83
+ #### Response
84
+
85
+ ```ruby
86
+ {
87
+ "pay_ids" => {
88
+ "id" => "46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
89
+ "pay_id" => "jsmith@mydomain.com",
90
+ "type" => "EMAIL",
91
+ "status" => "pending_activation",
92
+ "created_at" => "2020-04-27T20:28:22.378Z",
93
+ "updated_at" => "2020-04-27T20:28:22.378Z",
94
+ "details" => {
95
+ "pay_id_name" => "J Smith",
96
+ "owner_legal_name" => "Mr John Smith"
97
+ },
98
+ "links" => {
99
+ "self" => "/pay_ids/46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
100
+ "virtual_accounts" => "/virtual_accounts/46deb476-c1a6-41eb-8eb7-26a695bbe5bc"
101
+ }
102
+ }
103
+ }
104
+ ```
105
+
106
+ **Response Fields:**
107
+
108
+ | Field | Type | Description |
109
+ |-------|------|-------------|
110
+ | `id` | String | Unique identifier for the PayID |
111
+ | `pay_id` | String | The registered PayID (email address) |
112
+ | `type` | String | Type of PayID (e.g., "EMAIL") |
113
+ | `status` | String | PayID status (pending_activation, active, etc.) |
114
+ | `created_at` | String | ISO 8601 timestamp of creation |
115
+ | `updated_at` | String | ISO 8601 timestamp of last update |
116
+ | `details` | Hash | Additional PayID details |
117
+ | `details.pay_id_name` | String | Name to identify the entity |
118
+ | `details.owner_legal_name` | String | Full legal account name |
119
+ | `links` | Hash | Related resource links |
120
+ | `links.self` | String | URL to the PayID resource |
121
+ | `links.virtual_accounts` | String | URL to the associated virtual account |
122
+
123
+ **Use Cases:**
124
+
125
+ - Register PayIDs for easy customer payments
126
+ - Enable NPP fast payments
127
+ - Provide user-friendly payment identifiers
128
+ - Link email addresses to virtual accounts for payment collection
129
+ - Set up payment collection for businesses and individuals
130
+
131
+ ### Show PayID
132
+
133
+ Show details of a specific PayID using the given PayID ID.
134
+
135
+ #### Parameters
136
+
137
+ - `pay_id_id` (required) - The PayID ID
138
+
139
+ #### Example
140
+
141
+ ```ruby
142
+ # Get specific PayID details
143
+ pay_ids = ZaiPayment::Resources::PayId.new
144
+ response = pay_ids.show('46deb476-c1a6-41eb-8eb7-26a695bbe5bc')
145
+
146
+ # Access PayID details
147
+ if response.success?
148
+ pay_id = response.data
149
+
150
+ puts "PayID: #{pay_id['pay_id']}"
151
+ puts "Type: #{pay_id['type']}"
152
+ puts "Status: #{pay_id['status']}"
153
+ puts "PayID Name: #{pay_id['details']['pay_id_name']}"
154
+ puts "Owner Legal Name: #{pay_id['details']['owner_legal_name']}"
155
+ end
156
+ ```
157
+
158
+ #### Response
159
+
160
+ ```ruby
161
+ {
162
+ "pay_ids" => {
163
+ "id" => "46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
164
+ "pay_id" => "jsmith@mydomain.com",
165
+ "type" => "EMAIL",
166
+ "status" => "active",
167
+ "created_at" => "2020-04-27T20:28:22.378Z",
168
+ "updated_at" => "2020-04-27T20:28:22.378Z",
169
+ "details" => {
170
+ "pay_id_name" => "J Smith",
171
+ "owner_legal_name" => "Mr John Smith"
172
+ },
173
+ "links" => {
174
+ "self" => "/pay_ids/46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
175
+ "virtual_accounts" => "/virtual_accounts/46deb476-c1a6-41eb-8eb7-26a695bbe5bc"
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ **Response Fields:**
182
+
183
+ The response contains a single PayID object with the same fields as described in the Register PayID section.
184
+
185
+ **Use Cases:**
186
+
187
+ - Verify PayID details before using
188
+ - Check PayID status
189
+ - Retrieve PayID information for display to users
190
+ - Audit PayID configurations
191
+ - Validate PayID information
192
+ - Monitor PayID updates
193
+
194
+ ### Update PayID Status
195
+
196
+ Update the status of a PayID. Currently, this endpoint only supports deregistering PayIDs by setting the status to 'deregistered'. This is an asynchronous operation that returns a 202 Accepted response.
197
+
198
+ **Important:** Once a PayID is deregistered, it cannot be re-registered to the same virtual account.
199
+
200
+ #### Parameters
201
+
202
+ - `pay_id_id` (required) - The PayID ID
203
+ - `status` (required) - The new status (must be 'deregistered')
204
+
205
+ #### Example
206
+
207
+ ```ruby
208
+ # Deregister a PayID
209
+ pay_ids = ZaiPayment::Resources::PayId.new
210
+
211
+ response = pay_ids.update_status(
212
+ '46deb476-c1a6-41eb-8eb7-26a695bbe5bc',
213
+ 'deregistered'
214
+ )
215
+
216
+ # Check the response
217
+ if response.success?
218
+ puts "PayID deregistration initiated"
219
+ puts "ID: #{response.data['id']}"
220
+ puts "Message: #{response.data['message']}"
221
+ puts "Link: #{response.data['links']['self']}"
222
+ end
223
+ ```
224
+
225
+ #### Response
226
+
227
+ ```ruby
228
+ {
229
+ "id" => "46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
230
+ "message" => "PayID deregistration has been accepted for processing",
231
+ "links" => {
232
+ "self" => "/pay_ids/46deb476-c1a6-41eb-8eb7-26a695bbe5bc"
233
+ }
234
+ }
235
+ ```
236
+
237
+ **Response Fields:**
238
+
239
+ | Field | Type | Description |
240
+ |-------|------|-------------|
241
+ | `id` | String | The PayID ID |
242
+ | `message` | String | Confirmation message about the status update |
243
+ | `links` | Hash | Related resource links |
244
+ | `links.self` | String | URL to the PayID resource |
245
+
246
+ **Use Cases:**
247
+
248
+ - Deregister PayIDs when no longer needed
249
+ - Remove PayID from a virtual account
250
+ - Clean up unused payment identifiers
251
+ - Comply with customer requests to remove their email from the system
252
+ - Deactivate PayIDs as part of account closure
253
+
254
+ **Important Notes:**
255
+
256
+ - This operation returns 202 Accepted because it's processed asynchronously
257
+ - The actual status change may take a few moments to complete
258
+ - Only 'deregistered' is a valid status value; other values will raise a ValidationError
259
+ - Deregistered PayIDs cannot be re-registered
260
+
261
+ ## Validation Rules
262
+
263
+ ### virtual_account_id
264
+
265
+ - **Required**: Yes
266
+ - **Type**: String (UUID)
267
+ - **Description**: The ID of the virtual account that this PayID will be linked to. The virtual account must exist before registering a PayID.
268
+
269
+ ### pay_id
270
+
271
+ - **Required**: Yes
272
+ - **Type**: String
273
+ - **Max Length**: 256 characters
274
+ - **Description**: The PayID being registered. For EMAIL type, this should be a valid email address.
275
+
276
+ ### type
277
+
278
+ - **Required**: Yes
279
+ - **Type**: String (Enum)
280
+ - **Allowed Values**: EMAIL
281
+ - **Description**: The type of PayID being registered. Currently, only EMAIL type is supported.
282
+
283
+ ### details
284
+
285
+ - **Required**: Yes
286
+ - **Type**: Hash
287
+ - **Description**: Additional details about the PayID registration.
288
+
289
+ #### details.pay_id_name
290
+
291
+ - **Required**: No
292
+ - **Type**: String
293
+ - **Length**: 1-140 characters (when provided)
294
+ - **Description**: A name that can be used to identify the entity registering the PayID. This helps customers confirm they're sending to the right recipient.
295
+
296
+ #### details.owner_legal_name
297
+
298
+ - **Required**: No
299
+ - **Type**: String
300
+ - **Length**: 1-140 characters (when provided)
301
+ - **Description**: The full legal account name. This is displayed to customers during payment confirmation.
302
+
303
+ ## Error Handling
304
+
305
+ The PayId methods can raise the following errors:
306
+
307
+ ### ZaiPayment::Errors::ValidationError
308
+
309
+ Raised when input parameters fail validation:
310
+
311
+ ```ruby
312
+ begin
313
+ response = pay_ids.create(
314
+ '', # Empty virtual_account_id
315
+ pay_id: 'test@example.com',
316
+ type: 'EMAIL',
317
+ details: {}
318
+ )
319
+ rescue ZaiPayment::Errors::ValidationError => e
320
+ puts "Validation failed: #{e.message}"
321
+ # Output: "virtual_account_id is required and cannot be blank"
322
+ end
323
+ ```
324
+
325
+ **Common validation errors:**
326
+ - `virtual_account_id is required and cannot be blank`
327
+ - `pay_id is required and cannot be blank`
328
+ - `pay_id must be 256 characters or less`
329
+ - `type is required and cannot be blank`
330
+ - `type must be one of: EMAIL`
331
+ - `details is required and must be a hash`
332
+ - `pay_id_name must be between 1 and 140 characters`
333
+ - `owner_legal_name must be between 1 and 140 characters`
334
+
335
+ ### ZaiPayment::Errors::NotFoundError
336
+
337
+ Raised when the virtual account doesn't exist:
338
+
339
+ ```ruby
340
+ begin
341
+ response = pay_ids.create(
342
+ 'invalid-virtual-account-id',
343
+ pay_id: 'test@example.com',
344
+ type: 'EMAIL',
345
+ details: {}
346
+ )
347
+ rescue ZaiPayment::Errors::NotFoundError => e
348
+ puts "Not found: #{e.message}"
349
+ end
350
+ ```
351
+
352
+ ### ZaiPayment::Errors::UnauthorizedError
353
+
354
+ Raised when authentication fails:
355
+
356
+ ```ruby
357
+ begin
358
+ response = pay_ids.create(
359
+ virtual_account_id,
360
+ pay_id: 'test@example.com',
361
+ type: 'EMAIL',
362
+ details: {}
363
+ )
364
+ rescue ZaiPayment::Errors::UnauthorizedError => e
365
+ puts "Authentication failed: #{e.message}"
366
+ # Check your API credentials
367
+ end
368
+ ```
369
+
370
+ ### ZaiPayment::Errors::BadRequestError
371
+
372
+ Raised when the request is malformed or contains invalid data (e.g., PayID already registered):
373
+
374
+ ```ruby
375
+ begin
376
+ response = pay_ids.create(
377
+ virtual_account_id,
378
+ pay_id: 'test@example.com',
379
+ type: 'EMAIL',
380
+ details: {}
381
+ )
382
+ rescue ZaiPayment::Errors::BadRequestError => e
383
+ puts "Bad request: #{e.message}"
384
+ # May indicate PayID is already registered
385
+ end
386
+ ```
387
+
388
+ ## Complete Example
389
+
390
+ Here's a complete workflow showing how to create a virtual account and register a PayID with proper error handling:
391
+
392
+ ```ruby
393
+ require 'zai_payment'
394
+
395
+ # Configure ZaiPayment
396
+ ZaiPayment.configure do |config|
397
+ config.environment = :prelive
398
+ config.client_id = ENV['ZAI_CLIENT_ID']
399
+ config.client_secret = ENV['ZAI_CLIENT_SECRET']
400
+ config.scope = ENV['ZAI_SCOPE']
401
+ end
402
+
403
+ # Initialize resources
404
+ virtual_accounts = ZaiPayment::Resources::VirtualAccount.new
405
+ pay_ids = ZaiPayment::Resources::PayId.new
406
+ wallet_account_id = 'ae07556e-22ef-11eb-adc1-0242ac120002'
407
+
408
+ begin
409
+ # Step 1: Create a Virtual Account
410
+ puts "Creating virtual account..."
411
+
412
+ va_response = virtual_accounts.create(
413
+ wallet_account_id,
414
+ account_name: 'Customer Payment Account',
415
+ aka_names: ['Customer Account']
416
+ )
417
+
418
+ if va_response.success?
419
+ virtual_account = va_response.data
420
+ virtual_account_id = virtual_account['id']
421
+
422
+ puts "✓ Virtual Account Created"
423
+ puts " ID: #{virtual_account_id}"
424
+ puts " BSB: #{virtual_account['routing_number']}"
425
+ puts " Account: #{virtual_account['account_number']}"
426
+ puts " Status: #{virtual_account['status']}"
427
+
428
+ # Step 2: Register PayID
429
+ puts "\nRegistering PayID..."
430
+
431
+ payid_response = pay_ids.create(
432
+ virtual_account_id,
433
+ pay_id: 'customer@mybusiness.com',
434
+ type: 'EMAIL',
435
+ details: {
436
+ pay_id_name: 'My Business',
437
+ owner_legal_name: 'My Business Pty Ltd'
438
+ }
439
+ )
440
+
441
+ if payid_response.success?
442
+ pay_id = payid_response.data
443
+
444
+ puts "✓ PayID Registered Successfully!"
445
+ puts "─" * 60
446
+ puts "PayID Details:"
447
+ puts " ID: #{pay_id['id']}"
448
+ puts " PayID: #{pay_id['pay_id']}"
449
+ puts " Type: #{pay_id['type']}"
450
+ puts " Status: #{pay_id['status']}"
451
+ puts ""
452
+ puts "Payment Information:"
453
+ puts " PayID Name: #{pay_id['details']['pay_id_name']}"
454
+ puts " Owner Legal Name: #{pay_id['details']['owner_legal_name']}"
455
+ puts ""
456
+ puts "Customers can now send payments using:"
457
+ puts " PayID: #{pay_id['pay_id']}"
458
+ puts " OR"
459
+ puts " BSB: #{virtual_account['routing_number']}"
460
+ puts " Account: #{virtual_account['account_number']}"
461
+ puts "─" * 60
462
+
463
+ # Store the details in your database for future reference
464
+ # YourDatabase.store_pay_id(pay_id)
465
+ else
466
+ puts "Failed to register PayID"
467
+ end
468
+ else
469
+ puts "Failed to create virtual account"
470
+ end
471
+
472
+ rescue ZaiPayment::Errors::ValidationError => e
473
+ puts "Validation Error: #{e.message}"
474
+ puts "Please check your input parameters"
475
+
476
+ rescue ZaiPayment::Errors::NotFoundError => e
477
+ puts "Resource Not Found: #{e.message}"
478
+ puts "Please verify the wallet account or virtual account exists"
479
+
480
+ rescue ZaiPayment::Errors::UnauthorizedError => e
481
+ puts "Authentication Failed: #{e.message}"
482
+ puts "Please check your API credentials"
483
+
484
+ rescue ZaiPayment::Errors::BadRequestError => e
485
+ puts "Bad Request: #{e.message}"
486
+ puts "The PayID may already be registered or request is invalid"
487
+
488
+ rescue ZaiPayment::Errors::ApiError => e
489
+ puts "API Error: #{e.message}"
490
+ end
491
+ ```
492
+
493
+ ## Configuration
494
+
495
+ PayIDs use the `va_base` endpoint, which is automatically configured based on your environment:
496
+
497
+ ### Prelive Environment
498
+
499
+ ```ruby
500
+ ZaiPayment.configure do |config|
501
+ config.environment = :prelive
502
+ # Uses: https://sandbox.au-0000.api.assemblypay.com
503
+ end
504
+ ```
505
+
506
+ ### Production Environment
507
+
508
+ ```ruby
509
+ ZaiPayment.configure do |config|
510
+ config.environment = :production
511
+ # Uses: https://secure.api.promisepay.com
512
+ end
513
+ ```
514
+
515
+ The PayId resource automatically uses the correct endpoint based on your configuration.
516
+
517
+ ## Best Practices
518
+
519
+ ### 1. Use Meaningful Names
520
+
521
+ Use descriptive names in the details to help customers confirm the recipient:
522
+
523
+ ```ruby
524
+ # Good
525
+ details: {
526
+ pay_id_name: 'Acme Corporation',
527
+ owner_legal_name: 'Acme Corporation Pty Ltd'
528
+ }
529
+
530
+ # Avoid
531
+ details: {
532
+ pay_id_name: 'Acc1',
533
+ owner_legal_name: 'A'
534
+ }
535
+ ```
536
+
537
+ ### 2. Validate Email Format
538
+
539
+ Pre-validate the email format before registration:
540
+
541
+ ```ruby
542
+ def valid_email?(email)
543
+ email =~ /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
544
+ end
545
+
546
+ pay_id = 'customer@example.com'
547
+
548
+ if valid_email?(pay_id)
549
+ # Proceed with registration
550
+ else
551
+ puts "Invalid email format"
552
+ end
553
+ ```
554
+
555
+ ### 3. Store PayID Details
556
+
557
+ Always store the PayID details in your database:
558
+
559
+ ```ruby
560
+ response = pay_ids.create(virtual_account_id, pay_id: email, type: 'EMAIL', details: details)
561
+
562
+ if response.success?
563
+ pay_id = response.data
564
+
565
+ # Store in database
566
+ PayIdRecord.create!(
567
+ external_id: pay_id['id'],
568
+ virtual_account_id: virtual_account_id,
569
+ pay_id: pay_id['pay_id'],
570
+ pay_id_type: pay_id['type'],
571
+ pay_id_name: pay_id['details']['pay_id_name'],
572
+ owner_legal_name: pay_id['details']['owner_legal_name'],
573
+ status: pay_id['status']
574
+ )
575
+ end
576
+ ```
577
+
578
+ ### 4. Handle Errors Gracefully
579
+
580
+ Always implement proper error handling:
581
+
582
+ ```ruby
583
+ def register_pay_id_safely(virtual_account_id, pay_id_email, details)
584
+ pay_ids = ZaiPayment::Resources::PayId.new
585
+
586
+ begin
587
+ response = pay_ids.create(
588
+ virtual_account_id,
589
+ pay_id: pay_id_email,
590
+ type: 'EMAIL',
591
+ details: details
592
+ )
593
+ { success: true, data: response.data }
594
+ rescue ZaiPayment::Errors::ValidationError => e
595
+ { success: false, error: 'validation', message: e.message }
596
+ rescue ZaiPayment::Errors::NotFoundError => e
597
+ { success: false, error: 'not_found', message: e.message }
598
+ rescue ZaiPayment::Errors::BadRequestError => e
599
+ { success: false, error: 'bad_request', message: e.message }
600
+ rescue ZaiPayment::Errors::ApiError => e
601
+ { success: false, error: 'api_error', message: e.message }
602
+ end
603
+ end
604
+ ```
605
+
606
+ ### 5. Verify Virtual Account Exists
607
+
608
+ Check that the virtual account exists before registering a PayID:
609
+
610
+ ```ruby
611
+ virtual_accounts = ZaiPayment::Resources::VirtualAccount.new
612
+
613
+ begin
614
+ # Verify virtual account exists
615
+ va_response = virtual_accounts.show(virtual_account_id)
616
+
617
+ if va_response.success? && va_response.data['status'] == 'active'
618
+ # Proceed with PayID registration
619
+ pay_ids.create(virtual_account_id, pay_id: email, type: 'EMAIL', details: details)
620
+ else
621
+ puts "Virtual account is not active yet"
622
+ end
623
+ rescue ZaiPayment::Errors::NotFoundError
624
+ puts "Virtual account does not exist"
625
+ end
626
+ ```
627
+
628
+ ### 6. Monitor PayID Status
629
+
630
+ After registration, monitor the PayID status:
631
+
632
+ ```ruby
633
+ pay_id = response.data
634
+
635
+ case pay_id['status']
636
+ when 'pending_activation'
637
+ puts "PayID registered, awaiting activation"
638
+ when 'active'
639
+ puts "PayID is active and ready to receive payments"
640
+ when 'inactive'
641
+ puts "PayID is inactive"
642
+ else
643
+ puts "Unknown status: #{pay_id['status']}"
644
+ end
645
+ ```
646
+
647
+ ### 7. Secure PayID Information
648
+
649
+ Treat PayID details as sensitive payment information:
650
+
651
+ ```ruby
652
+ # Don't log sensitive details in production
653
+ if Rails.env.production?
654
+ logger.info "PayID registered: #{pay_id['id']}"
655
+ else
656
+ logger.debug "PayID details: #{pay_id.inspect}"
657
+ end
658
+
659
+ # Use HTTPS for all communications
660
+ # Store securely in your database
661
+ # Limit access to authorized personnel only
662
+ ```
663
+
664
+ ## Testing
665
+
666
+ For testing in prelive environment:
667
+
668
+ ```ruby
669
+ # spec/services/pay_id_service_spec.rb
670
+ require 'spec_helper'
671
+
672
+ RSpec.describe PayIdService do
673
+ let(:virtual_account_id) { 'test-virtual-account-id' }
674
+
675
+ describe '#register_pay_id' do
676
+ it 'registers a PayID successfully' do
677
+ VCR.use_cassette('pay_id_register') do
678
+ service = PayIdService.new
679
+ result = service.register_pay_id(
680
+ virtual_account_id,
681
+ pay_id: 'test@example.com',
682
+ details: {
683
+ pay_id_name: 'Test User',
684
+ owner_legal_name: 'Test User Full Name'
685
+ }
686
+ )
687
+
688
+ expect(result[:success]).to be true
689
+ expect(result[:pay_id]['id']).to be_present
690
+ expect(result[:pay_id]['pay_id']).to eq('test@example.com')
691
+ expect(result[:pay_id]['type']).to eq('EMAIL')
692
+ end
693
+ end
694
+ end
695
+ end
696
+ ```
697
+
698
+ ## Troubleshooting
699
+
700
+ ### Issue: ValidationError - "virtual_account_id is required"
701
+
702
+ **Solution**: Ensure you're passing a valid virtual account ID:
703
+
704
+ ```ruby
705
+ # Wrong
706
+ pay_ids.create('', pay_id: 'test@example.com', type: 'EMAIL', details: {})
707
+
708
+ # Correct
709
+ pay_ids.create('46deb476-c1a6-41eb-8eb7-26a695bbe5bc', pay_id: 'test@example.com', type: 'EMAIL', details: {})
710
+ ```
711
+
712
+ ### Issue: NotFoundError - "Virtual account not found"
713
+
714
+ **Solution**: Verify the virtual account exists before registering a PayID:
715
+
716
+ ```ruby
717
+ # Check virtual account exists first
718
+ virtual_accounts = ZaiPayment::Resources::VirtualAccount.new
719
+ begin
720
+ va_response = virtual_accounts.show(virtual_account_id)
721
+ if va_response.success?
722
+ # Virtual account exists, proceed with PayID registration
723
+ pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'EMAIL', details: {})
724
+ end
725
+ rescue ZaiPayment::Errors::NotFoundError
726
+ puts "Virtual account does not exist"
727
+ end
728
+ ```
729
+
730
+ ### Issue: ValidationError - "pay_id must be 256 characters or less"
731
+
732
+ **Solution**: Ensure the PayID (email) is not too long:
733
+
734
+ ```ruby
735
+ pay_id_email = 'verylongemail@example.com'
736
+
737
+ if pay_id_email.length <= 256
738
+ pay_ids.create(virtual_account_id, pay_id: pay_id_email, type: 'EMAIL', details: {})
739
+ else
740
+ puts "PayID is too long"
741
+ end
742
+ ```
743
+
744
+ ### Issue: ValidationError - "type must be one of: EMAIL"
745
+
746
+ **Solution**: Ensure you're using a valid type:
747
+
748
+ ```ruby
749
+ # Wrong
750
+ pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'PHONE', details: {})
751
+
752
+ # Correct
753
+ pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'EMAIL', details: {})
754
+ ```
755
+
756
+ ### Issue: BadRequestError - PayID already registered
757
+
758
+ **Solution**: PayIDs must be unique. Check if the PayID is already registered:
759
+
760
+ ```ruby
761
+ begin
762
+ pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'EMAIL', details: {})
763
+ rescue ZaiPayment::Errors::BadRequestError => e
764
+ if e.message.include?('already registered')
765
+ puts "This PayID is already registered to another account"
766
+ else
767
+ puts "Bad request: #{e.message}"
768
+ end
769
+ end
770
+ ```
771
+
772
+ ## See Also
773
+
774
+ - [Virtual Accounts Documentation](virtual_accounts.md)
775
+ - [Examples](../examples/pay_ids.md)
776
+ - [Zai API Documentation](https://developer.hellozai.com/docs)
777
+