Skip to content

Commit

Permalink
Update StripeClient to optionally take app_info in parameter
Browse files Browse the repository at this point in the history
This app_info will take precedence over the globally defined
Stripe.app_info
  • Loading branch information
JacekD98 committed Jan 27, 2025
1 parent 5304640 commit 48f0c00
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 7 deletions.
2 changes: 1 addition & 1 deletion OPENAPI_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1412
v1455
10 changes: 8 additions & 2 deletions lib/stripe/api_requestor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def initialize(config_arg = {})
else
raise ArgumentError, "Can't handle argument: #{config_arg}"
end

@app_info = config.app_info
end

attr_reader :config, :options
Expand Down Expand Up @@ -918,7 +920,7 @@ def self.maybe_gc_connection_managers

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?
user_agent += " " + format_app_info(fetch_app_info) unless fetch_app_info.nil?

headers = {
"User-Agent" => user_agent,
Expand Down Expand Up @@ -963,6 +965,10 @@ def self.maybe_gc_connection_managers
headers.update(req_opts[:headers])
end

private def fetch_app_info
@app_info || Stripe.app_info
end

private def log_request(context, num_retries)
Util.log_info("Request to Stripe API",
account: context.account,
Expand Down Expand Up @@ -1090,7 +1096,7 @@ def user_agent
"(#{RUBY_RELEASE_DATE})"

{
application: Stripe.app_info,
application: fetch_app_info,
bindings_version: Stripe::VERSION,
lang: "ruby",
lang_version: lang_version,
Expand Down
2 changes: 1 addition & 1 deletion lib/stripe/api_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

module Stripe
module ApiVersion
CURRENT = "2024-12-18.acacia"
CURRENT = "2025-01-27.acacia"
end
end
6 changes: 4 additions & 2 deletions lib/stripe/resources/issuing/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def approve(params = {}, opts = {})
opts: opts
)
end
deprecate :approve, :none, 2024, 3

deprecate :approve, :none, 2024, 3
# [Deprecated] Approves a pending Issuing Authorization object. This request should be made within the timeout window of the [real-time authorization](https://stripe.com/docs/issuing/controls/real-time-authorizations) flow.
# This method is deprecated. Instead, [respond directly to the webhook request to approve an authorization](https://stripe.com/docs/issuing/controls/real-time-authorizations#authorization-handling).
def self.approve(authorization, params = {}, opts = {})
Expand All @@ -40,6 +40,7 @@ def self.approve(authorization, params = {}, opts = {})
opts: opts
)
end

class << self
extend Gem::Deprecate
deprecate :approve, :none, 2024, 3
Expand All @@ -55,8 +56,8 @@ def decline(params = {}, opts = {})
opts: opts
)
end
deprecate :decline, :none, 2024, 3

deprecate :decline, :none, 2024, 3
# [Deprecated] Declines a pending Issuing Authorization object. This request should be made within the timeout window of the [real time authorization](https://stripe.com/docs/issuing/controls/real-time-authorizations) flow.
# This method is deprecated. Instead, [respond directly to the webhook request to decline an authorization](https://stripe.com/docs/issuing/controls/real-time-authorizations#authorization-handling).
def self.decline(authorization, params = {}, opts = {})
Expand All @@ -67,6 +68,7 @@ def self.decline(authorization, params = {}, opts = {})
opts: opts
)
end

class << self
extend Gem::Deprecate
deprecate :decline, :none, 2024, 3
Expand Down
20 changes: 20 additions & 0 deletions lib/stripe/resources/treasury/financial_account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ def self.object_name
"treasury.financial_account"
end

# Closes a FinancialAccount. A FinancialAccount can only be closed if it has a zero balance, has no pending InboundTransfers, and has canceled all attached Issuing cards.
def close(params = {}, opts = {})
request_stripe_object(
method: :post,
path: format("/v1/treasury/financial_accounts/%<financial_account>s/close", { financial_account: CGI.escape(self["id"]) }),
params: params,
opts: opts
)
end

