Class: Mpp::Methods::Tempo::Relay

Inherits:
Object
  • Object
show all
Defined in:
lib/mpp/methods/tempo/relay.rb

Overview

HTTP client for a Tempo API-compatible MPP relay.

resolve accepts:

* a URL string (unauthenticated relay)
* a config hash (`url:` plus optional `headers:`, or mppx-style
`api_base_url:` / `api_key:`)
* any object that responds to `#validate` and `#broadcast`

Constant Summary collapse

DEFAULT_API_BASE_URL =
"https://api.tempo.xyz"
VALIDATE_PATH =
"/v1/mpp/validate"
BROADCAST_PATH =
"/v1/mpp/broadcast"
DEFAULT_TIMEOUT =
Rpc::DEFAULT_TIMEOUT
ERROR_CODES =
%w[
  already_used
  broadcast_failed
  expired
  invalid_payment
  insufficient_funds
  policy_denied
  screen_rejected
  simulation_failed
  temporarily_unavailable
  unsupported
  unknown
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url, headers: nil) ⇒ Relay

Returns a new instance of Relay.

Raises:

  • (ArgumentError)


44
45
46
47
48
49
# File 'lib/mpp/methods/tempo/relay.rb', line 44

def initialize(url, headers: nil)
  @base_url = Mpp::Http::Headers.normalize_base_url(url)
  raise ArgumentError, "relay url is required" if @base_url.empty?

  @headers = headers
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



42
43
44
# File 'lib/mpp/methods/tempo/relay.rb', line 42

def base_url
  @base_url
end

Class Method Details

.duck_type?(relay) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/mpp/methods/tempo/relay.rb', line 74

def self.duck_type?(relay)
  !relay.nil? && relay.respond_to?(:validate) && relay.respond_to?(:broadcast)
end

.from_config(config) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/mpp/methods/tempo/relay.rb', line 66

def self.from_config(config)
  cfg = Mpp::Http::Headers.symbolize(config)
  url = cfg[:url] || cfg[:api_base_url] || cfg[:apiBaseUrl] || cfg[:base_url] || cfg[:baseUrl]
  url = DEFAULT_API_BASE_URL if url.nil? || url.to_s.empty?
  headers = merge_api_key_headers(cfg[:headers], cfg[:api_key] || cfg[:apiKey])
  new(url.to_s, headers: headers)
end

.resolve(relay) ⇒ Object

Raises:

  • (ArgumentError)


57
58
59
60
61
62
63
64
# File 'lib/mpp/methods/tempo/relay.rb', line 57

def self.resolve(relay)
  return relay if relay.is_a?(Relay)
  return new(relay) if relay.is_a?(String) && !relay.empty?
  return from_config(relay) if relay.is_a?(Hash)
  return relay if duck_type?(relay)

  raise ArgumentError, "relay must be a URL, {url:, headers:}, or an object that implements #validate and #broadcast"
end

.resolve_optional(relay) ⇒ Object



51
52
53
54
55
# File 'lib/mpp/methods/tempo/relay.rb', line 51

def self.resolve_optional(relay)
  return if relay.nil? || relay == false

  resolve(relay)
end

Instance Method Details

#broadcast(input) ⇒ Object



112
113
114
115
116
117
118
119
# File 'lib/mpp/methods/tempo/relay.rb', line 112

def broadcast(input)
  response = post(BROADCAST_PATH, input, extra_headers: {
    "Idempotency-Key" => idempotency_key(input)
  })
  raise failure(response) unless broadcast_success?(response)

  to_receipt(response.fetch("receipt"))
end

#validate(input) ⇒ Object



105
106
107
108
109
110
# File 'lib/mpp/methods/tempo/relay.rb', line 105

def validate(input)
  response = post(VALIDATE_PATH, input)
  raise failure(response) unless success?(response)

  true
end

#verify(credential, _request = nil) ⇒ Object



99
100
101
102
103
# File 'lib/mpp/methods/tempo/relay.rb', line 99

def verify(credential, _request = nil)
  input = to_relay_input(credential)
  validate(input)
  broadcast(input)
end