zai_payment 2.3.1 → 2.4.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/badges/coverage.json +1 -1
- data/changelog.md +54 -0
- data/docs/items.md +575 -0
- data/examples/items.md +2115 -0
- data/examples/rails_card_payment.md +550 -14
- data/lib/zai_payment/client.rb +13 -2
- data/lib/zai_payment/config.rb +4 -3
- data/lib/zai_payment/resources/item.rb +257 -0
- data/lib/zai_payment/version.rb +1 -1
- data/readme.md +5 -0
- metadata +1 -2
- data/token_auth_implementation_summary.md +0 -249
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b608e4fd66f94a5cad5ed8cab447031e67cd61523d5bffa36a858d9e868a244
|
|
4
|
+
data.tar.gz: 1565178be12a4c07bbac95b9e913b57e59fe446f5de956bb994f173b157ed4a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 69b6546e10a89af5eb203e4ed08af91ab1eaacc7e5564073c60a66f059ef2cc61b94fbd0f508abd1a97e4ff5de836df99299786e49da473d9f39d43e4c65efa8
|
|
7
|
+
data.tar.gz: 8303047008ad3e9af60a4a15eba7ee984bc0a45aac3b2ee25e0bb3b6d5a492394d966db230ac454f919f7ccac29cdf1f63c6892b183df7711d0f56e5275d9190
|
data/badges/coverage.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion": 1, "label": "coverage", "message": "
|
|
1
|
+
{"schemaVersion": 1, "label": "coverage", "message": "97.0%", "color": "brightgreen"}
|
data/changelog.md
CHANGED
|
@@ -1,4 +1,58 @@
|
|
|
1
1
|
## [Released]
|
|
2
|
+
## [2.4.0] - 2025-11-02
|
|
3
|
+
### Added
|
|
4
|
+
- **Item Payment Actions API**: Advanced payment operations for managing item transactions 💳
|
|
5
|
+
- `ZaiPayment.items.make_payment(item_id, account_id:, device_id:, ip_address:, cvv:, merchant_phone:)` - Process a payment for an item
|
|
6
|
+
- `ZaiPayment.items.cancel(item_id)` - Cancel an item/transaction
|
|
7
|
+
- `ZaiPayment.items.refund(item_id, refund_amount:, refund_message:, account_id:)` - Refund a payment
|
|
8
|
+
- `ZaiPayment.items.authorize_payment(item_id, account_id:, cvv:, merchant_phone:)` - Authorize a payment without capturing
|
|
9
|
+
- `ZaiPayment.items.capture_payment(item_id, amount:)` - Capture a previously authorized payment
|
|
10
|
+
- `ZaiPayment.items.void_payment(item_id)` - Void an authorized payment
|
|
11
|
+
- `ZaiPayment.items.make_payment_async(item_id, account_id:, request_three_d_secure:)` - Process an async payment with 3D Secure 2.0 support
|
|
12
|
+
- Support for pre-authorization workflows (authorize then capture)
|
|
13
|
+
- Support for async payment methods (direct debit, bank transfers, PayPal)
|
|
14
|
+
- Comprehensive validation for payment parameters
|
|
15
|
+
- Full RSpec test suite for all payment actions
|
|
16
|
+
- Comprehensive documentation in `docs/items.md` and `examples/items.md`
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
- **Updated Items Guide** (`docs/items.md`):
|
|
20
|
+
- Complete payment workflow examples
|
|
21
|
+
- Pre-authorization and capture patterns
|
|
22
|
+
- Refund and cancellation examples
|
|
23
|
+
- Async payment handling
|
|
24
|
+
- Error handling for payment operations
|
|
25
|
+
- **Updated Items Examples** (`examples/items.md`):
|
|
26
|
+
- Real-world payment scenarios
|
|
27
|
+
- Card payment workflows
|
|
28
|
+
- Direct debit examples
|
|
29
|
+
- PayPal integration patterns
|
|
30
|
+
- Refund and dispute handling
|
|
31
|
+
|
|
32
|
+
**Full Changelog**: https://github.com/Sentia/zai-payment/compare/v2.3.2...v2.4.0
|
|
33
|
+
|
|
34
|
+
## [2.3.2] - 2025-10-29
|
|
35
|
+
### Fixed
|
|
36
|
+
- **Timeout Error Handling**: Improved handling of timeout errors to prevent crashes
|
|
37
|
+
- Added explicit rescue for `Net::ReadTimeout` and `Net::OpenTimeout` errors
|
|
38
|
+
- Previously, these errors could sometimes bypass Faraday's error handling and crash the application
|
|
39
|
+
- Now properly converts all timeout errors to `Errors::TimeoutError` with descriptive messages
|
|
40
|
+
- Fixes issue: "Request timed out: Net::ReadTimeout with #<TCPSocket:(closed)>"
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- **Increased Default Timeouts**: Adjusted default timeout values for better reliability
|
|
44
|
+
- Default `timeout` increased from 10 to 30 seconds (general request timeout)
|
|
45
|
+
- Added separate `read_timeout` configuration (default: 30 seconds)
|
|
46
|
+
- `open_timeout` remains at 10 seconds (connection establishment)
|
|
47
|
+
- Users can still customize timeouts via configuration:
|
|
48
|
+
```ruby
|
|
49
|
+
ZaiPayment.configure do |config|
|
|
50
|
+
config.timeout = 60 # Custom general timeout
|
|
51
|
+
config.read_timeout = 60 # Custom read timeout
|
|
52
|
+
config.open_timeout = 15 # Custom open timeout
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
2
56
|
## [2.3.1] - 2025-10-28
|
|
3
57
|
### Fixed
|
|
4
58
|
- **Token Refresh Bug**: Fixed authentication token not being refreshed after expiration
|
data/docs/items.md
CHANGED
|
@@ -313,6 +313,581 @@ if response.success?
|
|
|
313
313
|
end
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
+
### Make Payment
|
|
317
|
+
|
|
318
|
+
Process a payment for an item using a card account.
|
|
319
|
+
|
|
320
|
+
#### Required Parameters
|
|
321
|
+
|
|
322
|
+
- `account_id` - The card account ID to charge (Required)
|
|
323
|
+
|
|
324
|
+
#### Optional Parameters
|
|
325
|
+
|
|
326
|
+
- `device_id` - Device identifier for fraud detection
|
|
327
|
+
- `ip_address` - IP address of the customer
|
|
328
|
+
- `cvv` - Card CVV for additional verification
|
|
329
|
+
- `merchant_phone` - Merchant contact phone number
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
# Make a payment with required parameters
|
|
333
|
+
response = ZaiPayment.items.make_payment(
|
|
334
|
+
"item-123",
|
|
335
|
+
account_id: "card_account-456" # Required
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
if response.success?
|
|
339
|
+
item = response.data
|
|
340
|
+
puts "Payment initiated for item: #{item['id']}"
|
|
341
|
+
puts "State: #{item['state']}"
|
|
342
|
+
puts "Payment State: #{item['payment_state']}"
|
|
343
|
+
end
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### With Optional Parameters
|
|
347
|
+
|
|
348
|
+
For enhanced fraud protection, include device and IP address information:
|
|
349
|
+
|
|
350
|
+
```ruby
|
|
351
|
+
response = ZaiPayment.items.make_payment(
|
|
352
|
+
"item-123",
|
|
353
|
+
account_id: "card_account-456", # Required
|
|
354
|
+
device_id: "device_789",
|
|
355
|
+
ip_address: "192.168.1.1",
|
|
356
|
+
cvv: "123",
|
|
357
|
+
merchant_phone: "+61412345678"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
if response.success?
|
|
361
|
+
item = response.data
|
|
362
|
+
puts "Payment initiated successfully"
|
|
363
|
+
puts "Item State: #{item['state']}"
|
|
364
|
+
puts "Payment State: #{item['payment_state']}"
|
|
365
|
+
end
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Make Async Payment
|
|
369
|
+
|
|
370
|
+
Initiate a card payment with 3D Secure 2.0 (3DS2) authentication support. This endpoint initiates the payment process and returns a `payment_token` that is required for 3DS2 component initialisation on the client side.
|
|
371
|
+
|
|
372
|
+
This method is specifically designed for payments that require 3D Secure verification, providing enhanced security for card transactions.
|
|
373
|
+
|
|
374
|
+
#### Required Parameters
|
|
375
|
+
|
|
376
|
+
- `account_id` - The card account ID to charge (Required). Note: This is the account ID, not the user ID.
|
|
377
|
+
|
|
378
|
+
#### Optional Parameters
|
|
379
|
+
|
|
380
|
+
- `request_three_d_secure` - Customise the 3DS preference for this payment. Allowed values:
|
|
381
|
+
- `'automatic'` (default) - 3DS preference is determined automatically by the system
|
|
382
|
+
- `'challenge'` - Request a 3DS challenge is presented to the user
|
|
383
|
+
- `'any'` - Request a 3DS challenge regardless of the challenge flow
|
|
384
|
+
|
|
385
|
+
#### Response
|
|
386
|
+
|
|
387
|
+
The response includes:
|
|
388
|
+
- `payment_id` - Unique identifier for the payment
|
|
389
|
+
- `payment_token` - Token required for 3DS2 component initialisation
|
|
390
|
+
- `account_id` - The account ID used for the payment
|
|
391
|
+
- `items` - Complete item details including state, amount, and related information
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
# Make an async payment with required parameters
|
|
395
|
+
response = ZaiPayment.items.make_payment_async(
|
|
396
|
+
"item-123",
|
|
397
|
+
account_id: "card_account-456" # Required
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
if response.success?
|
|
401
|
+
payment_id = response.data['payment_id']
|
|
402
|
+
payment_token = response.data['payment_token']
|
|
403
|
+
item = response.data['items']
|
|
404
|
+
|
|
405
|
+
puts "Payment initiated: #{payment_id}"
|
|
406
|
+
puts "Payment token for 3DS2: #{payment_token}"
|
|
407
|
+
puts "Item state: #{item['state']}"
|
|
408
|
+
puts "Amount: $#{item['amount'] / 100.0}"
|
|
409
|
+
end
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### With 3DS Challenge
|
|
413
|
+
|
|
414
|
+
To explicitly request a 3D Secure challenge:
|
|
415
|
+
|
|
416
|
+
```ruby
|
|
417
|
+
response = ZaiPayment.items.make_payment_async(
|
|
418
|
+
"item-123",
|
|
419
|
+
account_id: "card_account-456",
|
|
420
|
+
request_three_d_secure: "challenge"
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
if response.success?
|
|
424
|
+
payment_token = response.data['payment_token']
|
|
425
|
+
|
|
426
|
+
# Use the payment_token to initialise the 3DS2 web component
|
|
427
|
+
puts "Payment token: #{payment_token}"
|
|
428
|
+
puts "Initialise 3DS2 component on client side with this token"
|
|
429
|
+
end
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Automatic 3DS Determination
|
|
433
|
+
|
|
434
|
+
When using the default 'automatic' mode, the system determines whether 3DS is required:
|
|
435
|
+
|
|
436
|
+
```ruby
|
|
437
|
+
response = ZaiPayment.items.make_payment_async(
|
|
438
|
+
"item-123",
|
|
439
|
+
account_id: "card_account-456",
|
|
440
|
+
request_three_d_secure: "automatic"
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
if response.success?
|
|
444
|
+
item = response.data['items']
|
|
445
|
+
payment_token = response.data['payment_token']
|
|
446
|
+
|
|
447
|
+
puts "3DS handled automatically"
|
|
448
|
+
puts "Item state: #{item['state']}"
|
|
449
|
+
|
|
450
|
+
# The payment_token will be provided if 3DS is required
|
|
451
|
+
if payment_token
|
|
452
|
+
puts "3DS verification required - use token: #{payment_token}"
|
|
453
|
+
else
|
|
454
|
+
puts "3DS verification not required - payment processed"
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### Important Notes
|
|
460
|
+
|
|
461
|
+
- The `payment_token` returned must be used to initialise the 3DS2 web component on the client side
|
|
462
|
+
- After 3DS authentication is complete, the payment will automatically be processed
|
|
463
|
+
- If 3DS verification fails or is abandoned, the payment will be cancelled
|
|
464
|
+
- This endpoint supports 3D Secure 2.0, providing a better user experience than legacy 3DS 1.0
|
|
465
|
+
- Always handle the response appropriately and provide clear instructions to users if 3DS verification is required
|
|
466
|
+
|
|
467
|
+
**See also:** For more information on implementing 3D Secure on the client side, refer to the [Zai 3DS Integration Guide](https://developer.hellozai.com).
|
|
468
|
+
|
|
469
|
+
### Authorize Payment
|
|
470
|
+
|
|
471
|
+
Authorize a payment without immediately capturing funds. This is useful for pre-authorization scenarios where you want to verify the card and hold funds before completing the transaction.
|
|
472
|
+
|
|
473
|
+
#### Required Parameters
|
|
474
|
+
|
|
475
|
+
- `account_id` - The card account ID to authorize (Required)
|
|
476
|
+
|
|
477
|
+
#### Optional Parameters
|
|
478
|
+
|
|
479
|
+
- `cvv` - Card CVV for additional verification
|
|
480
|
+
- `merchant_phone` - Merchant contact phone number
|
|
481
|
+
|
|
482
|
+
```ruby
|
|
483
|
+
# Authorize a payment
|
|
484
|
+
response = ZaiPayment.items.authorize_payment(
|
|
485
|
+
"item-123",
|
|
486
|
+
account_id: "card_account-456" # Required
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
if response.success?
|
|
490
|
+
item = response.data
|
|
491
|
+
puts "Payment authorized for item: #{item['id']}"
|
|
492
|
+
puts "State: #{item['state']}"
|
|
493
|
+
puts "Payment State: #{item['payment_state']}"
|
|
494
|
+
end
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
#### Authorize with Optional Parameters
|
|
498
|
+
|
|
499
|
+
```ruby
|
|
500
|
+
response = ZaiPayment.items.authorize_payment(
|
|
501
|
+
"item-123",
|
|
502
|
+
account_id: "card_account-456", # Required
|
|
503
|
+
cvv: "123",
|
|
504
|
+
merchant_phone: "+61412345678"
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
if response.success?
|
|
508
|
+
item = response.data
|
|
509
|
+
puts "Payment authorized with CVV verification"
|
|
510
|
+
puts "Item State: #{item['state']}"
|
|
511
|
+
puts "Payment State: #{item['payment_state']}"
|
|
512
|
+
end
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Note:** After authorizing a payment, you'll need to capture it separately to complete the transaction. Authorized funds are typically held for 7 days before being automatically released.
|
|
516
|
+
|
|
517
|
+
### Capture Payment
|
|
518
|
+
|
|
519
|
+
Capture a previously authorized payment to complete the transaction and transfer funds. This is the second step in a two-step payment process (authorize → capture).
|
|
520
|
+
|
|
521
|
+
#### Optional Parameters
|
|
522
|
+
|
|
523
|
+
- `amount` - Amount to capture in cents (Optional). If not provided, captures the full authorized amount.
|
|
524
|
+
|
|
525
|
+
```ruby
|
|
526
|
+
# Capture the full authorized amount
|
|
527
|
+
response = ZaiPayment.items.capture_payment("item-123")
|
|
528
|
+
|
|
529
|
+
if response.success?
|
|
530
|
+
item = response.data
|
|
531
|
+
puts "Payment captured for item: #{item['id']}"
|
|
532
|
+
puts "State: #{item['state']}"
|
|
533
|
+
puts "Payment State: #{item['payment_state']}"
|
|
534
|
+
end
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### Capture with Specific Amount (Partial Capture)
|
|
538
|
+
|
|
539
|
+
You can capture a partial amount of the authorized funds:
|
|
540
|
+
|
|
541
|
+
```ruby
|
|
542
|
+
# Capture only $50 of a $100 authorized payment
|
|
543
|
+
response = ZaiPayment.items.capture_payment(
|
|
544
|
+
"item-123",
|
|
545
|
+
amount: 5000 # $50.00 in cents
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
if response.success?
|
|
549
|
+
item = response.data
|
|
550
|
+
puts "Partial payment captured: $#{item['amount'] / 100.0}"
|
|
551
|
+
puts "Item State: #{item['state']}"
|
|
552
|
+
puts "Payment State: #{item['payment_state']}"
|
|
553
|
+
end
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### Capture with Status Check
|
|
557
|
+
|
|
558
|
+
Check authorization status before attempting to capture:
|
|
559
|
+
|
|
560
|
+
```ruby
|
|
561
|
+
# Check current status
|
|
562
|
+
status_response = ZaiPayment.items.show_status("item-123")
|
|
563
|
+
|
|
564
|
+
if status_response.success?
|
|
565
|
+
payment_state = status_response.data['payment_state']
|
|
566
|
+
|
|
567
|
+
# Only capture if payment is authorized
|
|
568
|
+
if payment_state == 'authorized' || payment_state == 'payment_authorized'
|
|
569
|
+
capture_response = ZaiPayment.items.capture_payment("item-123")
|
|
570
|
+
|
|
571
|
+
if capture_response.success?
|
|
572
|
+
puts "✓ Payment captured successfully"
|
|
573
|
+
else
|
|
574
|
+
puts "✗ Capture failed: #{capture_response.error_message}"
|
|
575
|
+
end
|
|
576
|
+
else
|
|
577
|
+
puts "Payment cannot be captured - current state: #{payment_state}"
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### Authorization and Capture Workflow
|
|
583
|
+
|
|
584
|
+
Complete example of the authorize → capture workflow:
|
|
585
|
+
|
|
586
|
+
```ruby
|
|
587
|
+
# Step 1: Authorize the payment
|
|
588
|
+
auth_response = ZaiPayment.items.authorize_payment(
|
|
589
|
+
"item-123",
|
|
590
|
+
account_id: "card_account-456",
|
|
591
|
+
cvv: "123"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
if auth_response.success?
|
|
595
|
+
puts "✓ Payment authorized"
|
|
596
|
+
|
|
597
|
+
# Step 2: Verify authorization
|
|
598
|
+
status = ZaiPayment.items.show_status("item-123")
|
|
599
|
+
puts "Payment State: #{status.data['payment_state']}"
|
|
600
|
+
|
|
601
|
+
# Step 3: Capture the payment (can be done immediately or later)
|
|
602
|
+
capture_response = ZaiPayment.items.capture_payment("item-123")
|
|
603
|
+
|
|
604
|
+
if capture_response.success?
|
|
605
|
+
puts "✓ Payment captured and completed"
|
|
606
|
+
puts "Final State: #{capture_response.data['payment_state']}"
|
|
607
|
+
else
|
|
608
|
+
puts "✗ Capture failed: #{capture_response.error_message}"
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
#### Capture States and Conditions
|
|
614
|
+
|
|
615
|
+
Payments can be captured when in these states:
|
|
616
|
+
|
|
617
|
+
| State | Can Capture? | Description |
|
|
618
|
+
|-------|-------------|-------------|
|
|
619
|
+
| `authorized` | ✓ Yes | Payment authorized and ready to capture |
|
|
620
|
+
| `payment_authorized` | ✓ Yes | Payment authorized and ready to capture |
|
|
621
|
+
| `pending` | ✗ No | Payment not authorized yet |
|
|
622
|
+
| `payment_pending` | ✗ No | Payment processing, not authorized |
|
|
623
|
+
| `completed` | ✗ No | Already captured |
|
|
624
|
+
| `payment_deposited` | ✗ No | Already captured and deposited |
|
|
625
|
+
| `cancelled` | ✗ No | Authorization cancelled |
|
|
626
|
+
| `refunded` | ✗ No | Payment refunded |
|
|
627
|
+
|
|
628
|
+
**Important Notes:**
|
|
629
|
+
- Authorizations typically expire after 7 days if not captured
|
|
630
|
+
- Partial captures are supported (capturing less than the authorized amount)
|
|
631
|
+
- Once captured, the payment cannot be un-captured (but can be refunded)
|
|
632
|
+
- Some card networks may support multiple partial captures, check with Zai support
|
|
633
|
+
- The remaining authorized amount (if any) is automatically released after capture
|
|
634
|
+
|
|
635
|
+
### Void Payment
|
|
636
|
+
|
|
637
|
+
Void a previously authorized payment to immediately release the held funds without capturing them. This is typically used when you need to cancel an authorized payment before capturing it.
|
|
638
|
+
|
|
639
|
+
```ruby
|
|
640
|
+
# Void an authorized payment
|
|
641
|
+
response = ZaiPayment.items.void_payment("item-123")
|
|
642
|
+
|
|
643
|
+
if response.success?
|
|
644
|
+
item = response.data
|
|
645
|
+
puts "Payment voided successfully"
|
|
646
|
+
puts "Item ID: #{item['id']}"
|
|
647
|
+
puts "State: #{item['state']}"
|
|
648
|
+
puts "Payment State: #{item['payment_state']}"
|
|
649
|
+
else
|
|
650
|
+
puts "Void failed: #{response.error_message}"
|
|
651
|
+
end
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
#### Void with Status Check
|
|
655
|
+
|
|
656
|
+
Check authorization status before attempting to void:
|
|
657
|
+
|
|
658
|
+
```ruby
|
|
659
|
+
# Check current status
|
|
660
|
+
status_response = ZaiPayment.items.show_status("item-123")
|
|
661
|
+
|
|
662
|
+
if status_response.success?
|
|
663
|
+
payment_state = status_response.data['payment_state']
|
|
664
|
+
|
|
665
|
+
# Only void if payment is authorized
|
|
666
|
+
if payment_state == 'authorized' || payment_state == 'payment_authorized'
|
|
667
|
+
void_response = ZaiPayment.items.void_payment("item-123")
|
|
668
|
+
|
|
669
|
+
if void_response.success?
|
|
670
|
+
puts "✓ Payment voided successfully"
|
|
671
|
+
else
|
|
672
|
+
puts "✗ Void failed: #{void_response.error_message}"
|
|
673
|
+
end
|
|
674
|
+
else
|
|
675
|
+
puts "Payment cannot be voided - current state: #{payment_state}"
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### Authorization Management Workflow
|
|
681
|
+
|
|
682
|
+
Complete example showing authorize → void workflow:
|
|
683
|
+
|
|
684
|
+
```ruby
|
|
685
|
+
# Step 1: Authorize the payment
|
|
686
|
+
auth_response = ZaiPayment.items.authorize_payment(
|
|
687
|
+
"item-123",
|
|
688
|
+
account_id: "card_account-456",
|
|
689
|
+
cvv: "123"
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
if auth_response.success?
|
|
693
|
+
puts "✓ Payment authorized"
|
|
694
|
+
|
|
695
|
+
# Step 2: Verify authorization
|
|
696
|
+
status = ZaiPayment.items.show_status("item-123")
|
|
697
|
+
puts "Payment State: #{status.data['payment_state']}"
|
|
698
|
+
|
|
699
|
+
# Step 3: Void the payment (cancel the authorization)
|
|
700
|
+
void_response = ZaiPayment.items.void_payment("item-123")
|
|
701
|
+
|
|
702
|
+
if void_response.success?
|
|
703
|
+
puts "✓ Payment voided - funds released"
|
|
704
|
+
puts "Final State: #{void_response.data['payment_state']}"
|
|
705
|
+
else
|
|
706
|
+
puts "✗ Void failed: #{void_response.error_message}"
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
#### Void vs Cancel vs Refund
|
|
712
|
+
|
|
713
|
+
Understanding when to use each operation:
|
|
714
|
+
|
|
715
|
+
| Operation | Use When | Payment State | Funds Status |
|
|
716
|
+
|-----------|----------|---------------|--------------|
|
|
717
|
+
| **Void** | Payment is authorized but not captured | `authorized`, `payment_authorized` | Funds held but not transferred |
|
|
718
|
+
| **Cancel** | Item created but payment not yet authorized | `pending`, `payment_pending` | No funds involved |
|
|
719
|
+
| **Refund** | Payment captured and completed | `completed`, `payment_deposited` | Funds already transferred |
|
|
720
|
+
|
|
721
|
+
**Key Differences:**
|
|
722
|
+
- **Void**: Releases authorized (held) funds immediately without any transfer
|
|
723
|
+
- **Cancel**: Cancels the entire transaction before any payment authorization
|
|
724
|
+
- **Refund**: Returns funds after they've been captured and transferred
|
|
725
|
+
|
|
726
|
+
#### Void States and Conditions
|
|
727
|
+
|
|
728
|
+
Payments can be voided when in these states:
|
|
729
|
+
|
|
730
|
+
| State | Can Void? | Description |
|
|
731
|
+
|-------|-----------|-------------|
|
|
732
|
+
| `authorized` | ✓ Yes | Payment authorized and ready to void |
|
|
733
|
+
| `payment_authorized` | ✓ Yes | Payment authorized and ready to void |
|
|
734
|
+
| `pending` | ✗ No | Payment not authorized yet, use cancel |
|
|
735
|
+
| `payment_pending` | ✗ No | Payment processing, use cancel |
|
|
736
|
+
| `completed` | ✗ No | Already captured, use refund |
|
|
737
|
+
| `payment_deposited` | ✗ No | Already captured, use refund |
|
|
738
|
+
| `cancelled` | ✗ No | Already cancelled |
|
|
739
|
+
| `refunded` | ✗ No | Already refunded |
|
|
740
|
+
| `voided` | ✗ No | Already voided |
|
|
741
|
+
|
|
742
|
+
**Important Notes:**
|
|
743
|
+
- Voiding a payment immediately releases the held funds to the cardholder
|
|
744
|
+
- Once voided, the authorization cannot be reversed or captured
|
|
745
|
+
- Void is instant - no settlement period required
|
|
746
|
+
- No fees are charged for voiding an authorization
|
|
747
|
+
- Voided authorizations do not appear on cardholder statements
|
|
748
|
+
|
|
749
|
+
### Cancel Item
|
|
750
|
+
|
|
751
|
+
Cancel a pending item/payment. This operation is typically used to cancel an item before payment has been processed or completed.
|
|
752
|
+
|
|
753
|
+
```ruby
|
|
754
|
+
response = ZaiPayment.items.cancel("item-123")
|
|
755
|
+
|
|
756
|
+
if response.success?
|
|
757
|
+
item = response.data
|
|
758
|
+
puts "Item cancelled successfully"
|
|
759
|
+
puts "Item ID: #{item['id']}"
|
|
760
|
+
puts "State: #{item['state']}"
|
|
761
|
+
puts "Payment State: #{item['payment_state']}"
|
|
762
|
+
else
|
|
763
|
+
puts "Cancel failed: #{response.error_message}"
|
|
764
|
+
end
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
#### Cancel with Status Check
|
|
768
|
+
|
|
769
|
+
Check item status before attempting to cancel:
|
|
770
|
+
|
|
771
|
+
```ruby
|
|
772
|
+
# Check current status
|
|
773
|
+
status_response = ZaiPayment.items.show_status("item-123")
|
|
774
|
+
|
|
775
|
+
if status_response.success?
|
|
776
|
+
current_state = status_response.data['state']
|
|
777
|
+
|
|
778
|
+
# Only cancel if in a cancellable state
|
|
779
|
+
if ['pending', 'payment_pending'].include?(current_state)
|
|
780
|
+
cancel_response = ZaiPayment.items.cancel("item-123")
|
|
781
|
+
|
|
782
|
+
if cancel_response.success?
|
|
783
|
+
puts "✓ Item cancelled successfully"
|
|
784
|
+
else
|
|
785
|
+
puts "✗ Cancel failed: #{cancel_response.error_message}"
|
|
786
|
+
end
|
|
787
|
+
else
|
|
788
|
+
puts "Item cannot be cancelled - current state: #{current_state}"
|
|
789
|
+
end
|
|
790
|
+
end
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
#### Cancel States and Conditions
|
|
794
|
+
|
|
795
|
+
Items can typically be cancelled when in these states:
|
|
796
|
+
|
|
797
|
+
| State | Can Cancel? | Description |
|
|
798
|
+
|-------|-------------|-------------|
|
|
799
|
+
| `pending` | ✓ Yes | Item created but no payment initiated |
|
|
800
|
+
| `payment_pending` | ✓ Yes | Payment initiated but not yet processed |
|
|
801
|
+
| `payment_processing` | Maybe | Depends on payment processor |
|
|
802
|
+
| `completed` | ✗ No | Payment completed, must refund instead |
|
|
803
|
+
| `payment_held` | Maybe | May require admin approval |
|
|
804
|
+
| `cancelled` | ✗ No | Already cancelled |
|
|
805
|
+
| `refunded` | ✗ No | Already refunded |
|
|
806
|
+
|
|
807
|
+
**Note:** If an item is already completed or funds have been disbursed, you cannot cancel it. In those cases, you may need to process a refund instead (contact Zai support for refund procedures).
|
|
808
|
+
|
|
809
|
+
### Refund Item
|
|
810
|
+
|
|
811
|
+
Process a refund for a completed payment. This operation returns funds to the buyer and is typically used for customer returns, disputes, or service issues.
|
|
812
|
+
|
|
813
|
+
```ruby
|
|
814
|
+
response = ZaiPayment.items.refund("item-123")
|
|
815
|
+
|
|
816
|
+
if response.success?
|
|
817
|
+
item = response.data
|
|
818
|
+
puts "Item refunded successfully"
|
|
819
|
+
puts "Item ID: #{item['id']}"
|
|
820
|
+
puts "State: #{item['state']}"
|
|
821
|
+
puts "Payment State: #{item['payment_state']}"
|
|
822
|
+
else
|
|
823
|
+
puts "Refund failed: #{response.error_message}"
|
|
824
|
+
end
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
#### Refund with Optional Parameters
|
|
828
|
+
|
|
829
|
+
You can optionally specify a refund amount (for partial refunds), a refund message, and the account to refund to:
|
|
830
|
+
|
|
831
|
+
```ruby
|
|
832
|
+
response = ZaiPayment.items.refund(
|
|
833
|
+
"item-123",
|
|
834
|
+
refund_amount: 5000, # Partial refund of $50.00 (in cents)
|
|
835
|
+
refund_message: "Refund for damaged product",
|
|
836
|
+
account_id: "account_789" # Specific account to refund to
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
if response.success?
|
|
840
|
+
item = response.data
|
|
841
|
+
puts "✓ Partial refund processed: $#{item['refund_amount'] / 100.0}"
|
|
842
|
+
puts " State: #{item['state']}"
|
|
843
|
+
puts " Payment State: #{item['payment_state']}"
|
|
844
|
+
end
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
#### Refund with Status Check
|
|
848
|
+
|
|
849
|
+
Check item status before attempting to refund:
|
|
850
|
+
|
|
851
|
+
```ruby
|
|
852
|
+
# Check current status
|
|
853
|
+
status_response = ZaiPayment.items.show_status("item-123")
|
|
854
|
+
|
|
855
|
+
if status_response.success?
|
|
856
|
+
current_state = status_response.data['state']
|
|
857
|
+
payment_state = status_response.data['payment_state']
|
|
858
|
+
|
|
859
|
+
# Only refund if in a refundable state
|
|
860
|
+
if ['completed', 'payment_deposited'].include?(payment_state)
|
|
861
|
+
refund_response = ZaiPayment.items.refund("item-123")
|
|
862
|
+
|
|
863
|
+
if refund_response.success?
|
|
864
|
+
puts "✓ Item refunded successfully"
|
|
865
|
+
else
|
|
866
|
+
puts "✗ Refund failed: #{refund_response.error_message}"
|
|
867
|
+
end
|
|
868
|
+
else
|
|
869
|
+
puts "Item cannot be refunded - payment state: #{payment_state}"
|
|
870
|
+
end
|
|
871
|
+
end
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
#### Refund States and Conditions
|
|
875
|
+
|
|
876
|
+
Items can typically be refunded when in these states:
|
|
877
|
+
|
|
878
|
+
| State | Can Refund? | Description |
|
|
879
|
+
|-------|-------------|-------------|
|
|
880
|
+
| `pending` | ✗ No | Item not yet paid, cancel instead |
|
|
881
|
+
| `payment_pending` | ✗ No | Payment not completed, cancel instead |
|
|
882
|
+
| `completed` | ✓ Yes | Payment completed successfully |
|
|
883
|
+
| `payment_deposited` | ✓ Yes | Payment received and deposited |
|
|
884
|
+
| `work_completed` | ✓ Yes | Work completed, funds can be refunded |
|
|
885
|
+
| `cancelled` | ✗ No | Already cancelled |
|
|
886
|
+
| `refunded` | ✗ No | Already refunded |
|
|
887
|
+
| `payment_held` | Maybe | May require admin approval |
|
|
888
|
+
|
|
889
|
+
**Note:** Full refunds return the entire item amount. Partial refunds return a specified amount less than the total. Multiple partial refunds may be possible depending on your Zai configuration.
|
|
890
|
+
|
|
316
891
|
## Field Reference
|
|
317
892
|
|
|
318
893
|
### Item Fields
|