# Closes a FinancialAccount. A FinancialAccount can only be closed if it has a zero balance, has no pending InboundTransfers, and has canceled all attached Issuing cards.
def self.close(financial_account, params = {}, opts = {})
request_stripe_object(
method: :post,
path: format("/v1/treasury/financial_accounts/%<financial_account>s/close", { financial_account: CGI.escape(financial_account) }),
params: params,
opts: opts
)
end

# Creates a new FinancialAccount. For now, each connected account can only have one FinancialAccount.
def self.create(params = {}, opts = {})
request_stripe_object(
Expand Down
11 changes: 11 additions & 0 deletions lib/stripe/services/treasury/financial_account_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ def initialize(requestor)
@features = Stripe::Treasury::FinancialAccountFeaturesService.new(@requestor)
end

# Closes a FinancialAccount. A FinancialAccount can only be closed if it has a zero balance, has no pending InboundTransfers, and has canceled all attached Issuing cards.
def close(financial_account, params = {}, opts = {})
request(
method: :post,
path: format("/v1/treasury/financial_accounts/%<financial_account>s/close", { financial_account: CGI.escape(financial_account) }),
params: params,
opts: opts,
base_address: :api
)
end

# Creates a new FinancialAccount. For now, each connected account can only have one FinancialAccount.
def create(params = {}, opts = {})
request(
Expand Down
4 changes: 3 additions & 1 deletion lib/stripe/stripe_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def initialize(api_key, # rubocop:todo Metrics/ParameterLists
uploads_base: nil,
connect_base: nil,
meter_events_base: nil,
client_id: nil)
client_id: nil,
app_info: nil,)
unless api_key
raise AuthenticationError, "No API key provided. " \
'Set your API key using "client = Stripe::StripeClient.new(<API-KEY>)". ' \
Expand All @@ -42,6 +43,7 @@ def initialize(api_key, # rubocop:todo Metrics/ParameterLists
connect_base: connect_base,
meter_events_base: meter_events_base,
client_id: client_id,
app_info: app_info
}.reject { |_k, v| v.nil? }

config = StripeConfiguration.client_init(config_opts)
Expand Down
39 changes: 39 additions & 0 deletions test/stripe/api_requestor_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,45 @@ class RequestorTest < Test::Unit::TestCase
Stripe.app_info = old
end
end

should "send app_info if set on the APIRequestor's config" do
begin
old = Stripe.app_info
Stripe.app_info = nil

custom_config = Stripe::StripeConfiguration.setup do |c|
c.app_info = {
name: "MyAwesomePluginInConfig",
partner_id: "partner_4567",
url: "https://myawesomeplugininconfig.info",
version: "4.5.6",
}
end

stub_request(:post, "#{Stripe::DEFAULT_API_BASE}/v1/account")
.with do |req|
expect_user_agent = "Stripe/v1 RubyBindings/#{Stripe::VERSION}" \
"MyAwesomePluginInConfig/4.5.6 (https://myawesomeplugininconfig.info)"
assert_equal expect_user_agent, req.headers["User-Agent"]

data = JSON.parse(req.headers["X-Stripe-Client-User-Agent"], symbolize_names: true)
assert_equal({
name: "MyAwesomePluginInConfig",
partner_id: "partner_4567",
url: "https://myawesomeplugininconfig.info",
version: "4.5.6",
}, data[:application])

true
end
.to_return(body: JSON.generate(object: "account"))

client = APIRequestor.new(custom_config)
client.execute_request(:post, "/v1/account", :api)
ensure
Stripe.app_info = old
end
end
end

context "error handling" do
Expand Down
26 changes: 26 additions & 0 deletions test/stripe/generated_examples_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5429,6 +5429,32 @@ class CodegennedExampleTest < Test::Unit::TestCase
)
assert_requested :post, "#{Stripe::DEFAULT_API_BASE}/v1/terminal/readers/tmr_xxxxxxxxxxxxx/process_payment_intent"
end
should "Test terminal readers process setup intent post" do
Stripe::Terminal::Reader.process_setup_intent(
"tmr_xxxxxxxxxxxxx",
{
setup_intent: "seti_xxxxxxxxxxxxx",
allow_redisplay: "always",
}
)
assert_requested :post, "#{Stripe.api_base}/v1/terminal/readers/tmr_xxxxxxxxxxxxx/process_setup_intent"
end
should "Test terminal readers process setup intent post (service)" do
stub_request(
:post,
"#{Stripe::DEFAULT_API_BASE}/v1/terminal/readers/tmr_xxxxxxxxxxxxx/process_setup_intent"
).to_return(body: "{}")
client = StripeClient.new("sk_test_123")

