zai_payment 2.6.1 โ 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +83 -0
- data/docs/batch_transactions.md +340 -0
- data/docs/direct_api_usage.md +489 -0
- data/docs/wallet_accounts.md +493 -0
- data/examples/batch_transactions.md +450 -0
- data/examples/wallet_accounts.md +733 -0
- data/lib/zai_payment/resources/batch_transaction.rb +302 -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 +12 -0
- data/readme.md +84 -2
- metadata +10 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ZaiPayment
|
|
4
|
+
module Resources
|
|
5
|
+
# BatchTransaction resource for managing Zai batch transactions (Prelive only)
|
|
6
|
+
#
|
|
7
|
+
# @note These endpoints are only available in the prelive environment
|
|
8
|
+
class BatchTransaction
|
|
9
|
+
attr_reader :client, :config
|
|
10
|
+
|
|
11
|
+
# Valid transaction types
|
|
12
|
+
TRANSACTION_TYPES = %w[payment refund disbursement fee deposit withdrawal].freeze
|
|
13
|
+
|
|
14
|
+
# Valid transaction type methods
|
|
15
|
+
TRANSACTION_TYPE_METHODS = %w[credit_card npp bpay wallet_account_transfer wire_transfer misc].freeze
|
|
16
|
+
|
|
17
|
+
# Valid directions
|
|
18
|
+
DIRECTIONS = %w[debit credit].freeze
|
|
19
|
+
|
|
20
|
+
def initialize(client: nil, config: nil)
|
|
21
|
+
@client = client || Client.new(base_endpoint: :core_base)
|
|
22
|
+
@config = config || ZaiPayment.config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# List batch transactions
|
|
26
|
+
#
|
|
27
|
+
# Retrieve an ordered and paginated list of existing batch transactions.
|
|
28
|
+
# The list can be filtered by account, batch ID, item, and transaction type.
|
|
29
|
+
#
|
|
30
|
+
# @param options [Hash] optional filters
|
|
31
|
+
# @option options [Integer] :limit number of records to return (default: 10, max: 200)
|
|
32
|
+
# @option options [Integer] :offset number of records to skip (default: 0)
|
|
33
|
+
# @option options [String] :account_id Bank, Card or Wallet Account ID
|
|
34
|
+
# @option options [String] :batch_id Batch ID
|
|
35
|
+
# @option options [String] :item_id Item ID
|
|
36
|
+
# @option options [String] :transaction_type transaction type
|
|
37
|
+
# (payment, refund, disbursement, fee, deposit, withdrawal)
|
|
38
|
+
# @option options [String] :transaction_type_method transaction method (credit_card, npp, bpay, etc.)
|
|
39
|
+
# @option options [String] :direction direction (debit, credit)
|
|
40
|
+
# @option options [String] :created_before ISO 8601 date/time to filter transactions created before
|
|
41
|
+
# @option options [String] :created_after ISO 8601 date/time to filter transactions created after
|
|
42
|
+
# @option options [String] :disbursement_bank the bank used for disbursing the payment
|
|
43
|
+
# @option options [String] :processing_bank the bank used for processing the payment
|
|
44
|
+
# @return [Response] the API response containing batch_transactions array
|
|
45
|
+
#
|
|
46
|
+
# @example List all batch transactions
|
|
47
|
+
# batch_transactions = ZaiPayment.batch_transactions
|
|
48
|
+
# response = batch_transactions.list
|
|
49
|
+
# response.data # => [{"id" => 12484, "status" => 12200, ...}]
|
|
50
|
+
#
|
|
51
|
+
# @example List with filters
|
|
52
|
+
# response = batch_transactions.list(
|
|
53
|
+
# transaction_type: 'disbursement',
|
|
54
|
+
# direction: 'credit',
|
|
55
|
+
# limit: 50
|
|
56
|
+
# )
|
|
57
|
+
#
|
|
58
|
+
# @see https://developer.hellozai.com/reference/listbatchtransactions
|
|
59
|
+
def list(**options)
|
|
60
|
+
validate_list_options(options)
|
|
61
|
+
|
|
62
|
+
params = build_list_params(options)
|
|
63
|
+
|
|
64
|
+
client.get('/batch_transactions', params: params)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Show a batch transaction
|
|
68
|
+
#
|
|
69
|
+
# Get a batch transaction using its ID (UUID or numeric ID).
|
|
70
|
+
#
|
|
71
|
+
# @param id [String] the batch transaction ID
|
|
72
|
+
# @return [Response] the API response containing batch_transactions object
|
|
73
|
+
#
|
|
74
|
+
# @example Get a batch transaction by UUID
|
|
75
|
+
# batch_transactions = ZaiPayment.batch_transactions
|
|
76
|
+
# response = batch_transactions.show('90c1418b-f4f4-413e-a4ba-f29c334e7f55')
|
|
77
|
+
# response.data # => {"id" => 13143, "uuid" => "90c1418b-f4f4-413e-a4ba-f29c334e7f55", ...}
|
|
78
|
+
#
|
|
79
|
+
# @example Get a batch transaction by numeric ID
|
|
80
|
+
# response = batch_transactions.show('13143')
|
|
81
|
+
# response.data['state'] # => "successful"
|
|
82
|
+
#
|
|
83
|
+
# @raise [Errors::ValidationError] if id is blank
|
|
84
|
+
#
|
|
85
|
+
# @see https://developer.hellozai.com/reference/showbatchtransaction
|
|
86
|
+
def show(id)
|
|
87
|
+
validate_id!(id, 'id')
|
|
88
|
+
|
|
89
|
+
client.get("/batch_transactions/#{id}")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Export batch transactions (Prelive only)
|
|
93
|
+
#
|
|
94
|
+
# Calls the GET /batch_transactions/export_transactions API which moves all pending
|
|
95
|
+
# batch_transactions into batched state. As a result, this API will return all the
|
|
96
|
+
# batch_transactions that have moved from pending to batched. Please store the id
|
|
97
|
+
# in order to progress it in Pre-live.
|
|
98
|
+
#
|
|
99
|
+
# @return [Response] the API response containing transactions array with batch_id
|
|
100
|
+
#
|
|
101
|
+
# @example Export transactions
|
|
102
|
+
# batch_transactions = ZaiPayment.batch_transactions
|
|
103
|
+
# response = batch_transactions.export_transactions
|
|
104
|
+
# response.data # => {"transactions" => [{"id" => "...", "batch_id" => "...", "status" => "batched", ...}]}
|
|
105
|
+
#
|
|
106
|
+
# @raise [Errors::ConfigurationError] if not in prelive environment
|
|
107
|
+
#
|
|
108
|
+
# @see https://developer.hellozai.com/reference (Prelive endpoints)
|
|
109
|
+
def export_transactions
|
|
110
|
+
ensure_prelive_environment!
|
|
111
|
+
|
|
112
|
+
client.get('/batch_transactions/export_transactions')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Update batch transaction states (Prelive only)
|
|
116
|
+
#
|
|
117
|
+
# Calls the PATCH /batches/:id/transaction_states API which moves one or more
|
|
118
|
+
# batch_transactions into a specific state. You will need to pass in the batch_id
|
|
119
|
+
# from the export_transactions response.
|
|
120
|
+
#
|
|
121
|
+
# State codes:
|
|
122
|
+
# - 12700: bank_processing state
|
|
123
|
+
# - 12000: successful state (final state, triggers webhook)
|
|
124
|
+
#
|
|
125
|
+
# @param batch_id [String] the batch ID from export_transactions response
|
|
126
|
+
# @param exported_ids [Array<String>] array of transaction IDs to update
|
|
127
|
+
# @param state [Integer] the target state code (12700 or 12000)
|
|
128
|
+
# @return [Response] the API response containing job information
|
|
129
|
+
#
|
|
130
|
+
# @example Move transactions to bank_processing state
|
|
131
|
+
# batch_transactions = ZaiPayment.batch_transactions
|
|
132
|
+
# response = batch_transactions.update_transaction_states(
|
|
133
|
+
# "batch_id",
|
|
134
|
+
# exported_ids: ["439970a2-e0a1-418e-aecf-6b519c115c55"],
|
|
135
|
+
# state: 12700
|
|
136
|
+
# )
|
|
137
|
+
# response.body # => {
|
|
138
|
+
# "aggregated_jobs_uuid" => "c1cbc502-9754-42fd-9731-2330ddd7a41f",
|
|
139
|
+
# "msg" => "1 jobs have been sent to the queue.",
|
|
140
|
+
# "errors" => []
|
|
141
|
+
# }
|
|
142
|
+
#
|
|
143
|
+
# @example Move transactions to successful state
|
|
144
|
+
# response = batch_transactions.update_transaction_states(
|
|
145
|
+
# "batch_id",
|
|
146
|
+
# exported_ids: ["439970a2-e0a1-418e-aecf-6b519c115c55"],
|
|
147
|
+
# state: 12000
|
|
148
|
+
# )
|
|
149
|
+
# response.body # => {
|
|
150
|
+
# "aggregated_jobs_uuid" => "...",
|
|
151
|
+
# "msg" => "1 jobs have been sent to the queue.",
|
|
152
|
+
# "errors" => []
|
|
153
|
+
# }
|
|
154
|
+
#
|
|
155
|
+
# @raise [Errors::ConfigurationError] if not in prelive environment
|
|
156
|
+
# @raise [Errors::ValidationError] if parameters are invalid
|
|
157
|
+
#
|
|
158
|
+
# @see https://developer.hellozai.com/reference (Prelive endpoints)
|
|
159
|
+
def update_transaction_states(batch_id, exported_ids:, state:)
|
|
160
|
+
ensure_prelive_environment!
|
|
161
|
+
validate_id!(batch_id, 'batch_id')
|
|
162
|
+
validate_exported_ids!(exported_ids)
|
|
163
|
+
validate_state!(state)
|
|
164
|
+
|
|
165
|
+
body = {
|
|
166
|
+
exported_ids: exported_ids,
|
|
167
|
+
state: state
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
client.patch("/batches/#{batch_id}/transaction_states", body: body)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Move transactions to bank_processing state (Prelive only)
|
|
174
|
+
#
|
|
175
|
+
# Convenience method that calls update_transaction_states with state 12700.
|
|
176
|
+
# This simulates the step where transactions are moved to bank_processing state.
|
|
177
|
+
#
|
|
178
|
+
# @param batch_id [String] the batch ID from export_transactions response
|
|
179
|
+
# @param exported_ids [Array<String>] array of transaction IDs to update
|
|
180
|
+
# @return [Response] the API response with aggregated_jobs_uuid, msg, and errors
|
|
181
|
+
#
|
|
182
|
+
# @example
|
|
183
|
+
# batch_transactions = ZaiPayment.batch_transactions
|
|
184
|
+
# response = batch_transactions.process_to_bank_processing(
|
|
185
|
+
# "batch_id",
|
|
186
|
+
# exported_ids: ["439970a2-e0a1-418e-aecf-6b519c115c55"]
|
|
187
|
+
# )
|
|
188
|
+
# response.body["msg"] # => "1 jobs have been sent to the queue."
|
|
189
|
+
#
|
|
190
|
+
# @raise [Errors::ConfigurationError] if not in prelive environment
|
|
191
|
+
# @raise [Errors::ValidationError] if parameters are invalid
|
|
192
|
+
def process_to_bank_processing(batch_id, exported_ids:)
|
|
193
|
+
update_transaction_states(batch_id, exported_ids: exported_ids, state: 12_700)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Move transactions to successful state (Prelive only)
|
|
197
|
+
#
|
|
198
|
+
# Convenience method that calls update_transaction_states with state 12000.
|
|
199
|
+
# This simulates the final step where transactions are marked as successful
|
|
200
|
+
# and triggers the batch_transactions webhook.
|
|
201
|
+
#
|
|
202
|
+
# @param batch_id [String] the batch ID from export_transactions response
|
|
203
|
+
# @param exported_ids [Array<String>] array of transaction IDs to update
|
|
204
|
+
# @return [Response] the API response with aggregated_jobs_uuid, msg, and errors
|
|
205
|
+
#
|
|
206
|
+
# @example
|
|
207
|
+
# batch_transactions = ZaiPayment.batch_transactions
|
|
208
|
+
# response = batch_transactions.process_to_successful(
|
|
209
|
+
# "batch_id",
|
|
210
|
+
# exported_ids: ["439970a2-e0a1-418e-aecf-6b519c115c55"]
|
|
211
|
+
# )
|
|
212
|
+
# response.body["msg"] # => "1 jobs have been sent to the queue."
|
|
213
|
+
#
|
|
214
|
+
# @raise [Errors::ConfigurationError] if not in prelive environment
|
|
215
|
+
# @raise [Errors::ValidationError] if parameters are invalid
|
|
216
|
+
def process_to_successful(batch_id, exported_ids:)
|
|
217
|
+
update_transaction_states(batch_id, exported_ids: exported_ids, state: 12_000)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
private
|
|
221
|
+
|
|
222
|
+
def ensure_prelive_environment!
|
|
223
|
+
return if config.environment.to_sym == :prelive
|
|
224
|
+
|
|
225
|
+
raise Errors::ConfigurationError,
|
|
226
|
+
'Batch transaction endpoints are only available in prelive environment. ' \
|
|
227
|
+
"Current environment: #{config.environment}"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def validate_id!(value, field_name)
|
|
231
|
+
return unless value.nil? || value.to_s.strip.empty?
|
|
232
|
+
|
|
233
|
+
raise Errors::ValidationError, "#{field_name} is required and cannot be blank"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def validate_exported_ids!(exported_ids)
|
|
237
|
+
if exported_ids.nil? || !exported_ids.is_a?(Array) || exported_ids.empty?
|
|
238
|
+
raise Errors::ValidationError,
|
|
239
|
+
'exported_ids is required and must be a non-empty array'
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
return unless exported_ids.any? { |id| id.nil? || id.to_s.strip.empty? }
|
|
243
|
+
|
|
244
|
+
raise Errors::ValidationError,
|
|
245
|
+
'exported_ids cannot contain nil or empty values'
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def validate_state!(state)
|
|
249
|
+
valid_states = [12_700, 12_000]
|
|
250
|
+
|
|
251
|
+
return if valid_states.include?(state)
|
|
252
|
+
|
|
253
|
+
raise Errors::ValidationError,
|
|
254
|
+
"state must be 12700 (bank_processing) or 12000 (successful), got: #{state}"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def validate_transaction_type!(transaction_type)
|
|
258
|
+
return if TRANSACTION_TYPES.include?(transaction_type.to_s)
|
|
259
|
+
|
|
260
|
+
raise Errors::ValidationError,
|
|
261
|
+
"transaction_type must be one of: #{TRANSACTION_TYPES.join(', ')}"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def validate_transaction_type_method!(transaction_type_method)
|
|
265
|
+
return if TRANSACTION_TYPE_METHODS.include?(transaction_type_method.to_s)
|
|
266
|
+
|
|
267
|
+
raise Errors::ValidationError,
|
|
268
|
+
"transaction_type_method must be one of: #{TRANSACTION_TYPE_METHODS.join(', ')}"
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def validate_direction!(direction)
|
|
272
|
+
return if DIRECTIONS.include?(direction.to_s)
|
|
273
|
+
|
|
274
|
+
raise Errors::ValidationError,
|
|
275
|
+
"direction must be one of: #{DIRECTIONS.join(', ')}"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def validate_list_options(options)
|
|
279
|
+
validate_transaction_type!(options[:transaction_type]) if options[:transaction_type]
|
|
280
|
+
validate_transaction_type_method!(options[:transaction_type_method]) if options[:transaction_type_method]
|
|
281
|
+
validate_direction!(options[:direction]) if options[:direction]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def build_list_params(options)
|
|
285
|
+
{
|
|
286
|
+
limit: options.fetch(:limit, 10),
|
|
287
|
+
offset: options.fetch(:offset, 0),
|
|
288
|
+
account_id: options[:account_id],
|
|
289
|
+
batch_id: options[:batch_id],
|
|
290
|
+
item_id: options[:item_id],
|
|
291
|
+
transaction_type: options[:transaction_type],
|
|
292
|
+
transaction_type_method: options[:transaction_type_method],
|
|
293
|
+
direction: options[:direction],
|
|
294
|
+
created_before: options[:created_before],
|
|
295
|
+
created_after: options[:created_after],
|
|
296
|
+
disbursement_bank: options[:disbursement_bank],
|
|
297
|
+
processing_bank: options[:processing_bank]
|
|
298
|
+
}.compact
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ZaiPayment
|
|
4
|
+
module Resources
|
|
5
|
+
# WalletAccount resource for managing Zai wallet accounts
|
|
6
|
+
#
|
|
7
|
+
# @see https://developer.hellozai.com/reference
|
|
8
|
+
class WalletAccount
|
|
9
|
+
attr_reader :client
|
|
10
|
+
|
|
11
|
+
# Map of attribute keys to API field names for pay_bill
|
|
12
|
+
PAY_BILL_FIELD_MAPPING = {
|
|
13
|
+
account_id: :account_id,
|
|
14
|
+
amount: :amount,
|
|
15
|
+
reference_id: :reference_id
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
def initialize(client: nil)
|
|
19
|
+
@client = client || Client.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Get a specific wallet account by ID
|
|
23
|
+
#
|
|
24
|
+
# @param wallet_account_id [String] the wallet account ID
|
|
25
|
+
# @return [Response] the API response containing wallet account details
|
|
26
|
+
#
|
|
27
|
+
# @example
|
|
28
|
+
# wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
29
|
+
# response = wallet_accounts.show("wallet_account_id")
|
|
30
|
+
# response.data # => {"id" => "wallet_account_id", "active" => true, ...}
|
|
31
|
+
#
|
|
32
|
+
# @see https://developer.hellozai.com/reference
|
|
33
|
+
def show(wallet_account_id)
|
|
34
|
+
validate_id!(wallet_account_id, 'wallet_account_id')
|
|
35
|
+
client.get("/wallet_accounts/#{wallet_account_id}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get the user associated with a Wallet Account
|
|
39
|
+
#
|
|
40
|
+
# Show the User the Wallet Account is associated with using a given wallet_account_id.
|
|
41
|
+
#
|
|
42
|
+
# @param wallet_account_id [String] the wallet account ID
|
|
43
|
+
# @return [Response] the API response containing user details
|
|
44
|
+
#
|
|
45
|
+
# @example
|
|
46
|
+
# wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
47
|
+
# response = wallet_accounts.show_user("wallet_account_id")
|
|
48
|
+
# response.data # => {"id" => "user_id", "full_name" => "Samuel Seller", ...}
|
|
49
|
+
#
|
|
50
|
+
# @see https://developer.hellozai.com/reference
|
|
51
|
+
def show_user(wallet_account_id)
|
|
52
|
+
validate_id!(wallet_account_id, 'wallet_account_id')
|
|
53
|
+
client.get("/wallet_accounts/#{wallet_account_id}/users")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Get NPP details for a Wallet Account
|
|
57
|
+
#
|
|
58
|
+
# Show NPP details of a specific Wallet Account using a given wallet_account_id.
|
|
59
|
+
# NPP (New Payments Platform) details include PayID and payment reference information.
|
|
60
|
+
#
|
|
61
|
+
# @param wallet_account_id [String] the wallet account ID
|
|
62
|
+
# @return [Response] the API response containing NPP details
|
|
63
|
+
#
|
|
64
|
+
# @example
|
|
65
|
+
# wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
66
|
+
# response = wallet_accounts.show_npp_details("wallet_account_id")
|
|
67
|
+
# response.data # => {"id" => "wallet_account_id", "npp_details" => {...}}
|
|
68
|
+
#
|
|
69
|
+
# @see https://developer.hellozai.com/reference
|
|
70
|
+
def show_npp_details(wallet_account_id)
|
|
71
|
+
validate_id!(wallet_account_id, 'wallet_account_id')
|
|
72
|
+
client.get("/wallet_accounts/#{wallet_account_id}/npp_details")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get BPay details for a Wallet Account
|
|
76
|
+
#
|
|
77
|
+
# Show BPay details of a specific Wallet Account using a given wallet_account_id.
|
|
78
|
+
# BPay details include biller code, reference, and amount information.
|
|
79
|
+
#
|
|
80
|
+
# @param wallet_account_id [String] the wallet account ID
|
|
81
|
+
# @return [Response] the API response containing BPay details
|
|
82
|
+
#
|
|
83
|
+
# @example
|
|
84
|
+
# wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
85
|
+
# response = wallet_accounts.show_bpay_details("wallet_account_id")
|
|
86
|
+
# response.data # => {"id" => "wallet_account_id", "bpay_details" => {...}}
|
|
87
|
+
#
|
|
88
|
+
# @see https://developer.hellozai.com/reference
|
|
89
|
+
def show_bpay_details(wallet_account_id)
|
|
90
|
+
validate_id!(wallet_account_id, 'wallet_account_id')
|
|
91
|
+
client.get("/wallet_accounts/#{wallet_account_id}/bpay_details")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Pay a bill by withdrawing funds from a Wallet Account to a specified BPay account
|
|
95
|
+
#
|
|
96
|
+
# @param wallet_account_id [String] the wallet account ID
|
|
97
|
+
# @param attributes [Hash] bill payment attributes
|
|
98
|
+
# @option attributes [String] :account_id (Required) BPay account ID to withdraw to
|
|
99
|
+
# @option attributes [Integer] :amount (Required) Amount in cents to withdraw
|
|
100
|
+
# @option attributes [String] :reference_id Optional unique reference information
|
|
101
|
+
# @return [Response] the API response containing disbursement details
|
|
102
|
+
#
|
|
103
|
+
# @example Pay a bill
|
|
104
|
+
# wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
105
|
+
# response = wallet_accounts.pay_bill(
|
|
106
|
+
# '901d8cd0-6af3-0138-967d-0a58a9feac04',
|
|
107
|
+
# account_id: 'c1824ad0-73f1-0138-3700-0a58a9feac09',
|
|
108
|
+
# amount: 173,
|
|
109
|
+
# reference_id: 'test100'
|
|
110
|
+
# )
|
|
111
|
+
#
|
|
112
|
+
# @see https://developer.hellozai.com/reference
|
|
113
|
+
def pay_bill(wallet_account_id, **attributes)
|
|
114
|
+
validate_id!(wallet_account_id, 'wallet_account_id')
|
|
115
|
+
validate_pay_bill_attributes!(attributes)
|
|
116
|
+
|
|
117
|
+
body = build_pay_bill_body(attributes)
|
|
118
|
+
client.post("/wallet_accounts/#{wallet_account_id}/bill_payment", body: body)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def validate_id!(value, field_name)
|
|
124
|
+
return unless value.nil? || value.to_s.strip.empty?
|
|
125
|
+
|
|
126
|
+
raise Errors::ValidationError, "#{field_name} is required and cannot be blank"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def validate_pay_bill_attributes!(attributes)
|
|
130
|
+
validate_required_pay_bill_attributes!(attributes)
|
|
131
|
+
validate_amount!(attributes[:amount]) if attributes[:amount]
|
|
132
|
+
validate_reference_id!(attributes[:reference_id]) if attributes[:reference_id]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def validate_required_pay_bill_attributes!(attributes)
|
|
136
|
+
required_fields = %i[account_id amount]
|
|
137
|
+
|
|
138
|
+
missing_fields = required_fields.select do |field|
|
|
139
|
+
attributes[field].nil? || (attributes[field].respond_to?(:to_s) && attributes[field].to_s.strip.empty?)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
return if missing_fields.empty?
|
|
143
|
+
|
|
144
|
+
raise Errors::ValidationError,
|
|
145
|
+
"Missing required fields: #{missing_fields.join(', ')}"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def validate_amount!(amount)
|
|
149
|
+
# Amount must be a positive integer
|
|
150
|
+
return if amount.is_a?(Integer) && amount.positive?
|
|
151
|
+
|
|
152
|
+
raise Errors::ValidationError, 'amount must be a positive integer'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def validate_reference_id!(reference_id)
|
|
156
|
+
# Reference ID cannot contain single quote character
|
|
157
|
+
return unless reference_id.to_s.include?("'")
|
|
158
|
+
|
|
159
|
+
raise Errors::ValidationError, "reference_id cannot contain single quote (') character"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def build_pay_bill_body(attributes)
|
|
163
|
+
body = {}
|
|
164
|
+
|
|
165
|
+
attributes.each do |key, value|
|
|
166
|
+
next if value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
|
167
|
+
|
|
168
|
+
api_field = PAY_BILL_FIELD_MAPPING[key]
|
|
169
|
+
body[api_field] = value if api_field
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
body
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
data/lib/zai_payment/response.rb
CHANGED
|
@@ -7,8 +7,8 @@ module ZaiPayment
|
|
|
7
7
|
|
|
8
8
|
RESPONSE_DATA_KEYS = %w[
|
|
9
9
|
webhooks users items fees transactions
|
|
10
|
-
batch_transactions bpay_accounts bank_accounts card_accounts
|
|
11
|
-
wallet_accounts routing_number
|
|
10
|
+
batch_transactions batches bpay_accounts bank_accounts card_accounts
|
|
11
|
+
wallet_accounts routing_number disbursements
|
|
12
12
|
].freeze
|
|
13
13
|
|
|
14
14
|
def initialize(faraday_response)
|
data/lib/zai_payment/version.rb
CHANGED
data/lib/zai_payment.rb
CHANGED
|
@@ -16,6 +16,8 @@ require_relative 'zai_payment/resources/item'
|
|
|
16
16
|
require_relative 'zai_payment/resources/token_auth'
|
|
17
17
|
require_relative 'zai_payment/resources/bank_account'
|
|
18
18
|
require_relative 'zai_payment/resources/bpay_account'
|
|
19
|
+
require_relative 'zai_payment/resources/batch_transaction'
|
|
20
|
+
require_relative 'zai_payment/resources/wallet_account'
|
|
19
21
|
|
|
20
22
|
module ZaiPayment
|
|
21
23
|
class << self
|
|
@@ -69,5 +71,15 @@ module ZaiPayment
|
|
|
69
71
|
def bpay_accounts
|
|
70
72
|
@bpay_accounts ||= Resources::BpayAccount.new(client: Client.new(base_endpoint: :core_base))
|
|
71
73
|
end
|
|
74
|
+
|
|
75
|
+
# @return [ZaiPayment::Resources::BatchTransaction] batch_transaction resource instance (prelive only)
|
|
76
|
+
def batch_transactions
|
|
77
|
+
@batch_transactions ||= Resources::BatchTransaction.new(client: Client.new(base_endpoint: :core_base))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @return [ZaiPayment::Resources::WalletAccount] wallet_account resource instance
|
|
81
|
+
def wallet_accounts
|
|
82
|
+
@wallet_accounts ||= Resources::WalletAccount.new(client: Client.new(base_endpoint: :core_base))
|
|
83
|
+
end
|
|
72
84
|
end
|
|
73
85
|
end
|
data/readme.md
CHANGED
|
@@ -22,8 +22,11 @@ A lightweight and extensible Ruby client for the **Zai (AssemblyPay)** API โ s
|
|
|
22
22
|
- ๐ฅ **User Management** - Create and manage payin (buyers) & payout (sellers) users
|
|
23
23
|
- ๐ฆ **Item Management** - Full CRUD for transactions/payments between buyers and sellers
|
|
24
24
|
- ๐ฆ **Bank Account Management** - Complete CRUD + validation for AU/UK bank accounts
|
|
25
|
+
- ๐ณ **BPay Account Management** - Manage BPay accounts for Australian bill payments
|
|
26
|
+
- ๐ผ **Wallet Account Management** - Show wallet accounts, check balances, and pay bills via BPay
|
|
25
27
|
- ๐ซ **Token Auth** - Generate secure tokens for bank and card account data collection
|
|
26
28
|
- ๐ช **Webhooks** - Full CRUD + secure signature verification (HMAC SHA256)
|
|
29
|
+
- ๐งช **Batch Transactions** - Prelive-only endpoints for testing batch transaction flows
|
|
27
30
|
- โ๏ธ **Environment-Aware** - Seamless Pre-live / Production switching
|
|
28
31
|
- ๐งฑ **Modular & Extensible** - Clean resource-based architecture
|
|
29
32
|
- ๐งฐ **Zero Heavy Dependencies** - Lightweight, fast, and reliable
|
|
@@ -115,6 +118,48 @@ Manage bank accounts for Australian and UK users, with routing number validation
|
|
|
115
118
|
- ๐ก [Bank Account Examples](examples/bank_accounts.md) - Real-world patterns and integration
|
|
116
119
|
- ๐ [Zai: Bank Accounts API Reference](https://developer.hellozai.com/reference/showbankaccount)
|
|
117
120
|
|
|
121
|
+
### BPay Accounts
|
|
122
|
+
|
|
123
|
+
Manage BPay accounts for Australian bill payments.
|
|
124
|
+
|
|
125
|
+
**๐ Documentation:**
|
|
126
|
+
- ๐ [BPay Account Management Guide](docs/bpay_accounts.md) - Complete guide for BPay accounts
|
|
127
|
+
- ๐ก [BPay Account Examples](examples/bpay_accounts.md) - Real-world patterns and bill payment workflows
|
|
128
|
+
- ๐ [Zai: BPay Accounts API Reference](https://developer.hellozai.com/reference/createbpayaccount)
|
|
129
|
+
|
|
130
|
+
### Wallet Accounts
|
|
131
|
+
|
|
132
|
+
Manage wallet accounts, check balances, and pay bills via BPay.
|
|
133
|
+
|
|
134
|
+
**๐ Documentation:**
|
|
135
|
+
- ๐ [Wallet Account Management Guide](docs/wallet_accounts.md) - Complete guide for wallet accounts
|
|
136
|
+
- ๐ก [Wallet Account Examples](examples/wallet_accounts.md) - Real-world patterns and payment workflows
|
|
137
|
+
- ๐ [Zai: Wallet Accounts API Reference](https://developer.hellozai.com/reference)
|
|
138
|
+
|
|
139
|
+
**Quick Example:**
|
|
140
|
+
```ruby
|
|
141
|
+
wallet_accounts = ZaiPayment::Resources::WalletAccount.new
|
|
142
|
+
|
|
143
|
+
# Check wallet balance
|
|
144
|
+
response = wallet_accounts.show('wallet_account_id')
|
|
145
|
+
balance = response.data['balance'] # in cents
|
|
146
|
+
puts "Balance: $#{balance / 100.0}"
|
|
147
|
+
|
|
148
|
+
# Pay a bill from wallet to BPay account
|
|
149
|
+
payment_response = wallet_accounts.pay_bill(
|
|
150
|
+
'wallet_account_id',
|
|
151
|
+
account_id: 'bpay_account_id',
|
|
152
|
+
amount: 17300, # $173.00 in cents
|
|
153
|
+
reference_id: 'bill_nov_2024'
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if payment_response.success?
|
|
157
|
+
disbursement = payment_response.data
|
|
158
|
+
puts "Payment successful: #{disbursement['id']}"
|
|
159
|
+
puts "State: #{disbursement['state']}"
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
118
163
|
### Token Auth
|
|
119
164
|
|
|
120
165
|
Generate secure tokens for collecting bank and card account information.
|
|
@@ -133,6 +178,35 @@ Manage webhook endpoints with secure signature verification.
|
|
|
133
178
|
- ๐๏ธ [Architecture & Implementation](docs/webhooks.md) - Detailed technical documentation
|
|
134
179
|
- ๐ [Signature Verification Details](docs/webhook_signature.md) - Security implementation specs
|
|
135
180
|
|
|
181
|
+
### Batch Transactions (Prelive Only)
|
|
182
|
+
|
|
183
|
+
Simulate batch transaction processing for testing in the prelive environment.
|
|
184
|
+
|
|
185
|
+
**๐ Documentation:**
|
|
186
|
+
- ๐ [Batch Transaction Guide](docs/batch_transactions.md) - Complete guide and method reference
|
|
187
|
+
- ๐ก [Batch Transaction Examples](examples/batch_transactions.md) - Testing workflows and webhook simulation
|
|
188
|
+
- โ ๏ธ **Note:** These endpoints are only available in prelive environment
|
|
189
|
+
|
|
190
|
+
**Quick Example:**
|
|
191
|
+
```ruby
|
|
192
|
+
# Export pending transactions to batched state
|
|
193
|
+
export_response = ZaiPayment.batch_transactions.export_transactions
|
|
194
|
+
batch_id = export_response.data.first['batch_id']
|
|
195
|
+
transaction_ids = export_response.data.map { |t| t['id'] }
|
|
196
|
+
|
|
197
|
+
# Move to bank_processing state
|
|
198
|
+
ZaiPayment.batch_transactions.process_to_bank_processing(
|
|
199
|
+
batch_id,
|
|
200
|
+
exported_ids: transaction_ids
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Complete processing (triggers webhooks)
|
|
204
|
+
ZaiPayment.batch_transactions.process_to_successful(
|
|
205
|
+
batch_id,
|
|
206
|
+
exported_ids: transaction_ids
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
136
210
|
### Error Handling
|
|
137
211
|
|
|
138
212
|
The gem provides specific error classes for different scenarios:
|
|
@@ -167,10 +241,12 @@ end
|
|
|
167
241
|
| โ
Users | Manage PayIn / PayOut users | Done |
|
|
168
242
|
| โ
Items | Transactions/payments (CRUD) | Done |
|
|
169
243
|
| โ
Bank Accounts | AU/UK bank accounts + validation | Done |
|
|
244
|
+
| โ
BPay Accounts | Manage BPay accounts | Done |
|
|
245
|
+
| โ
Wallet Accounts | Show, check balance, pay bills | Done |
|
|
170
246
|
| โ
Token Auth | Generate bank/card tokens | Done |
|
|
171
|
-
|
|
|
247
|
+
| โ
Batch Transactions (Prelive) | Simulate batch processing flows | Done |
|
|
248
|
+
| โ
Payments | Single and recurring payments | Done |
|
|
172
249
|
| ๐ฆ Virtual Accounts (VA / PIPU) | Manage virtual accounts & PayTo | โณ Planned |
|
|
173
|
-
| ๐ผ Wallets | Create and manage wallet accounts | โณ Planned |
|
|
174
250
|
|
|
175
251
|
## ๐งช Development
|
|
176
252
|
|
|
@@ -227,6 +303,8 @@ Everyone interacting in the ZaiPayment project's codebases, issue trackers, chat
|
|
|
227
303
|
- [**User Management Guide**](docs/users.md) - Managing payin and payout users
|
|
228
304
|
- [**Item Management Guide**](docs/items.md) - Creating and managing transactions/payments
|
|
229
305
|
- [**Bank Account Guide**](docs/bank_accounts.md) - Managing bank accounts for AU/UK users
|
|
306
|
+
- [**BPay Account Guide**](docs/bpay_accounts.md) - Managing BPay accounts for Australian bill payments
|
|
307
|
+
- [**Wallet Account Guide**](docs/wallet_accounts.md) - Managing wallet accounts, checking balances, and paying bills
|
|
230
308
|
- [**Webhook Examples**](examples/webhooks.md) - Complete webhook usage guide
|
|
231
309
|
- [**Documentation Index**](docs/readme.md) - Full documentation navigation
|
|
232
310
|
|
|
@@ -234,12 +312,16 @@ Everyone interacting in the ZaiPayment project's codebases, issue trackers, chat
|
|
|
234
312
|
- [User Examples](examples/users.md) - Real-world user management patterns
|
|
235
313
|
- [Item Examples](examples/items.md) - Transaction and payment workflows
|
|
236
314
|
- [Bank Account Examples](examples/bank_accounts.md) - Bank account integration patterns
|
|
315
|
+
- [BPay Account Examples](examples/bpay_accounts.md) - BPay account integration patterns
|
|
316
|
+
- [Wallet Account Examples](examples/wallet_accounts.md) - Wallet account and bill payment workflows
|
|
237
317
|
- [Token Auth Examples](examples/token_auths.md) - Secure token generation and integration
|
|
238
318
|
- [Webhook Examples](examples/webhooks.md) - Webhook integration patterns
|
|
319
|
+
- [Batch Transaction Examples](examples/batch_transactions.md) - Testing batch transaction flows (prelive only)
|
|
239
320
|
|
|
240
321
|
### Technical Guides
|
|
241
322
|
- [Webhook Architecture](docs/webhooks.md) - Technical implementation details
|
|
242
323
|
- [Architecture Overview](docs/architecture.md) - System architecture and design
|
|
324
|
+
- [**Direct API Usage Guide**](docs/direct_api_usage.md) - ๐ฅ How to call unimplemented APIs directly
|
|
243
325
|
|
|
244
326
|
### Security
|
|
245
327
|
- [Webhook Security Quick Start](docs/webhook_security_quickstart.md) - 5-minute setup guide
|