Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
zai_payment (2.8.6)
zai_payment (2.9.0)
base64 (~> 0.3.0)
faraday (~> 2.0)
openssl (~> 3.3)
Expand Down
22 changes: 22 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
## [Released]

## [2.9.0] - 2025-12-16

### Added
- **Webhook Jobs API**: Retrieve and monitor webhook delivery jobs 📋
- `ZaiPayment.webhooks.list_jobs(webhook_id, limit:, offset:, status:, object_id:)` - List jobs associated with a webhook
- `ZaiPayment.webhooks.show_job(webhook_id, job_id)` - Get details of a specific webhook job
- Support for pagination with `limit` (1-200) and `offset` parameters
- Support for filtering by `status` ('success' or 'failed')
- Support for filtering by `object_id`
- Job details include: `uuid`, `webhook_uuid`, `object_id`, `payload`, `request_responses`, `created_at`, `updated_at`
- Request responses include delivery attempts with `response_code`, `message`, and timestamps
- Validation for status parameter (must be 'success' or 'failed')
- Full RSpec test suite with 21 test examples
- Comprehensive YARD documentation with examples

### Enhanced
- **Response Class**: Added `jobs` to `RESPONSE_DATA_KEYS` for automatic data extraction
- `response.data` now properly extracts jobs array from list_jobs responses
- Consistent with other resource response handling

**Full Changelog**: https://github.com/Sentia/zai-payment/compare/v2.8.6...v2.9.0

## [2.8.6] - 2025-12-12

### Fixed
Expand Down
64 changes: 64 additions & 0 deletions lib/zai_payment/resources/webhook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,62 @@ def delete(webhook_id)
client.delete("/webhooks/#{webhook_id}")
end

# List jobs associated with a webhook
#
# Retrieves an ordered and paginated list of jobs garnered from a webhook.
#
# @param webhook_id [String] the webhook ID (UUID)
# @param limit [Integer] number of records to retrieve (1-200, default: 10)
# @param offset [Integer] number of records to skip (default: 0)
# @param status [String] filter by status ('success', 'failed', or nil for all)
# @param object_id [String] filter by object_id
# @return [Response] the API response containing jobs array
#
# @example List all jobs for a webhook
# webhooks = ZaiPayment::Resources::Webhook.new
# response = webhooks.list_jobs("webhook_uuid")
# response.data # => [{"id" => "...", "status" => "success", ...}, ...]
#
# @example Filter jobs by status
# response = webhooks.list_jobs("webhook_uuid", status: "failed")
#
# @example Paginate through jobs
# response = webhooks.list_jobs("webhook_uuid", limit: 50, offset: 100)
#
# @see https://developer.hellozai.com/reference/getjobs
def list_jobs(webhook_id, limit: 10, offset: 0, status: nil, object_id: nil)
validate_id!(webhook_id, 'webhook_id')
validate_job_status!(status) if status

params = {
limit: limit,
offset: offset
}
params[:status] = status if status
params[:object_id] = object_id if object_id

client.get("/webhooks/#{webhook_id}/jobs", params: params)
end

# Show a specific job associated with a webhook
#
# @param webhook_id [String] the webhook ID (UUID)
# @param job_id [String] the job ID
# @return [Response] the API response containing job details
#
# @example
# webhooks = ZaiPayment::Resources::Webhook.new
# response = webhooks.show_job("webhook_uuid", "job_id")
# response.data # => {"id" => "job_id", "status" => "success", ...}
#
# @see https://developer.hellozai.com/reference/getjob
def show_job(webhook_id, job_id)
validate_id!(webhook_id, 'webhook_id')
validate_id!(job_id, 'job_id')

client.get("/webhooks/#{webhook_id}/jobs/#{job_id}")
end

# Create a secret key for webhook signature verification
#
# @param secret_key [String] the secret key to use for HMAC signature generation
Expand Down Expand Up @@ -262,6 +318,14 @@ def validate_secret_key!(secret_key)
raise Errors::ValidationError, 'secret_key must be at least 32 bytes in size'
end

def validate_job_status!(status)
valid_statuses = %w[success failed]
return if valid_statuses.include?(status)

raise Errors::ValidationError,
"status must be one of: #{valid_statuses.join(', ')}"
end

def parse_signature_header(header)
# Format: "t=1257894000,v=signature1,v=signature2"
parts = header.split(',').map(&:strip)
Expand Down
2 changes: 1 addition & 1 deletion lib/zai_payment/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Response
attr_reader :status, :body, :headers, :raw_response

RESPONSE_DATA_KEYS = %w[
webhooks users items fees transactions
webhooks users items fees transactions jobs
batch_transactions batches bpay_accounts bank_accounts card_accounts
wallet_accounts virtual_accounts disbursements pay_ids
].freeze
Expand Down
2 changes: 1 addition & 1 deletion lib/zai_payment/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module ZaiPayment
VERSION = '2.8.6'
VERSION = '2.9.0'
end
Loading