client.v1.terminal.readers.process_setup_intent(
"tmr_xxxxxxxxxxxxx",
{
setup_intent: "seti_xxxxxxxxxxxxx",
allow_redisplay: "always",
}
)
assert_requested :post, "#{Stripe::DEFAULT_API_BASE}/v1/terminal/readers/tmr_xxxxxxxxxxxxx/process_setup_intent"
end
should "Test test helpers customers fund cash balance post" do
Stripe::Customer::TestHelpers.fund_cash_balance(
"cus_123",
Expand Down
47 changes: 47 additions & 0 deletions test/stripe/stripe_client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@ class StripeClientTest < Test::Unit::TestCase
@orig_stripe_account = Stripe.stripe_account
@orig_open_timeout = Stripe.open_timeout
@orig_api_version = Stripe.api_version
@orig_app_info = Stripe.app_info

Stripe.api_key = "DONT_USE_THIS_KEY"
Stripe.stripe_account = "DONT_USE_THIS_ACCOUNT"
Stripe.open_timeout = 30_000
Stripe.app_info = nil
end

teardown do
Stripe.api_key = @orig_api_key
Stripe.stripe_account = @orig_stripe_account
Stripe.open_timeout = @orig_open_timeout
Stripe.api_version = @orig_api_version
Stripe.app_info = @orig_app_info
end

should "use default config options" do
Expand Down Expand Up @@ -112,6 +115,50 @@ class StripeClientTest < Test::Unit::TestCase

refute_equal APIRequestor.active_requestor, client.instance_variable_get(:@requestor)
end

should "use client app_info over global app_info" do
Stripe.app_info = { name: "global_app", version: "1.2.3" }
client_app_info = { name: "client_app", version: "4.5.6" }
client = StripeClient.new("test_123", app_info: client_app_info)

req = nil
stub_request(:get, "#{Stripe::DEFAULT_API_BASE}/v1/customers/cus_123")
.with { |request| req = request }
.to_return(body: JSON.generate(object: "customer"))

client.v1.customers.retrieve("cus_123")

assert_match(/client_app\/4\.5\.6/, req.headers["User-Agent"])
assert_no_match(/global_app/, req.headers["User-Agent"])
end

should "fall back to global app_info when client app_info not provided" do
Stripe.app_info = { name: "global_app", version: "1.2.3" }
client = StripeClient.new("test_123")

req = nil
stub_request(:get, "#{Stripe::DEFAULT_API_BASE}/v1/customers/cus_123")
.with { |request| req = request }
.to_return(body: JSON.generate(object: "customer"))

client.v1.customers.retrieve("cus_123")

assert_match(%r{global_app/1\.2\.3}, req.headers["User-Agent"])
end

should "work with no app_info set" do
Stripe.app_info = nil
client = StripeClient.new("test_123")

req = nil
stub_request(:get, "#{Stripe::DEFAULT_API_BASE}/v1/customers/cus_123")
.with { |request| req = request }
.to_return(body: JSON.generate(object: "customer"))

client.v1.customers.retrieve("cus_123")

assert_no_match(%r{app/}, req.headers["User-Agent"])
end
end

context "#request" do
Expand Down

0 comments on commit 48f0c00

Please sign in to comment.