Class: Clickwrap::ExternalAction
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Clickwrap::ExternalAction
- Defined in:
- lib/clickwrap/models/external_action.rb
Overview
The outbox row for an action Clickwrap cannot make atomic.
Stripe, an identity provider, a timestamp authority: none of them can join your database transaction, and pretending otherwise is how a provider timeout becomes either a fictional success or a second debit. So the local transaction commits a pending authorization and an idempotency key, the provider is called outside it, and the outcome is appended back idempotently.
unknown is a first-class state, not an error state. A timeout is not a
failure — it is an absence of information — and the reconciliation task
exists precisely to resolve those later rather than guessing now.
Constant Summary collapse
- STATES =
%w[pending succeeded failed unknown].freeze
Instance Method Summary collapse
- #pending? ⇒ Boolean
- #record_provider_failure!(reason:, provider_receipt: nil) ⇒ Object
-
#record_provider_outcome_unknown!(reason:) ⇒ Object
For the genuinely ambiguous case: the request may or may not have been carried out, and the honest record says so rather than picking one.
-
#record_provider_success_and_consume!(provider_receipt = nil) ⇒ Object
Each resolution is one idempotent local transaction.
- #resolved? ⇒ Boolean
- #to_s ⇒ Object
Instance Method Details
#pending? ⇒ Boolean
36 |
# File 'lib/clickwrap/models/external_action.rb', line 36 def pending? = state == "pending" |
#record_provider_failure!(reason:, provider_receipt: nil) ⇒ Object
49 50 51 |
# File 'lib/clickwrap/models/external_action.rb', line 49 def record_provider_failure!(reason:, provider_receipt: nil) resolve!("failed", provider_receipt: provider_receipt, failure_reason: reason) end |
#record_provider_outcome_unknown!(reason:) ⇒ Object
For the genuinely ambiguous case: the request may or may not have been carried out, and the honest record says so rather than picking one.
55 56 57 |
# File 'lib/clickwrap/models/external_action.rb', line 55 def record_provider_outcome_unknown!(reason:) resolve!("unknown", failure_reason: reason, resolved: false) end |
#record_provider_success_and_consume!(provider_receipt = nil) ⇒ Object
Each resolution is one idempotent local transaction. Calling it twice with the same outcome is a no-op; calling it with a different outcome after the action already resolved raises, because silently overwriting "succeeded" with "failed" would rewrite the record of what the provider told us.
43 44 45 46 47 |
# File 'lib/clickwrap/models/external_action.rb', line 43 def record_provider_success_and_consume!(provider_receipt = nil) resolve!("succeeded", provider_receipt: provider_receipt) do Lifecycle.(event: event, because: "External action succeeded") end end |
#resolved? ⇒ Boolean
37 |
# File 'lib/clickwrap/models/external_action.rb', line 37 def resolved? = %w[succeeded failed].include?(state) |
#to_s ⇒ Object
59 |
# File 'lib/clickwrap/models/external_action.rb', line 59 def to_s = "external action #{idempotency_key} (#{state})" |