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.
- checksums.yaml +4 -4
- data/IMPLEMENTATION_SUMMARY.md +183 -0
- data/RESPONSE_FORMAT_CORRECTION.md +75 -0
- data/badges/coverage.json +1 -1
- data/changelog.md +158 -0
- data/docs/batch_transactions.md +340 -0
- data/docs/direct_api_usage.md +489 -0
- data/docs/pay_ids.md +777 -0
- data/docs/virtual_accounts.md +916 -0
- data/docs/wallet_accounts.md +493 -0
- data/examples/batch_transactions.md +450 -0
- data/examples/pay_ids.md +871 -0
- data/examples/virtual_accounts.md +1530 -0
- data/examples/wallet_accounts.md +733 -0
- data/lib/zai_payment/resources/batch_transaction.rb +302 -0
- data/lib/zai_payment/resources/pay_id.rb +197 -0
- data/lib/zai_payment/resources/virtual_account.rb +212 -0
- data/lib/zai_payment/resources/wallet_account.rb +176 -0
- data/lib/zai_payment/response.rb +2 -2
- data/lib/zai_payment/version.rb +1 -1
- data/lib/zai_payment.rb +24 -0
- data/readme.md +109 -3
- metadata +16 -1
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
# Wallet Account Management Examples
|
|
2
|
+
|
|
3
|
+
This document provides practical examples for managing wallet accounts in Zai Payment.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Setup](#setup)
|
|
8
|
+
- [Show Wallet Account Example](#show-wallet-account-example)
|
|
9
|
+
- [Show Wallet Account User Example](#show-wallet-account-user-example)
|
|
10
|
+
- [Pay a Bill Example](#pay-a-bill-example)
|
|
11
|
+
- [Common Patterns](#common-patterns)
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require 'zai_payment'
|
|
17
|
+
|
|
18
|
+
# Configure ZaiPayment
|
|
19
|
+
ZaiPayment.configure do |config|
|
|
20
|
+
config.environment = :prelive # or :production
|
|
21
|
+
config.client_id = ENV['ZAI_CLIENT_ID']
|
|
22
|
+
config.client_secret = ENV['ZAI_CLIENT_SECRET']
|
|
23
|
+
config.scope = ENV['ZAI_SCOPE']
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Show Wallet Account Example
|
|
28
|
+
|
|
29
|
+
### Example 1: Get Wallet Account Details
|
|
30
|
+
|
|
31
|
+
Retrieve details of a specific wallet account.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
# Get wallet account details
|
|
35
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
36
|
+
|
|
37
|
+
response = wallet_accounts.show('5c1c6b10-4c56-0137-8cd7-0242ac110002')
|
|
38
|
+
|
|
39
|
+
if response.success?
|
|
40
|
+
wallet = response.data
|
|
41
|
+
puts "Wallet Account ID: #{wallet['id']}"
|
|
42
|
+
puts "Active: #{wallet['active']}"
|
|
43
|
+
puts "Balance: $#{wallet['balance'] / 100.0}"
|
|
44
|
+
puts "Currency: #{wallet['currency']}"
|
|
45
|
+
puts "Created At: #{wallet['created_at']}"
|
|
46
|
+
puts "Updated At: #{wallet['updated_at']}"
|
|
47
|
+
|
|
48
|
+
# Access links
|
|
49
|
+
links = wallet['links']
|
|
50
|
+
puts "\nLinks:"
|
|
51
|
+
puts " Self: #{links['self']}"
|
|
52
|
+
puts " Users: #{links['users']}"
|
|
53
|
+
puts " Transactions: #{links['transactions']}"
|
|
54
|
+
puts " BPay Details: #{links['bpay_details']}"
|
|
55
|
+
puts " NPP Details: #{links['npp_details']}"
|
|
56
|
+
else
|
|
57
|
+
puts "Failed to retrieve wallet account"
|
|
58
|
+
puts "Error: #{response.error}"
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Example 2: Check Balance Before Payment
|
|
63
|
+
|
|
64
|
+
Check wallet balance before initiating a payment.
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
68
|
+
|
|
69
|
+
begin
|
|
70
|
+
response = wallet_accounts.show('wallet_account_id_here')
|
|
71
|
+
|
|
72
|
+
if response.success?
|
|
73
|
+
wallet = response.data
|
|
74
|
+
balance = wallet['balance'] # in cents
|
|
75
|
+
|
|
76
|
+
# Check if account is active
|
|
77
|
+
if wallet['active']
|
|
78
|
+
puts "Wallet is active"
|
|
79
|
+
puts "Current balance: $#{balance / 100.0}"
|
|
80
|
+
|
|
81
|
+
# Check if sufficient funds for payment
|
|
82
|
+
payment_amount = 17300 # $173.00
|
|
83
|
+
|
|
84
|
+
if balance >= payment_amount
|
|
85
|
+
puts "Sufficient funds for payment of $#{payment_amount / 100.0}"
|
|
86
|
+
else
|
|
87
|
+
puts "Insufficient funds"
|
|
88
|
+
puts " Required: $#{payment_amount / 100.0}"
|
|
89
|
+
puts " Available: $#{balance / 100.0}"
|
|
90
|
+
puts " Shortfall: $#{(payment_amount - balance) / 100.0}"
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
puts "Wallet account is inactive"
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
puts "Failed to retrieve wallet account: #{response.error}"
|
|
97
|
+
end
|
|
98
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
99
|
+
puts "Wallet account not found: #{e.message}"
|
|
100
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
101
|
+
puts "Invalid wallet account ID: #{e.message}"
|
|
102
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
103
|
+
puts "API error occurred: #{e.message}"
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Example 3: Monitor Wallet Account Status
|
|
108
|
+
|
|
109
|
+
Check wallet account status and available resources.
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
113
|
+
|
|
114
|
+
# Step 1: Retrieve wallet account
|
|
115
|
+
response = wallet_accounts.show('wallet_account_id')
|
|
116
|
+
|
|
117
|
+
if response.success?
|
|
118
|
+
wallet = response.data
|
|
119
|
+
|
|
120
|
+
# Step 2: Display wallet status
|
|
121
|
+
puts "Wallet Account Status:"
|
|
122
|
+
puts " ID: #{wallet['id']}"
|
|
123
|
+
puts " Active: #{wallet['active']}"
|
|
124
|
+
puts " Balance: $#{wallet['balance'] / 100.0}"
|
|
125
|
+
puts " Currency: #{wallet['currency']}"
|
|
126
|
+
|
|
127
|
+
# Step 3: Check available resources
|
|
128
|
+
links = wallet['links']
|
|
129
|
+
|
|
130
|
+
puts "\nAvailable Resources:"
|
|
131
|
+
puts " ✓ Users" if links['users']
|
|
132
|
+
puts " ✓ Transactions" if links['transactions']
|
|
133
|
+
puts " ✓ Batch Transactions" if links['batch_transactions']
|
|
134
|
+
puts " ✓ BPay Details" if links['bpay_details']
|
|
135
|
+
puts " ✓ NPP Details" if links['npp_details']
|
|
136
|
+
puts " ✓ Virtual Accounts" if links['virtual_accounts']
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Show Wallet Account User Example
|
|
141
|
+
|
|
142
|
+
### Example 1: Get User Associated with Wallet Account
|
|
143
|
+
|
|
144
|
+
Retrieve user details for a wallet account.
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
# Get user associated with wallet account
|
|
148
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
149
|
+
|
|
150
|
+
response = wallet_accounts.show_user('5c1c6b10-4c56-0137-8cd7-0242ac110002')
|
|
151
|
+
|
|
152
|
+
if response.success?
|
|
153
|
+
user = response.data
|
|
154
|
+
puts "User ID: #{user['id']}"
|
|
155
|
+
puts "Full Name: #{user['full_name']}"
|
|
156
|
+
puts "Email: #{user['email']}"
|
|
157
|
+
puts "First Name: #{user['first_name']}"
|
|
158
|
+
puts "Last Name: #{user['last_name']}"
|
|
159
|
+
puts "Location: #{user['location']}"
|
|
160
|
+
puts "Verification State: #{user['verification_state']}"
|
|
161
|
+
puts "Held State: #{user['held_state']}"
|
|
162
|
+
puts "Roles: #{user['roles'].join(', ')}"
|
|
163
|
+
|
|
164
|
+
# Access links
|
|
165
|
+
links = user['links']
|
|
166
|
+
puts "\nLinks:"
|
|
167
|
+
puts " Self: #{links['self']}"
|
|
168
|
+
puts " Items: #{links['items']}"
|
|
169
|
+
puts " Wallet Accounts: #{links['wallet_accounts']}"
|
|
170
|
+
else
|
|
171
|
+
puts "Failed to retrieve user"
|
|
172
|
+
puts "Error: #{response.error}"
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Example 2: Verify User Before Payment
|
|
177
|
+
|
|
178
|
+
Check user details before processing a payment from wallet account.
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
182
|
+
|
|
183
|
+
begin
|
|
184
|
+
# Step 1: Get user associated with wallet account
|
|
185
|
+
user_response = wallet_accounts.show_user('wallet_account_id')
|
|
186
|
+
|
|
187
|
+
if user_response.success?
|
|
188
|
+
user = user_response.data
|
|
189
|
+
|
|
190
|
+
# Step 2: Verify user details
|
|
191
|
+
if user['verification_state'] == 'verified' && !user['held_state']
|
|
192
|
+
puts "User verified and not on hold"
|
|
193
|
+
puts "Name: #{user['full_name']}"
|
|
194
|
+
puts "Email: #{user['email']}"
|
|
195
|
+
|
|
196
|
+
# Proceed with payment
|
|
197
|
+
puts "✓ Ready to process payment"
|
|
198
|
+
else
|
|
199
|
+
puts "Cannot process payment:"
|
|
200
|
+
puts " Verification: #{user['verification_state']}"
|
|
201
|
+
puts " On Hold: #{user['held_state']}"
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
205
|
+
puts "Wallet account not found: #{e.message}"
|
|
206
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
207
|
+
puts "API error: #{e.message}"
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Example 3: Get User Contact Information for Notifications
|
|
212
|
+
|
|
213
|
+
Retrieve user contact details for sending payment notifications.
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
217
|
+
|
|
218
|
+
response = wallet_accounts.show_user('wallet_account_id')
|
|
219
|
+
|
|
220
|
+
if response.success?
|
|
221
|
+
user = response.data
|
|
222
|
+
|
|
223
|
+
# Extract contact information
|
|
224
|
+
contact_info = {
|
|
225
|
+
name: user['full_name'],
|
|
226
|
+
email: user['email'],
|
|
227
|
+
mobile: user['mobile'],
|
|
228
|
+
location: user['location']
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
puts "Contact Information:"
|
|
232
|
+
puts " Name: #{contact_info[:name]}"
|
|
233
|
+
puts " Email: #{contact_info[:email]}"
|
|
234
|
+
puts " Mobile: #{contact_info[:mobile]}"
|
|
235
|
+
puts " Location: #{contact_info[:location]}"
|
|
236
|
+
|
|
237
|
+
# Send notification
|
|
238
|
+
# NotificationService.send_payment_confirmation(contact_info)
|
|
239
|
+
end
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Pay a Bill Example
|
|
243
|
+
|
|
244
|
+
### Example 1: Basic Bill Payment
|
|
245
|
+
|
|
246
|
+
Pay a bill using funds from a wallet account.
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
# Pay a bill from wallet account
|
|
250
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
251
|
+
|
|
252
|
+
response = wallet_accounts.pay_bill(
|
|
253
|
+
'901d8cd0-6af3-0138-967d-0a58a9feac04',
|
|
254
|
+
account_id: 'c1824ad0-73f1-0138-3700-0a58a9feac09',
|
|
255
|
+
amount: 173,
|
|
256
|
+
reference_id: 'test100'
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
if response.success?
|
|
260
|
+
disbursement = response.data
|
|
261
|
+
puts "Disbursement ID: #{disbursement['id']}"
|
|
262
|
+
puts "Reference: #{disbursement['reference_id']}"
|
|
263
|
+
puts "Amount: $#{disbursement['amount'] / 100.0}"
|
|
264
|
+
puts "Currency: #{disbursement['currency']}"
|
|
265
|
+
puts "State: #{disbursement['state']}"
|
|
266
|
+
puts "To: #{disbursement['to']}"
|
|
267
|
+
puts "Account Name: #{disbursement['account_name']}"
|
|
268
|
+
puts "Biller Name: #{disbursement['biller_name']}"
|
|
269
|
+
puts "Biller Code: #{disbursement['biller_code']}"
|
|
270
|
+
puts "CRN: #{disbursement['crn']}"
|
|
271
|
+
puts "Created At: #{disbursement['created_at']}"
|
|
272
|
+
|
|
273
|
+
# Access links
|
|
274
|
+
links = disbursement['links']
|
|
275
|
+
puts "\nLinks:"
|
|
276
|
+
puts " Transactions: #{links['transactions']}"
|
|
277
|
+
puts " Wallet Accounts: #{links['wallet_accounts']}"
|
|
278
|
+
puts " BPay Accounts: #{links['bpay_accounts']}"
|
|
279
|
+
else
|
|
280
|
+
puts "Failed to pay bill"
|
|
281
|
+
puts "Error: #{response.error}"
|
|
282
|
+
end
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Example 2: Pay Bill with Balance Check
|
|
286
|
+
|
|
287
|
+
Check balance before paying a bill.
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
291
|
+
wallet_id = '901d8cd0-6af3-0138-967d-0a58a9feac04'
|
|
292
|
+
payment_amount = 17300 # $173.00
|
|
293
|
+
|
|
294
|
+
begin
|
|
295
|
+
# Step 1: Check wallet balance
|
|
296
|
+
wallet_response = wallet_accounts.show(wallet_id)
|
|
297
|
+
|
|
298
|
+
if wallet_response.success?
|
|
299
|
+
wallet = wallet_response.data
|
|
300
|
+
balance = wallet['balance']
|
|
301
|
+
|
|
302
|
+
puts "Current balance: $#{balance / 100.0}"
|
|
303
|
+
puts "Payment amount: $#{payment_amount / 100.0}"
|
|
304
|
+
|
|
305
|
+
# Step 2: Verify sufficient funds
|
|
306
|
+
if balance >= payment_amount
|
|
307
|
+
puts "Sufficient funds available"
|
|
308
|
+
|
|
309
|
+
# Step 3: Process payment
|
|
310
|
+
payment_response = wallet_accounts.pay_bill(
|
|
311
|
+
wallet_id,
|
|
312
|
+
account_id: 'bpay_account_id',
|
|
313
|
+
amount: payment_amount,
|
|
314
|
+
reference_id: "bill_#{Time.now.to_i}"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if payment_response.success?
|
|
318
|
+
disbursement = payment_response.data
|
|
319
|
+
puts "\n✓ Bill payment successful"
|
|
320
|
+
puts "Disbursement ID: #{disbursement['id']}"
|
|
321
|
+
puts "New balance: $#{(balance - payment_amount) / 100.0}"
|
|
322
|
+
else
|
|
323
|
+
puts "\n✗ Payment failed: #{payment_response.error}"
|
|
324
|
+
end
|
|
325
|
+
else
|
|
326
|
+
shortfall = payment_amount - balance
|
|
327
|
+
puts "\n✗ Insufficient funds"
|
|
328
|
+
puts "Shortfall: $#{shortfall / 100.0}"
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
332
|
+
puts "Validation error: #{e.message}"
|
|
333
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
334
|
+
puts "API error: #{e.message}"
|
|
335
|
+
end
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Example 3: Pay Multiple Bills
|
|
339
|
+
|
|
340
|
+
Process multiple bill payments from a wallet account.
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
344
|
+
wallet_id = '901d8cd0-6af3-0138-967d-0a58a9feac04'
|
|
345
|
+
|
|
346
|
+
bills = [
|
|
347
|
+
{
|
|
348
|
+
account_id: 'bpay_water_account',
|
|
349
|
+
amount: 15000, # $150.00
|
|
350
|
+
reference: 'water_bill_nov_2024',
|
|
351
|
+
name: 'Water Bill'
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
account_id: 'bpay_electricity_account',
|
|
355
|
+
amount: 22000, # $220.00
|
|
356
|
+
reference: 'electricity_bill_nov_2024',
|
|
357
|
+
name: 'Electricity Bill'
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
account_id: 'bpay_gas_account',
|
|
361
|
+
amount: 8500, # $85.00
|
|
362
|
+
reference: 'gas_bill_nov_2024',
|
|
363
|
+
name: 'Gas Bill'
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
# Check total payment amount
|
|
368
|
+
total_amount = bills.sum { |bill| bill[:amount] }
|
|
369
|
+
puts "Total payment amount: $#{total_amount / 100.0}"
|
|
370
|
+
|
|
371
|
+
# Check balance
|
|
372
|
+
wallet_response = wallet_accounts.show(wallet_id)
|
|
373
|
+
if wallet_response.success?
|
|
374
|
+
balance = wallet_response.data['balance']
|
|
375
|
+
puts "Available balance: $#{balance / 100.0}"
|
|
376
|
+
|
|
377
|
+
if balance >= total_amount
|
|
378
|
+
# Process each bill
|
|
379
|
+
bills.each do |bill|
|
|
380
|
+
response = wallet_accounts.pay_bill(
|
|
381
|
+
wallet_id,
|
|
382
|
+
account_id: bill[:account_id],
|
|
383
|
+
amount: bill[:amount],
|
|
384
|
+
reference_id: bill[:reference]
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if response.success?
|
|
388
|
+
disbursement = response.data
|
|
389
|
+
puts "\n✓ #{bill[:name]} paid: $#{bill[:amount] / 100.0}"
|
|
390
|
+
puts " Disbursement ID: #{disbursement['id']}"
|
|
391
|
+
puts " State: #{disbursement['state']}"
|
|
392
|
+
else
|
|
393
|
+
puts "\n✗ Failed to pay #{bill[:name]}"
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Small delay between payments
|
|
397
|
+
sleep(0.5)
|
|
398
|
+
end
|
|
399
|
+
else
|
|
400
|
+
puts "\n✗ Insufficient funds for all bills"
|
|
401
|
+
puts "Shortfall: $#{(total_amount - balance) / 100.0}"
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Common Patterns
|
|
407
|
+
|
|
408
|
+
### Pattern 1: Complete Payment Workflow
|
|
409
|
+
|
|
410
|
+
Full workflow from balance check to payment confirmation.
|
|
411
|
+
|
|
412
|
+
```ruby
|
|
413
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
414
|
+
wallet_id = '901d8cd0-6af3-0138-967d-0a58a9feac04'
|
|
415
|
+
bpay_account_id = 'c1824ad0-73f1-0138-3700-0a58a9feac09'
|
|
416
|
+
payment_amount = 17300 # $173.00
|
|
417
|
+
|
|
418
|
+
begin
|
|
419
|
+
# Step 1: Verify user eligibility
|
|
420
|
+
user_response = wallet_accounts.show_user(wallet_id)
|
|
421
|
+
|
|
422
|
+
if user_response.success?
|
|
423
|
+
user = user_response.data
|
|
424
|
+
|
|
425
|
+
unless user['verification_state'] == 'verified' && !user['held_state']
|
|
426
|
+
puts "User not eligible for payment"
|
|
427
|
+
exit
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
puts "✓ User verified: #{user['full_name']}"
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Step 2: Check wallet balance
|
|
434
|
+
wallet_response = wallet_accounts.show(wallet_id)
|
|
435
|
+
|
|
436
|
+
if wallet_response.success?
|
|
437
|
+
wallet = wallet_response.data
|
|
438
|
+
balance = wallet['balance']
|
|
439
|
+
|
|
440
|
+
unless wallet['active'] && balance >= payment_amount
|
|
441
|
+
puts "✗ Wallet not ready for payment"
|
|
442
|
+
puts " Active: #{wallet['active']}"
|
|
443
|
+
puts " Balance: $#{balance / 100.0}"
|
|
444
|
+
exit
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
puts "✓ Sufficient balance: $#{balance / 100.0}"
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Step 3: Process payment
|
|
451
|
+
payment_response = wallet_accounts.pay_bill(
|
|
452
|
+
wallet_id,
|
|
453
|
+
account_id: bpay_account_id,
|
|
454
|
+
amount: payment_amount,
|
|
455
|
+
reference_id: "bill_#{Time.now.to_i}"
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
if payment_response.success?
|
|
459
|
+
disbursement = payment_response.data
|
|
460
|
+
|
|
461
|
+
puts "\n✓ Payment successful"
|
|
462
|
+
puts " Disbursement ID: #{disbursement['id']}"
|
|
463
|
+
puts " Amount: $#{disbursement['amount'] / 100.0}"
|
|
464
|
+
puts " State: #{disbursement['state']}"
|
|
465
|
+
puts " To: #{disbursement['account_name']}"
|
|
466
|
+
puts " Reference: #{disbursement['reference_id']}"
|
|
467
|
+
|
|
468
|
+
# Step 4: Send notification
|
|
469
|
+
# NotificationService.send_payment_confirmation(user['email'], disbursement)
|
|
470
|
+
else
|
|
471
|
+
puts "\n✗ Payment failed: #{payment_response.error}"
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
475
|
+
puts "Validation error: #{e.message}"
|
|
476
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
477
|
+
puts "Not found: #{e.message}"
|
|
478
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
479
|
+
puts "API error: #{e.message}"
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Pattern 2: Wallet Payment Service
|
|
484
|
+
|
|
485
|
+
Implement a payment service class for wallet payments.
|
|
486
|
+
|
|
487
|
+
```ruby
|
|
488
|
+
class WalletPaymentService
|
|
489
|
+
def initialize
|
|
490
|
+
@wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def pay_bill(wallet_id, bpay_account_id, amount, reference_id)
|
|
494
|
+
# Validate inputs
|
|
495
|
+
validate_amount!(amount)
|
|
496
|
+
|
|
497
|
+
# Check balance
|
|
498
|
+
unless sufficient_balance?(wallet_id, amount)
|
|
499
|
+
return { success: false, error: 'Insufficient balance' }
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Process payment
|
|
503
|
+
response = @wallet_accounts.pay_bill(
|
|
504
|
+
wallet_id,
|
|
505
|
+
account_id: bpay_account_id,
|
|
506
|
+
amount: amount,
|
|
507
|
+
reference_id: reference_id
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
if response.success?
|
|
511
|
+
disbursement = response.data
|
|
512
|
+
|
|
513
|
+
{
|
|
514
|
+
success: true,
|
|
515
|
+
disbursement_id: disbursement['id'],
|
|
516
|
+
amount: disbursement['amount'],
|
|
517
|
+
state: disbursement['state'],
|
|
518
|
+
reference: disbursement['reference_id']
|
|
519
|
+
}
|
|
520
|
+
else
|
|
521
|
+
{
|
|
522
|
+
success: false,
|
|
523
|
+
error: response.error
|
|
524
|
+
}
|
|
525
|
+
end
|
|
526
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
527
|
+
{ success: false, error: "Validation error: #{e.message}" }
|
|
528
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
529
|
+
{ success: false, error: "API error: #{e.message}" }
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def get_balance(wallet_id)
|
|
533
|
+
response = @wallet_accounts.show(wallet_id)
|
|
534
|
+
|
|
535
|
+
if response.success?
|
|
536
|
+
wallet = response.data
|
|
537
|
+
{
|
|
538
|
+
success: true,
|
|
539
|
+
balance: wallet['balance'],
|
|
540
|
+
currency: wallet['currency'],
|
|
541
|
+
active: wallet['active']
|
|
542
|
+
}
|
|
543
|
+
else
|
|
544
|
+
{ success: false, error: response.error }
|
|
545
|
+
end
|
|
546
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
547
|
+
{ success: false, error: e.message }
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
private
|
|
551
|
+
|
|
552
|
+
def validate_amount!(amount)
|
|
553
|
+
raise ArgumentError, 'Amount must be positive' unless amount.positive?
|
|
554
|
+
raise ArgumentError, 'Amount must be an integer' unless amount.is_a?(Integer)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
def sufficient_balance?(wallet_id, amount)
|
|
558
|
+
result = get_balance(wallet_id)
|
|
559
|
+
result[:success] && result[:balance] >= amount
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# Usage
|
|
564
|
+
service = WalletPaymentService.new
|
|
565
|
+
|
|
566
|
+
# Check balance
|
|
567
|
+
balance_result = service.get_balance('wallet_id')
|
|
568
|
+
if balance_result[:success]
|
|
569
|
+
puts "Balance: $#{balance_result[:balance] / 100.0}"
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# Pay bill
|
|
573
|
+
payment_result = service.pay_bill(
|
|
574
|
+
'wallet_id',
|
|
575
|
+
'bpay_account_id',
|
|
576
|
+
17300,
|
|
577
|
+
'bill_123'
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
if payment_result[:success]
|
|
581
|
+
puts "Payment successful: #{payment_result[:disbursement_id]}"
|
|
582
|
+
else
|
|
583
|
+
puts "Payment failed: #{payment_result[:error]}"
|
|
584
|
+
end
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Pattern 3: Rails Controller for Wallet Payments
|
|
588
|
+
|
|
589
|
+
Implement wallet payments in a Rails controller.
|
|
590
|
+
|
|
591
|
+
```ruby
|
|
592
|
+
# In a Rails controller
|
|
593
|
+
class WalletPaymentsController < ApplicationController
|
|
594
|
+
before_action :authenticate_user!
|
|
595
|
+
|
|
596
|
+
def create
|
|
597
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
598
|
+
|
|
599
|
+
begin
|
|
600
|
+
# Validate parameters
|
|
601
|
+
validate_payment_params!
|
|
602
|
+
|
|
603
|
+
# Process payment
|
|
604
|
+
response = wallet_accounts.pay_bill(
|
|
605
|
+
params[:wallet_account_id],
|
|
606
|
+
account_id: params[:bpay_account_id],
|
|
607
|
+
amount: params[:amount].to_i,
|
|
608
|
+
reference_id: params[:reference_id]
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
if response.success?
|
|
612
|
+
disbursement = response.data
|
|
613
|
+
|
|
614
|
+
# Log payment
|
|
615
|
+
Rails.logger.info("Payment successful: #{disbursement['id']}")
|
|
616
|
+
|
|
617
|
+
render json: {
|
|
618
|
+
success: true,
|
|
619
|
+
disbursement_id: disbursement['id'],
|
|
620
|
+
amount: disbursement['amount'],
|
|
621
|
+
state: disbursement['state'],
|
|
622
|
+
message: 'Bill payment successful'
|
|
623
|
+
}, status: :created
|
|
624
|
+
else
|
|
625
|
+
render json: {
|
|
626
|
+
success: false,
|
|
627
|
+
message: response.error
|
|
628
|
+
}, status: :unprocessable_entity
|
|
629
|
+
end
|
|
630
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
631
|
+
render json: {
|
|
632
|
+
success: false,
|
|
633
|
+
message: e.message
|
|
634
|
+
}, status: :bad_request
|
|
635
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
636
|
+
render json: {
|
|
637
|
+
success: false,
|
|
638
|
+
message: 'Wallet or BPay account not found'
|
|
639
|
+
}, status: :not_found
|
|
640
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
641
|
+
Rails.logger.error("Payment API error: #{e.message}")
|
|
642
|
+
|
|
643
|
+
render json: {
|
|
644
|
+
success: false,
|
|
645
|
+
message: 'An error occurred while processing the payment'
|
|
646
|
+
}, status: :internal_server_error
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
def show_balance
|
|
651
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
652
|
+
|
|
653
|
+
begin
|
|
654
|
+
response = wallet_accounts.show(params[:id])
|
|
655
|
+
|
|
656
|
+
if response.success?
|
|
657
|
+
wallet = response.data
|
|
658
|
+
|
|
659
|
+
render json: {
|
|
660
|
+
success: true,
|
|
661
|
+
balance: wallet['balance'],
|
|
662
|
+
currency: wallet['currency'],
|
|
663
|
+
active: wallet['active']
|
|
664
|
+
}
|
|
665
|
+
else
|
|
666
|
+
render json: {
|
|
667
|
+
success: false,
|
|
668
|
+
message: response.error
|
|
669
|
+
}, status: :unprocessable_entity
|
|
670
|
+
end
|
|
671
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
672
|
+
render json: {
|
|
673
|
+
success: false,
|
|
674
|
+
message: 'Wallet account not found'
|
|
675
|
+
}, status: :not_found
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
private
|
|
680
|
+
|
|
681
|
+
def validate_payment_params!
|
|
682
|
+
required_params = [:wallet_account_id, :bpay_account_id, :amount]
|
|
683
|
+
missing_params = required_params.select { |param| params[param].blank? }
|
|
684
|
+
|
|
685
|
+
if missing_params.any?
|
|
686
|
+
raise ActionController::ParameterMissing, "Missing parameters: #{missing_params.join(', ')}"
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
amount = params[:amount].to_i
|
|
690
|
+
if amount <= 0
|
|
691
|
+
raise ArgumentError, 'Amount must be positive'
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
end
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
## Important Notes
|
|
698
|
+
|
|
699
|
+
1. **Required Fields**:
|
|
700
|
+
- `wallet_account_id` - The wallet account ID
|
|
701
|
+
- `account_id` - BPay account ID (for pay_bill)
|
|
702
|
+
- `amount` - Payment amount in cents (for pay_bill)
|
|
703
|
+
|
|
704
|
+
2. **Amount Validation**:
|
|
705
|
+
- Must be a positive integer
|
|
706
|
+
- Specified in cents (e.g., 100 = $1.00)
|
|
707
|
+
- Cannot exceed wallet balance
|
|
708
|
+
|
|
709
|
+
3. **Reference ID**:
|
|
710
|
+
- Optional but recommended for tracking
|
|
711
|
+
- Cannot contain single quote (') character
|
|
712
|
+
- Should be unique for each payment
|
|
713
|
+
|
|
714
|
+
4. **Balance Check**:
|
|
715
|
+
- Always check balance before payment
|
|
716
|
+
- Balance returned in cents
|
|
717
|
+
- Verify wallet is active
|
|
718
|
+
|
|
719
|
+
5. **Disbursement States**:
|
|
720
|
+
- `pending` - Payment initiated
|
|
721
|
+
- `successful` - Payment completed
|
|
722
|
+
- `failed` - Payment failed
|
|
723
|
+
|
|
724
|
+
6. **Error Handling**:
|
|
725
|
+
- Always wrap API calls in error handling
|
|
726
|
+
- Check for ValidationError, NotFoundError, ApiError
|
|
727
|
+
- Log errors for debugging
|
|
728
|
+
|
|
729
|
+
7. **User Verification**:
|
|
730
|
+
- Verify user state before payment
|
|
731
|
+
- Check `verification_state` == 'verified'
|
|
732
|
+
- Ensure `held_state` == false
|
|
733
|
+
|