
Generating Sorbet
- 1 installs
- 26 repo stars
- Updated January 29, 2026
- dmitrypogrebnoy/ruby-agent-skills
Generate Sorbet type annotations for Ruby code.
About
Skill for adding Sorbet type safety to Ruby projects. Generates type annotations and helps enforce static typing.
- Sorbet annotation generation
- Type-checking integration for Ruby
Generating Sorbet by the numbers
- 1 all-time installs (skills.sh)
- Ranked #3,834 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 9, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dmitrypogrebnoy/ruby-agent-skills --skill generating-sorbetAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 26 |
| Last updated | January 29, 2026 |
| Repository | dmitrypogrebnoy/ruby-agent-skills ↗ |
What it does
Generate Sorbet type annotations for Ruby code.
Files
# frozen_string_literal: true
# Stripe Ruby bindings
# API spec at https://stripe.com/docs/api
require "cgi"
require "json"
require "logger"
require "net/http"
require "openssl"
require "rbconfig"
require "securerandom"
require "set"
require "socket"
require "uri"
require "forwardable"
# Version
require "stripe/api_version"
require "stripe/version"
# API operations
require "stripe/api_operations/create"
require "stripe/api_operations/delete"
require "stripe/api_operations/list"
require "stripe/api_operations/nested_resource"
require "stripe/api_operations/request"
require "stripe/api_operations/save"
require "stripe/api_operations/singleton_save"
require "stripe/api_operations/search"
# API resource support classes
require "stripe/errors"
require "stripe/object_types"
require "stripe/event_types"
require "stripe/request_options"
require "stripe/request_params"
require "stripe/stripe_context"
require "stripe/util"
require "stripe/connection_manager"
require "stripe/multipart_encoder"
require "stripe/api_requestor"
require "stripe/stripe_service"
require "stripe/stripe_client"
require "stripe/stripe_object"
require "stripe/stripe_response"
require "stripe/list_object"
require "stripe/v2_list_object"
require "stripe/search_result_object"
require "stripe/error_object"
require "stripe/api_resource"
require "stripe/api_resource_test_helpers"
require "stripe/singleton_api_resource"
require "stripe/webhook"
require "stripe/stripe_configuration"
require "stripe/resources/v2/amount"
require "stripe/resources/v2/deleted_object"
require "stripe/resources/v2/core/event_notification"
# Named API resources
require "stripe/resources"
require "stripe/services"
require "stripe/params"
# OAuth
require "stripe/oauth"
require "stripe/services/oauth_service"
module Stripe
DEFAULT_CA_BUNDLE_PATH = __dir__ + "/data/ca-certificates.crt"
# map to the same values as the standard library's logger
LEVEL_DEBUG = Logger::DEBUG
LEVEL_ERROR = Logger::ERROR
LEVEL_INFO = Logger::INFO
# API base constants
DEFAULT_API_BASE = "https://api.stripe.com"
DEFAULT_CONNECT_BASE = "https://connect.stripe.com"
DEFAULT_UPLOAD_BASE = "https://files.stripe.com"
DEFAULT_METER_EVENTS_BASE = "https://meter-events.stripe.com"
# Options that can be configured globally by users
USER_CONFIGURABLE_GLOBAL_OPTIONS = Set.new(%i[
api_key
api_version
stripe_account
api_base
uploads_base
connect_base
meter_events_base
open_timeout
read_timeout
write_timeout
proxy
verify_ssl_certs
ca_bundle_path
log_level
logger
max_network_retries
enable_telemetry
client_id
])
@app_info = nil
@config = Stripe::StripeConfiguration.setup
class << self
extend Forwardable
attr_reader :config
# User configurable options
def_delegators :@config, :api_key, :api_key=
def_delegators :@config, :api_version, :api_version=
def_delegators :@config, :stripe_account, :stripe_account=
def_delegators :@config, :api_base, :api_base=
def_delegators :@config, :uploads_base, :uploads_base=
def_delegators :@config, :connect_base, :connect_base=
def_delegators :@config, :meter_events_base, :meter_events_base=
def_delegators :@config, :open_timeout, :open_timeout=
def_delegators :@config, :read_timeout, :read_timeout=
def_delegators :@config, :write_timeout, :write_timeout=
def_delegators :@config, :proxy, :proxy=
def_delegators :@config, :verify_ssl_certs, :verify_ssl_certs=
def_delegators :@config, :ca_bundle_path, :ca_bundle_path=
def_delegators :@config, :log_level, :log_level=
def_delegators :@config, :logger, :logger=
def_delegators :@config, :max_network_retries, :max_network_retries=
def_delegators :@config, :enable_telemetry=, :enable_telemetry?
def_delegators :@config, :client_id=, :client_id
# Internal configurations
def_delegators :@config, :max_network_retry_delay
def_delegators :@config, :initial_network_retry_delay
def_delegators :@config, :ca_store
end
# Gets the application for a plugin that's identified some. See
# #set_app_info.
def self.app_info
@app_info
end
def self.app_info=(info)
@app_info = info
end
# Sets some basic information about the running application that's sent along
# with API requests. Useful for plugin authors to identify their plugin when
# communicating with Stripe.
#
# Takes a name and optional partner program ID, plugin URL, and version.
def self.set_app_info(name, partner_id: nil, url: nil, version: nil)
@app_info = {
name: name,
partner_id: partner_id,
url: url,
version: version,
}
end
end
Stripe.log_level = ENV["STRIPE_LOG"] unless ENV["STRIPE_LOG"].nil?
# frozen_string_literal: true
module Stripe
module APIOperations
module Create
def create(params = {}, opts = {})
request_stripe_object(
method: :post,
path: resource_url,
params: params,
opts: opts
)
end
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
module Delete
module ClassMethods
# Deletes an API resource
#
# Deletes the identified resource with the passed in parameters.
#
# ==== Attributes
#
# * +id+ - ID of the resource to delete.
# * +params+ - A hash of parameters to pass to the API
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def delete(id, params = {}, opts = {})
request_stripe_object(
method: :delete,
path: "#{resource_url}/#{id}",
params: params,
opts: opts
)
end
end
def delete(params = {}, opts = {})
request_stripe_object(
method: :delete,
path: resource_url,
params: params,
opts: opts
)
end
def self.included(base)
base.extend(ClassMethods)
end
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
module List
def list(filters = {}, opts = {})
request_stripe_object(
method: :get,
path: resource_url,
params: filters,
opts: opts
)
end
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
# Adds methods to help manipulate a subresource from its parent resource so
# that it's possible to do so from a static context (i.e. without a
# pre-existing collection of subresources on the parent).
#
# For example, a transfer gains the static methods for reversals so that the
# methods `.create_reversal`, `.retrieve_reversal`, `.update_reversal`,
# etc. all become available.
module NestedResource
def nested_resource_class_methods(resource, path: nil, operations: nil,
resource_plural: nil)
resource_plural ||= "#{resource}s"
path ||= resource_plural
raise ArgumentError, "operations array required" if operations.nil?
resource_url_method = :"#{resource}s_url"
define_singleton_method(resource_url_method) do |id, nested_id = nil|
url = "#{resource_url}/#{CGI.escape(id)}/#{CGI.escape(path)}"
url += "/#{CGI.escape(nested_id)}" unless nested_id.nil?
url
end
operations.each do |operation|
define_operation(
resource,
operation,
resource_url_method,
resource_plural
)
end
end
private def define_operation(
resource,
operation,
resource_url_method,
resource_plural
)
case operation
when :create
define_singleton_method(:"create_#{resource}") \
do |id, params = {}, opts = {}|
request_stripe_object(
method: :post,
path: send(resource_url_method, id),
params: params,
opts: opts
)
end
when :retrieve
define_singleton_method(:"retrieve_#{resource}") \
do |id, nested_id, params = {}, opts = {}|
request_stripe_object(
method: :get,
path: send(resource_url_method, id, nested_id),
params: params,
opts: opts
)
end
when :update
define_singleton_method(:"update_#{resource}") \
do |id, nested_id, params = {}, opts = {}|
request_stripe_object(
method: :post,
path: send(resource_url_method, id, nested_id),
params: params,
opts: opts
)
end
when :delete
define_singleton_method(:"delete_#{resource}") \
do |id, nested_id, params = {}, opts = {}|
request_stripe_object(
method: :delete,
path: send(resource_url_method, id, nested_id),
params: params,
opts: opts
)
end
when :list
define_singleton_method(:"list_#{resource_plural}") \
do |id, params = {}, opts = {}|
request_stripe_object(
method: :get,
path: send(resource_url_method, id),
params: params,
opts: opts
)
end
else
raise ArgumentError, "Unknown operation: #{operation.inspect}"
end
end
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
module Request
module ClassMethods
def execute_resource_request(method, url, base_address = :api,
params = {}, opts = {}, usage = [])
execute_resource_request_internal(
:execute_request, method, url, base_address, params, opts, usage
)
end
def execute_resource_request_stream(method, url, base_address = :api,
params = {}, opts = {}, usage = [],
&read_body_chunk_block)
execute_resource_request_internal(
:execute_request_stream,
method,
url,
base_address,
params,
opts,
usage,
&read_body_chunk_block
)
end
private def request_stripe_object(method:, path:, base_address: :api, params: {}, opts: {}, usage: [])
execute_resource_request(method, path, base_address, params, opts, usage)
end
private def execute_resource_request_internal(client_request_method_sym,
method, url, base_address,
params, opts, usage,
&read_body_chunk_block)
params = params.to_h if params.is_a?(Stripe::RequestParams)
params ||= {}
error_on_invalid_params(params)
warn_on_opts_in_params(params)
opts = Util.normalize_opts(opts)
req_opts = RequestOptions.extract_opts_from_hash(opts)
APIRequestor.active_requestor.send(
client_request_method_sym,
method, url,
base_address,
params: params, opts: req_opts, usage: usage,
&read_body_chunk_block
)
end
private def error_on_invalid_params(params)
return if params.nil? || params.is_a?(Hash)
raise ArgumentError,
"request params should be either a Hash or nil " \
"(was a #{params.class})"
end
private def warn_on_opts_in_params(params)
RequestOptions::OPTS_USER_SPECIFIED.each do |opt|
warn("WARNING: '#{opt}' should be in opts instead of params.") if params.key?(opt)
end
end
end
def self.included(base)
base.extend(ClassMethods)
end
protected def execute_resource_request(method, url, base_address = :api,
params = {}, opts = {}, usage = [])
opts = @opts.merge(Util.normalize_opts(opts))
self.class.execute_resource_request(method, url, base_address, params, opts, usage)
end
protected def execute_resource_request_stream(method, url, base_address = :api,
params = {}, opts = {}, usage = [],
&read_body_chunk_block)
opts = @opts.merge(Util.normalize_opts(opts))
self.class.execute_resource_request_stream(
method, url, base_address, params, opts, usage, &read_body_chunk_block
)
end
private def request_stripe_object(method:, path:, params:, base_address: :api, opts: {}, usage: [])
execute_resource_request(method, path, base_address, params, opts, usage)
end
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
module Save
module ClassMethods
# Updates an API resource
#
# Updates the identified resource with the passed in parameters.
#
# ==== Attributes
#
# * +id+ - ID of the resource to update.
# * +params+ - A hash of parameters to pass to the API
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def update(id, params = {}, opts = {})
params.each_key do |k|
raise ArgumentError, "Cannot update protected field: #{k}" if protected_fields.include?(k)
end
request_stripe_object(
method: :post,
path: "#{resource_url}/#{id}",
params: params,
opts: opts
)
end
end
# The `save` method is DEPRECATED and will be removed in a future major
# version of the library. Use the `update` method on the resource instead.
#
# Creates or updates an API resource.
#
# If the resource doesn't yet have an assigned ID and the resource is one
# that can be created, then the method attempts to create the resource.
# The resource is updated otherwise.
#
# ==== Attributes
#
# * +params+ - Overrides any parameters in the resource's serialized data
# and includes them in the create or update. If +:req_url:+ is included
# in the list, it overrides the update URL used for the create or
# update.
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def save(params = {}, opts = {})
# We started unintentionally (sort of) allowing attributes sent to
# +save+ to override values used during the update. So as not to break
# the API, this makes that official here.
update_attributes(params)
# Now remove any parameters that look like object attributes.
params = params.reject { |k, _| respond_to?(k) }
values = serialize_params(self).merge(params)
# Please note that id gets removed here our call to #url above has already
# generated a uri for this object with an identifier baked in
values.delete(:id)
opts = Util.normalize_opts(opts)
APIRequestor.active_requestor.execute_request_initialize_from(:post, save_url, :api, self,
params: values,
opts: RequestOptions.extract_opts_from_hash(opts),
usage: ["save"])
end
extend Gem::Deprecate
deprecate :save, "the `update` class method (for examples " \
"see https://github.com/stripe/stripe-ruby" \
"/wiki/Migration-guide-for-v8)", 2022, 11
def self.included(base)
# Set `metadata` as additive so that when it's set directly we remember
# to clear keys that may have been previously set by sending empty
# values for them.
#
# It's possible that not every object with `Save` has `metadata`, but
# it's a close enough heuristic, and having this option set when there
# is no `metadata` field is not harmful.
base.additive_object_param(:metadata)
base.extend(ClassMethods)
end
private def save_url
# This switch essentially allows us "upsert"-like functionality. If the
# API resource doesn't have an ID set (suggesting that it's new) and
# its class responds to .create (which comes from
# Stripe::APIOperations::Create), then use the URL to create a new
# resource. Otherwise, generate a URL based on the object's identifier
# for a normal update.
if self[:id].nil? && self.class.respond_to?(:create)
self.class.resource_url
else
resource_url
end
end
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
# The _search method via API Operations is deprecated.
# Please use the search method from within the resource instead.
module Search
def _search(search_url, filters = {}, opts = {})
request_stripe_object(
method: :get,
path: search_url,
params: filters,
opts: opts
)
end
extend Gem::Deprecate
deprecate :_search, "request_stripe_object", 2024, 1
end
end
end
# frozen_string_literal: true
module Stripe
module APIOperations
module SingletonSave
module ClassMethods
# Updates a singleton API resource
#
# Updates the identified resource with the passed in parameters.
#
# ==== Attributes
#
# * +params+ - A hash of parameters to pass to the API
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def update(params = {}, opts = {})
params.each_key do |k|
raise ArgumentError, "Cannot update protected field: #{k}" if protected_fields.include?(k)
end
request_stripe_object(
method: :post,
path: resource_url,
params: params,
opts: opts
)
end
end
# The `save` method is DEPRECATED and will be removed in a future major
# version of the library. Use the `update` method on the resource instead.
#
# Updates a singleton API resource.
#
# If the resource doesn't yet have an assigned ID and the resource is one
# that can be created, then the method attempts to create the resource.
# The resource is updated otherwise.
#
# ==== Attributes
#
# * +params+ - Overrides any parameters in the resource's serialized data
# and includes them in the create or update. If +:req_url:+ is included
# in the list, it overrides the update URL used for the create or
# update.
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def save(params = {}, opts = {})
# We started unintentionally (sort of) allowing attributes sent to
# +save+ to override values used during the update. So as not to break
# the API, this makes that official here.
update_attributes(params)
# Now remove any parameters that look like object attributes.
params = params.reject { |k, _| respond_to?(k) }
values = serialize_params(self).merge(params)
opts = Util.normalize_opts(opts)
APIRequestor.active_requestor.execute_request_initialize_from(:post, resource_url, :api, self,
params: values,
opts: RequestOptions.extract_opts_from_hash(opts),
usage: ["save"])
end
extend Gem::Deprecate
deprecate :save, "the `update` class method (for examples " \
"see https://github.com/stripe/stripe-ruby" \
"/wiki/Migration-guide-for-v8)", 2022, 11
def self.included(base)
# Set `metadata` as additive so that when it's set directly we remember
# to clear keys that may have been previously set by sending empty
# values for them.
#
# It's possible that not every object with `Save` has `metadata`, but
# it's a close enough heuristic, and having this option set when there
# is no `metadata` field is not harmful.
base.additive_object_param(:metadata)
base.extend(ClassMethods)
end
end
end
end
# frozen_string_literal: true
require "stripe/instrumentation"
module Stripe
# APIRequestor executes requests against the Stripe API and allows a user to
# recover both a resource a call returns as well as a response object that
# contains information on the HTTP call.
class APIRequestor
# A set of all known thread contexts across all threads and a mutex to
# synchronize global access to them.
@thread_contexts_with_connection_managers = Set.new
@thread_contexts_with_connection_managers_mutex = Mutex.new
@last_connection_manager_gc = Util.monotonic_time
# Initializes a new APIRequestor
def initialize(config_arg = {})
@system_profiler = SystemProfiler.new
@last_request_metrics = Queue.new
@config = case config_arg
when Hash
StripeConfiguration.new.reverse_duplicate_merge(config_arg)
when Stripe::StripeConfiguration
config_arg
when String
StripeConfiguration.new.reverse_duplicate_merge(
{ api_key: config_arg }
)
else
raise ArgumentError, "Can't handle argument: #{config_arg}"
end
end
attr_reader :config, :options
# Gets a currently active `APIRequestor`. Set for the current thread when
# `APIRequestor#request` is being run so that API operations being executed
# inside of that block can find the currently active requestor. It's reset to
# the original value (hopefully `nil`) after the block ends.
#
# For internal use only. Does not provide a stable API and may be broken
# with future non-major changes.
def self.active_requestor
current_thread_context.active_requestor || default_requestor
end
# Finishes any active connections by closing their TCP connection and
# clears them from internal tracking in all connection managers across all
# threads.
#
# If passed a `config` object, only clear connection managers for that
# particular configuration.
#
# For internal use only. Does not provide a stable API and may be broken
# with future non-major changes.
def self.clear_all_connection_managers(config: nil)
# Just a quick path for when configuration is being set for the first
# time before any connections have been opened. There is technically some
# potential for thread raciness here, but not in a practical sense.
return if @thread_contexts_with_connection_managers.empty?
@thread_contexts_with_connection_managers_mutex.synchronize do
pruned_contexts = Set.new
@thread_contexts_with_connection_managers.each do |thread_context|
# Note that the thread context itself is not destroyed, but we clear
# its connection manager and remove our reference to it. If it ever
# makes a new request we'll give it a new connection manager and
# it'll go back into `@thread_contexts_with_connection_managers`.
thread_context.default_connection_managers.reject! do |cm_config, cm|
if config.nil? || config.key == cm_config
cm.clear
true
end
end
pruned_contexts << thread_context if thread_context.default_connection_managers.empty?
end
@thread_contexts_with_connection_managers.subtract(pruned_contexts)
end
end
# A default requestor for the current thread.
def self.default_requestor
current_thread_context.default_requestor ||= APIRequestor.new(Stripe.config)
end
# A default connection manager for the current thread scoped to the
# configuration object that may be provided.
def self.default_connection_manager(config = Stripe.config)
current_thread_context.default_connection_managers[config.key] ||= begin
connection_manager = ConnectionManager.new(config)
@thread_contexts_with_connection_managers_mutex.synchronize do
maybe_gc_connection_managers
@thread_contexts_with_connection_managers << current_thread_context
end
connection_manager
end
end
# Checks if an error is a problem that we should retry on. This includes
# both socket errors that may represent an intermittent problem and some
# special HTTP statuses.
def self.should_retry?(error,
num_retries:, config: Stripe.config)
return false if num_retries >= config.max_network_retries
case error
when Net::OpenTimeout, Net::ReadTimeout
# Retry on timeout-related problems (either on open or read).
true
when EOFError, Errno::ECONNREFUSED, Errno::ECONNRESET, # rubocop:todo Lint/DuplicateBranch
Errno::EHOSTUNREACH, Errno::ETIMEDOUT, SocketError
# Destination refused the connection, the connection was reset, or a
# variety of other connection failures. This could occur from a single
# saturated server, so retry in case it's intermittent.
true
when Stripe::StripeError
# The API may ask us not to retry (e.g. if doing so would be a no-op),
# or advise us to retry (e.g. in cases of lock timeouts). Defer to
# those instructions if given.
return false if error.http_headers["stripe-should-retry"] == "false"
return true if error.http_headers["stripe-should-retry"] == "true"
# 409 Conflict
return true if error.http_status == 409
# 429 Too Many Requests
#
# There are a few different problems that can lead to a 429. The most
# common is rate limiting, on which we *don't* want to retry because
# that'd likely contribute to more contention problems. However, some
# 429s are lock timeouts, which is when a request conflicted with
# another request or an internal process on some particular object.
# These 429s are safe to retry.
return true if error.http_status == 429 && error.code == "lock_timeout"
# Retry on 500, 503, and other internal errors.
#
# Note that we expect the stripe-should-retry header to be false
# in most cases when a 500 is returned, since our idempotency framework
# would typically replay it anyway.
true if error.http_status >= 500
else
false
end
end
def self.sleep_time(num_retries, config: Stripe.config)
# Apply exponential backoff with initial_network_retry_delay on the
# number of num_retries so far as inputs. Do not allow the number to
# exceed max_network_retry_delay.
sleep_seconds = [
config.initial_network_retry_delay * (2**(num_retries - 1)),
config.max_network_retry_delay,
].min
# Apply some jitter by randomizing the value in the range of
# (sleep_seconds / 2) to (sleep_seconds).
sleep_seconds *= (0.5 * (1 + rand))
# But never sleep less than the base sleep seconds.
[config.initial_network_retry_delay, sleep_seconds].max
end
# Executes the API call within the given block. Usage looks like:
#
# client = APIRequestor.new
# charge, resp = client.request { Charge.create }
#
def request
old_api_requestor = self.class.current_thread_context.active_requestor
self.class.current_thread_context.active_requestor = self
if self.class.current_thread_context.last_responses&.key?(object_id)
raise "calls to APIRequestor#request cannot be nested within a thread"
end
self.class.current_thread_context.last_responses ||= {}
self.class.current_thread_context.last_responses[object_id] = nil
begin
res = yield
[res, self.class.current_thread_context.last_responses[object_id]]
ensure
self.class.current_thread_context.active_requestor = old_api_requestor
self.class.current_thread_context.last_responses.delete(object_id)
end
end
extend Gem::Deprecate
deprecate :request, "StripeClient#raw_request", 2024, 9
def execute_request(method, path, base_address,
params: {}, opts: {}, usage: [])
params = params.to_h if params.is_a?(RequestParams)
http_resp, req_opts = execute_request_internal(
method, path, base_address, params, opts, usage
)
req_opts = RequestOptions.extract_opts_from_hash(req_opts)
resp = interpret_response(http_resp)
# If being called from `APIRequestor#request`, put the last response in
# thread-local memory so that it can be returned to the user. Don't store
# anything otherwise so that we don't leak memory.
store_last_response(object_id, resp)
api_mode = Util.get_api_mode(path)
Util.convert_to_stripe_object_with_params(resp.data, params, RequestOptions.persistable(req_opts), resp,
api_mode: api_mode, requestor: self,
v2_deleted_object: method == :delete && api_mode == :v2)
end
# Execute request without instantiating a new object if the relevant object's name matches the class
#
# For internal use only. Does not provide a stable API and may be broken
# with future non-major changes.
def execute_request_initialize_from(method, path, base_address, object,
params: {}, opts: {}, usage: [])
opts = RequestOptions.combine_opts(object.instance_variable_get(:@opts) || {}, opts)
opts = Util.normalize_opts(opts)
params = params.to_h if params.is_a?(RequestParams)
http_resp, req_opts = execute_request_internal(
method, path, base_address, params, opts, usage
)
req_opts = RequestOptions.extract_opts_from_hash(req_opts)
resp = interpret_response(http_resp)
# If being called from `APIRequestor#request`, put the last response in
# thread-local memory so that it can be returned to the user. Don't store
# anything otherwise so that we don't leak memory.
store_last_response(object_id, resp)
if Util.object_name_matches_class?(resp.data[:object], object.class)
object.send(:initialize_from,
resp.data, RequestOptions.persistable(req_opts), resp,
api_mode: :v1, requestor: self)
else
Util.convert_to_stripe_object_with_params(resp.data, params,
RequestOptions.persistable(req_opts),
resp, api_mode: :v1, requestor: self)
end
end
def interpret_response(http_resp)
StripeResponse.from_net_http(http_resp)
rescue JSON::ParserError
raise general_api_error(http_resp.code.to_i, http_resp.body)
end
# Executes a request and returns the body as a stream instead of converting
# it to a StripeObject. This should be used for any request where we expect
# an arbitrary binary response.
#
# A `read_body_chunk` block can be passed, which will be called repeatedly
# with the body chunks read from the socket.
#
# If a block is passed, a StripeHeadersOnlyResponse is returned as the
# block is expected to do all the necessary body processing. If no block is
# passed, then a StripeStreamResponse is returned containing an IO stream
# with the response body.
def execute_request_stream(method, path,
base_address,
params: {}, opts: {}, usage: [],
&read_body_chunk_block)
unless block_given?
raise ArgumentError,
"execute_request_stream requires a read_body_chunk_block"
end
params = params.to_h if params.is_a?(RequestParams)
http_resp, api_key = execute_request_internal(
method, path, base_address, params, opts, usage, &read_body_chunk_block
)
# When the read_body_chunk_block is given, we no longer have access to the
# response body at this point and so return a response object containing
# only the headers. This is because the body was consumed by the block.
resp = StripeHeadersOnlyResponse.from_net_http(http_resp)
[resp, api_key]
end
def store_last_response(object_id, resp)
return unless last_response_has_key?(object_id)
self.class.current_thread_context.last_responses[object_id] = resp
end
def last_response_has_key?(object_id)
self.class.current_thread_context.last_responses&.key?(object_id)
end
#
# private
#
# Time (in seconds) that a connection manager has not been used before it's
# eligible for garbage collection.
CONNECTION_MANAGER_GC_LAST_USED_EXPIRY = 120
# How often to check (in seconds) for connection managers that haven't been
# used in a long time and which should be garbage collected.
CONNECTION_MANAGER_GC_PERIOD = 60
ERROR_MESSAGE_CONNECTION =
"Unexpected error communicating when trying to connect to " \
"Stripe (%s). You may be seeing this message because your DNS is not " \
"working or you don't have an internet connection. To check, try " \
"running `host stripe.com` from the command line."
ERROR_MESSAGE_SSL =
"Could not establish a secure connection to Stripe (%s), you " \
"may need to upgrade your OpenSSL version. To check, try running " \
"`openssl s_client -connect api.stripe.com:443` from the command " \
"line."
# Common error suffix sared by both connect and read timeout messages.
ERROR_MESSAGE_TIMEOUT_SUFFIX =
"Please check your internet connection and try again. " \
"If this problem persists, you should check Stripe's service " \
"status at https://status.stripe.com, or let us know at " \
"support@stripe.com."
ERROR_MESSAGE_TIMEOUT_CONNECT = (
"Timed out connecting to Stripe (%s). " +
ERROR_MESSAGE_TIMEOUT_SUFFIX
).freeze
ERROR_MESSAGE_TIMEOUT_READ = (
"Timed out communicating with Stripe (%s). " +
ERROR_MESSAGE_TIMEOUT_SUFFIX
).freeze
# Maps types of exceptions that we're likely to see during a network
# request to more user-friendly messages that we put in front of people.
# The original error message is also appended onto the final exception for
# full transparency.
NETWORK_ERROR_MESSAGES_MAP = {
EOFError => ERROR_MESSAGE_CONNECTION,
Errno::ECONNREFUSED => ERROR_MESSAGE_CONNECTION,
Errno::ECONNRESET => ERROR_MESSAGE_CONNECTION,
Errno::EHOSTUNREACH => ERROR_MESSAGE_CONNECTION,
Errno::ETIMEDOUT => ERROR_MESSAGE_TIMEOUT_CONNECT,
SocketError => ERROR_MESSAGE_CONNECTION,
Net::OpenTimeout => ERROR_MESSAGE_TIMEOUT_CONNECT,
Net::ReadTimeout => ERROR_MESSAGE_TIMEOUT_READ,
OpenSSL::SSL::SSLError => ERROR_MESSAGE_SSL,
}.freeze
private_constant :NETWORK_ERROR_MESSAGES_MAP
# A record representing any data that `APIRequestor` puts into
# `Thread.current`. Making it a class likes this gives us a little extra
# type safety and lets us document what each field does.
#
# For internal use only. Does not provide a stable API and may be broken
# with future non-major changes.
class ThreadContext
# A `APIRequestor` that's been flagged as currently active within a
# thread by `APIRequestor#request`. A requestor stays active until the
# completion of the request block.
attr_accessor :active_requestor
# A default `APIRequestor` object for the thread. Used in all cases where
# the user hasn't specified their own.
attr_accessor :default_requestor
# A temporary map of object IDs to responses from last executed API
# calls. Used to return a responses from calls to `APIRequestor#request`.
#
# Stored in the thread data to make the use of a single `APIRequestor`
# object safe across multiple threads. Stored as a map so that multiple
# `APIRequestor` objects can run concurrently on the same thread.
#
# Responses are only left in as long as they're needed, which means
# they're removed as soon as a call leaves `APIRequestor#request`, and
# because that's wrapped in an `ensure` block, they should never leave
# garbage in `Thread.current`.
attr_accessor :last_responses
# A map of connection mangers for the thread. Normally shared between
# all `APIRequestor` objects on a particular thread, and created so as to
# minimize the number of open connections that an application needs.
def default_connection_managers
@default_connection_managers ||= {}
end
def reset_connection_managers
@default_connection_managers = {}
end
end
# Access data stored for `APIRequestor` within the thread's current
# context. Returns `ThreadContext`.
#
# For internal use only. Does not provide a stable API and may be broken
# with future non-major changes.
def self.current_thread_context
Thread.current[:api_requestor__internal_use_only] ||= ThreadContext.new
end
# Garbage collects connection managers that haven't been used in some time,
# with the idea being that we want to remove old connection managers that
# belong to dead threads and the like.
#
# Prefixed with `maybe_` because garbage collection will only run
# periodically so that we're not constantly engaged in busy work. If
# connection managers live a little passed their useful age it's not
# harmful, so it's not necessary to get them right away.
#
# For testability, returns `nil` if it didn't run and the number of
# connection managers that were garbage collected otherwise.
#
# IMPORTANT: This method is not thread-safe and expects to be called inside
# a lock on `@thread_contexts_with_connection_managers_mutex`.
#
# For internal use only. Does not provide a stable API and may be broken
# with future non-major changes.
def self.maybe_gc_connection_managers
next_gc_time = @last_connection_manager_gc + CONNECTION_MANAGER_GC_PERIOD
return nil if next_gc_time > Util.monotonic_time
last_used_threshold =
Util.monotonic_time - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
pruned_contexts = []
@thread_contexts_with_connection_managers.each do |thread_context|
thread_context
.default_connection_managers
.each do |config_key, connection_manager|
next if connection_manager.last_used > last_used_threshold
connection_manager.clear
thread_context.default_connection_managers.delete(config_key)
end
end
@thread_contexts_with_connection_managers.each do |thread_context|
next unless thread_context.default_connection_managers.empty?
pruned_contexts << thread_context
end
@thread_contexts_with_connection_managers -= pruned_contexts
@last_connection_manager_gc = Util.monotonic_time
pruned_contexts.count
end
private def execute_request_internal(method, path,
base_address, params, opts, usage,
&read_body_chunk_block)
api_mode = Util.get_api_mode(path)
opts = RequestOptions.merge_config_and_opts(config, opts)
raise ArgumentError, "method should be a symbol" \
unless method.is_a?(Symbol)
raise ArgumentError, "path should be a string" \
unless path.is_a?(String)
base_url ||= config.base_addresses[base_address]
raise ArgumentError, "api_base cannot be empty" if base_url.nil? || base_url.empty?
api_key ||= opts[:api_key]
params = Util.objects_to_ids(params)
check_api_key!(api_key)
body_params = nil
query_params = nil
case method
when :get, :head, :delete
query_params = params
else
body_params = params
end
query_params, path = merge_query_params(query_params, path)
headers = request_headers(method, api_mode, opts)
url = api_url(path, base_url)
# Merge given query parameters with any already encoded in the path.
query = query_params ? Util.encode_parameters(query_params, api_mode) : nil
# Encoding body parameters is a little more complex because we may have
# to send a multipart-encoded body. `body_log` is produced separately as
# a log-friendly variant of the encoded form. File objects are displayed
# as such instead of as their file contents.
body, body_log =
body_params ? encode_body(body_params, headers, api_mode) : [nil, nil]
# stores information on the request we're about to make so that we don't
# have to pass as many parameters around for logging.
context = RequestLogContext.new
context.account = headers["Stripe-Account"]
context.api_key = api_key
context.api_version = headers["Stripe-Version"]
context.body = body_log
context.idempotency_key = headers["Idempotency-Key"]
context.method = method
context.path = path
context.query = query
# A block can be passed in to read the content directly from the response.
# We want to execute this block only when the response was actually
# successful. When it wasn't, we defer to the standard error handling as
# we have to read the body and parse the error JSON.
response_block =
if block_given?
lambda do |response|
response.read_body(&read_body_chunk_block) unless should_handle_as_error(response.code.to_i)
end
end
http_resp =
execute_request_with_rescues(base_url, headers, api_mode, usage, context) do
self.class
.default_connection_manager(config)
.execute_request(method, url,
body: body,
headers: headers,
query: query,
&response_block)
end
[http_resp, opts]
end
private def api_url(url, base_url)
base_url + url
end
private def check_api_key!(api_key)
unless api_key
raise AuthenticationError, "No API key provided. " \
'Set your API key using "Stripe.api_key = <API-KEY>". ' \
"You can generate API keys from the Stripe web interface. " \
"See https://stripe.com/api for details, or email " \
"support@stripe.com if you have any questions."
end
return unless api_key =~ /\s/
raise AuthenticationError, "Your API key is invalid, as it contains " \
"whitespace. (HINT: You can double-check your API key from the " \
"Stripe web interface. See https://stripe.com/api for details, or " \
"email support@stripe.com if you have any questions.)"
end
# Encodes a set of body parameters using multipart if `Content-Type` is set
# for that, or standard form-encoding otherwise. Returns the encoded body
# and a version of the encoded body that's safe to be logged.
private def encode_body(body_params, headers, api_mode)
body = nil
flattened_params = Util.flatten_params(body_params, api_mode)
if headers["Content-Type"] == MultipartEncoder::MULTIPART_FORM_DATA
body, content_type = MultipartEncoder.encode(flattened_params)
# Set a new content type that also includes the multipart boundary.
# See `MultipartEncoder` for details.
headers["Content-Type"] = content_type
# `#to_s` any complex objects like files and the like to build output
# that's more conducive to logging.
flattened_params =
# https://go/j/RUN_DEVSDK-1956 - this is probably a bug
# once fixed, you can remove the exclusions referencing this ticket in .rubocop.yml
flattened_params.map { |k, v| [k, v.is_a?(String) ? v : v.to_s] }.to_h
elsif api_mode == :v2
body = JSON.generate(body_params)
headers["Content-Type"] = "application/json"
else
body = Util.encode_parameters(body_params, api_mode)
headers["Content-Type"] = "application/x-www-form-urlencoded"
end
body_log = if api_mode == :v2
body
else
# We don't use `Util.encode_parameters` partly as an optimization (to
# not redo work we've already done), and partly because the encoded
# forms of certain characters introduce a lot of visual noise and it's
# nice to have a clearer format for logs.
flattened_params.map { |k, v| "#{k}=#{v}" }.join("&")
end
[body, body_log]
end
private def should_handle_as_error(http_status)
http_status >= 400
end
private def execute_request_with_rescues(base_url, headers, api_mode, usage, context)
num_retries = 0
begin
request_start = nil
user_data = nil
log_request(context, num_retries)
user_data = notify_request_begin(context)
request_start = Util.monotonic_time
resp = yield
request_duration = Util.monotonic_time - request_start
http_status = resp.code.to_i
context = context.dup_from_response_headers(resp)
handle_error_response(resp, context, api_mode) if should_handle_as_error(http_status)
log_response(context, request_start, http_status, resp.body, resp)
notify_request_end(context, request_duration, http_status,
num_retries, user_data, resp, headers)
if config.enable_telemetry? && context.request_id
request_duration_ms = (request_duration * 1000).to_i
@last_request_metrics << StripeRequestMetrics.new(context.request_id, request_duration_ms, usage: usage)
end
# We rescue all exceptions from a request so that we have an easy spot to
# implement our retry logic across the board. We'll re-raise if it's a
# type of exception that we didn't expect to handle.
rescue StandardError => e
# If we modify context we copy it into a new variable so as not to
# taint the original on a retry.
error_context = context
http_status = nil
request_duration = Util.monotonic_time - request_start if request_start
if e.is_a?(Stripe::StripeError)
error_context = context.dup_from_response_headers(e.http_headers)
http_status = resp.code.to_i
log_response(error_context, request_start,
e.http_status, e.http_body, resp)
else
log_response_error(error_context, request_start, e)
end
notify_request_end(context, request_duration, http_status, num_retries,
user_data, resp, headers)
if self.class.should_retry?(e,
num_retries: num_retries,
config: config)
num_retries += 1
sleep self.class.sleep_time(num_retries, config: config)
retry
end
case e
when Stripe::StripeError
raise
when *NETWORK_ERROR_MESSAGES_MAP.keys
handle_network_error(e, error_context, num_retries, base_url)
# Only handle errors when we know we can do so, and re-raise otherwise.
# This should be pretty infrequent.
else # rubocop:todo Lint/DuplicateBranch
raise
end
end
resp
end
private def notify_request_begin(context)
return unless Instrumentation.any_subscribers?(:request_begin)
event = Instrumentation::RequestBeginEvent.new(
method: context.method,
path: context.path,
user_data: {}
)
Stripe::Instrumentation.notify(:request_begin, event)
# This field may be set in the `request_begin` callback. If so, we'll
# forward it onto `request_end`.
event.user_data
end
private def notify_request_end(context, duration, http_status, num_retries,
user_data, resp, headers)
return if !Instrumentation.any_subscribers?(:request_end) &&
!Instrumentation.any_subscribers?(:request)
request_context = Stripe::Instrumentation::RequestContext.new(
duration: duration,
context: context,
header: headers
)
response_context = Stripe::Instrumentation::ResponseContext.new(
http_status: http_status,
response: resp
)
event = Instrumentation::RequestEndEvent.new(
request_context: request_context,
response_context: response_context,
num_retries: num_retries,
user_data: user_data || {}
)
Stripe::Instrumentation.notify(:request_end, event)
# The name before `request_begin` was also added. Provided for backwards
# compatibility.
Stripe::Instrumentation.notify(:request, event)
end
private def general_api_error(status, body)
APIError.new("Invalid response object from API: #{body.inspect} " \
"(HTTP response code was #{status})",
http_status: status, http_body: body)
end
# Formats a plugin "app info" hash into a string that we can tack onto the
# end of a User-Agent string where it'll be fairly prominent in places like
# the Dashboard. Note that this formatting has been implemented to match
# other libraries, and shouldn't be changed without universal consensus.
private def format_app_info(info)
str = info[:name]
str = "#{str}/#{info[:version]}" unless info[:version].nil?
str = "#{str} (#{info[:url]})" unless info[:url].nil?
str
end
private def handle_error_response(http_resp, context, api_mode)
begin
resp = StripeResponse.from_net_http(http_resp)
error_data = resp.data[:error]
raise StripeError, "Indeterminate error" unless error_data
rescue JSON::ParserError, StripeError
raise general_api_error(http_resp.code.to_i, http_resp.body)
end
error = if error_data.is_a?(String)
specific_oauth_error(resp, error_data, context)
elsif api_mode == :v2
specific_v2_api_error(resp, error_data, context)
else
specific_api_error(resp, error_data, context)
end
error.response = resp
raise(error)
end
# Works around an edge case where we end up with both query parameters from
# parameteers and query parameters that were appended onto the end of the
# given path.
#
# Decode any parameters that were added onto the end of a path and add them
# to a unified query parameter hash so that all parameters end up in one
# place and all of them are correctly included in the final request.
private def merge_query_params(query_params, path)
u = URI.parse(path)
# Return original results if there was nothing to be found.
return query_params, path if u.query.nil?
query_params ||= {}
query_params = Hash[URI.decode_www_form(u.query)].merge(query_params)
# Reset the path minus any query parameters that were specified.
path = u.path
[query_params, path]
end
private def specific_api_error(resp, error_data, context)
Util.log_error("Stripe API error",
status: resp.http_status,
error_code: error_data[:code],
error_message: error_data[:message],
error_param: error_data[:param],
error_type: error_data[:type],
idempotency_key: context.idempotency_key,
request_id: context.request_id,
config: config)
# The standard set of arguments that can be used to initialize most of
# the exceptions.
opts = {
http_body: resp.http_body,
http_headers: resp.http_headers,
http_status: resp.http_status,
json_body: resp.data,
code: error_data[:code],
}
case resp.http_status
when 400, 404
case error_data[:type]
when "idempotency_error"
IdempotencyError.new(error_data[:message], **opts)
else
InvalidRequestError.new(
error_data[:message], error_data[:param],
**opts
)
end
when 401
AuthenticationError.new(error_data[:message], **opts)
when 402
CardError.new(
error_data[:message], error_data[:param],
**opts
)
when 403
PermissionError.new(error_data[:message], **opts)
when 429
RateLimitError.new(error_data[:message], **opts)
else
APIError.new(error_data[:message], **opts)
end
end
private def specific_v2_api_error(resp, error_data, context)
Util.log_error("Stripe v2 API error",
status: resp.http_status,
error_code: error_data[:code],
error_message: error_data[:message],
error_param: error_data[:param],
error_type: error_data[:type],
idempotency_key: context.idempotency_key,
request_id: context.request_id,
config: config)
# The standard set of arguments that can be used to initialize most of
# the exceptions.
opts = {
http_body: resp.http_body,
http_headers: resp.http_headers,
http_status: resp.http_status,
json_body: resp.data,
code: error_data[:code],
}
case error_data[:type]
when "idempotency_error"
IdempotencyError.new(error_data[:message], **opts)
# switch cases: The beginning of the section generated from our OpenAPI spec
when "temporary_session_expired"
TemporarySessionExpiredError.new(error_data[:message], **opts)
# switch cases: The end of the section generated from our OpenAPI spec
else
specific_api_error(resp, error_data, context)
end
end
# Attempts to look at a response's error code and return an OAuth error if
# one matches. Will return `nil` if the code isn't recognized.
private def specific_oauth_error(resp, error_code, context)
description = resp.data[:error_description] || error_code
Util.log_error("Stripe OAuth error",
status: resp.http_status,
error_code: error_code,
error_description: description,
idempotency_key: context.idempotency_key,
request_id: context.request_id,
config: config)
args = {
http_status: resp.http_status, http_body: resp.http_body,
json_body: resp.data, http_headers: resp.http_headers,
}
case error_code
when "invalid_client"
OAuth::InvalidClientError.new(error_code, description, **args)
when "invalid_grant"
OAuth::InvalidGrantError.new(error_code, description, **args)
when "invalid_request"
OAuth::InvalidRequestError.new(error_code, description, **args)
when "invalid_scope"
OAuth::InvalidScopeError.new(error_code, description, **args)
when "unsupported_grant_type"
OAuth::UnsupportedGrantTypeError.new(error_code, description, **args)
when "unsupported_response_type"
OAuth::UnsupportedResponseTypeError.new(error_code, description, **args)
else
# We'd prefer that all errors are typed, but we create a generic
# OAuthError in case we run into a code that we don't recognize.
OAuth::OAuthError.new(error_code, description, **args)
end
end
private def handle_network_error(error, context, num_retries,
base_url)
Util.log_error("Stripe network error",
error_message: error.message,
idempotency_key: context.idempotency_key,
request_id: context.request_id,
config: config)
errors, message = NETWORK_ERROR_MESSAGES_MAP.detect do |(e, _)|
error.is_a?(e)
end
if errors.nil?
message = "Unexpected error #{error.class.name} communicating " \
"with Stripe. Please let us know at support@stripe.com."
end
message %= base_url
message += " Request was retried #{num_retries} times." if num_retries > 0
raise APIConnectionError,
message + "\n\n(Network error: #{error.message})"
end
private def request_headers(method, api_mode, req_opts)
user_agent = "Stripe/#{api_mode} RubyBindings/#{Stripe::VERSION}"
user_agent += " " + format_app_info(Stripe.app_info) unless Stripe.app_info.nil?
headers = {
"User-Agent" => user_agent,
"Authorization" => "Bearer #{req_opts[:api_key]}",
}
if config.enable_telemetry?
begin
headers["X-Stripe-Client-Telemetry"] = JSON.generate(
last_request_metrics: @last_request_metrics.pop(true)&.payload
)
rescue ThreadError
# last_request_metrics is best effort, ignore failures when queue
# is empty. should fail if this is the first request ever sent
# on a requestor
end
end
headers["Idempotency-Key"] = req_opts[:idempotency_key] if req_opts[:idempotency_key]
# It is only safe to retry network failures on post and delete
# requests if we add an Idempotency-Key header
if %i[post delete].include?(method) && (api_mode == :v2 || config.max_network_retries > 0)
headers["Idempotency-Key"] ||= SecureRandom.uuid
end
headers["Stripe-Version"] = req_opts[:stripe_version] if req_opts[:stripe_version]
headers["Stripe-Account"] = req_opts[:stripe_account] if req_opts[:stripe_account]
headers["Stripe-Context"] = req_opts[:stripe_context] if req_opts[:stripe_context]
user_agent = @system_profiler.user_agent
begin
headers.update(
"X-Stripe-Client-User-Agent" => JSON.generate(user_agent)
)
rescue StandardError => e
headers.update(
"X-Stripe-Client-Raw-User-Agent" => user_agent.inspect,
:error => "#{e} (#{e.class})"
)
end
headers.update(req_opts[:headers])
end
private def log_request(context, num_retries)
Util.log_info("Request to Stripe API",
account: context.account,
api_version: context.api_version,
idempotency_key: context.idempotency_key,
method: context.method,
num_retries: num_retries,
path: context.path,
config: config)
Util.log_debug("Request details",
body: context.body,
idempotency_key: context.idempotency_key,
query: context.query,
config: config,
process_id: Process.pid,
thread_object_id: Thread.current.object_id,
log_timestamp: Util.monotonic_time)
end
private def log_response(context, request_start, status, body, resp)
Util.log_info("Response from Stripe API",
account: context.account,
api_version: context.api_version,
elapsed: Util.monotonic_time - request_start,
idempotency_key: context.idempotency_key,
method: context.method,
path: context.path,
request_id: context.request_id,
status: status,
config: config)
Util.log_debug("Response details",
body: body,
idempotency_key: context.idempotency_key,
request_id: context.request_id,
config: config,
process_id: Process.pid,
thread_object_id: Thread.current.object_id,
response_object_id: resp.object_id,
log_timestamp: Util.monotonic_time)
return unless context.request_id
Util.log_debug("Dashboard link for request",
idempotency_key: context.idempotency_key,
request_id: context.request_id,
url: Util.request_id_dashboard_url(context.request_id,
context.api_key),
config: config)
end
private def log_response_error(context, request_start, error)
elapsed = request_start ? Util.monotonic_time - request_start : nil
Util.log_error("Request error",
elapsed: elapsed,
error_message: error.message,
idempotency_key: context.idempotency_key,
method: context.method,
path: context.path,
config: config)
end
# RequestLogContext stores information about a request that's begin made so
# that we can log certain information. It's useful because it means that we
# don't have to pass around as many parameters.
class RequestLogContext
attr_accessor :body, :account, :api_key, :api_version, :idempotency_key, :method, :path, :query, :request_id
# The idea with this method is that we might want to update some of
# context information because a response that we've received from the API
# contains information that's more authoritative than what we started
# with for a request. For example, we should trust whatever came back in
# a `Stripe-Version` header beyond what configuration information that we
# might have had available.
def dup_from_response_headers(headers)
context = dup
context.account = headers["Stripe-Account"]
context.api_version = headers["Stripe-Version"]
context.idempotency_key = headers["Idempotency-Key"]
context.request_id = headers["Request-Id"]
context
end
end
# SystemProfiler extracts information about the system that we're running
# in so that we can generate a rich user agent header to help debug
# integrations.
class SystemProfiler
def self.uname
if ::File.exist?("/proc/version")
::File.read("/proc/version").strip
else
case RbConfig::CONFIG["host_os"]
when /linux|darwin|bsd|sunos|solaris|cygwin/i
uname_from_system
when /mswin|mingw/i
uname_from_system_ver
else
"unknown platform"
end
end
end
def self.uname_from_system
(`uname -a 2>/dev/null` || "").strip
rescue Errno::ENOENT
"uname executable not found"
rescue Errno::ENOMEM # couldn't create subprocess
"uname lookup failed"
end
def self.uname_from_system_ver
(`ver` || "").strip
rescue Errno::ENOENT
"ver executable not found"
rescue Errno::ENOMEM # couldn't create subprocess
"uname lookup failed"
end
def initialize
@uname = self.class.uname
end
def user_agent
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} " \
"(#{RUBY_RELEASE_DATE})"
{
application: Stripe.app_info,
bindings_version: Stripe::VERSION,
lang: "ruby",
lang_version: lang_version,
platform: RUBY_PLATFORM,
engine: defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
publisher: "stripe",
uname: @uname,
hostname: Socket.gethostname,
}.delete_if { |_k, v| v.nil? }
end
end
# StripeRequestMetrics tracks metadata to be reported to stripe for metrics
# collection
class StripeRequestMetrics
# The Stripe request ID of the response.
attr_accessor :request_id
# Request duration in milliseconds
attr_accessor :request_duration_ms
# list of names of tracked behaviors associated with this request
attr_accessor :usage
def initialize(request_id, request_duration_ms, usage: [])
self.request_id = request_id
self.request_duration_ms = request_duration_ms
self.usage = usage
end
def payload
ret = { request_id: request_id, request_duration_ms: request_duration_ms }
ret[:usage] = usage if !usage.nil? && !usage.empty?
ret
end
end
end
end
# frozen_string_literal: true
module Stripe
# The base class for nested TestHelpers classes in resource objects.
# The APIResourceTestHelpers handles URL generation and custom method
# support for test-helper methods.
#
# class MyAPIResource < APIResource
# class TestHelpers < APIResourceTestHelpers
class APIResourceTestHelpers
include Stripe::APIOperations::Request
def initialize(resource)
@resource = resource
end
def self.resource_class
nil
end
# Adds a custom method to a test helper. This is used to add support for
# non-CRUDL API requests, e.g. capturing charges. custom_method takes the
# following parameters:
# - name: the name of the custom method to create (as a symbol)
# - http_verb: the HTTP verb for the API request (:get, :post, or :delete)
# - http_path: the path to append to the resource's URL. If not provided,
# the name is used as the path
#
# For example, this call:
# custom_method :capture, http_verb: post
# adds a `capture` class method to the resource class that, when called,
# will send a POST request to `/v1/<object_name>/capture`.
def self.custom_method(name, http_verb:, http_path: nil)
Util.custom_method resource_class, self, name, http_verb, http_path
end
def self.resource_url
"/v1/test_helpers/" \
"#{resource_class.object_name.downcase.tr('.', '/')}s"
end
def resource_url
unless (id = @resource["id"])
raise InvalidRequestError.new(
"Could not determine which URL to request: #{self.class} instance " \
"has invalid ID: #{id.inspect}",
"id"
)
end
"#{self.class.resource_url}/#{CGI.escape(id)}"
end
end
end
# frozen_string_literal: true
module Stripe
class APIResource < StripeObject
include Stripe::APIOperations::Request
# A flag that can be set a behavior that will cause this resource to be
# encoded and sent up along with an update of its parent resource. This is
# usually not desirable because resources are updated individually on their
# own endpoints, but there are certain cases, replacing a customer's source
# for example, where this is allowed.
attr_accessor :save_with_parent
# TODO: (major) Remove OBJECT_NAME and stop using const_get here
# This is a workaround to avoid breaking users who have defined their own
# APIResource subclasses with a custom OBJECT_NAME. We should never fallback
# on this case otherwise.
OBJECT_NAME = ""
def self.object_name
const_get(:OBJECT_NAME)
end
def self.class_name
name.split("::")[-1]
end
def self.resource_url
if name.include?("Stripe::V2")
raise NotImplementedError,
"V2 resources do not have a defined URL. Please use the StripeClient " \
"to make V2 requests"
end
if self == APIResource
raise NotImplementedError,
"APIResource is an abstract class. You should perform actions " \
"on its subclasses (Charge, Customer, etc.)"
end
# Namespaces are separated in object names with periods (.) and in URLs
# with forward slashes (/), so replace the former with the latter.
"/v1/#{object_name.downcase.tr('.', '/')}s"
end
# A metaprogramming call that specifies that a field of a resource can be
# its own type of API resource (say a nested card under an account for
# example), and if that resource is set, it should be transmitted to the
# API on a create or update. Doing so is not the default behavior because
# API resources should normally be persisted on their own RESTful
# endpoints.
def self.save_nested_resource(name)
define_method(:"#{name}=") do |value|
super(value)
# The parent setter will perform certain useful operations like
# converting to an APIResource if appropriate. Refresh our argument
# value to whatever it mutated to.
value = send(name)
# Note that the value may be subresource, but could also be a scalar
# (like a tokenized card's ID for example), so we check the type before
# setting #save_with_parent here.
value.save_with_parent = true if value.is_a?(APIResource)
value
end
end
# Adds a custom method to a resource class. This is used to add support for
# non-CRUDL API requests, e.g. capturing charges. custom_method takes the
# following parameters:
# - name: the name of the custom method to create (as a symbol)
# - http_verb: the HTTP verb for the API request (:get, :post, or :delete)
# - http_path: the path to append to the resource's URL. If not provided,
# the name is used as the path
#
# For example, this call:
# custom_method :capture, http_verb: post
# adds a `capture` class method to the resource class that, when called,
# will send a POST request to `/v1/<object_name>/capture`.
def self.custom_method(name, http_verb:, http_path: nil)
Util.custom_method self, self, name, http_verb, http_path
end
def resource_url
unless (id = self["id"])
raise InvalidRequestError.new(
"Could not determine which URL to request: #{self.class} instance " \
"has invalid ID: #{id.inspect}",
"id"
)
end
"#{self.class.resource_url}/#{CGI.escape(id)}"
end
def refresh
if self.class.name.include?("Stripe::V2")
raise NotImplementedError,
"It is not possible to refresh v2 objects. Please retrieve the object using the StripeClient instead."
end
opts = RequestOptions.extract_opts_from_hash(@retrieve_opts || {})
@retrieve_opts = {} # Make sure to clear the retrieve options
@obj = @requestor.execute_request_initialize_from(:get, resource_url, :api, self, params: @retrieve_params,
opts: opts)
initialize_from(
@obj.last_response.data,
@obj.instance_variable_get(:@opts),
@obj.last_response,
api_mode: :v1,
requestor: @requestor
)
end
# Retrieve by passing id like `retrieve(id)`
# If passing parameters, pass a single map with parameters and the `id` as well.
# For example: retrieve({id: some_id, some_param: some_param_value})
def self.retrieve(id, opts = {})
if name.include?("Stripe::V2")
raise NotImplementedError,
"It is not possible to retrieve v2 objects on the resource. Please use the StripeClient instead."
end
instance = new(id)
# Explicitly send options for the retrieve call, to preserve custom headers
# This is so we can pass custom headers from this static function
# to the instance variable method call
# The custom headers will be cleaned up with the rest of the RequestOptions
instance.instance_variable_set(:@retrieve_opts, Util.normalize_opts(opts))
instance.refresh
end
def request_stripe_object(method:, path:, params:, base_address: :api, opts: {})
APIRequestor.active_requestor.execute_request_initialize_from(method, path, base_address, self,
params: params, opts: opts)
end
protected def request_stream(method:, path:, params:, base_address: :api, opts: {},
&read_body_chunk_block)
resp, = execute_resource_request_stream(
method, path, base_address, params, opts, &read_body_chunk_block
)
resp
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
module ApiVersion
CURRENT = "2025-12-15.clover"
CURRENT_MAJOR = "clover"
end
end
# frozen_string_literal: true
module Stripe
# Manages connections across multiple hosts which is useful because the
# library may connect to multiple hosts during a typical session (main API,
# Connect, Uploads). Ruby doesn't provide an easy way to make this happen
# easily, so this class is designed to track what we're connected to and
# manage the lifecycle of those connections.
#
# Note that this class in itself is *not* thread safe. We expect it to be
# instantiated once per thread.
class ConnectionManager
# Timestamp (in seconds procured from the system's monotonic clock)
# indicating when the connection manager last made a request. This is used
# by `APIRequestor` to determine whether a connection manager should be
# garbage collected or not.
attr_reader :last_used
attr_reader :config
def initialize(config = Stripe.config)
@config = config
@active_connections = {}
@last_used = Util.monotonic_time
# A connection manager may be accessed across threads as one thread makes
# requests on it while another is trying to clear it (either because it's
# trying to garbage collect the manager or trying to clear it because a
# configuration setting has changed). That's probably thread-safe already
# because of Ruby's GIL, but just in case the library's running on JRuby
# or the like, use a mutex to synchronize access in this connection
# manager.
@mutex = Mutex.new
end
# Finishes any active connections by closing their TCP connection and
# clears them from internal tracking.
def clear
@mutex.synchronize do
@active_connections.each_value(&:finish)
@active_connections = {}
end
end
# Gets a connection for a given URI. This is for internal use only as it's
# subject to change (we've moved between HTTP client schemes in the past
# and may do it again).
#
# `uri` is expected to be a string.
def connection_for(uri)
@mutex.synchronize do
u = URI.parse(uri)
connection = @active_connections[[u.host, u.port]]
if connection.nil?
connection = create_connection(u)
connection.start
@active_connections[[u.host, u.port]] = connection
end
connection
end
end
# Executes an HTTP request to the given URI with the given method. Also
# allows a request body, headers, and query string to be specified.
def execute_request(method, uri, body: nil, headers: nil, query: nil,
&block)
# Perform some basic argument validation because it's easy to get
# confused between strings and hashes for things like body and query
# parameters.
raise ArgumentError, "method should be a symbol" \
unless method.is_a?(Symbol)
raise ArgumentError, "uri should be a string" \
unless uri.is_a?(String)
raise ArgumentError, "body should be a string" \
if body && !body.is_a?(String)
raise ArgumentError, "headers should be a hash" \
if headers && !headers.is_a?(Hash)
raise ArgumentError, "query should be a string" \
if query && !query.is_a?(String)
@last_used = Util.monotonic_time
connection = connection_for(uri)
u = URI.parse(uri)
path = if query
u.path + "?" + query
else
u.path
end
method_name = method.to_s.upcase
has_response_body = method_name != "HEAD"
request = Net::HTTPGenericRequest.new(
method_name,
(body ? true : false),
has_response_body,
path,
headers
)
Util.log_debug("ConnectionManager starting request",
method_name: method_name,
path: path,
process_id: Process.pid,
thread_object_id: Thread.current.object_id,
connection_manager_object_id: object_id,
connection_object_id: connection.object_id,
log_timestamp: Util.monotonic_time)
resp = @mutex.synchronize do
# The block parameter is special here. If a block is provided, the block
# is invoked with the Net::HTTPResponse. However, the body will not have
# been read yet in the block, and can be streamed by calling
# HTTPResponse#read_body.
connection.request(request, body, &block)
end
Util.log_debug("ConnectionManager request complete",
method_name: method_name,
path: path,
process_id: Process.pid,
thread_object_id: Thread.current.object_id,
connection_manager_object_id: object_id,
connection_object_id: connection.object_id,
response_object_id: resp.object_id,
log_timestamp: Util.monotonic_time)
resp
end
#
# private
#
# `uri` should be a parsed `URI` object.
private def create_connection(uri)
# These all come back as `nil` if no proxy is configured.
proxy_host, proxy_port, proxy_user, proxy_pass = proxy_parts
connection = Net::HTTP.new(uri.host, uri.port,
proxy_host, proxy_port,
proxy_user, proxy_pass)
# Time in seconds within which Net::HTTP will try to reuse an already
# open connection when issuing a new operation. Outside this window, Ruby
# will transparently close and re-open the connection without trying to
# reuse it.
#
# Ruby's default of 2 seconds is almost certainly too short. Here I've
# reused Go's default for `DefaultTransport`.
connection.keep_alive_timeout = 30
connection.open_timeout = config.open_timeout
connection.read_timeout = config.read_timeout
connection.write_timeout = config.write_timeout if connection.respond_to?(:write_timeout=)
connection.use_ssl = uri.scheme == "https"
if config.verify_ssl_certs
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
connection.cert_store = config.ca_store
else
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
warn_ssl_verify_none
end
connection
end
# `Net::HTTP` somewhat awkwardly requires each component of a proxy URI
# (host, port, etc.) rather than the URI itself. This method simply parses
# out those pieces to make passing them into a new connection a little less
# ugly.
private def proxy_parts
if config.proxy.nil?
[nil, nil, nil, nil]
else
u = URI.parse(config.proxy)
[u.host, u.port, u.user, u.password]
end
end
private def warn_ssl_verify_none
return if @verify_ssl_warned
@verify_ssl_warned = true
warn("WARNING: Running without SSL cert verification. " \
"You should never do this in production. " \
"Execute `Stripe.verify_ssl_certs = true` to enable " \
"verification.")
end
end
end
# frozen_string_literal: true
module Stripe
# Represents an error object as returned by the API.
#
# @see https://stripe.com/docs/api/errors
class ErrorObject < StripeObject
# Unlike other objects, we explicitly declare getter methods here. This
# is because the API doesn't return `null` values for fields on this
# object, rather the fields are omitted entirely. Not declaring the getter
# methods would cause users to run into `NoMethodError` exceptions and
# get in the way of generic error handling.
# For card errors, the ID of the failed charge.
def charge
@values[:charge]
end
# For some errors that could be handled programmatically, a short string
# indicating the error code reported.
def code
@values[:code]
end
# For card errors resulting from a card issuer decline, a short string
# indicating the card issuer's reason for the decline if they provide one.
def decline_code
@values[:decline_code]
end
# A URL to more information about the error code reported.
def doc_url
@values[:doc_url]
end
# A human-readable message providing more details about the error. For card
# errors, these messages can be shown to your users.
def message
@values[:message]
end
# If the error is parameter-specific, the parameter related to the error.
# For example, you can use this to display a message near the correct form
# field.
def param
@values[:param]
end
# The PaymentIntent object for errors returned on a request involving a
# PaymentIntent.
def payment_intent
@values[:payment_intent]
end
# The PaymentMethod object for errors returned on a request involving a
# PaymentMethod.
def payment_method
@values[:payment_method]
end
# The SetupIntent object for errors returned on a request involving a
# SetupIntent.
def setup_intent
@values[:setup_intent]
end
# The source object for errors returned on a request involving a source.
def source
@values[:source]
end
# The type of error returned. One of `api_error`, `card_error`,
# `idempotency_error`, or `invalid_request_error`.
def type
@values[:type]
end
end
# Represents on OAuth error returned by the OAuth API.
#
# @see https://stripe.com/docs/connect/oauth-reference#post-token-errors
class OAuthErrorObject < StripeObject
# A unique error code per error type.
def error
@values[:error]
end
# A human readable description of the error.
def error_description
@values[:error_description]
end
end
end
# frozen_string_literal: true
module Stripe
# StripeError is the base error from which all other more specific Stripe
# errors derive.
class StripeError < StandardError
attr_reader :message, :code, :error, :http_body, :http_headers, :http_status, :json_body, :request_id
# Response contains a StripeResponse object that has some basic information
# about the response that conveyed the error.
attr_accessor :response # equivalent to #data
# Initializes a StripeError.
def initialize(message = nil, http_status: nil, http_body: nil, # rubocop:todo Lint/MissingSuper
json_body: nil, http_headers: nil, code: nil)
@message = message
@http_status = http_status
@http_body = http_body
@http_headers = http_headers || {}
@idempotent_replayed = @http_headers["idempotent-replayed"] == "true"
@json_body = json_body
@code = code
@request_id = @http_headers["request-id"]
@error = construct_error_object
end
def construct_error_object
return nil if @json_body.nil? || !@json_body.key?(:error)
# ErrorObject is shared between v1 and v2, so use original object_classes to find
ErrorObject.construct_from(@json_body[:error], {}, nil, :v1)
end
def to_s
status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
id_string = @request_id.nil? ? "" : "(Request #{@request_id}) "
"#{status_string}#{id_string}#{@message}"
end
end
# AuthenticationError is raised when invalid credentials are used to connect
# to Stripe's servers.
class AuthenticationError < StripeError
end
# APIConnectionError is raised in the event that the SDK can't connect to
# Stripe's servers. That can be for a variety of different reasons from a
# downed network to a bad TLS certificate.
class APIConnectionError < StripeError
end
# APIError is a generic error that may be raised in cases where none of the
# other named errors cover the problem. It could also be raised in the case
# that a new error has been introduced in the API, but this version of the
# Ruby SDK doesn't know how to handle it.
class APIError < StripeError
end
# CardError is raised when a user enters a card that can't be charged for
# some reason.
class CardError < StripeError
attr_reader :param
def initialize(message, param, code: nil, http_status: nil, http_body: nil,
json_body: nil, http_headers: nil)
super(message, http_status: http_status, http_body: http_body,
json_body: json_body, http_headers: http_headers,
code: code)
@param = param
end
end
# IdempotencyError is raised in cases where an idempotency key was used
# improperly.
class IdempotencyError < StripeError
end
# InvalidRequestError is raised when a request is initiated with invalid
# parameters.
class InvalidRequestError < StripeError
attr_accessor :param
def initialize(message, param, http_status: nil, http_body: nil,
json_body: nil, http_headers: nil, code: nil)
super(message, http_status: http_status, http_body: http_body,
json_body: json_body, http_headers: http_headers,
code: code)
@param = param
end
end
# PermissionError is raised in cases where access was attempted on a resource
# that wasn't allowed.
class PermissionError < StripeError
end
# RateLimitError is raised in cases where an account is putting too much load
# on Stripe's API servers (usually by performing too many requests). Please
# back off on request rate.
class RateLimitError < StripeError
end
# SignatureVerificationError is raised when the signature verification for a
# webhook fails
class SignatureVerificationError < StripeError
attr_accessor :sig_header
def initialize(message, sig_header, http_body: nil)
super(message, http_body: http_body)
@sig_header = sig_header
end
end
module OAuth
# OAuthError is raised when the OAuth API returns an error.
class OAuthError < StripeError
def initialize(code, description, http_status: nil, http_body: nil,
json_body: nil, http_headers: nil)
super(description, http_status: http_status, http_body: http_body,
json_body: json_body, http_headers: http_headers,
code: code)
end
def construct_error_object
return nil if @json_body.nil?
OAuthErrorObject.construct_from(@json_body, {}, nil, :v1)
end
end
# InvalidClientError is raised when the client doesn't belong to you, or
# the API key mode (live or test) doesn't match the client mode. Or the
# stripe_user_id doesn't exist or isn't connected to your application.
class InvalidClientError < OAuthError
end
# InvalidGrantError is raised when a specified code doesn't exist, is
# expired, has been used, or doesn't belong to you; a refresh token doesn't
# exist, or doesn't belong to you; or if an API key's mode (live or test)
# doesn't match the mode of a code or refresh token.
class InvalidGrantError < OAuthError
end
# InvalidRequestError is raised when a code, refresh token, or grant type
# parameter is not provided, but was required.
class InvalidRequestError < OAuthError
end
# InvalidScopeError is raised when an invalid scope parameter is provided.
class InvalidScopeError < OAuthError
end
# UnsupportedGrantTypeError is raised when an unuspported grant type
# parameter is specified.
class UnsupportedGrantTypeError < OAuthError
end
# UnsupportedResponseTypeError is raised when an unsupported response type
# parameter is specified.
class UnsupportedResponseTypeError < OAuthError
end
end
# class definitions: The beginning of the section generated from our OpenAPI spec
class TemporarySessionExpiredError < StripeError
end
# class definitions: The end of the section generated from our OpenAPI spec
end
# frozen_string_literal: true
module Stripe
module EventTypes
def self.v2_event_types_to_classes
{
# v2 event types: The beginning of the section generated from our OpenAPI spec
Events::V1BillingMeterErrorReportTriggeredEvent.lookup_type =>
Events::V1BillingMeterErrorReportTriggeredEvent,
Events::V1BillingMeterNoMeterFoundEvent.lookup_type => Events::V1BillingMeterNoMeterFoundEvent,
Events::V2CoreEventDestinationPingEvent.lookup_type => Events::V2CoreEventDestinationPingEvent,
# v2 event types: The end of the section generated from our OpenAPI spec
}
end
def self.event_notification_types_to_classes
{
# event notification types: The beginning of the section generated from our OpenAPI spec
Events::V1BillingMeterErrorReportTriggeredEventNotification.lookup_type =>
Events::V1BillingMeterErrorReportTriggeredEventNotification,
Events::V1BillingMeterNoMeterFoundEventNotification.lookup_type =>
Events::V1BillingMeterNoMeterFoundEventNotification,
Events::V2CoreEventDestinationPingEventNotification.lookup_type =>
Events::V2CoreEventDestinationPingEventNotification,
# event notification types: The end of the section generated from our OpenAPI spec
}
end
end
end
# frozen_string_literal: true
require "stripe/resources/v2/core/event_notification"
module Stripe
module Events
class UnknownEventNotification < Stripe::V2::Core::EventNotification
attr_reader :related_object
def fetch_related_object
return nil if @related_object.nil?
resp = @client.raw_request(:get, related_object.url, opts: { stripe_context: context },
usage: ["fetch_related_object"])
@client.deserialize(resp.http_body, api_mode: Util.get_api_mode(related_object.url))
end
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
module Events
# Occurs when a Meter has invalid async usage events.
class V1BillingMeterErrorReportTriggeredEvent < Stripe::V2::Core::Event
def self.lookup_type
"v1.billing.meter.error_report_triggered"
end
class V1BillingMeterErrorReportTriggeredEventData < ::Stripe::StripeObject
class Reason < ::Stripe::StripeObject
class ErrorType < ::Stripe::StripeObject
class SampleError < ::Stripe::StripeObject
class Request < ::Stripe::StripeObject
# The request idempotency key.
attr_reader :identifier
def self.inner_class_types
@inner_class_types = {}
end
def self.field_remappings
@field_remappings = {}
end
end
# The error message.
attr_reader :error_message
# The request causes the error.
attr_reader :request
def self.inner_class_types
@inner_class_types = { request: Request }
end
def self.field_remappings
@field_remappings = {}
end
end
# Open Enum.
attr_reader :code
# The number of errors of this type.
attr_reader :error_count
# A list of sample errors of this type.
attr_reader :sample_errors
def self.inner_class_types
@inner_class_types = { sample_errors: SampleError }
end
def self.field_remappings
@field_remappings = {}
end
end
# The total error count within this window.
attr_reader :error_count
# The error details.
attr_reader :error_types
def self.inner_class_types
@inner_class_types = { error_types: ErrorType }
end
def self.field_remappings
@field_remappings = {}
end
end
# This contains information about why meter error happens.
attr_reader :reason
# Extra field included in the event's `data` when fetched from /v2/events.
attr_reader :developer_message_summary
# The start of the window that is encapsulated by this summary.
attr_reader :validation_start
# The end of the window that is encapsulated by this summary.
attr_reader :validation_end
def self.inner_class_types
@inner_class_types = { reason: Reason }
end
def self.field_remappings
@field_remappings = {}
end
end
def self.inner_class_types
@inner_class_types = { data: V1BillingMeterErrorReportTriggeredEventData }
end
attr_reader :data, :related_object
# Retrieves the related object from the API. Makes an API request on every call.
def fetch_related_object
_request(
method: :get,
path: related_object.url,
base_address: :api,
opts: { stripe_context: context }
)
end
end
# Occurs when a Meter has invalid async usage events.
class V1BillingMeterErrorReportTriggeredEventNotification < Stripe::V2::Core::EventNotification
def self.lookup_type
"v1.billing.meter.error_report_triggered"
end
attr_reader :related_object
# Retrieves the Meter related to this EventNotification from the Stripe API. Makes an API request on every call.
def fetch_related_object
resp = @client.raw_request(
:get,
related_object.url,
opts: { stripe_context: context },
usage: ["fetch_related_object"]
)
@client.deserialize(resp.http_body, api_mode: Util.get_api_mode(related_object.url))
end
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
module Events
# Occurs when a Meter's id is missing or invalid in async usage events.
class V1BillingMeterNoMeterFoundEvent < Stripe::V2::Core::Event
def self.lookup_type
"v1.billing.meter.no_meter_found"
end
class V1BillingMeterNoMeterFoundEventData < ::Stripe::StripeObject
class Reason < ::Stripe::StripeObject
class ErrorType < ::Stripe::StripeObject
class SampleError < ::Stripe::StripeObject
class Request < ::Stripe::StripeObject
# The request idempotency key.
attr_reader :identifier
def self.inner_class_types
@inner_class_types = {}
end
def self.field_remappings
@field_remappings = {}
end
end
# The error message.
attr_reader :error_message
# The request causes the error.
attr_reader :request
def self.inner_class_types
@inner_class_types = { request: Request }
end
def self.field_remappings
@field_remappings = {}
end
end
# Open Enum.
attr_reader :code
# The number of errors of this type.
attr_reader :error_count
# A list of sample errors of this type.
attr_reader :sample_errors
def self.inner_class_types
@inner_class_types = { sample_errors: SampleError }
end
def self.field_remappings
@field_remappings = {}
end
end
# The total error count within this window.
attr_reader :error_count
# The error details.
attr_reader :error_types
def self.inner_class_types
@inner_class_types = { error_types: ErrorType }
end
def self.field_remappings
@field_remappings = {}
end
end
# This contains information about why meter error happens.
attr_reader :reason
# Extra field included in the event's `data` when fetched from /v2/events.
attr_reader :developer_message_summary
# The start of the window that is encapsulated by this summary.
attr_reader :validation_start
# The end of the window that is encapsulated by this summary.
attr_reader :validation_end
def self.inner_class_types
@inner_class_types = { reason: Reason }
end
def self.field_remappings
@field_remappings = {}
end
end
def self.inner_class_types
@inner_class_types = { data: V1BillingMeterNoMeterFoundEventData }
end
attr_reader :data
end
# Occurs when a Meter's id is missing or invalid in async usage events.
class V1BillingMeterNoMeterFoundEventNotification < Stripe::V2::Core::EventNotification
def self.lookup_type
"v1.billing.meter.no_meter_found"
end
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
module Events
# A ping event used to test the connection to an EventDestination.
class V2CoreEventDestinationPingEvent < Stripe::V2::Core::Event
def self.lookup_type
"v2.core.event_destination.ping"
end
# Retrieves the related object from the API. Makes an API request on every call.
def fetch_related_object
_request(
method: :get,
path: related_object.url,
base_address: :api,
opts: { stripe_context: context }
)
end
attr_reader :related_object
end
# A ping event used to test the connection to an EventDestination.
class V2CoreEventDestinationPingEventNotification < Stripe::V2::Core::EventNotification
def self.lookup_type
"v2.core.event_destination.ping"
end
attr_reader :related_object
# Retrieves the EventDestination related to this EventNotification from the Stripe API. Makes an API request on every call.
def fetch_related_object
resp = @client.raw_request(
:get,
related_object.url,
opts: { stripe_context: context },
usage: ["fetch_related_object"]
)
@client.deserialize(resp.http_body, api_mode: Util.get_api_mode(related_object.url))
end
end
end
end
# frozen_string_literal: true
module Stripe
class Instrumentation
# Event emitted on `request_begin` callback.
class RequestBeginEvent
attr_reader :method, :path
# Arbitrary user-provided data in the form of a Ruby hash that's passed
# from subscribers on `request_begin` to subscribers on `request_end`.
# `request_begin` subscribers can set keys which will then be available
# in `request_end`.
#
# Note that all subscribers of `request_begin` share the same object, so
# they must be careful to set unique keys so as to not conflict with data
# set by other subscribers.
attr_reader :user_data
def initialize(method:, path:, user_data:)
@method = method
@path = path
@user_data = user_data
freeze
end
end
# Event emitted on `request_end` callback.
class RequestEndEvent
attr_reader :duration, :http_status, :method, :num_retries, :path, :request_id, :response_header, :response_body,
:request_header, :request_body
# Arbitrary user-provided data in the form of a Ruby hash that's passed
# from subscribers on `request_begin` to subscribers on `request_end`.
# `request_begin` subscribers can set keys which will then be available
# in `request_end`.
attr_reader :user_data
def initialize(request_context:, response_context:,
num_retries:, user_data: nil)
@duration = request_context.duration
@http_status = response_context.http_status
@method = request_context.method
@num_retries = num_retries
@path = request_context.path
@request_id = request_context.request_id
@user_data = user_data
@response_header = response_context.header
@response_body = response_context.body
@request_header = request_context.header
@request_body = request_context.body
freeze
end
end
class RequestContext
attr_reader :duration, :method, :path, :request_id, :body, :header
def initialize(duration:, context:, header:)
@duration = duration
@method = context.method
@path = context.path
@request_id = context.request_id
@body = context.body
@header = header
end
end
class ResponseContext
attr_reader :http_status, :body, :header
def initialize(http_status:, response:)
@http_status = http_status
@header = response ? response.to_hash : nil
@body = response ? response.body : nil
end
end
# This class was renamed for consistency. This alias is here for backwards
# compatibility.
RequestEvent = RequestEndEvent
# Returns true if there are a non-zero number of subscribers on the given
# topic, and false otherwise.
def self.any_subscribers?(topic)
!subscribers[topic].empty?
end
def self.subscribe(topic, name = rand, &block)
subscribers[topic][name] = block
name
end
def self.unsubscribe(topic, name)
subscribers[topic].delete(name)
end
def self.notify(topic, event)
subscribers[topic].each_value { |subscriber| subscriber.call(event) }
end
def self.subscribers
@subscribers ||= Hash.new { |hash, key| hash[key] = {} }
end
private_class_method :subscribers
end
end
# frozen_string_literal: true
module Stripe
class ListObject < StripeObject
include Enumerable
include Stripe::APIOperations::List
include Stripe::APIOperations::Request
include Stripe::APIOperations::Create
OBJECT_NAME = "list"
def self.object_name
"list"
end
# This accessor allows a `ListObject` to inherit various filters that were
# given to a predecessor. This allows for things like consistent limits,
# expansions, and predicates as a user pages through resources.
attr_accessor :filters
# An empty list object. This is returned from +next+ when we know that
# there isn't a next page in order to replicate the behavior of the API
# when it attempts to return a page beyond the last.
def self.empty_list(opts = {})
ListObject.construct_from({ data: [] }, opts, nil, :v1)
end
def initialize(*args)
super
self.filters = {}
end
def [](key)
case key
when String, Symbol
super
else
raise ArgumentError,
"You tried to access the #{key.inspect} index, but ListObject " \
"types only support String keys. (HINT: List calls return an " \
"object with a 'data' (which is the data array). You likely " \
"want to call #data[#{key.inspect}])"
end
end
# Iterates through each resource in the page represented by the current
# `ListObject`.
#
# Note that this method makes no effort to fetch a new page when it gets to
# the end of the current page's resources. See also +auto_paging_each+.
def each(&blk)
data.each(&blk)
end
# Iterates through each resource in all pages, making additional fetches to
# the API as necessary.
#
# The default iteration direction is forwards according to Stripe's API
# "natural" ordering direction -- newer objects first, and moving towards
# older objects.
#
# However, if the initial list object was fetched using an `ending_before`
# cursor (and only `ending_before`, `starting_after` cannot also be
# included), the method assumes that the user is trying to iterate
# backwards compared to natural ordering and returns results that way --
# older objects first, and moving towards newer objects.
#
# Note that this method will make as many API calls as necessary to fetch
# all resources. For more granular control, please see +each+ and
# +next_page+.
def auto_paging_each(&blk)
return enum_for(:auto_paging_each) unless block_given?
page = self
loop do
# Backward iterating activates if we have an `ending_before` constraint
# and _just_ an `ending_before` constraint. If `starting_after` was
# also used, we iterate forwards normally.
if filters.include?(:ending_before) &&
!filters.include?(:starting_after)
page.reverse_each(&blk)
page = page.previous_page
else
page.each(&blk)
page = page.next_page
end
break if page.empty?
end
end
# Returns true if the page object contains no elements.
def empty?
data.empty?
end
def retrieve(id, opts = {})
id, retrieve_params = Util.normalize_id(id)
url = "#{resource_url}/#{CGI.escape(id)}"
execute_resource_request(:get, url, :api, retrieve_params, opts)
end
# Fetches the next page in the resource list (if there is one).
#
# This method will try to respect the limit of the current page. If none
# was given, the default limit will be fetched again.
def next_page(params = {}, opts = {})
return self.class.empty_list(opts) unless has_more
last_id = data.last.id
params = filters.merge(starting_after: last_id).merge(params)
list(params, opts)
end
# Fetches the previous page in the resource list (if there is one).
#
# This method will try to respect the limit of the current page. If none
# was given, the default limit will be fetched again.
def previous_page(params = {}, opts = {})
return self.class.empty_list(opts) unless has_more
first_id = data.first.id
params = filters.merge(ending_before: first_id).merge(params)
list(params, opts)
end
def resource_url
url ||
raise(ArgumentError, "List object does not contain a 'url' field.")
end
# Iterates through each resource in the page represented by the current
# `ListObject` in reverse.
def reverse_each(&blk)
data.reverse_each(&blk)
end
end
end
# frozen_string_literal: true
require "securerandom"
require "tempfile"
module Stripe
# Encodes parameters into a `multipart/form-data` payload as described by RFC
# 2388:
#
# https://tools.ietf.org/html/rfc2388
#
# This is most useful for transferring file-like objects.
#
# Parameters should be added with `#encode`. When ready, use `#body` to get
# the encoded result and `#content_type` to get the value that should be
# placed in the `Content-Type` header of a subsequent request (which includes
# a boundary value).
class MultipartEncoder
MULTIPART_FORM_DATA = "multipart/form-data"
# A shortcut for encoding a single set of parameters and finalizing a
# result.
#
# Returns an encoded body and the value that should be set in the content
# type header of a subsequent request.
def self.encode(params)
encoder = MultipartEncoder.new
encoder.encode(params)
encoder.close
[encoder.body, encoder.content_type]
end
# Gets the object's randomly generated boundary string.
attr_reader :boundary
# Initializes a new multipart encoder.
def initialize
# Kind of weird, but required by Rubocop because the unary plus operator
# is considered faster than `Stripe.new`.
@body = +""
# Chose the same number of random bytes that Go uses in its standard
# library implementation. Easily enough entropy to ensure that it won't
# be present in a file we're sending.
@boundary = SecureRandom.hex(30)
@closed = false
@first_field = true
end
# Gets the encoded body. `#close` must be called first.
def body
raise "object must be closed before getting body" unless @closed
@body
end
# Finalizes the object by writing the final boundary.
def close
raise "object already closed" if @closed
@body << "\r\n"
@body << "--#{@boundary}--"
@closed = true
nil
end
# Gets the value including boundary that should be put into a multipart
# request's `Content-Type`.
def content_type
"#{MULTIPART_FORM_DATA}; boundary=#{@boundary}"
end
# Encodes a set of parameters to the body.
#
# Note that parameters are expected to be a hash, but a "flat" hash such
# that complex substructures like hashes and arrays have already been
# appropriately Stripe-encoded. Pass a complex structure through
# `Util.flatten_params` first before handing it off to this method.
def encode(params)
raise "no more parameters can be written to closed object" if @closed
params.each do |name, val|
if val.is_a?(::File) || val.is_a?(::Tempfile)
write_field(name, val.read, filename: ::File.basename(val.path))
elsif val.respond_to?(:read)
write_field(name, val.read, filename: "blob")
else
write_field(name, val, filename: nil)
end
end
nil
end
#
# private
#
# Escapes double quotes so that the given value can be used in a
# double-quoted string and replaces any linebreak characters with spaces.
private def escape(str)
str.gsub('"', "%22").tr("\n", " ").tr("\r", " ")
end
private def write_field(name, data, filename:)
if @first_field
@first_field = false
else
@body << "\r\n"
end
@body << "--#{@boundary}\r\n"
if filename
@body << (%(Content-Disposition: form-data) +
%(; name="#{escape(name.to_s)}") +
%(; filename="#{escape(filename)}"\r\n))
@body << %(Content-Type: application/octet-stream\r\n)
else
@body << (%(Content-Disposition: form-data) +
%(; name="#{escape(name.to_s)}"\r\n))
end
@body << "\r\n"
@body << data.to_s
end
end
end
# frozen_string_literal: true
module Stripe
module OAuth
module OAuthOperations
extend APIOperations::Request::ClassMethods
def self.execute_resource_request(method, url, base_address, params, opts)
opts = Util.normalize_opts(opts)
super
end
end
def self.get_client_id(params = {})
client_id = params[:client_id] || Stripe.client_id
unless client_id
raise AuthenticationError, "No client_id provided. " \
'Set your client_id using "Stripe.client_id = <CLIENT-ID>". ' \
"You can find your client_ids in your Stripe dashboard at " \
"https://dashboard.stripe.com/account/applications/settings, " \
"after registering your account as a platform. See " \
"https://stripe.com/docs/connect/standalone-accounts for details, " \
"or email support@stripe.com if you have any questions."
end
client_id
end
def self.authorize_url(params = {}, opts = {})
base = opts[:connect_base] || APIRequestor.active_requestor.config.connect_base
path = "/oauth/authorize"
path = "/express" + path if opts[:express]
params[:client_id] = get_client_id(params)
params[:response_type] ||= "code"
query = Util.encode_parameters(params, :v1)
"#{base}#{path}?#{query}"
end
def self.token(params = {}, opts = {})
opts = Util.normalize_opts(opts)
opts[:api_key] = params[:client_secret] if params[:client_secret]
OAuthOperations.execute_resource_request(
:post, "/oauth/token", :connect, params, opts
)
end
def self.deauthorize(params = {}, opts = {})
opts = Util.normalize_opts(opts)
params[:client_id] = get_client_id(params)
OAuthOperations.execute_resource_request(
:post, "/oauth/deauthorize", :connect, params, opts
)
end
end
end
# frozen_string_literal: true
# rubocop:disable Metrics/MethodLength
module Stripe
module ObjectTypes
def self.object_names_to_classes
{
# data structures
ListObject.object_name => ListObject,
SearchResultObject.object_name => SearchResultObject,
File.object_name_alt => File,
# object classes: The beginning of the section generated from our OpenAPI spec
Account.object_name => Account,
AccountLink.object_name => AccountLink,
AccountSession.object_name => AccountSession,
ApplePayDomain.object_name => ApplePayDomain,
Application.object_name => Application,
ApplicationFee.object_name => ApplicationFee,
ApplicationFeeRefund.object_name => ApplicationFeeRefund,
Apps::Secret.object_name => Apps::Secret,
Balance.object_name => Balance,
BalanceSettings.object_name => BalanceSettings,
BalanceTransaction.object_name => BalanceTransaction,
BankAccount.object_name => BankAccount,
Billing::Alert.object_name => Billing::Alert,
Billing::AlertTriggered.object_name => Billing::AlertTriggered,
Billing::CreditBalanceSummary.object_name => Billing::CreditBalanceSummary,
Billing::CreditBalanceTransaction.object_name => Billing::CreditBalanceTransaction,
Billing::CreditGrant.object_name => Billing::CreditGrant,
Billing::Meter.object_name => Billing::Meter,
Billing::MeterEvent.object_name => Billing::MeterEvent,
Billing::MeterEventAdjustment.object_name => Billing::MeterEventAdjustment,
Billing::MeterEventSummary.object_name => Billing::MeterEventSummary,
BillingPortal::Configuration.object_name => BillingPortal::Configuration,
BillingPortal::Session.object_name => BillingPortal::Session,
Capability.object_name => Capability,
Card.object_name => Card,
CashBalance.object_name => CashBalance,
Charge.object_name => Charge,
Checkout::Session.object_name => Checkout::Session,
Climate::Order.object_name => Climate::Order,
Climate::Product.object_name => Climate::Product,
Climate::Supplier.object_name => Climate::Supplier,
ConfirmationToken.object_name => ConfirmationToken,
ConnectCollectionTransfer.object_name => ConnectCollectionTransfer,
CountrySpec.object_name => CountrySpec,
Coupon.object_name => Coupon,
CreditNote.object_name => CreditNote,
CreditNoteLineItem.object_name => CreditNoteLineItem,
Customer.object_name => Customer,
CustomerBalanceTransaction.object_name => CustomerBalanceTransaction,
CustomerCashBalanceTransaction.object_name => CustomerCashBalanceTransaction,
CustomerSession.object_name => CustomerSession,
Discount.object_name => Discount,
Dispute.object_name => Dispute,
Entitlements::ActiveEntitlement.object_name => Entitlements::ActiveEntitlement,
Entitlements::ActiveEntitlementSummary.object_name => Entitlements::ActiveEntitlementSummary,
Entitlements::Feature.object_name => Entitlements::Feature,
EphemeralKey.object_name => EphemeralKey,
Event.object_name => Event,
ExchangeRate.object_name => ExchangeRate,
File.object_name => File,
FileLink.object_name => FileLink,
FinancialConnections::Account.object_name => FinancialConnections::Account,
FinancialConnections::AccountOwner.object_name => FinancialConnections::AccountOwner,
FinancialConnections::AccountOwnership.object_name => FinancialConnections::AccountOwnership,
FinancialConnections::Session.object_name => FinancialConnections::Session,
FinancialConnections::Transaction.object_name => FinancialConnections::Transaction,
Forwarding::Request.object_name => Forwarding::Request,
FundingInstructions.object_name => FundingInstructions,
Identity::VerificationReport.object_name => Identity::VerificationReport,
Identity::VerificationSession.object_name => Identity::VerificationSession,
Invoice.object_name => Invoice,
InvoiceItem.object_name => InvoiceItem,
InvoiceLineItem.object_name => InvoiceLineItem,
InvoicePayment.object_name => InvoicePayment,
InvoiceRenderingTemplate.object_name => InvoiceRenderingTemplate,
Issuing::Authorization.object_name => Issuing::Authorization,
Issuing::Card.object_name => Issuing::Card,
Issuing::Cardholder.object_name => Issuing::Cardholder,
Issuing::Dispute.object_name => Issuing::Dispute,
Issuing::PersonalizationDesign.object_name => Issuing::PersonalizationDesign,
Issuing::PhysicalBundle.object_name => Issuing::PhysicalBundle,
Issuing::Token.object_name => Issuing::Token,
Issuing::Transaction.object_name => Issuing::Transaction,
LineItem.object_name => LineItem,
LoginLink.object_name => LoginLink,
Mandate.object_name => Mandate,
PaymentAttemptRecord.object_name => PaymentAttemptRecord,
PaymentIntent.object_name => PaymentIntent,
PaymentIntentAmountDetailsLineItem.object_name => PaymentIntentAmountDetailsLineItem,
PaymentLink.object_name => PaymentLink,
PaymentMethod.object_name => PaymentMethod,
PaymentMethodConfiguration.object_name => PaymentMethodConfiguration,
PaymentMethodDomain.object_name => PaymentMethodDomain,
PaymentRecord.object_name => PaymentRecord,
Payout.object_name => Payout,
Person.object_name => Person,
Plan.object_name => Plan,
Price.object_name => Price,
Product.object_name => Product,
ProductFeature.object_name => ProductFeature,
PromotionCode.object_name => PromotionCode,
Quote.object_name => Quote,
Radar::EarlyFraudWarning.object_name => Radar::EarlyFraudWarning,
Radar::ValueList.object_name => Radar::ValueList,
Radar::ValueListItem.object_name => Radar::ValueListItem,
Refund.object_name => Refund,
Reporting::ReportRun.object_name => Reporting::ReportRun,
Reporting::ReportType.object_name => Reporting::ReportType,
ReserveTransaction.object_name => ReserveTransaction,
Reversal.object_name => Reversal,
Review.object_name => Review,
SetupAttempt.object_name => SetupAttempt,
SetupIntent.object_name => SetupIntent,
ShippingRate.object_name => ShippingRate,
Sigma::ScheduledQueryRun.object_name => Sigma::ScheduledQueryRun,
Source.object_name => Source,
SourceMandateNotification.object_name => SourceMandateNotification,
SourceTransaction.object_name => SourceTransaction,
Subscription.object_name => Subscription,
SubscriptionItem.object_name => SubscriptionItem,
SubscriptionSchedule.object_name => SubscriptionSchedule,
Tax::Association.object_name => Tax::Association,
Tax::Calculation.object_name => Tax::Calculation,
Tax::CalculationLineItem.object_name => Tax::CalculationLineItem,
Tax::Registration.object_name => Tax::Registration,
Tax::Settings.object_name => Tax::Settings,
Tax::Transaction.object_name => Tax::Transaction,
Tax::TransactionLineItem.object_name => Tax::TransactionLineItem,
TaxCode.object_name => TaxCode,
TaxDeductedAtSource.object_name => TaxDeductedAtSource,
TaxId.object_name => TaxId,
TaxRate.object_name => TaxRate,
Terminal::Configuration.object_name => Terminal::Configuration,
Terminal::ConnectionToken.object_name => Terminal::ConnectionToken,
Terminal::Location.object_name => Terminal::Location,
Terminal::OnboardingLink.object_name => Terminal::OnboardingLink,
Terminal::Reader.object_name => Terminal::Reader,
TestHelpers::TestClock.object_name => TestHelpers::TestClock,
Token.object_name => Token,
Topup.object_name => Topup,
Transfer.object_name => Transfer,
Treasury::CreditReversal.object_name => Treasury::CreditReversal,
Treasury::DebitReversal.object_name => Treasury::DebitReversal,
Treasury::FinancialAccount.object_name => Treasury::FinancialAccount,
Treasury::FinancialAccountFeatures.object_name => Treasury::FinancialAccountFeatures,
Treasury::InboundTransfer.object_name => Treasury::InboundTransfer,
Treasury::OutboundPayment.object_name => Treasury::OutboundPayment,
Treasury::OutboundTransfer.object_name => Treasury::OutboundTransfer,
Treasury::ReceivedCredit.object_name => Treasury::ReceivedCredit,
Treasury::ReceivedDebit.object_name => Treasury::ReceivedDebit,
Treasury::Transaction.object_name => Treasury::Transaction,
Treasury::TransactionEntry.object_name => Treasury::TransactionEntry,
WebhookEndpoint.object_name => WebhookEndpoint,
# object classes: The end of the section generated from our OpenAPI spec
}
end
def self.v2_object_names_to_classes
{
V2::ListObject.object_name => V2::ListObject,
# v2 object classes: The beginning of the section generated from our OpenAPI spec
V2::Billing::MeterEvent.object_name => V2::Billing::MeterEvent,
V2::Billing::MeterEventAdjustment.object_name => V2::Billing::MeterEventAdjustment,
V2::Billing::MeterEventSession.object_name => V2::Billing::MeterEventSession,
V2::Core::Account.object_name => V2::Core::Account,
V2::Core::AccountLink.object_name => V2::Core::AccountLink,
V2::Core::AccountPerson.object_name => V2::Core::AccountPerson,
V2::Core::AccountPersonToken.object_name => V2::Core::AccountPersonToken,
V2::Core::AccountToken.object_name => V2::Core::AccountToken,
V2::Core::Event.object_name => V2::Core::Event,
V2::Core::EventDestination.object_name => V2::Core::EventDestination,
# v2 object classes: The end of the section generated from our OpenAPI spec
}
end
end
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountCapabilityListParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
def initialize(expand: nil)
@expand = expand
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountCapabilityRetrieveParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
def initialize(expand: nil)
@expand = expand
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountCapabilityUpdateParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
# To request a new capability for an account, pass true. There can be a delay before the requested capability becomes active. If the capability has any activation requirements, the response includes them in the `requirements` arrays.
#
# If a capability isn't permanent, you can remove it from the account by passing false. Some capabilities are permanent after they've been requested. Attempting to remove a permanent capability returns an error.
attr_accessor :requested
def initialize(expand: nil, requested: nil)
@expand = expand
@requested = requested
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountDeleteParams < ::Stripe::RequestParams; end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountExternalAccountDeleteParams < ::Stripe::RequestParams; end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountExternalAccountListParams < ::Stripe::RequestParams
# A cursor for use in pagination. `ending_before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with `obj_bar`, your subsequent call can include `ending_before=obj_bar` in order to fetch the previous page of the list.
attr_accessor :ending_before
# Specifies which fields in the response should be expanded.
attr_accessor :expand
# A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.
attr_accessor :limit
# Filter external accounts according to a particular object type.
attr_accessor :object
# A cursor for use in pagination. `starting_after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include `starting_after=obj_foo` in order to fetch the next page of the list.
attr_accessor :starting_after
def initialize(ending_before: nil, expand: nil, limit: nil, object: nil, starting_after: nil)
@ending_before = ending_before
@expand = expand
@limit = limit
@object = object
@starting_after = starting_after
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountExternalAccountRetrieveParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
def initialize(expand: nil)
@expand = expand
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountLoginLinkCreateParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
def initialize(expand: nil)
@expand = expand
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountPersonDeleteParams < ::Stripe::RequestParams; end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountPersonRetrieveParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
def initialize(expand: nil)
@expand = expand
end
end
end
# File generated from our OpenAPI spec
# frozen_string_literal: true
module Stripe
class AccountRejectParams < ::Stripe::RequestParams
# Specifies which fields in the response should be expanded.
attr_accessor :expand
# The reason for rejecting the account. Can be `fraud`, `terms_of_service`, or `other`.
attr_accessor :reason
def initialize(expand: nil, reason: nil)
@expand = expand
@reason = reason
end
end
end
Related skills
Backend & APIsbackend