Class: Portage::Ucp::Confirmer::Webhook

Inherits:
Object
  • Object
show all
Includes:
Support::HttpClient
Defined in:
lib/portage/ucp/confirmer.rb

Overview

Out-of-band approval over plain HTTP: POST the request, then poll a status endpoint until the out-of-band channel (Slack, WhatsApp, whatever a caller wires up) records an answer. Core only ever speaks HTTP here — the actual notification transport is the caller's job, same "transport-agnostic by design" posture as this module's top-of-file comment.

Deliberately its own timeout default, not Terminal

DEFAULT_TIMEOUT_SECONDS: 120s fits a human already at a keyboard, not someone who has to notice a Slack message and tap approve.

Constant Summary collapse

DEFAULT_TIMEOUT_SECONDS =
900
DEFAULT_POLL_INTERVAL_SECONDS =
5

Constants included from Support::HttpClient

Support::HttpClient::DEFAULT_OPEN_TIMEOUT, Support::HttpClient::DEFAULT_READ_TIMEOUT

Instance Method Summary collapse

Constructor Details

#initialize(confirm_url:, status_url:, timeout_seconds: DEFAULT_TIMEOUT_SECONDS, poll_interval_seconds: DEFAULT_POLL_INTERVAL_SECONDS, headers: {}, wait: nil) ⇒ Webhook

Returns a new instance of Webhook.

Parameters:

  • confirm_url (String) —

    posted {amount, currency, merchant, idempotency_key} once, to kick off the out-of-band approval.

  • status_url (String) —

    polled (GET, ?idempotency_key=...) for {"status" => "approved" | "denied" | "pending"} until it stops answering "pending" or timeout_seconds elapses.

  • wait (#call, nil) (defaults to: nil) —

    escape hatch for push-based transports — when given, called with idempotency_key instead of polling, and must itself return "approved" or "denied" (blocking as long as it needs to; timeout_seconds isn't enforced around it, since a push transport is expected to enforce its own).



116
117
118
119
120
121
122
123
124
# File 'lib/portage/ucp/confirmer.rb', line 116

def initialize(confirm_url:, status_url:, timeout_seconds: DEFAULT_TIMEOUT_SECONDS,
               poll_interval_seconds: DEFAULT_POLL_INTERVAL_SECONDS, headers: {}, wait: nil)
  @confirm_url = confirm_url
  @status_url = status_url
  @timeout_seconds = timeout_seconds
  @poll_interval_seconds = poll_interval_seconds
  @headers = headers
  @wait = wait
end

Instance Method Details

#confirm!(amount:, currency:, merchant:, idempotency_key:) ⇒ Hash

Returns {approved: true} on approval.

Returns:

  • (Hash) —

    {approved: true} on approval.

Raises:



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/portage/ucp/confirmer.rb', line 131

def confirm!(amount:, currency:, merchant:, idempotency_key:)
  json_request(Net::HTTP::Post, @confirm_url, route: :notify,
                                              body: { amount: amount, currency: currency, merchant: merchant,
                                                      idempotency_key: idempotency_key },
                                              headers: @headers)

  status = @wait ? @wait.call(idempotency_key) : poll(idempotency_key)
  return { approved: true } if status == "approved"

  deny!(status == "denied" ? :denied : :timeout, idempotency_key)
end