The PayId resource provides methods for registering and managing Zai PayIDs for virtual accounts.
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.
PayIDs are particularly useful for:
- Simplifying payment collection with memorable identifiers
- Enabling fast payments through the New Payments Platform
- Providing customers with an alternative to BSB and account numbers
- Enhancing user experience with familiar email-based identifiers
- Email-based PayIDs: Register email addresses as payment identifiers
- Linked to Virtual Accounts: Each PayID is associated with a virtual account
- Status Tracking: Monitor PayID status (pending_activation, active, etc.)
- Secure Registration: Validated registration process with proper error handling
# Using a new instance
pay_ids = ZaiPayment::Resources::PayId.new
# Or use with custom client
client = ZaiPayment::Client.new(base_endpoint: :va_base)
pay_ids = ZaiPayment::Resources::PayId.new(client: client)Register a PayID for a given Virtual Account. This creates a PayID that customers can use to send payments to the virtual account.
virtual_account_id(required) - The virtual account IDpay_id(required) - The PayID being registered (max 256 characters)type(required) - The type of PayID (currently only 'EMAIL' is supported)details(required) - Hash containing additional details:pay_id_name(optional) - Name to identify the entity (1-140 characters)owner_legal_name(optional) - Full legal account name (1-140 characters)
# Register an EMAIL PayID
pay_ids = ZaiPayment::Resources::PayId.new
response = pay_ids.create(
'46deb476-c1a6-41eb-8eb7-26a695bbe5bc',
pay_id: 'jsmith@mydomain.com',
type: 'EMAIL',
details: {
pay_id_name: 'J Smith',
owner_legal_name: 'Mr John Smith'
}
)
# Access PayID details
if response.success?
pay_id = response.data
puts "PayID: #{pay_id['pay_id']}"
puts "Type: #{pay_id['type']}"
puts "Status: #{pay_id['status']}"
puts "PayID Name: #{pay_id['details']['pay_id_name']}"
puts "Owner Legal Name: #{pay_id['details']['owner_legal_name']}"
end{
"pay_ids" => {
"id" => "46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
"pay_id" => "jsmith@mydomain.com",
"type" => "EMAIL",
"status" => "pending_activation",
"created_at" => "2020-04-27T20:28:22.378Z",
"updated_at" => "2020-04-27T20:28:22.378Z",
"details" => {
"pay_id_name" => "J Smith",
"owner_legal_name" => "Mr John Smith"
},
"links" => {
"self" => "/pay_ids/46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
"virtual_accounts" => "/virtual_accounts/46deb476-c1a6-41eb-8eb7-26a695bbe5bc"
}
}
}Response Fields:
| Field | Type | Description |
|---|---|---|
id |
String | Unique identifier for the PayID |
pay_id |
String | The registered PayID (email address) |
type |
String | Type of PayID (e.g., "EMAIL") |
status |
String | PayID status (pending_activation, active, etc.) |
created_at |
String | ISO 8601 timestamp of creation |
updated_at |
String | ISO 8601 timestamp of last update |
details |
Hash | Additional PayID details |
details.pay_id_name |
String | Name to identify the entity |
details.owner_legal_name |
String | Full legal account name |
links |
Hash | Related resource links |
links.self |
String | URL to the PayID resource |
links.virtual_accounts |
String | URL to the associated virtual account |
Use Cases:
- Register PayIDs for easy customer payments
- Enable NPP fast payments
- Provide user-friendly payment identifiers
- Link email addresses to virtual accounts for payment collection
- Set up payment collection for businesses and individuals
Show details of a specific PayID using the given PayID ID.
pay_id_id(required) - The PayID ID
# Get specific PayID details
pay_ids = ZaiPayment::Resources::PayId.new
response = pay_ids.show('46deb476-c1a6-41eb-8eb7-26a695bbe5bc')
# Access PayID details
if response.success?
pay_id = response.data
puts "PayID: #{pay_id['pay_id']}"
puts "Type: #{pay_id['type']}"
puts "Status: #{pay_id['status']}"
puts "PayID Name: #{pay_id['details']['pay_id_name']}"
puts "Owner Legal Name: #{pay_id['details']['owner_legal_name']}"
end{
"pay_ids" => {
"id" => "46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
"pay_id" => "jsmith@mydomain.com",
"type" => "EMAIL",
"status" => "active",
"created_at" => "2020-04-27T20:28:22.378Z",
"updated_at" => "2020-04-27T20:28:22.378Z",
"details" => {
"pay_id_name" => "J Smith",
"owner_legal_name" => "Mr John Smith"
},
"links" => {
"self" => "/pay_ids/46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
"virtual_accounts" => "/virtual_accounts/46deb476-c1a6-41eb-8eb7-26a695bbe5bc"
}
}
}Response Fields:
The response contains a single PayID object with the same fields as described in the Register PayID section.
Use Cases:
- Verify PayID details before using
- Check PayID status
- Retrieve PayID information for display to users
- Audit PayID configurations
- Validate PayID information
- Monitor PayID updates
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.
Important: Once a PayID is deregistered, it cannot be re-registered to the same virtual account.
pay_id_id(required) - The PayID IDstatus(required) - The new status (must be 'deregistered')
# Deregister a PayID
pay_ids = ZaiPayment::Resources::PayId.new
response = pay_ids.update_status(
'46deb476-c1a6-41eb-8eb7-26a695bbe5bc',
'deregistered'
)
# Check the response
if response.success?
puts "PayID deregistration initiated"
puts "ID: #{response.data['id']}"
puts "Message: #{response.data['message']}"
puts "Link: #{response.data['links']['self']}"
end{
"id" => "46deb476-c1a6-41eb-8eb7-26a695bbe5bc",
"message" => "PayID deregistration has been accepted for processing",
"links" => {
"self" => "/pay_ids/46deb476-c1a6-41eb-8eb7-26a695bbe5bc"
}
}Response Fields:
| Field | Type | Description |
|---|---|---|
id |
String | The PayID ID |
message |
String | Confirmation message about the status update |
links |
Hash | Related resource links |
links.self |
String | URL to the PayID resource |
Use Cases:
- Deregister PayIDs when no longer needed
- Remove PayID from a virtual account
- Clean up unused payment identifiers
- Comply with customer requests to remove their email from the system
- Deactivate PayIDs as part of account closure
Important Notes:
- This operation returns 202 Accepted because it's processed asynchronously
- The actual status change may take a few moments to complete
- Only 'deregistered' is a valid status value; other values will raise a ValidationError
- Deregistered PayIDs cannot be re-registered
- Required: Yes
- Type: String (UUID)
- Description: The ID of the virtual account that this PayID will be linked to. The virtual account must exist before registering a PayID.
- Required: Yes
- Type: String
- Max Length: 256 characters
- Description: The PayID being registered. For EMAIL type, this should be a valid email address.
- Required: Yes
- Type: String (Enum)
- Allowed Values: EMAIL
- Description: The type of PayID being registered. Currently, only EMAIL type is supported.
- Required: Yes
- Type: Hash
- Description: Additional details about the PayID registration.
- Required: No
- Type: String
- Length: 1-140 characters (when provided)
- 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.
- Required: No
- Type: String
- Length: 1-140 characters (when provided)
- Description: The full legal account name. This is displayed to customers during payment confirmation.
The PayId methods can raise the following errors:
Raised when input parameters fail validation:
begin
response = pay_ids.create(
'', # Empty virtual_account_id
pay_id: 'test@example.com',
type: 'EMAIL',
details: {}
)
rescue ZaiPayment::Errors::ValidationError => e
puts "Validation failed: #{e.message}"
# Output: "virtual_account_id is required and cannot be blank"
endCommon validation errors:
virtual_account_id is required and cannot be blankpay_id is required and cannot be blankpay_id must be 256 characters or lesstype is required and cannot be blanktype must be one of: EMAILdetails is required and must be a hashpay_id_name must be between 1 and 140 charactersowner_legal_name must be between 1 and 140 characters
Raised when the virtual account doesn't exist:
begin
response = pay_ids.create(
'invalid-virtual-account-id',
pay_id: 'test@example.com',
type: 'EMAIL',
details: {}
)
rescue ZaiPayment::Errors::NotFoundError => e
puts "Not found: #{e.message}"
endRaised when authentication fails:
begin
response = pay_ids.create(
virtual_account_id,
pay_id: 'test@example.com',
type: 'EMAIL',
details: {}
)
rescue ZaiPayment::Errors::UnauthorizedError => e
puts "Authentication failed: #{e.message}"
# Check your API credentials
endRaised when the request is malformed or contains invalid data (e.g., PayID already registered):
begin
response = pay_ids.create(
virtual_account_id,
pay_id: 'test@example.com',
type: 'EMAIL',
details: {}
)
rescue ZaiPayment::Errors::BadRequestError => e
puts "Bad request: #{e.message}"
# May indicate PayID is already registered
endHere's a complete workflow showing how to create a virtual account and register a PayID with proper error handling:
require 'zai_payment'
# Configure ZaiPayment
ZaiPayment.configure do |config|
config.environment = :prelive
config.client_id = ENV['ZAI_CLIENT_ID']
config.client_secret = ENV['ZAI_CLIENT_SECRET']
config.scope = ENV['ZAI_SCOPE']
end
# Initialize resources
virtual_accounts = ZaiPayment::Resources::VirtualAccount.new
pay_ids = ZaiPayment::Resources::PayId.new
wallet_account_id = 'ae07556e-22ef-11eb-adc1-0242ac120002'
begin
# Step 1: Create a Virtual Account
puts "Creating virtual account..."
va_response = virtual_accounts.create(
wallet_account_id,
account_name: 'Customer Payment Account',
aka_names: ['Customer Account']
)
if va_response.success?
virtual_account = va_response.data
virtual_account_id = virtual_account['id']
puts "✓ Virtual Account Created"
puts " ID: #{virtual_account_id}"
puts " BSB: #{virtual_account['routing_number']}"
puts " Account: #{virtual_account['account_number']}"
puts " Status: #{virtual_account['status']}"
# Step 2: Register PayID
puts "\nRegistering PayID..."
payid_response = pay_ids.create(
virtual_account_id,
pay_id: 'customer@mybusiness.com',
type: 'EMAIL',
details: {
pay_id_name: 'My Business',
owner_legal_name: 'My Business Pty Ltd'
}
)
if payid_response.success?
pay_id = payid_response.data
puts "✓ PayID Registered Successfully!"
puts "─" * 60
puts "PayID Details:"
puts " ID: #{pay_id['id']}"
puts " PayID: #{pay_id['pay_id']}"
puts " Type: #{pay_id['type']}"
puts " Status: #{pay_id['status']}"
puts ""
puts "Payment Information:"
puts " PayID Name: #{pay_id['details']['pay_id_name']}"
puts " Owner Legal Name: #{pay_id['details']['owner_legal_name']}"
puts ""
puts "Customers can now send payments using:"
puts " PayID: #{pay_id['pay_id']}"
puts " OR"
puts " BSB: #{virtual_account['routing_number']}"
puts " Account: #{virtual_account['account_number']}"
puts "─" * 60
# Store the details in your database for future reference
# YourDatabase.store_pay_id(pay_id)
else
puts "Failed to register PayID"
end
else
puts "Failed to create virtual account"
end
rescue ZaiPayment::Errors::ValidationError => e
puts "Validation Error: #{e.message}"
puts "Please check your input parameters"
rescue ZaiPayment::Errors::NotFoundError => e
puts "Resource Not Found: #{e.message}"
puts "Please verify the wallet account or virtual account exists"
rescue ZaiPayment::Errors::UnauthorizedError => e
puts "Authentication Failed: #{e.message}"
puts "Please check your API credentials"
rescue ZaiPayment::Errors::BadRequestError => e
puts "Bad Request: #{e.message}"
puts "The PayID may already be registered or request is invalid"
rescue ZaiPayment::Errors::ApiError => e
puts "API Error: #{e.message}"
endPayIDs use the va_base endpoint, which is automatically configured based on your environment:
ZaiPayment.configure do |config|
config.environment = :prelive
# Uses: https://sandbox.au-0000.api.assemblypay.com
endZaiPayment.configure do |config|
config.environment = :production
# Uses: https://secure.api.promisepay.com
endThe PayId resource automatically uses the correct endpoint based on your configuration.
Use descriptive names in the details to help customers confirm the recipient:
# Good
details: {
pay_id_name: 'Acme Corporation',
owner_legal_name: 'Acme Corporation Pty Ltd'
}
# Avoid
details: {
pay_id_name: 'Acc1',
owner_legal_name: 'A'
}Pre-validate the email format before registration:
def valid_email?(email)
email =~ /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
end
pay_id = 'customer@example.com'
if valid_email?(pay_id)
# Proceed with registration
else
puts "Invalid email format"
endAlways store the PayID details in your database:
response = pay_ids.create(virtual_account_id, pay_id: email, type: 'EMAIL', details: details)
if response.success?
pay_id = response.data
# Store in database
PayIdRecord.create!(
external_id: pay_id['id'],
virtual_account_id: virtual_account_id,
pay_id: pay_id['pay_id'],
pay_id_type: pay_id['type'],
pay_id_name: pay_id['details']['pay_id_name'],
owner_legal_name: pay_id['details']['owner_legal_name'],
status: pay_id['status']
)
endAlways implement proper error handling:
def register_pay_id_safely(virtual_account_id, pay_id_email, details)
pay_ids = ZaiPayment::Resources::PayId.new
begin
response = pay_ids.create(
virtual_account_id,
pay_id: pay_id_email,
type: 'EMAIL',
details: details
)
{ success: true, data: response.data }
rescue ZaiPayment::Errors::ValidationError => e
{ success: false, error: 'validation', message: e.message }
rescue ZaiPayment::Errors::NotFoundError => e
{ success: false, error: 'not_found', message: e.message }
rescue ZaiPayment::Errors::BadRequestError => e
{ success: false, error: 'bad_request', message: e.message }
rescue ZaiPayment::Errors::ApiError => e
{ success: false, error: 'api_error', message: e.message }
end
endCheck that the virtual account exists before registering a PayID:
virtual_accounts = ZaiPayment::Resources::VirtualAccount.new
begin
# Verify virtual account exists
va_response = virtual_accounts.show(virtual_account_id)
if va_response.success? && va_response.data['status'] == 'active'
# Proceed with PayID registration
pay_ids.create(virtual_account_id, pay_id: email, type: 'EMAIL', details: details)
else
puts "Virtual account is not active yet"
end
rescue ZaiPayment::Errors::NotFoundError
puts "Virtual account does not exist"
endAfter registration, monitor the PayID status:
pay_id = response.data
case pay_id['status']
when 'pending_activation'
puts "PayID registered, awaiting activation"
when 'active'
puts "PayID is active and ready to receive payments"
when 'inactive'
puts "PayID is inactive"
else
puts "Unknown status: #{pay_id['status']}"
endTreat PayID details as sensitive payment information:
# Don't log sensitive details in production
if Rails.env.production?
logger.info "PayID registered: #{pay_id['id']}"
else
logger.debug "PayID details: #{pay_id.inspect}"
end
# Use HTTPS for all communications
# Store securely in your database
# Limit access to authorized personnel onlyFor testing in prelive environment:
# spec/services/pay_id_service_spec.rb
require 'spec_helper'
RSpec.describe PayIdService do
let(:virtual_account_id) { 'test-virtual-account-id' }
describe '#register_pay_id' do
it 'registers a PayID successfully' do
VCR.use_cassette('pay_id_register') do
service = PayIdService.new
result = service.register_pay_id(
virtual_account_id,
pay_id: 'test@example.com',
details: {
pay_id_name: 'Test User',
owner_legal_name: 'Test User Full Name'
}
)
expect(result[:success]).to be true
expect(result[:pay_id]['id']).to be_present
expect(result[:pay_id]['pay_id']).to eq('test@example.com')
expect(result[:pay_id]['type']).to eq('EMAIL')
end
end
end
endSolution: Ensure you're passing a valid virtual account ID:
# Wrong
pay_ids.create('', pay_id: 'test@example.com', type: 'EMAIL', details: {})
# Correct
pay_ids.create('46deb476-c1a6-41eb-8eb7-26a695bbe5bc', pay_id: 'test@example.com', type: 'EMAIL', details: {})Solution: Verify the virtual account exists before registering a PayID:
# Check virtual account exists first
virtual_accounts = ZaiPayment::Resources::VirtualAccount.new
begin
va_response = virtual_accounts.show(virtual_account_id)
if va_response.success?
# Virtual account exists, proceed with PayID registration
pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'EMAIL', details: {})
end
rescue ZaiPayment::Errors::NotFoundError
puts "Virtual account does not exist"
endSolution: Ensure the PayID (email) is not too long:
pay_id_email = 'verylongemail@example.com'
if pay_id_email.length <= 256
pay_ids.create(virtual_account_id, pay_id: pay_id_email, type: 'EMAIL', details: {})
else
puts "PayID is too long"
endSolution: Ensure you're using a valid type:
# Wrong
pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'PHONE', details: {})
# Correct
pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'EMAIL', details: {})Solution: PayIDs must be unique. Check if the PayID is already registered:
begin
pay_ids.create(virtual_account_id, pay_id: 'test@example.com', type: 'EMAIL', details: {})
rescue ZaiPayment::Errors::BadRequestError => e
if e.message.include?('already registered')
puts "This PayID is already registered to another account"
else
puts "Bad request: #{e.message}"
end
end