Skip to content

[BUG] Possible Stripe Charge / Invoice race condition #1196

@alexrudall

Description

@alexrudall

Bug Report

Describe the Bug:
Thanks for this excellent library!

Not sure if this is me doing something wrong, but there seems to be a possible race condition when a Stripe invoice is not completed(?) fast enough during Pay::Charge creation causing invoice.to_hash to raise a NoMethodError when invoice is a string ID. It occurs intermittently, usually the full invoice object is returned, but sometimes only the ID seems to be available in the response.

The error is raised in app/models/pay/stripe/charge.rb on line 53, attrs[:stripe_invoice] = invoice.to_hash

EDIT: I'm not using the Stripe CLI locally currently, would this be the fix for this?

To Reproduce:
I am only able to reproduce through repeatedly creating a test payment. About 1/5 times, it creates this error.

Expected Behavior:
The library could wait and retry, or otherwise raise an error with a clearer explanation.

Actual Behavior:
This error is raised: NoMethodError: undefined method 'to_hash' for an instance of String

Environment:

  • Pay gem version: 11.2
  • Ruby version: ruby 3.4.5 (2025-07-16 revision 20cda200d3) +PRISM [aarch64-linux]
  • Rails version: Rails 8.0.2.1
  • Operating System: Docker image ruby:3.4-slim, which uses debian/13. Hosted on macOS Sequoia 15.6.1 (24G90)

Additional Context:

Full trace:

