spree/spree · error · Spree::Core::GatewayError
gateway_error
gateway_error
Error message
Unable to connect to gateway.
What it means
Raised by Spree::Refund#process! when the refund request never reaches a gateway response: the underlying payment call raises Spree::PaymentConnectionError (timeout, DNS failure, refused connection) and it is rescued and re-raised as Spree::Core::GatewayError with the translated 'unable_to_connect_to_gateway' text. It is distinct from a declined refund, which surfaces the gateway's own decline message instead. It means the state of the refund at the provider is unknown, not that the refund failed.
Source
Thrown at spree/core/app/models/spree/refund.rb:154
if payment.payment_method.payment_profiles_supported?
payment.payment_method.credit(refund_total_in_cents, payment.source, payment.transaction_id, originator: self)
else
payment.payment_method.credit(refund_total_in_cents, payment.transaction_id, originator: self)
end
end
if response.success?
track_order_as_refunded(refund_total_in_cents)
else
Rails.logger.error(Spree.t(:gateway_error) + " #{response.to_yaml}")
text = response.params['message'] || response.params['response_reason_text'] || response.message
raise Core::GatewayError, text
end
response
rescue Spree::PaymentConnectionError => e
Rails.logger.error(Spree.t(:gateway_error) + " #{e.inspect}")
raise Core::GatewayError, Spree.t(:unable_to_connect_to_gateway)
end
def calculate_refund_amount(credit_cents)
# Overwrite this for more complex calculations
credit_cents
end
def track_order_as_refunded(credit_cents)
# You can track refunds here
end
def amount_is_less_than_or_equal_to_allowed_amount
if amount > payment.credit_allowed
errors.add(:amount, :greater_than_allowed)
end
end
# Re-sums the order this refund put right. Read through the refund's ownView on GitHub (pinned to 06bf66a868)
Solutions
- Verify network egress from the app host to the payment provider API (curl the provider's API hostname) and fix firewall/proxy/VPN issues.
- Check the payment method's configuration (mode, API endpoint, credentials) in the Admin API and correct any wrong values.
- Before retrying, check the provider's dashboard (or API) for whether the refund actually landed despite the connection error — a timed-out request may still have been processed.
- Retry the refund once connectivity is confirmed; if the provider is unreachable for an extended period, refund in the provider console and record it, or queue the refund for a background retry job.
Example fix
# before
refund.process!(amount_in_cents)
# after — handle connection loss distinctly from a decline
begin
refund.process!(amount_in_cents)
rescue Spree::Core::GatewayError => e
if e.message == Spree.t(:unable_to_connect_to_gateway)
Rails.logger.warn("Refund #{refund.number} not confirmed at gateway: #{e.message}")
RefundRetryJob.perform_later(refund.id, amount_in_cents) # verifies state at gateway before retrying
else
raise # real decline: surface it
end
end Defensive patterns
Strategy: retry
Try / catch
begin
refund.process!(amount_in_cents)
rescue Spree::Core::GatewayError => e
if e.message == Spree.t(:unable_to_connect_to_gateway)
# outcome unknown: check the refund at the provider BEFORE retrying,
# then retry once from a background job — never blind-loop
RefundVerificationJob.perform_later(refund.id)
else
raise # decline or other gateway error: surface, do not retry
end
end Prevention
- Monitor egress from app hosts to payment provider API domains and alert on connection-level failures.
- Wrap refund execution in jobs with bounded retry so transient network blips self-heal without manual work.
- Log the refunded payment's transaction id before attempting the gateway call so an unknown-outcome refund can be reconciled later.
- Never auto-retry on an interval without first checking whether the refund landed at the provider — duplicate refunds are the real risk here.
When it happens
Trigger: Calling refund.process! / creating a refund through Payments::Refund while the app host cannot open a connection to the payment provider's API: network outage, egress firewall blocking the provider hostname, wrong API endpoint configured on the payment method, or the provider itself being down.
Common situations: Local development without internet access, staging servers behind restrictive proxies, a typo in the gateway URL preference, provider-side incidents, VPN dropping mid-request, or test suites accidentally hitting live gateway endpoints.
Related errors
- No payment found
- gateway_error
- Presign failed: ${err instanceof Error ? err.message : err}
- Email is required
- Locale is required
AI-assisted analysis of spree/spree@06bf66a868 (2026-08-21).
Data as JSON: /api/errors/03d0bc449501b78e.
Report an issue: GitHub.