Class: Weft::Facilitator::RackMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/weft/facilitator/middleware.rb

Constant Summary collapse

PAYMENT_HEADERS =
%w[HTTP_X_PAYMENT HTTP_PAYMENT_SIGNATURE].freeze
X402_VERSION =
2

Instance Method Summary collapse

Constructor Details

#initialize(app, routes: [], config: {}) ⇒ RackMiddleware

Returns a new instance of RackMiddleware.

Parameters:

  • app (#call)

    The downstream Rack app

  • routes (Array<Hash>) (defaults to: [])

    Route payment configurations

  • config (Hash) (defaults to: {})

    Options including :facilitator_url



37
38
39
40
41
42
43
44
# File 'lib/weft/facilitator/middleware.rb', line 37

def initialize(app, routes: [], config: {})
  @app = app
  @config = config
  @routes = routes.map do |r|
    r.is_a?(RouteConfig) ? r : RouteConfig.new(**r)
  end
  @client = Client.new(url: config[:facilitator_url])
end

Instance Method Details

#call(env) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/weft/facilitator/middleware.rb', line 46

def call(env)
  return @app.call(env) if @routes.empty?

  request_path = env['PATH_INFO'] || '/'
  route = match_route(request_path)

  return @app.call(env) unless route

  payment_header = extract_payment_header(env)

  return payment_required_response(route, env) unless payment_header

  # Verify the payment via facilitator
  payment_payload = parse_payment(payment_header)
  requirements = build_requirements(route, env)

  begin
    verify_result = @client.verify(
      payment_payload: payment_payload,
      payment_requirements: requirements
    )
  rescue StandardError => e
    return error_response(402, "Payment verification failed: #{e.message}")
  end

  unless verify_result['valid']
    return error_response(402, verify_result['message'] || 'Payment verification failed')
  end

  # Call the downstream app
  status, headers, body = @app.call(env)

  # If the app returned an error, don't settle
  return [status, headers, body] if status >= 400

  # Settle the payment
  begin
    settle_result = @client.settle(
      payment_payload: payment_payload,
      payment_requirements: requirements
    )

    return error_response(402, "Settlement failed: #{settle_result['message']}") unless settle_result['success']

    headers['X-Payment-TxHash'] = settle_result['txHash'] if settle_result['txHash']
  rescue StandardError => e
    return error_response(402, "Settlement failed: #{e.message}")
  end

  [status, headers, body]
end