pay (11.2.2) app/models/pay/stripe/charge.rb:53:in 'Pay::Stripe::Charge.sync'
pay (11.2.2) lib/pay/stripe.rb:151:in 'Pay::Stripe.sync_checkout_session'
pay (11.2.2) lib/pay.rb:138:in 'block in <module:Pay>'
pay (11.2.2) lib/pay.rb:146:in 'block in Pay.sync'
pay (11.2.2) lib/pay.rb:144:in 'Hash#each'
pay (11.2.2) lib/pay.rb:144:in 'Pay.sync'
app/controllers/checkout/returns_controller.rb:6:in 'Checkout::ReturnsController#show'
actionpack (8.0.2.1) lib/action_controller/metal/basic_implicit_render.rb:8:in 'ActionController::BasicImplicitRender#send_action'
actionpack (8.0.2.1) lib/abstract_controller/base.rb:226:in 'AbstractController::Base#process_action'
actionpack (8.0.2.1) lib/action_controller/metal/rendering.rb:193:in 'ActionController::Rendering#process_action'
actionpack (8.0.2.1) lib/abstract_controller/callbacks.rb:261:in 'block in AbstractController::Callbacks#process_action'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:120:in 'block in ActiveSupport::Callbacks#run_callbacks'
i18n (1.14.7) lib/i18n.rb:353:in 'I18n::Base#with_locale'
app/controllers/concerns/set_locale.rb:13:in 'SetLocale#set_locale'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'
turbo-rails (2.0.16) lib/turbo-rails.rb:24:in 'Turbo.with_request_id'
turbo-rails (2.0.16) app/controllers/concerns/turbo/request_id_tracking.rb:10:in 'Turbo::RequestIdTracking#turbo_tracking_request_id'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'
ahoy_matey (5.4.0) lib/ahoy/controller.rb:50:in 'Ahoy::Controller#set_ahoy_request_store'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'
actiontext (8.0.2.1) lib/action_text/rendering.rb:25:in 'ActionText::Rendering::ClassMethods#with_renderer'
actiontext (8.0.2.1) lib/action_text/engine.rb:71:in 'block (4 levels) in <class:Engine>'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:129:in 'BasicObject#instance_exec'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:140:in 'ActiveSupport::Callbacks#run_callbacks'
actionpack (8.0.2.1) lib/abstract_controller/callbacks.rb:260:in 'AbstractController::Callbacks#process_action'
actionpack (8.0.2.1) lib/action_controller/metal/rescue.rb:27:in 'ActionController::Rescue#process_action'
actionpack (8.0.2.1) lib/action_controller/metal/instrumentation.rb:76:in 'block in ActionController::Instrumentation#process_action'
activesupport (8.0.2.1) lib/active_support/notifications.rb:210:in 'block in ActiveSupport::Notifications.instrument'
activesupport (8.0.2.1) lib/active_support/notifications/instrumenter.rb:58:in 'ActiveSupport::Notifications::Instrumenter#instrument'
activesupport (8.0.2.1) lib/active_support/notifications.rb:210:in 'ActiveSupport::Notifications.instrument'
actionpack (8.0.2.1) lib/action_controller/metal/instrumentation.rb:75:in 'ActionController::Instrumentation#process_action'
actionpack (8.0.2.1) lib/action_controller/metal/params_wrapper.rb:259:in 'ActionController::ParamsWrapper#process_action'
activerecord (8.0.2.1) lib/active_record/railties/controller_runtime.rb:39:in 'ActiveRecord::Railties::ControllerRuntime#process_action'
actionpack (8.0.2.1) lib/abstract_controller/base.rb:163:in 'AbstractController::Base#process'
actionview (8.0.2.1) lib/action_view/rendering.rb:40:in 'ActionView::Rendering#process'
actionpack (8.0.2.1) lib/action_controller/metal.rb:252:in 'ActionController::Metal#dispatch'
actionpack (8.0.2.1) lib/action_controller/metal.rb:335:in 'ActionController::Metal.dispatch'
actionpack (8.0.2.1) lib/action_dispatch/routing/route_set.rb:67:in 'ActionDispatch::Routing::RouteSet::Dispatcher#dispatch'
actionpack (8.0.2.1) lib/action_dispatch/routing/route_set.rb:50:in 'ActionDispatch::Routing::RouteSet::Dispatcher#serve'
actionpack (8.0.2.1) lib/action_dispatch/journey/router.rb:53:in 'block in ActionDispatch::Journey::Router#serve'
actionpack (8.0.2.1) lib/action_dispatch/journey/router.rb:133:in 'block in ActionDispatch::Journey::Router#find_routes'
actionpack (8.0.2.1) lib/action_dispatch/journey/router.rb:126:in 'Array#each'
actionpack (8.0.2.1) lib/action_dispatch/journey/router.rb:126:in 'ActionDispatch::Journey::Router#find_routes'
actionpack (8.0.2.1) lib/action_dispatch/journey/router.rb:34:in 'ActionDispatch::Journey::Router#serve'
actionpack (8.0.2.1) lib/action_dispatch/routing/route_set.rb:908:in 'ActionDispatch::Routing::RouteSet#call'
omniauth (2.1.3) lib/omniauth/strategy.rb:202:in 'OmniAuth::Strategy#call!'
omniauth (2.1.3) lib/omniauth/strategy.rb:169:in 'OmniAuth::Strategy#call'
omniauth (2.1.3) lib/omniauth/strategy.rb:202:in 'OmniAuth::Strategy#call!'
omniauth (2.1.3) lib/omniauth/strategy.rb:169:in 'OmniAuth::Strategy#call'
warden (1.2.9) lib/warden/manager.rb:36:in 'block in Warden::Manager#call'
warden (1.2.9) lib/warden/manager.rb:34:in 'Kernel#catch'
warden (1.2.9) lib/warden/manager.rb:34:in 'Warden::Manager#call'
rack (3.2.1) lib/rack/tempfile_reaper.rb:20:in 'Rack::TempfileReaper#call'
rack (3.2.1) lib/rack/etag.rb:29:in 'Rack::ETag#call'
rack (3.2.1) lib/rack/conditional_get.rb:31:in 'Rack::ConditionalGet#call'
rack (3.2.1) lib/rack/head.rb:15:in 'Rack::Head#call'
actionpack (8.0.2.1) lib/action_dispatch/http/permissions_policy.rb:38:in 'ActionDispatch::PermissionsPolicy::Middleware#call'
actionpack (8.0.2.1) lib/action_dispatch/http/content_security_policy.rb:38:in 'ActionDispatch::ContentSecurityPolicy::Middleware#call'
rack-session (2.1.1) lib/rack/session/abstract/id.rb:274:in 'Rack::Session::Abstract::Persisted#context'
rack-session (2.1.1) lib/rack/session/abstract/id.rb:268:in 'Rack::Session::Abstract::Persisted#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/cookies.rb:706:in 'ActionDispatch::Cookies#call'
activerecord (8.0.2.1) lib/active_record/migration.rb:671:in 'ActiveRecord::Migration::CheckPending#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/callbacks.rb:31:in 'block in ActionDispatch::Callbacks#call'
activesupport (8.0.2.1) lib/active_support/callbacks.rb:100:in 'ActiveSupport::Callbacks#run_callbacks'
actionpack (8.0.2.1) lib/action_dispatch/middleware/callbacks.rb:30:in 'ActionDispatch::Callbacks#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/executor.rb:16:in 'ActionDispatch::Executor#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/actionable_exceptions.rb:18:in 'ActionDispatch::ActionableExceptions#call'
sentry-rails (5.26.0) lib/sentry/rails/rescued_exception_interceptor.rb:11:in 'Sentry::Rails::RescuedExceptionInterceptor#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/debug_exceptions.rb:31:in 'ActionDispatch::DebugExceptions#call'
web-console (4.2.1) lib/web_console/middleware.rb:132:in 'WebConsole::Middleware#call_app'
web-console (4.2.1) lib/web_console/middleware.rb:28:in 'block in WebConsole::Middleware#call'
web-console (4.2.1) lib/web_console/middleware.rb:17:in 'Kernel#catch'
web-console (4.2.1) lib/web_console/middleware.rb:17:in 'WebConsole::Middleware#call'
sentry-ruby (5.26.0) lib/sentry/rack/capture_exceptions.rb:15:in 'Sentry::Rack::CaptureExceptions#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/show_exceptions.rb:32:in 'ActionDispatch::ShowExceptions#call'
railties (8.0.2.1) lib/rails/rack/logger.rb:41:in 'Rails::Rack::Logger#call_app'
railties (8.0.2.1) lib/rails/rack/logger.rb:29:in 'Rails::Rack::Logger#call'
ahoy_matey (5.4.0) lib/ahoy/engine.rb:22:in 'Rails::Rack::Logger#call_with_quiet_ahoy'
actionpack (8.0.2.1) lib/action_dispatch/middleware/remote_ip.rb:96:in 'ActionDispatch::RemoteIp#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/request_id.rb:34:in 'ActionDispatch::RequestId#call'
rack (3.2.1) lib/rack/method_override.rb:28:in 'Rack::MethodOverride#call'
rack (3.2.1) lib/rack/runtime.rb:24:in 'Rack::Runtime#call'
activesupport (8.0.2.1) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in 'ActiveSupport::Cache::Strategy::LocalCache::Middleware#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/server_timing.rb:61:in 'block in ActionDispatch::ServerTiming#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/server_timing.rb:26:in 'ActionDispatch::ServerTiming::Subscriber#collect_events'
actionpack (8.0.2.1) lib/action_dispatch/middleware/server_timing.rb:60:in 'ActionDispatch::ServerTiming#call'
hotwire-livereload (2.0.0) lib/hotwire/livereload/middleware.rb:10:in 'Hotwire::Livereload::Middleware#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/executor.rb:16:in 'ActionDispatch::Executor#call'
actionpack (8.0.2.1) lib/action_dispatch/middleware/static.rb:27:in 'ActionDispatch::Static#call'
rack (3.2.1) lib/rack/sendfile.rb:114:in 'Rack::Sendfile#call'
rack-mini-profiler (4.0.1) lib/mini_profiler.rb:334:in 'Rack::MiniProfiler#call'
railties (8.0.2.1) lib/rails/engine.rb:535:in 'Rails::Engine#call'
puma (6.6.1) lib/puma/configuration.rb:279:in 'Puma::Configuration::ConfigMiddleware#call'
puma (6.6.1) lib/puma/request.rb:99:in 'block in Puma::Request#handle_request'
puma (6.6.1) lib/puma/thread_pool.rb:390:in 'Puma::ThreadPool#with_force_shutdown'
puma (6.6.1) lib/puma/request.rb:98:in 'Puma::Request#handle_request'
puma (6.6.1) lib/puma/server.rb:472:in 'Puma::Server#process_client'
puma (6.6.1) lib/puma/server.rb:254:in 'block in Puma::Server#run'
puma (6.6.1) lib/puma/thread_pool.rb:167:in 'block in Puma::ThreadPool#spawn_thread'

Checklist:

  • I have searched for similar issues and couldn't find any
  • I have checked the documentation for relevant information
  • I have included all the required information

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions