From 1945a435dba751cb14ba17d30e4ca015c4a2de59 Mon Sep 17 00:00:00 2001 From: Alessandro Rodi Date: Wed, 18 Mar 2020 10:43:03 +0100 Subject: [PATCH 001/158] Remove unnecessary check on customer's currency --- lib/stripe_mock/request_handlers/subscriptions.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index ff4e1e48b..e52260888 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -88,14 +88,6 @@ def create_subscription(route, method_url, params, headers) customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s customer = assert_existence :customer, customer_id, customers[customer_id] - if subscription_plans && customer - subscription_plans.each do |plan| - unless customer[:currency].to_s == plan[:currency].to_s - raise Stripe::InvalidRequestError.new("Customer's currency of #{customer[:currency]} does not match plan's currency of #{plan[:currency]}", 'currency', http_status: 400) - end - end - end - if params[:source] new_card = get_card_by_token(params.delete(:source)) add_card_to_object(:customer, new_card, customer) From e0b4d539b23e4c756fbcd639d5c05f9324605d99 Mon Sep 17 00:00:00 2001 From: HIDEKUNI Kajita Date: Thu, 25 Jun 2020 14:19:56 +0900 Subject: [PATCH 002/158] support on price api --- lib/stripe_mock.rb | 1 + lib/stripe_mock/data.rb | 31 +++- lib/stripe_mock/instance.rb | 4 +- .../helpers/subscription_helpers.rb | 2 +- lib/stripe_mock/request_handlers/prices.rb | 36 ++++ .../request_handlers/subscriptions.rb | 6 +- .../validators/param_validators.rb | 26 +++ lib/stripe_mock/test_strategies/base.rb | 9 + spec/shared_stripe_examples/price_examples.rb | 163 ++++++++++++++++++ spec/support/stripe_examples.rb | 1 + 10 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 lib/stripe_mock/request_handlers/prices.rb create mode 100644 spec/shared_stripe_examples/price_examples.rb diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index 888523967..26cbc8726 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -63,6 +63,7 @@ require 'stripe_mock/request_handlers/invoice_items.rb' require 'stripe_mock/request_handlers/orders.rb' require 'stripe_mock/request_handlers/plans.rb' +require 'stripe_mock/request_handlers/prices.rb' require 'stripe_mock/request_handlers/recipients.rb' require 'stripe_mock/request_handlers/refunds.rb' require 'stripe_mock/request_handlers/transfers.rb' diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 1845ea2d7..a6abf3866 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -366,7 +366,8 @@ def self.mock_subscription(params={}) default_tax_rates: nil, default_payment_method: nil, pending_invoice_item_interval: nil, - next_pending_invoice_item_invoice: nil + next_pending_invoice_item_invoice: nil, + latest_invoice: nil }, params) end @@ -570,6 +571,34 @@ def self.mock_plan(params={}) }.merge(params) end + def self.mock_price(params={}) + currency = params[:currency] || StripeMock.default_currency + { + id: "mock_price_123", + object: "price", + active: true, + billing_scheme: "per_unit", + created: 1593044959, + currency: currency, + livemode: false, + lookup_key: nil, + metadata: {}, + nickname: 'My Mock Price', + product: "mock_prod_NONEXIST", # override this with your own existing product id + recurring: { + aggregate_usage: nil, + interval: "month", + interval_count: 1, + usage_type: "licensed" + }, + tiers_mode: nil, + transform_quantity: nil, + type: "recurring", + unit_amount: 2000, + unit_amount_decimal: "2000" + }.merge(params) + end + def self.mock_product(params={}) { id: "mock_prod_abc123", diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index 81d229fec..b608ab1cf 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -41,6 +41,7 @@ def self.handler_for_method_url(method_url) include StripeMock::RequestHandlers::InvoiceItems include StripeMock::RequestHandlers::Orders include StripeMock::RequestHandlers::Plans + include StripeMock::RequestHandlers::Prices include StripeMock::RequestHandlers::Products include StripeMock::RequestHandlers::Refunds include StripeMock::RequestHandlers::Recipients @@ -55,7 +56,7 @@ def self.handler_for_method_url(method_url) attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers, :disputes, :events, :invoices, :invoice_items, :orders, :payment_intents, :payment_methods, - :setup_intents, :plans, :recipients, :refunds, :transfers, :payouts, :subscriptions, :country_spec, + :setup_intents, :plans, :prices, :recipients, :refunds, :transfers, :payouts, :subscriptions, :country_spec, :subscriptions_items, :products, :tax_rates, :checkout_sessions attr_accessor :error_queue, :debug, :conversion_rate, :account_balance @@ -79,6 +80,7 @@ def initialize @orders = {} @payment_methods = {} @plans = {} + @prices = {} @products = {} @recipients = {} @refunds = {} diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 37f30c887..b9999bec8 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -13,7 +13,7 @@ def resolve_subscription_changes(subscription, plans, customer, options = {}) subscription[:items][:data] = plans.map do |plan| if items && items.size == plans.size quantity = items && - items.detect { |item| item[:plan] == plan[:id] }[:quantity] || 1 + items.detect { |item| [item[:price], item[:plan]].include? plan[:id] }[:quantity] || 1 Data.mock_subscription_item({ plan: plan, quantity: quantity }) else Data.mock_subscription_item({ plan: plan }) diff --git a/lib/stripe_mock/request_handlers/prices.rb b/lib/stripe_mock/request_handlers/prices.rb new file mode 100644 index 000000000..e1b051f1b --- /dev/null +++ b/lib/stripe_mock/request_handlers/prices.rb @@ -0,0 +1,36 @@ +module StripeMock + module RequestHandlers + module Prices + + def Prices.included(klass) + klass.add_handler 'post /v1/prices', :new_price + klass.add_handler 'post /v1/prices/(.*)', :update_price + klass.add_handler 'get /v1/prices/(.*)', :get_price + klass.add_handler 'get /v1/prices', :list_prices + end + + def new_price(route, method_url, params, headers) + params[:id] ||= new_id('price') + validate_create_price_params(params) + prices[ params[:id] ] = Data.mock_price(params) + end + + def update_price(route, method_url, params, headers) + route =~ method_url + assert_existence :price, $1, prices[$1] + prices[$1].merge!(params) + end + + def get_price(route, method_url, params, headers) + route =~ method_url + assert_existence :price, $1, prices[$1] + end + + def list_prices(route, method_url, params, headers) + limit = params[:limit] ? params[:limit] : 10 + Data.mock_list_object(prices.values.first(limit), params.merge!(limit: limit)) + end + + end + end +end diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index a3fcd11d8..ad68d602e 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -257,14 +257,16 @@ def get_subscription_plans_from_params(params) elsif params[:items] items = params[:items] items = items.values if items.respond_to?(:values) - items.map { |item| item[:plan].to_s if item[:plan] } + items.map { |item| item[:plan] ? item[:plan] : item[:price] } else [] end plan_ids.each do |plan_id| assert_existence :plan, plan_id, plans[plan_id] + rescue Stripe::InvalidRequestError + assert_existence :price, plan_id, prices[plan_id] end - plan_ids.map { |plan_id| plans[plan_id] } + plan_ids.map { |plan_id| plans[plan_id] || prices[plan_id]} end # Ensure customer has card to charge unless one of the following criterias is met: diff --git a/lib/stripe_mock/request_handlers/validators/param_validators.rb b/lib/stripe_mock/request_handlers/validators/param_validators.rb index 31d36e785..5a88af4e7 100644 --- a/lib/stripe_mock/request_handlers/validators/param_validators.rb +++ b/lib/stripe_mock/request_handlers/validators/param_validators.rb @@ -110,9 +110,35 @@ def validate_create_plan_params(params) end + def validate_create_price_params(params) + price_id = params[:id].to_s + product_id = params[:product] + + @base_strategy.create_price_params.keys.each do |attr_name| + message = "Missing required param: #{attr_name}." + raise Stripe::InvalidRequestError.new(message, attr_name) if params[attr_name].nil? + end + + if prices[price_id] + message = already_exists_message(Stripe::Price) + raise Stripe::InvalidRequestError.new(message, :id) + end + + unless products[product_id] + message = not_found_message(Stripe::Product, product_id) + raise Stripe::InvalidRequestError.new(message, :product) + end + + unless SUPPORTED_CURRENCIES.include?(params[:currency]) + message = invalid_currency_message(params[:currency]) + raise Stripe::InvalidRequestError.new(message, :currency) + end + end + def require_param(param_name) raise Stripe::InvalidRequestError.new("Missing required param: #{param_name}.", param_name.to_s, http_status: 400) end + end end end diff --git a/lib/stripe_mock/test_strategies/base.rb b/lib/stripe_mock/test_strategies/base.rb index 65981f3d3..eb50420bf 100644 --- a/lib/stripe_mock/test_strategies/base.rb +++ b/lib/stripe_mock/test_strategies/base.rb @@ -41,6 +41,15 @@ def create_plan_params(params={}) }.merge(params) end + def create_price(params={}) + Stripe::Price.create create_price_params(params) + end + + def create_price_params(params={}) + { + :currency => StripeMock.default_currency, + }.merge(params) + end def list_subscriptions(limit) Stripe::Subscription.list(limit: limit) diff --git a/spec/shared_stripe_examples/price_examples.rb b/spec/shared_stripe_examples/price_examples.rb new file mode 100644 index 000000000..394bd56f8 --- /dev/null +++ b/spec/shared_stripe_examples/price_examples.rb @@ -0,0 +1,163 @@ +require 'spec_helper' + +shared_examples 'Price API' do + let(:product) { stripe_helper.create_product } + let(:product_id) { product.id } + + let(:price_attributes) { { + :id => "price_abc123", + :product => product_id, + :nickname => "My Mock price", + :amount => 9900, + :currency => "usd", + :interval => "month" + } } + let(:price) { Stripe::Price.create(price_attributes) } + + let(:price_attributes_without_id){ price_attributes.merge(id: nil) } + let(:price_without_id){ Stripe::Price.create(price_attributes_without_id) } + + let(:price_attributes_with_trial) { price_attributes.merge(id: "prod_TRIAL", :trial_period_days => 30) } + let(:price_with_trial) { Stripe::Price.create(price_attributes_with_trial) } + + let(:metadata) { {:description => "desc text", :info => "info text"} } + let(:price_attributes_with_metadata) { price_attributes.merge(id: "prod_META", metadata: metadata) } + let(:price_with_metadata) { Stripe::Price.create(price_attributes_with_metadata) } + + before(:each) do + product + end + + it "creates a stripe price" do + expect(price.id).to eq('price_abc123') + expect(price.nickname).to eq('My Mock price') + expect(price.amount).to eq(9900) + + expect(price.currency).to eq('usd') + expect(price.interval).to eq("month") + + expect(price_with_metadata.metadata.description).to eq('desc text') + expect(price_with_metadata.metadata.info).to eq('info text') + + expect(price_with_trial.trial_period_days).to eq(30) + end + + it "creates a stripe price without specifying ID" do + expect(price_attributes_without_id[:id]).to be_nil + expect(price_without_id.id).to match(/^test_price_1/) + end + + it "stores a created stripe price in memory" do + price + price2 = Stripe::Price.create(price_attributes.merge(id: "price_def456", amount: 299)) + + data = test_data_source(:prices) + expect(data[price.id]).to_not be_nil + expect(data[price.id][:amount]).to eq(9900) + expect(data[price2.id]).to_not be_nil + expect(data[price2.id][:amount]).to eq(299) + end + + it "retrieves a stripe price" do + original = stripe_helper.create_price(product: product_id, amount: 1331, id: 'price_943843') + price = Stripe::Price.retrieve(original.id) + + expect(price.id).to eq(original.id) + expect(price.amount).to eq(original.amount) + end + + it "updates a stripe price" do + stripe_helper.create_price(id: 'super_member', product: product_id, amount: 111) + + price = Stripe::Price.retrieve('super_member') + expect(price.amount).to eq(111) + + price.amount = 789 + price.save + price = Stripe::Price.retrieve('super_member') + expect(price.amount).to eq(789) + end + + it "cannot retrieve a stripe price that doesn't exist" do + expect { Stripe::Price.retrieve('nope') }.to raise_error {|e| + expect(e).to be_a Stripe::InvalidRequestError + expect(e.param).to eq('price') + expect(e.http_status).to eq(404) + } + end + + it "retrieves all prices" do + stripe_helper.create_price(id: 'price One', product: product_id, amount: 54321) + stripe_helper.create_price(id: 'price Two', product: product_id, amount: 98765) + + all = Stripe::Price.list + expect(all.count).to eq(2) + expect(all.map &:id).to include('price One', 'price Two') + expect(all.map &:amount).to include(54321, 98765) + end + + it 'retrieves prices with limit' do + 101.times do | i| + stripe_helper.create_price(id: "price #{i}", product: product_id, amount: 11) + end + all = Stripe::Price.list(limit: 100) + + expect(all.count).to eq(100) + end + + describe "Validations", :live => true do + include_context "stripe validator" + let(:params) { stripe_helper.create_price_params(product: product_id) } + let(:subject) { Stripe::Price.create(params) } + + describe "Associations" do + let(:not_found_product_id){ "prod_NONEXIST" } + let(:not_found_message) { stripe_validator.not_found_message(Stripe::Product, not_found_product_id) } + let(:params) { stripe_helper.create_price_params(product: not_found_product_id) } + let(:products) { stripe_helper.list_products(100).data } + + it "validates associated product" do + expect(products.map(&:id)).to_not include(not_found_product_id) + expect { subject }.to raise_error(Stripe::InvalidRequestError, not_found_message) + end + end + + describe "Presence" do + after do + params.delete(@name) + message = "Missing required param: #{@name}." + expect { subject }.to raise_error(Stripe::InvalidRequestError, message) + end + + it("validates presence of currency") { @name = :currency } + end + end + + describe "Mock Data" do + let(:mock_object) { StripeMock::Data.mock_price } + let(:known_attributes) { [ + :id, + :object, + :active, + :billing_scheme, + :created, + :currency, + :livemode, + :lookup_key, + :metadata, + :nickname, + :product, + :recurring, + :tiers_mode, + :transform_quantity, + :type, + :unit_amount, + :unit_amount_decimal + ] } + + it "includes all retreived attributes" do + expect(mock_object.keys).to eql(known_attributes) + end + end + +end diff --git a/spec/support/stripe_examples.rb b/spec/support/stripe_examples.rb index b5025e9bf..776495200 100644 --- a/spec/support/stripe_examples.rb +++ b/spec/support/stripe_examples.rb @@ -21,6 +21,7 @@ def it_behaves_like_stripe(&block) it_behaves_like 'Invoice API', &block it_behaves_like 'Invoice Item API', &block it_behaves_like 'Plan API', &block + it_behaves_like 'Price API', &block it_behaves_like 'Product API', &block it_behaves_like 'Recipient API', &block it_behaves_like 'Refund API', &block From e007c08e718e1a67a5a1ea716d24730d17586d09 Mon Sep 17 00:00:00 2001 From: Derek Hammer Date: Mon, 29 Jun 2020 12:51:24 -0600 Subject: [PATCH 003/158] Adds some basic call/response mocking of refunds for payment intents * Does not link refunds through charges like I would expect because creation of payment intents do not seem to create and/or link charges --- lib/stripe_mock/request_handlers/refunds.rb | 15 +++++++++++++-- spec/shared_stripe_examples/refund_examples.rb | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/refunds.rb b/lib/stripe_mock/request_handlers/refunds.rb index c2ee7057e..62aabde7c 100644 --- a/lib/stripe_mock/request_handlers/refunds.rb +++ b/lib/stripe_mock/request_handlers/refunds.rb @@ -18,7 +18,18 @@ def new_refund(route, method_url, params, headers) end end - charge = assert_existence :charge, params[:charge], charges[params[:charge]] + if params[:payment_intent] + payment_intent = assert_existence( + :payment_intent, + params[:payment_intent], + payment_intents[params[:payment_intent]] + ) + charge = {} + else + charge = assert_existence :charge, params[:charge], charges[params[:charge]] + payment_intent = {} + end + params[:amount] ||= payment_intent[:amount] params[:amount] ||= charge[:amount] id = new_id('re') bal_trans_params = { @@ -32,7 +43,7 @@ def new_refund(route, method_url, params, headers) :id => id, :charge => charge[:id], ) - add_refund_to_charge(refund, charge) + add_refund_to_charge(refund, charge) unless charge.empty? refunds[id] = refund if params[:expand] == ['balance_transaction'] diff --git a/spec/shared_stripe_examples/refund_examples.rb b/spec/shared_stripe_examples/refund_examples.rb index 2cc3812c5..bf0645357 100644 --- a/spec/shared_stripe_examples/refund_examples.rb +++ b/spec/shared_stripe_examples/refund_examples.rb @@ -22,6 +22,19 @@ expect(charge.amount_refunded).to eq(999) end + it "refunds a stripe payment intent" do + payment_intent = Stripe::PaymentIntent.create( + amount: 999, + currency: 'USD' + ) + + refund = Stripe::Refund.create( + payment_intent: payment_intent.id + ) + expect(refund.amount).to eq(999) + end + + it "creates a stripe refund with a status" do charge = Stripe::Charge.create( amount: 999, From 1120c22c4760bfa2c8af79dd507f1ad8a6779530 Mon Sep 17 00:00:00 2001 From: Dave Urban Date: Mon, 27 Jul 2020 21:02:33 -0400 Subject: [PATCH 004/158] A succeeded PaymentIntent gets a persistent Charge --- .../request_handlers/payment_intents.rb | 8 ++- .../payment_intent_examples.rb | 64 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/payment_intents.rb b/lib/stripe_mock/request_handlers/payment_intents.rb index 3309e4879..57dcd1522 100644 --- a/lib/stripe_mock/request_handlers/payment_intents.rb +++ b/lib/stripe_mock/request_handlers/payment_intents.rb @@ -169,12 +169,18 @@ def succeeded_payment_intent(payment_intent) payment_intent[:status] = 'succeeded' btxn = new_balance_transaction('txn', { source: payment_intent[:id] }) - payment_intent[:charges][:data] << Data.mock_charge( + charge_id = new_id('ch') + + charges[charge_id] = Data.mock_charge( + id: charge_id, balance_transaction: btxn, + payment_intent: payment_intent[:id], amount: payment_intent[:amount], currency: payment_intent[:currency] ) + payment_intent[:charges][:data] << charges[charge_id].clone + payment_intent end end diff --git a/spec/shared_stripe_examples/payment_intent_examples.rb b/spec/shared_stripe_examples/payment_intent_examples.rb index 8f6993eab..4eeb89608 100644 --- a/spec/shared_stripe_examples/payment_intent_examples.rb +++ b/spec/shared_stripe_examples/payment_intent_examples.rb @@ -2,6 +2,11 @@ shared_examples 'PaymentIntent API' do + let(:customer) do + token = Stripe::Token.retrieve(stripe_helper.generate_card_token(number: '4242424242424242')) + Stripe::Customer.create(email: 'alice@bob.com', source: token.id) + end + it "creates a succeeded stripe payment_intent" do payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") @@ -92,6 +97,25 @@ expect(Stripe::BalanceTransaction.retrieve(balance_txn).id).to eq(balance_txn) end + it 'creates a charge for a stripe payment_intent with confirm flag to true' do + payment_intent = Stripe::PaymentIntent.create amount: 100, + currency: 'usd', + confirm: true, + customer: customer, + payment_method: customer.sources.first + + charge = payment_intent.charges.data.first + expect(charge.amount).to eq(payment_intent.amount) + expect(charge.payment_intent).to eq(payment_intent.id) + expect(charge.description).to be_nil + + charge.description = 'Updated description' + charge.save + + updated = Stripe::Charge.retrieve(charge.id) + expect(updated.description).to eq('Updated description') + end + it "confirms a stripe payment_intent" do payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") confirmed_payment_intent = payment_intent.confirm() @@ -100,6 +124,25 @@ expect(confirmed_payment_intent.charges.data.first.object).to eq('charge') end + it 'creates a charge for a confirmed stripe payment_intent' do + payment_intent = Stripe::PaymentIntent.create amount: 100, + currency: 'usd', + customer: customer, + payment_method: customer.sources.first + + confirmed_payment_intent = payment_intent.confirm + charge = confirmed_payment_intent.charges.data.first + expect(charge.amount).to eq(confirmed_payment_intent.amount) + expect(charge.payment_intent).to eq(confirmed_payment_intent.id) + expect(charge.description).to be_nil + + charge.description = 'Updated description' + charge.save + + updated = Stripe::Charge.retrieve(charge.id) + expect(updated.description).to eq('Updated description') + end + it "captures a stripe payment_intent" do payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") confirmed_payment_intent = payment_intent.capture() @@ -108,6 +151,27 @@ expect(confirmed_payment_intent.charges.data.first.object).to eq('charge') end + it 'creates a charge for a captured stripe payment_intent' do + payment_intent = Stripe::PaymentIntent.create amount: 3055, + currency: 'usd', + customer: customer, + payment_method: customer.sources.first, + confirm: true, + capture_method: 'manual' + + captured_payment_intent = payment_intent.capture + charge = captured_payment_intent.charges.data.first + expect(charge.amount).to eq(captured_payment_intent.amount) + expect(charge.payment_intent).to eq(captured_payment_intent.id) + expect(charge.description).to be_nil + + charge.description = 'Updated description' + charge.save + + updated = Stripe::Charge.retrieve(charge.id) + expect(updated.description).to eq('Updated description') + end + it "cancels a stripe payment_intent" do payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") confirmed_payment_intent = payment_intent.cancel() From 5cdbf6f0de86c7fca7e22e29b8febd6d82b26392 Mon Sep 17 00:00:00 2001 From: Bastian Bartmann Date: Thu, 1 Oct 2020 14:02:18 +0200 Subject: [PATCH 005/158] Fix ruby 2.7 deprecation warnings --- lib/stripe_mock/api/client.rb | 2 +- lib/stripe_mock/api/instance.rb | 2 +- lib/stripe_mock/client.rb | 3 ++- lib/stripe_mock/request_handlers/charges.rb | 2 +- lib/stripe_mock/request_handlers/ephemeral_key.rb | 2 +- lib/stripe_mock/request_handlers/payment_methods.rb | 4 +++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/stripe_mock/api/client.rb b/lib/stripe_mock/api/client.rb index 9ccdef21a..08a14f69c 100644 --- a/lib/stripe_mock/api/client.rb +++ b/lib/stripe_mock/api/client.rb @@ -8,7 +8,7 @@ def self.start_client(port=4999) return false if @state == 'live' return @client unless @client.nil? - Stripe::StripeClient.send(:define_method, :execute_request) { |*args| StripeMock.redirect_to_mock_server(*args) } + Stripe::StripeClient.send(:define_method, :execute_request) { |*args, **keyword_args| StripeMock.redirect_to_mock_server(*args, **keyword_args) } @client = StripeMock::Client.new(port) @state = 'remote' @client diff --git a/lib/stripe_mock/api/instance.rb b/lib/stripe_mock/api/instance.rb index 747bf7c75..65ed2c7f4 100644 --- a/lib/stripe_mock/api/instance.rb +++ b/lib/stripe_mock/api/instance.rb @@ -7,7 +7,7 @@ module StripeMock def self.start return false if @state == 'live' @instance = instance = Instance.new - Stripe::StripeClient.send(:define_method, :execute_request) { |*args| instance.mock_request(*args) } + Stripe::StripeClient.send(:define_method, :execute_request) { |*args, **keyword_args| instance.mock_request(*args, **keyword_args) } @state = 'local' end diff --git a/lib/stripe_mock/client.rb b/lib/stripe_mock/client.rb index b653c0cab..615fcdd04 100644 --- a/lib/stripe_mock/client.rb +++ b/lib/stripe_mock/client.rb @@ -18,7 +18,8 @@ def mock_request(method, url, api_key: nil, params: {}, headers: {}) @pipe.mock_request(method, url, api_key: api_key, params: params, headers: headers).tap {|result| response, api_key = result if response.is_a?(Hash) && response[:error_raised] == 'invalid_request' - raise Stripe::InvalidRequestError.new(*response[:error_params]) + args, keyword_args = response[:error_params].first(2), response[:error_params].last + raise Stripe::InvalidRequestError.new(*args, **keyword_args) end } end diff --git a/lib/stripe_mock/request_handlers/charges.rb b/lib/stripe_mock/request_handlers/charges.rb index 10415b5ff..138d1afda 100644 --- a/lib/stripe_mock/request_handlers/charges.rb +++ b/lib/stripe_mock/request_handlers/charges.rb @@ -146,7 +146,7 @@ def ensure_required_params(params) elsif non_positive_charge_amount?(params) raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400) elsif params[:source].nil? && params[:customer].nil? - raise Stripe::InvalidRequestError.new('Must provide source or customer.', http_status: nil) + raise Stripe::InvalidRequestError.new('Must provide source or customer.', nil, http_status: nil) end end diff --git a/lib/stripe_mock/request_handlers/ephemeral_key.rb b/lib/stripe_mock/request_handlers/ephemeral_key.rb index 7a25559f8..c0fef5132 100644 --- a/lib/stripe_mock/request_handlers/ephemeral_key.rb +++ b/lib/stripe_mock/request_handlers/ephemeral_key.rb @@ -6,7 +6,7 @@ def self.included(klass) end def create_ephemeral_key(route, method_url, params, headers) - Data.mock_ephemeral_key(params) + Data.mock_ephemeral_key(**params) end end end diff --git a/lib/stripe_mock/request_handlers/payment_methods.rb b/lib/stripe_mock/request_handlers/payment_methods.rb index 7a9c7ff5e..cc3ce3afe 100644 --- a/lib/stripe_mock/request_handlers/payment_methods.rb +++ b/lib/stripe_mock/request_handlers/payment_methods.rb @@ -51,7 +51,7 @@ def get_payment_methods(route, method_url, params, headers) Data.mock_list_object(clone.values, params) end - + # post /v1/payment_methods/:id/attach def attach_payment_method(route, method_url, params, headers) allowed_params = [:customer] @@ -86,6 +86,7 @@ def update_payment_method(route, method_url, params, headers) if payment_method[:customer].nil? raise Stripe::InvalidRequestError.new( 'You must save this PaymentMethod to a customer before you can update it.', + nil, http_status: 400 ) end @@ -104,6 +105,7 @@ def ensure_payment_method_required_params(params) if invalid_type?(params[:type]) raise Stripe::InvalidRequestError.new( 'Invalid type: must be one of card, ideal or sepa_debit', + nil, http_status: 400 ) end From 35efc5e4816c101b09291f25d523b617222e2a15 Mon Sep 17 00:00:00 2001 From: Huibin Date: Sun, 4 Oct 2020 11:24:46 -0400 Subject: [PATCH 006/158] [Data] Add receipt_url to charge and refund mock data `Stripe::Charge.create` and `Stripe::Refund.create` will throw `NoMethodError: undefined method `receipt_url'`. Add `receipt_url` to charge and refund mock data to fix this error --- lib/stripe_mock/data.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 1845ea2d7..b67fca862 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -184,6 +184,7 @@ def self.mock_charge(params={}) fraud_details: {}, receipt_email: nil, receipt_number: nil, + receipt_url: nil, refunded: false, shipping: {}, statement_descriptor: "Charge #{charge_id}", @@ -244,7 +245,8 @@ def self.mock_refund(params={}) charge: "ch_4fWhYjzQ23UFWT", receipt_number: nil, status: "succeeded", - reason: "requested_by_customer" + reason: "requested_by_customer", + receipt_url: nil }.merge(params) end From 184109302a718e8ea3bad0de412df786e5b71d9f Mon Sep 17 00:00:00 2001 From: Darnel Dominik Date: Fri, 27 Nov 2020 08:49:58 +0100 Subject: [PATCH 007/158] Add allowed params to subscriptions Add :off_session, :trial_from_plan, and :expand params to allowed params in subscription --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index a3fcd11d8..3f7eb0f2b 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -102,7 +102,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method) + allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan expand) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) From 7fb60fe8bbc32075a777cc253bc3ca035ea41367 Mon Sep 17 00:00:00 2001 From: James Prior Date: Fri, 11 Dec 2020 13:46:26 -0600 Subject: [PATCH 008/158] Update subscription helper for prices From https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/730#issuecomment-710474522 --- .../request_handlers/helpers/subscription_helpers.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index b9999bec8..fdcd47033 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -111,7 +111,11 @@ def verify_trial_end(trial_end) def total_items_amount(items) total = 0 - items.each { |i| total += (i[:quantity] || 1) * i[:plan][:amount] } + items.each do |item| + quantity = item[:quantity] || 1 + amount = item[:price].present? ? item[:price][:unit_amount]: item[:plan][:amount] + total += quantity * amount + end total end end From 43b6930a380eca1ae563eba0aff86bd344c39aea Mon Sep 17 00:00:00 2001 From: James Prior Date: Fri, 11 Dec 2020 14:02:02 -0600 Subject: [PATCH 009/158] Try adding specs for subscriptions and prices --- .../subscription_examples.rb | 49 +++++++++++++++++-- spec/support/stripe_examples.rb | 3 +- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 389bf226c..7a3ce46b1 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'securerandom' -shared_examples 'Customer Subscriptions' do +shared_examples 'Customer Subscriptions with plans' do let(:gen_card_tk) { stripe_helper.generate_card_token } let(:product) { stripe_helper.create_product } @@ -803,7 +803,7 @@ metadata: { foo: "bar", example: "yes" }, default_payment_method: payment_method_card.id, ) - + subscriptions = Stripe::Subscription.list(customer: customer) expect(subscriptions.data.first.default_payment_method).to eq(payment_method_card.id) @@ -812,7 +812,7 @@ default_payment_method: payment_method_sepa.id, collection_method: 'send_invoice', ) - + subscriptions = Stripe::Subscription.list(customer: customer) expect(subscriptions.data.first.collection_method).to eq('send_invoice') expect(subscriptions.data.first.default_payment_method).to eq(payment_method_sepa.id) @@ -1215,3 +1215,46 @@ end end + +shared_examples 'Customer Subscriptions with prices' do + let(:gen_card_tk) { stripe_helper.generate_card_token } + + let(:product) { stripe_helper.create_product } + let(:price) { {product: product.id, amount: 4999, currency: 'usd'} } + + context "creating a new subscription" do + it "adds a new subscription to customer with none using items", :live => true do + customer = Stripe::Customer.create(source: gen_card_tk) + + expect(customer.subscriptions.data).to be_empty + expect(customer.subscriptions.count).to eq(0) + + subscription = Stripe::Subscription.create({ + customer: customer.id, + items: [{ price: price.id }], + metadata: { foo: "bar", example: "yes" } + }) + + expect(subscription.object).to eq('subscription') + expect(subscription.plan.to_hash).to eq(price.to_hash) + expect(subscription.metadata.foo).to eq("bar") + expect(subscription.metadata.example).to eq("yes") + + customer = Stripe::Customer.retrieve(customer.id) + subscriptions = Stripe::Subscription.list(customer: customer.id) + charges = Stripe::Charge.list(customer: customer.id) + + expect(subscriptions.data).to_not be_empty + expect(subscriptions.count).to eq(1) + expect(subscriptions.data.length).to eq(1) + expect(charges.data.length).to eq(1) + expect(customer.currency).to eq("usd") + + expect(subscriptions.data.first.id).to eq(subscription.id) + expect(subscriptions.data.first.plan.to_hash).to eq(price.to_hash) + expect(subscriptions.data.first.customer).to eq(customer.id) + expect(subscriptions.data.first.metadata.foo).to eq( "bar" ) + expect(subscriptions.data.first.metadata.example).to eq( "yes" ) + end + end +end diff --git a/spec/support/stripe_examples.rb b/spec/support/stripe_examples.rb index 776495200..ce8356a03 100644 --- a/spec/support/stripe_examples.rb +++ b/spec/support/stripe_examples.rb @@ -31,7 +31,8 @@ def it_behaves_like_stripe(&block) it_behaves_like 'PaymentMethod API', &block it_behaves_like 'SetupIntent API', &block it_behaves_like 'Stripe Error Mocking', &block - it_behaves_like 'Customer Subscriptions', &block + it_behaves_like 'Customer Subscriptions with plans', &block + it_behaves_like 'Customer Subscriptions with prices', &block it_behaves_like 'Subscription Items API', &block it_behaves_like 'Webhook Events API', &block it_behaves_like 'Country Spec API', &block From fa7dce9d454fca5909aa80212486854dd48a2ab0 Mon Sep 17 00:00:00 2001 From: James Prior Date: Fri, 11 Dec 2020 14:02:21 -0600 Subject: [PATCH 010/158] List prices by lookup key --- lib/stripe_mock/request_handlers/prices.rb | 12 +++++++++-- .../validators/param_validators.rb | 6 ++++++ spec/shared_stripe_examples/price_examples.rb | 20 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/prices.rb b/lib/stripe_mock/request_handlers/prices.rb index e1b051f1b..573d2dcbe 100644 --- a/lib/stripe_mock/request_handlers/prices.rb +++ b/lib/stripe_mock/request_handlers/prices.rb @@ -28,9 +28,17 @@ def get_price(route, method_url, params, headers) def list_prices(route, method_url, params, headers) limit = params[:limit] ? params[:limit] : 10 - Data.mock_list_object(prices.values.first(limit), params.merge!(limit: limit)) - end + price_data = prices.values + validate_list_prices_params + + if params.key?(:lookup_keys) + price_data.select! do |price| + params[:lookup_keys].include?(price[:lookup_key]) + end + end + Data.mock_list_object(price_data.first(limit), params.merge!(limit: limit)) + end end end end diff --git a/lib/stripe_mock/request_handlers/validators/param_validators.rb b/lib/stripe_mock/request_handlers/validators/param_validators.rb index 5a88af4e7..5134c0d8d 100644 --- a/lib/stripe_mock/request_handlers/validators/param_validators.rb +++ b/lib/stripe_mock/request_handlers/validators/param_validators.rb @@ -135,6 +135,12 @@ def validate_create_price_params(params) end end + def validate_list_prices_params(params) + if params[:lookup_keys] && !params[:lookup_keys].is_a?(Array) + raise Stripe::InvalidRequestError.new('Invalid array', :lookup_keys) + end + end + def require_param(param_name) raise Stripe::InvalidRequestError.new("Missing required param: #{param_name}.", param_name.to_s, http_status: 400) end diff --git a/spec/shared_stripe_examples/price_examples.rb b/spec/shared_stripe_examples/price_examples.rb index 394bd56f8..e1767d657 100644 --- a/spec/shared_stripe_examples/price_examples.rb +++ b/spec/shared_stripe_examples/price_examples.rb @@ -105,6 +105,26 @@ expect(all.count).to eq(100) end + it "retrieves prices with lookup keys" do + stripe_helper.create_price(id: 'price One', product: product_id, amount: 54321, lookup_key: 'one') + stripe_helper.create_price(id: 'price Two', product: product_id, amount: 98765, lookup_key: 'two') + + all = Stripe::Price.list({lookup_keys: ['one', 'two']}) + expect(all.count).to eq(2) + expect(all.map &:id).to include('price One', 'price Two') + expect(all.map &:amount).to include(54321, 98765) + + one = Stripe::Price.list({lookup_keys: ['one']}) + expect(all.count).to eq(1) + expect(all.map &:id).to include('price One') + expect(all.map &:amount).to include(54321) + + two = Stripe::Price.list({lookup_keys: ['two']}) + expect(all.count).to eq(1) + expect(all.map &:id).to include('price Two') + expect(all.map &:amount).to include(98765) + end + describe "Validations", :live => true do include_context "stripe validator" let(:params) { stripe_helper.create_price_params(product: product_id) } From 2bb93e4e8011a1ee502fc9d66b0b7001ccbd7fce Mon Sep 17 00:00:00 2001 From: James Prior Date: Fri, 11 Dec 2020 14:15:36 -0600 Subject: [PATCH 011/158] Missing param to validate --- CHANGELOG.md | 5 +++-- lib/stripe_mock/request_handlers/prices.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31da9776e..341239dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,16 @@ ### 3.0.2 (Next) * Your contribution here. +* [#748](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/758): Support Prices - [@hidenba](https://github.com/hidenba) and [@jamesprior](https://github.com/jamesprior). ### 3.0.1 (TBD) -* Added Changelog file +* Added Changelog file * [#640](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/640): Support Payment Intent status requires_capture - [@theodorton](https://github.com/theodorton). * [#685](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/685): Adds support for pending_invoice_item_interval - [@joshcass](https://github.com/joshcass). * [#682](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/682): Prevent customer metadata from being overwritten with each update - [@sethkrasnianski](https://github.com/sethkrasnianski). * [#679](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/679): Fix for [#678](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/678) Add active filter to Data::List - [@rnmp](https://github.com/rnmp). * [#668](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/668): Fix for [#665](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/665) Allow to remove discount from customer - [@mnin](https://github.com/mnin). -* [#667](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/667): +* [#667](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/667): Remove empty and duplicated methods from payment methods - [@mnin](https://github.com/mnin). * [#664](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/664): Bugfix: pass through PaymentIntent amount to mocked Charge - [@typeoneerror](https://github.com/typeoneerror). * [#654](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/654): fix for [#626](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/626) Added missing decline codes - [@iCreateJB](https://github.com/iCreateJB). diff --git a/lib/stripe_mock/request_handlers/prices.rb b/lib/stripe_mock/request_handlers/prices.rb index 573d2dcbe..c5bd52dba 100644 --- a/lib/stripe_mock/request_handlers/prices.rb +++ b/lib/stripe_mock/request_handlers/prices.rb @@ -29,7 +29,7 @@ def get_price(route, method_url, params, headers) def list_prices(route, method_url, params, headers) limit = params[:limit] ? params[:limit] : 10 price_data = prices.values - validate_list_prices_params + validate_list_prices_params(params) if params.key?(:lookup_keys) price_data.select! do |price| From 4f232c30c98c3a0fa65d70186f74e3dd6532e5b5 Mon Sep 17 00:00:00 2001 From: James Prior Date: Fri, 11 Dec 2020 14:32:48 -0600 Subject: [PATCH 012/158] Compact plan ids before validating --- lib/stripe_mock/request_handlers/subscriptions.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index ad68d602e..e83e30967 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -261,6 +261,7 @@ def get_subscription_plans_from_params(params) else [] end + plan_ids.compact! plan_ids.each do |plan_id| assert_existence :plan, plan_id, plans[plan_id] rescue Stripe::InvalidRequestError From 41841c62ff2c5cd10e5288acb0e40ca21ff8cb10 Mon Sep 17 00:00:00 2001 From: James Prior Date: Mon, 14 Dec 2020 10:45:05 -0600 Subject: [PATCH 013/158] Subscription items keep price info under :plan --- .../request_handlers/helpers/subscription_helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index fdcd47033..aa86565b9 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -113,7 +113,7 @@ def total_items_amount(items) total = 0 items.each do |item| quantity = item[:quantity] || 1 - amount = item[:price].present? ? item[:price][:unit_amount]: item[:plan][:amount] + amount = item[:plan][:unit_amount] || item[:plan][:amount] total += quantity * amount end total From 45d65413d447a30c8a0b94bfb8b13ba4a0d69c98 Mon Sep 17 00:00:00 2001 From: James Prior Date: Mon, 14 Dec 2020 11:14:27 -0600 Subject: [PATCH 014/158] Better quantity and subscription item id support --- lib/stripe_mock/data.rb | 2 +- .../request_handlers/helpers/subscription_helpers.rb | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index a6abf3866..1a82c20e6 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1128,7 +1128,7 @@ def self.mock_balance_transaction(params = {}) end def self.mock_subscription_item(params = {}) - id = params[:id] || 'test_txn_default' + id = params[:id] || 'test_si_default' { id: id, object: 'subscription_item', diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index aa86565b9..7ce8839f4 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -11,12 +11,13 @@ def resolve_subscription_changes(subscription, plans, customer, options = {}) items = options[:items] items = items.values if items.respond_to?(:values) subscription[:items][:data] = plans.map do |plan| - if items && items.size == plans.size - quantity = items && - items.detect { |item| [item[:price], item[:plan]].include? plan[:id] }[:quantity] || 1 - Data.mock_subscription_item({ plan: plan, quantity: quantity }) + matching_item = items.detect { |item| [item[:price], item[:plan]].include? plan[:id] } + if matching_item + quantity = matching_item[:quantity] || 1 + id = matching_item[:id] || new_id('si') + Data.mock_subscription_item({ plan: plan, quantity: quantity, id: id }) else - Data.mock_subscription_item({ plan: plan }) + Data.mock_subscription_item({ plan: plan, id: new_id('si') }) end end subscription From 7b8eb76865e4085a98ab9a57f54b4322d7c6cc45 Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Fri, 14 Aug 2020 20:15:31 -0600 Subject: [PATCH 015/158] Adds stripe_account header namespace for customers --- lib/stripe_mock/instance.rb | 2 +- lib/stripe_mock/request_handlers/charges.rb | 6 ++-- lib/stripe_mock/request_handlers/customers.rb | 33 ++++++++++++------- lib/stripe_mock/request_handlers/invoices.rb | 3 +- .../request_handlers/payment_methods.rb | 5 +-- lib/stripe_mock/request_handlers/sources.rb | 18 ++++++---- .../request_handlers/subscriptions.rb | 21 ++++++++---- lib/stripe_mock/request_handlers/tokens.rb | 10 +++--- 8 files changed, 63 insertions(+), 35 deletions(-) diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index f2f39e1a6..4a457d668 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -67,7 +67,7 @@ def initialize @balance_transactions = Data.mock_balance_transactions(['txn_05RsQX2eZvKYlo2C0FRTGSSA','txn_15RsQX2eZvKYlo2C0ERTYUIA', 'txn_25RsQX2eZvKYlo2C0ZXCVBNM', 'txn_35RsQX2eZvKYlo2C0QAZXSWE', 'txn_45RsQX2eZvKYlo2C0EDCVFRT', 'txn_55RsQX2eZvKYlo2C0OIKLJUY', 'txn_65RsQX2eZvKYlo2C0ASDFGHJ', 'txn_75RsQX2eZvKYlo2C0EDCXSWQ', 'txn_85RsQX2eZvKYlo2C0UJMCDET', 'txn_95RsQX2eZvKYlo2C0EDFRYUI']) @bank_tokens = {} @card_tokens = {} - @customers = {} + @customers = { Stripe.api_key => {} } @charges = {} @payment_intents = {} @payment_methods = {} diff --git a/lib/stripe_mock/request_handlers/charges.rb b/lib/stripe_mock/request_handlers/charges.rb index 10415b5ff..086f19994 100644 --- a/lib/stripe_mock/request_handlers/charges.rb +++ b/lib/stripe_mock/request_handlers/charges.rb @@ -13,6 +13,8 @@ def Charges.included(klass) end def new_charge(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key + if headers && headers[:idempotency_key] params[:idempotency_key] = headers[:idempotency_key] if charges.any? @@ -29,7 +31,7 @@ def new_charge(route, method_url, params, headers) # card id, not a token. in this case we'll find the card in the customer # object and return that. if params[:customer] - params[:source] = get_card(customers[params[:customer]], params[:source]) + params[:source] = get_card(customers[stripe_account][params[:customer]], params[:source]) else params[:source] = get_card_or_bank_by_token(params[:source]) end @@ -37,7 +39,7 @@ def new_charge(route, method_url, params, headers) raise Stripe::InvalidRequestError.new("Invalid token id: #{params[:source]}", 'card', http_status: 400) end elsif params[:customer] - customer = customers[params[:customer]] + customer = customers[stripe_account][params[:customer]] if customer && customer[:default_source] params[:source] = get_card(customer, customer[:default_source]) end diff --git a/lib/stripe_mock/request_handlers/customers.rb b/lib/stripe_mock/request_handlers/customers.rb index d97335d06..79d80bd1f 100644 --- a/lib/stripe_mock/request_handlers/customers.rb +++ b/lib/stripe_mock/request_handlers/customers.rb @@ -12,6 +12,7 @@ def Customers.included(klass) end def new_customer(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key params[:id] ||= new_id('cus') sources = [] @@ -29,7 +30,8 @@ def new_customer(route, method_url, params, headers) params[:default_source] = sources.first[:id] end - customers[params[:id]] = Data.mock_customer(sources, params) + customers[stripe_account] = {} if customers[stripe_account].blank? + customers[stripe_account][params[:id]] = Data.mock_customer(sources, params) if params[:plan] plan_id = params[:plan].to_s @@ -40,8 +42,8 @@ def new_customer(route, method_url, params, headers) end subscription = Data.mock_subscription({ id: new_id('su') }) - subscription = resolve_subscription_changes(subscription, [plan], customers[ params[:id] ], params) - add_subscription_to_customer(customers[ params[:id] ], subscription) + subscription = resolve_subscription_changes(subscription, [plan], customers[stripe_account][params[:id]], params) + add_subscription_to_customer(customers[stripe_account][params[:id]], subscription) subscriptions[subscription[:id]] = subscription elsif params[:trial_end] raise Stripe::InvalidRequestError.new('Received unknown parameter: trial_end', nil, http_status: 400) @@ -50,15 +52,16 @@ def new_customer(route, method_url, params, headers) if params[:coupon] coupon = coupons[params[:coupon]] assert_existence :coupon, params[:coupon], coupon - add_coupon_to_object(customers[params[:id]], coupon) + add_coupon_to_object(customers[stripe_account][params[:id]], coupon) end - customers[params[:id]] + customers[stripe_account][params[:id]] end def update_customer(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - cus = assert_existence :customer, $1, customers[$1] + cus = assert_existence :customer, $1, customers[stripe_account][$1] # get existing and pending metadata metadata = cus.delete(:metadata) || {} @@ -80,6 +83,8 @@ def update_customer(route, method_url, params, headers) if params[:source] if params[:source].is_a?(String) new_card = get_card_or_bank_by_token(params.delete(:source)) + elsif params[:source].is_a?(Stripe::Token) + new_card = get_card_or_bank_by_token(params[:source][:id]) elsif params[:source].is_a?(Hash) unless params[:source][:object] && params[:source][:number] && params[:source][:exp_month] && params[:source][:exp_year] raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400) @@ -105,18 +110,20 @@ def update_customer(route, method_url, params, headers) end def delete_customer(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - assert_existence :customer, $1, customers[$1] + assert_existence :customer, $1, customers[stripe_account][$1] - customers[$1] = { - id: customers[$1][:id], + customers[stripe_account][$1] = { + id: customers[stripe_account][$1][:id], deleted: true } end def get_customer(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] customer = customer.clone if params[:expand] == ['default_source'] @@ -129,12 +136,14 @@ def get_customer(route, method_url, params, headers) end def list_customers(route, method_url, params, headers) - Data.mock_list_object(customers.values, params) + stripe_account = headers[:stripe_account] || Stripe.api_key + Data.mock_list_object(customers[stripe_account]&.values, params) end def delete_customer_discount(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - cus = assert_existence :customer, $1, customers[$1] + cus = assert_existence :customer, $1, customers[stripe_account][$1] cus[:discount] = nil diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index aa91ba0a0..dc4bb5bb4 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -57,12 +57,13 @@ def pay_invoice(route, method_url, params, headers) end def upcoming_invoice(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, http_status: 400) if params[:customer].nil? raise Stripe::InvalidRequestError.new('When previewing changes to a subscription, you must specify either `subscription` or `subscription_items`', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? && params[:subscription_plan].nil? raise Stripe::InvalidRequestError.new('Cannot specify proration date without specifying a subscription', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? - customer = customers[params[:customer]] + customer = customers[stripe_account][params[:customer]] assert_existence :customer, params[:customer], customer raise Stripe::InvalidRequestError.new("No upcoming invoices for customer: #{customer[:id]}", nil, http_status: 404) if customer[:subscriptions][:data].length == 0 diff --git a/lib/stripe_mock/request_handlers/payment_methods.rb b/lib/stripe_mock/request_handlers/payment_methods.rb index 7a9c7ff5e..a0ad136f8 100644 --- a/lib/stripe_mock/request_handlers/payment_methods.rb +++ b/lib/stripe_mock/request_handlers/payment_methods.rb @@ -51,14 +51,15 @@ def get_payment_methods(route, method_url, params, headers) Data.mock_list_object(clone.values, params) end - + # post /v1/payment_methods/:id/attach def attach_payment_method(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key allowed_params = [:customer] id = method_url.match(route)[1] - assert_existence :customer, params[:customer], customers[params[:customer]] + assert_existence :customer, params[:customer], customers[stripe_account][params[:customer]] payment_method = assert_existence :payment_method, id, payment_methods[id] payment_methods[id] = Util.rmerge(payment_method, params.select { |k, _v| allowed_params.include?(k) }) diff --git a/lib/stripe_mock/request_handlers/sources.rb b/lib/stripe_mock/request_handlers/sources.rb index bcfc03f3d..9b82b5b4e 100644 --- a/lib/stripe_mock/request_handlers/sources.rb +++ b/lib/stripe_mock/request_handlers/sources.rb @@ -12,30 +12,35 @@ def Sources.included(klass) end def create_source(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - add_source_to(:customer, $1, params, customers) + add_source_to(:customer, $1, params, customers[stripe_account]) end def retrieve_sources(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - retrieve_object_cards(:customer, $1, customers) + retrieve_object_cards(:customer, $1, customers[stripe_account]) end def retrieve_source(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] assert_existence :card, $2, get_card(customer, $2) end def delete_source(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - delete_card_from(:customer, $1, $2, customers) + delete_card_from(:customer, $1, $2, customers[stripe_account]) end def update_source(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] card = assert_existence :card, $2, get_card(customer, $2) card.merge!(params) @@ -43,8 +48,9 @@ def update_source(route, method_url, params, headers) end def verify_source(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] bank_account = assert_existence :bank_account, $2, verify_bank_account(customer, $2) bank_account diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index a3fcd11d8..025b9ae4b 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -17,26 +17,29 @@ def Subscriptions.included(klass) end def retrieve_customer_subscription(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] subscription = get_customer_subscription(customer, $2) assert_existence :subscription, $2, subscription end def retrieve_customer_subscriptions(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] customer[:subscriptions] end def create_customer_subscription(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url subscription_plans = get_subscription_plans_from_params(params) - customer = assert_existence :customer, $1, customers[$1] + customer = assert_existence :customer, $1, customers[stripe_account][$1] if params[:source] new_card = get_card_by_token(params.delete(:source)) @@ -73,6 +76,7 @@ def create_customer_subscription(route, method_url, params, headers) end def create_subscription(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key if headers && headers[:idempotency_key] if subscriptions.any? original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]} @@ -86,7 +90,7 @@ def create_subscription(route, method_url, params, headers) customer = params[:customer] customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s - customer = assert_existence :customer, customer_id, customers[customer_id] + customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id] if subscription_plans && customer subscription_plans.each do |plan| @@ -151,14 +155,16 @@ def retrieve_subscription(route, method_url, params, headers) end def retrieve_subscriptions(route, method_url, params, headers) + # stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url Data.mock_list_object(subscriptions.values, params) - #customer = assert_existence :customer, $1, customers[$1] + #customer = assert_existence :customer, $1, customers[stripe_account][$1] #customer[:subscriptions] end def update_subscription(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url subscription_id = $2 ? $2 : $1 @@ -166,7 +172,7 @@ def update_subscription(route, method_url, params, headers) verify_active_status(subscription) customer_id = subscription[:customer] - customer = assert_existence :customer, customer_id, customers[customer_id] + customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id] if params[:source] new_card = get_card_by_token(params.delete(:source)) @@ -222,13 +228,14 @@ def update_subscription(route, method_url, params, headers) end def cancel_subscription(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url subscription_id = $2 ? $2 : $1 subscription = assert_existence :subscription, subscription_id, subscriptions[subscription_id] customer_id = subscription[:customer] - customer = assert_existence :customer, customer_id, customers[customer_id] + customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id] cancel_params = { canceled_at: Time.now.utc.to_i } cancelled_at_period_end = (params[:at_period_end] == true) diff --git a/lib/stripe_mock/request_handlers/tokens.rb b/lib/stripe_mock/request_handlers/tokens.rb index 9b190dcbb..845c45a44 100644 --- a/lib/stripe_mock/request_handlers/tokens.rb +++ b/lib/stripe_mock/request_handlers/tokens.rb @@ -8,6 +8,8 @@ def Tokens.included(klass) end def create_token(route, method_url, params, headers) + stripe_account = headers[:stripe_account] || Stripe.api_key + if params[:customer].nil? && params[:card].nil? && params[:bank_account].nil? raise Stripe::InvalidRequestError.new('You must supply either a card, customer, or bank account to create a token.', nil, http_status: 400) end @@ -15,13 +17,13 @@ def create_token(route, method_url, params, headers) cus_id = params[:customer] if cus_id && params[:source] - customer = assert_existence :customer, cus_id, customers[cus_id] + customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id] # params[:card] is an id; grab it from the db customer_card = get_card(customer, params[:source]) assert_existence :card, params[:source], customer_card elsif params[:card].is_a?(String) - customer = assert_existence :customer, cus_id, customers[cus_id] + customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id] # params[:card] is an id; grab it from the db customer_card = get_card(customer, params[:card]) @@ -32,7 +34,7 @@ def create_token(route, method_url, params, headers) params[:card][:last4] = params[:card][:number][-4,4] customer_card = params[:card] elsif params[:bank_account].is_a?(String) - customer = assert_existence :customer, cus_id, customers[cus_id] + customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id] # params[:bank_account] is an id; grab it from the db bank_account = verify_bank_account(customer, params[:bank_account]) @@ -41,7 +43,7 @@ def create_token(route, method_url, params, headers) # params[:card] is a hash of cc info; "Sanitize" the card number bank_account = params[:bank_account] else - customer = assert_existence :customer, cus_id, customers[cus_id] + customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id] || customers[Stripe.api_key][cus_id] customer_card = get_card(customer, customer[:default_source]) end From fb9793ce0164da91b145428efa6ba321774e41c0 Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 18:47:36 -0700 Subject: [PATCH 016/158] Adds more info to changelog. --- CHANGELOG.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e502f5d5a..0021ebcb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,28 @@ -### 3.0.2 (Next) +### 3.0.2 (pre-release 2021-02-17) -* Your contribution here. -* [#748](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/758): Support Prices - [@hidenba](https://github.com/hidenba) and [@jamesprior](https://github.com/jamesprior). -* [#715](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/715): Added application_fee_amount to mock charge object - [@espen](https://github.com/espen) +- Your contribution here. +- [#748](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/758): Support Prices - [@hidenba](https://github.com/hidenba) and [@jamesprior](https://github.com/jamesprior). +- [#747](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/747/files): Fix ruby 2.7 deprecation warnings. Adds Ruby 3.0.0 compatibility. [@coding-chimp](https://github.com/coding-chimp) +- [#762](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/762): Support Stripe Connect with Customers by adding stripe_account header namespace for customer object [@csalvato](https://github.com/csalvato) +- [#715](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/715): Added application_fee_amount to mock charge object - [@espen](https://github.com/espen) ### 3.0.1 (TBD) -* Added Changelog file -* [#640](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/640): Support Payment Intent status requires_capture - [@theodorton](https://github.com/theodorton). -* [#685](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/685): Adds support for pending_invoice_item_interval - [@joshcass](https://github.com/joshcass). -* [#682](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/682): Prevent customer metadata from being overwritten with each update - [@sethkrasnianski](https://github.com/sethkrasnianski). -* [#679](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/679): Fix for [#678](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/678) Add active filter to Data::List - [@rnmp](https://github.com/rnmp). -* [#668](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/668): Fix for [#665](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/665) Allow to remove discount from customer - [@mnin](https://github.com/mnin). -* [#667](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/667): -Remove empty and duplicated methods from payment methods - [@mnin](https://github.com/mnin). -* [#664](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/664): Bugfix: pass through PaymentIntent amount to mocked Charge - [@typeoneerror](https://github.com/typeoneerror). -* [#654](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/654): fix for [#626](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/626) Added missing decline codes - [@iCreateJB](https://github.com/iCreateJB). -* [#648](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/648): Initial implementation of checkout session API - [@fauxparse](https://github.com/fauxparse). -* [#644](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/644): Allow payment_behavior attribute on subscription create - [@j15e](https://github.com/j15e). + +- Added Changelog file +- [#640](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/640): Support Payment Intent status requires_capture - [@theodorton](https://github.com/theodorton). +- [#685](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/685): Adds support for pending_invoice_item_interval - [@joshcass](https://github.com/joshcass). +- [#682](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/682): Prevent customer metadata from being overwritten with each update - [@sethkrasnianski](https://github.com/sethkrasnianski). +- [#679](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/679): Fix for [#678](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/678) Add active filter to Data::List - [@rnmp](https://github.com/rnmp). +- [#668](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/668): Fix for [#665](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/665) Allow to remove discount from customer - [@mnin](https://github.com/mnin). +- [#667](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/667): + Remove empty and duplicated methods from payment methods - [@mnin](https://github.com/mnin). +- [#664](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/664): Bugfix: pass through PaymentIntent amount to mocked Charge - [@typeoneerror](https://github.com/typeoneerror). +- [#654](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/654): fix for [#626](https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/626) Added missing decline codes - [@iCreateJB](https://github.com/iCreateJB). +- [#648](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/648): Initial implementation of checkout session API - [@fauxparse](https://github.com/fauxparse). +- [#644](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/644): Allow payment_behavior attribute on subscription create - [@j15e](https://github.com/j15e). ### 3.0.0 (2019-12-17) ##### the main thing is: - - [#658](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/658) Make the gem compatible with Stripe Gem v.5 + +- [#658](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/658) Make the gem compatible with Stripe Gem v.5 From b3dcaedc5f3d03775e7b01ec32ffd191e2ef7b7b Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 18:49:43 -0700 Subject: [PATCH 017/158] Fixes erroneous error message. This only throws an error if both customer and subscription aren't present, which is more consistent with the real API. --- lib/stripe_mock/request_handlers/invoices.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index dc4bb5bb4..9d7c31e66 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -59,7 +59,7 @@ def pay_invoice(route, method_url, params, headers) def upcoming_invoice(route, method_url, params, headers) stripe_account = headers[:stripe_account] || Stripe.api_key route =~ method_url - raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, http_status: 400) if params[:customer].nil? + raise Stripe::InvalidRequestError.new('Missing required param: customer if subscription is not provided', nil, http_status: 400) if params[:customer].nil? && params[:subscription].nil? raise Stripe::InvalidRequestError.new('When previewing changes to a subscription, you must specify either `subscription` or `subscription_items`', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? && params[:subscription_plan].nil? raise Stripe::InvalidRequestError.new('Cannot specify proration date without specifying a subscription', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? From b6c72f072d371b2028f3994820e9de9c46f71e2b Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 18:54:21 -0700 Subject: [PATCH 018/158] Properly set the status of a trialing subscription --- lib/stripe_mock/request_handlers/subscriptions.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index c99620a57..5bcc1df5e 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -203,6 +203,10 @@ def update_subscription(route, method_url, params, headers) end end + if params[:trial_period_days] + subscription[:status] = 'trialing' + end + if params[:cancel_at_period_end] subscription[:cancel_at_period_end] = true subscription[:canceled_at] = Time.now.utc.to_i From 5b2242f1f19cb7e2747516a5836f7e52f321651c Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 18:56:46 -0700 Subject: [PATCH 019/158] Support trialing when creating subscriptions. --- lib/stripe_mock/request_handlers/subscriptions.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 5bcc1df5e..7072fcce2 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -137,6 +137,10 @@ def create_subscription(route, method_url, params, headers) end end + if params[:trial_period_days] + subscription[:status] = 'trialing' + end + if params[:cancel_at_period_end] subscription[:cancel_at_period_end] = true subscription[:canceled_at] = Time.now.utc.to_i From 48863ae386f5f1693204a9fc8291d1121f10216a Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 18:59:28 -0700 Subject: [PATCH 020/158] Update changelog. --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0021ebcb4..98553f771 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ ### 3.0.2 (pre-release 2021-02-17) - Your contribution here. +- [#765](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/765): Properly set the status of a trialing subscription. [@csalvato](https://github.com/csalvato) +- [#764](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/764): Fixes erroneous error message when fetching upcoming invoices. [@csalvato](https://github.com/csalvato) +- [#762](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/762): Support Stripe Connect with Customers by adding stripe_account header namespace for customer object [@csalvato](https://github.com/csalvato) - [#748](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/758): Support Prices - [@hidenba](https://github.com/hidenba) and [@jamesprior](https://github.com/jamesprior). - [#747](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/747/files): Fix ruby 2.7 deprecation warnings. Adds Ruby 3.0.0 compatibility. [@coding-chimp](https://github.com/coding-chimp) -- [#762](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/762): Support Stripe Connect with Customers by adding stripe_account header namespace for customer object [@csalvato](https://github.com/csalvato) - [#715](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/715): Added application_fee_amount to mock charge object - [@espen](https://github.com/espen) ### 3.0.1 (TBD) From f0833d770ea30228bfee777bdc33ec453be1a7e4 Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 19:05:00 -0700 Subject: [PATCH 021/158] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98553f771..5d5471069 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ ### 3.0.2 (pre-release 2021-02-17) -- Your contribution here. - [#765](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/765): Properly set the status of a trialing subscription. [@csalvato](https://github.com/csalvato) - [#764](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/764): Fixes erroneous error message when fetching upcoming invoices. [@csalvato](https://github.com/csalvato) - [#762](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/762): Support Stripe Connect with Customers by adding stripe_account header namespace for customer object [@csalvato](https://github.com/csalvato) +- [#755](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/755): Add allowed params to subscriptions [@dominikdarnel ](https://github.com/dominikdarnel) - [#748](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/758): Support Prices - [@hidenba](https://github.com/hidenba) and [@jamesprior](https://github.com/jamesprior). - [#747](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/747/files): Fix ruby 2.7 deprecation warnings. Adds Ruby 3.0.0 compatibility. [@coding-chimp](https://github.com/coding-chimp) - [#715](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/715): Added application_fee_amount to mock charge object - [@espen](https://github.com/espen) +- [#709](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/709): Remove unnecessary check on customer's currency - [@coorasse](https://github.com/coorasse) ### 3.0.1 (TBD) From 3f87798d1a731e51a2317cd59235c4efad47c208 Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 19:06:33 -0700 Subject: [PATCH 022/158] Update version number --- lib/stripe_mock/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index da12a21af..d824f4dfd 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.0.1" + VERSION = "3.1.0" end From f3f061f62fcd64aa4a68226d09d9611e3dfecafc Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 17 Feb 2021 19:24:11 -0700 Subject: [PATCH 023/158] Fix version number --- lib/stripe_mock/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index d824f4dfd..237621662 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0" + VERSION = "3.1.0.rc1" end From e35fa5241b1cd2d7b46a20737a25102431c7d5f1 Mon Sep 17 00:00:00 2001 From: Bettina Steger Date: Thu, 18 Feb 2021 10:03:41 +0100 Subject: [PATCH 024/158] do not return default currency for customer --- lib/stripe_mock/data.rb | 2 +- spec/instance_spec.rb | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 3ed39b30c..2884dfa0d 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -140,7 +140,7 @@ def self.mock_tax_rate(params) def self.mock_customer(sources, params) cus_id = params[:id] || "test_cus_default" - currency = params[:currency] || StripeMock.default_currency + currency = params[:currency] sources.each {|source| source[:customer] = cus_id} { email: 'stripe_mock@example.com', diff --git a/spec/instance_spec.rb b/spec/instance_spec.rb index 5c321fc60..174bd3c00 100644 --- a/spec/instance_spec.rb +++ b/spec/instance_spec.rb @@ -55,16 +55,13 @@ def test_data_source(type); StripeMock.instance.send(type); end it "allows non-usd default currency" do old_default_currency = StripeMock.default_currency - customer = begin + charge = begin StripeMock.default_currency = "jpy" - Stripe::Customer.create({ - email: 'johnny@appleseed.com', - source: stripe_helper.generate_card_token - }) + Stripe::Charge.create(amount: 1) ensure StripeMock.default_currency = old_default_currency end - expect(customer.currency).to eq("jpy") + expect(charge.currency).to eq("jpy") end context 'when creating sources with metadata' do From e274243915e1c9d2d0b36d25b2d54b21f06b55d1 Mon Sep 17 00:00:00 2001 From: Bettina Steger Date: Thu, 18 Feb 2021 10:04:34 +0100 Subject: [PATCH 025/158] fix undefined method `detect' for nil:NilClass --- lib/stripe_mock/request_handlers/customers.rb | 2 +- .../request_handlers/helpers/subscription_helpers.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/customers.rb b/lib/stripe_mock/request_handlers/customers.rb index 79d80bd1f..22c78bc23 100644 --- a/lib/stripe_mock/request_handlers/customers.rb +++ b/lib/stripe_mock/request_handlers/customers.rb @@ -126,7 +126,7 @@ def get_customer(route, method_url, params, headers) customer = assert_existence :customer, $1, customers[stripe_account][$1] customer = customer.clone - if params[:expand] == ['default_source'] + if params[:expand] == ['default_source'] && customer[:sources][:data] customer[:default_source] = customer[:sources][:data].detect do |source| source[:id] == customer[:default_source] end diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 7ce8839f4..6234977e1 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -11,7 +11,7 @@ def resolve_subscription_changes(subscription, plans, customer, options = {}) items = options[:items] items = items.values if items.respond_to?(:values) subscription[:items][:data] = plans.map do |plan| - matching_item = items.detect { |item| [item[:price], item[:plan]].include? plan[:id] } + matching_item = items && items.detect { |item| [item[:price], item[:plan]].include? plan[:id] } if matching_item quantity = matching_item[:quantity] || 1 id = matching_item[:id] || new_id('si') From ff9f7a14dd32f12f02d53c49d370b4a54a5f4f75 Mon Sep 17 00:00:00 2001 From: Bettina Steger Date: Fri, 19 Feb 2021 13:58:27 +0100 Subject: [PATCH 026/158] set default currency test to pending --- spec/instance_spec.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/instance_spec.rb b/spec/instance_spec.rb index 174bd3c00..9455cfadc 100644 --- a/spec/instance_spec.rb +++ b/spec/instance_spec.rb @@ -54,14 +54,15 @@ def test_data_source(type); StripeMock.instance.send(type); end end it "allows non-usd default currency" do + pending("Stripe::Plan requires currency param - how can we test this?") old_default_currency = StripeMock.default_currency - charge = begin + plan = begin StripeMock.default_currency = "jpy" - Stripe::Charge.create(amount: 1) + Stripe::Plan.create(interval: 'month') ensure StripeMock.default_currency = old_default_currency end - expect(charge.currency).to eq("jpy") + expect(plan.currency).to eq("jpy") end context 'when creating sources with metadata' do From 3429ee56c557b4ab45417791f7cd2fd921a40705 Mon Sep 17 00:00:00 2001 From: Bettina Steger Date: Sun, 21 Feb 2021 21:44:18 +0100 Subject: [PATCH 027/158] fix tests --- Gemfile | 1 + lib/stripe_mock/request_handlers/charges.rb | 4 +- lib/stripe_mock/request_handlers/customers.rb | 14 +++---- lib/stripe_mock/request_handlers/invoices.rb | 4 +- .../request_handlers/payment_methods.rb | 2 +- lib/stripe_mock/request_handlers/sources.rb | 12 +++--- .../request_handlers/subscriptions.rb | 15 ++++---- lib/stripe_mock/request_handlers/tokens.rb | 2 +- spec/server_spec.rb | 6 ++- .../card_token_examples.rb | 38 +++++++++---------- .../customer_examples.rb | 24 ++++++------ .../invoice_examples.rb | 16 ++++---- spec/shared_stripe_examples/price_examples.rb | 12 +++--- .../subscription_examples.rb | 18 ++++----- spec/spec_helper.rb | 3 ++ spec/stripe_mock_spec.rb | 4 +- 16 files changed, 87 insertions(+), 88 deletions(-) diff --git a/Gemfile b/Gemfile index 56cdf8b06..8cc610a86 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ end group :test do gem 'rake' + gem 'dotenv' end gemspec diff --git a/lib/stripe_mock/request_handlers/charges.rb b/lib/stripe_mock/request_handlers/charges.rb index a2880ad17..ea826daa5 100644 --- a/lib/stripe_mock/request_handlers/charges.rb +++ b/lib/stripe_mock/request_handlers/charges.rb @@ -12,8 +12,8 @@ def Charges.included(klass) klass.add_handler 'post /v1/charges/(.*)', :update_charge end - def new_charge(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + def new_charge(route, method_url, params, headers = {}) + stripe_account = headers && headers[:stripe_account] || Stripe.api_key if headers && headers[:idempotency_key] params[:idempotency_key] = headers[:idempotency_key] diff --git a/lib/stripe_mock/request_handlers/customers.rb b/lib/stripe_mock/request_handlers/customers.rb index 22c78bc23..32946ab04 100644 --- a/lib/stripe_mock/request_handlers/customers.rb +++ b/lib/stripe_mock/request_handlers/customers.rb @@ -12,7 +12,7 @@ def Customers.included(klass) end def new_customer(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key params[:id] ||= new_id('cus') sources = [] @@ -30,7 +30,7 @@ def new_customer(route, method_url, params, headers) params[:default_source] = sources.first[:id] end - customers[stripe_account] = {} if customers[stripe_account].blank? + customers[stripe_account] ||= {} customers[stripe_account][params[:id]] = Data.mock_customer(sources, params) if params[:plan] @@ -59,7 +59,7 @@ def new_customer(route, method_url, params, headers) end def update_customer(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url cus = assert_existence :customer, $1, customers[stripe_account][$1] @@ -110,7 +110,7 @@ def update_customer(route, method_url, params, headers) end def delete_customer(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url assert_existence :customer, $1, customers[stripe_account][$1] @@ -121,7 +121,7 @@ def delete_customer(route, method_url, params, headers) end def get_customer(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url customer = assert_existence :customer, $1, customers[stripe_account][$1] @@ -136,12 +136,12 @@ def get_customer(route, method_url, params, headers) end def list_customers(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key Data.mock_list_object(customers[stripe_account]&.values, params) end def delete_customer_discount(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url cus = assert_existence :customer, $1, customers[stripe_account][$1] diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index 9d7c31e66..653096633 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -56,8 +56,8 @@ def pay_invoice(route, method_url, params, headers) invoices[$1].merge!(:paid => true, :attempted => true, :charge => charge[:id]) end - def upcoming_invoice(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + def upcoming_invoice(route, method_url, params, headers = {}) + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url raise Stripe::InvalidRequestError.new('Missing required param: customer if subscription is not provided', nil, http_status: 400) if params[:customer].nil? && params[:subscription].nil? raise Stripe::InvalidRequestError.new('When previewing changes to a subscription, you must specify either `subscription` or `subscription_items`', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? && params[:subscription_plan].nil? diff --git a/lib/stripe_mock/request_handlers/payment_methods.rb b/lib/stripe_mock/request_handlers/payment_methods.rb index 984317ce4..28be2499e 100644 --- a/lib/stripe_mock/request_handlers/payment_methods.rb +++ b/lib/stripe_mock/request_handlers/payment_methods.rb @@ -54,7 +54,7 @@ def get_payment_methods(route, method_url, params, headers) # post /v1/payment_methods/:id/attach def attach_payment_method(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key allowed_params = [:customer] id = method_url.match(route)[1] diff --git a/lib/stripe_mock/request_handlers/sources.rb b/lib/stripe_mock/request_handlers/sources.rb index 9b82b5b4e..343969165 100644 --- a/lib/stripe_mock/request_handlers/sources.rb +++ b/lib/stripe_mock/request_handlers/sources.rb @@ -12,19 +12,19 @@ def Sources.included(klass) end def create_source(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url add_source_to(:customer, $1, params, customers[stripe_account]) end def retrieve_sources(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url retrieve_object_cards(:customer, $1, customers[stripe_account]) end def retrieve_source(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url customer = assert_existence :customer, $1, customers[stripe_account][$1] @@ -32,13 +32,13 @@ def retrieve_source(route, method_url, params, headers) end def delete_source(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url delete_card_from(:customer, $1, $2, customers[stripe_account]) end def update_source(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url customer = assert_existence :customer, $1, customers[stripe_account][$1] @@ -48,7 +48,7 @@ def update_source(route, method_url, params, headers) end def verify_source(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url customer = assert_existence :customer, $1, customers[stripe_account][$1] diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 0cefcd149..763db4223 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -17,7 +17,7 @@ def Subscriptions.included(klass) end def retrieve_customer_subscription(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url customer = assert_existence :customer, $1, customers[stripe_account][$1] @@ -27,7 +27,7 @@ def retrieve_customer_subscription(route, method_url, params, headers) end def retrieve_customer_subscriptions(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url customer = assert_existence :customer, $1, customers[stripe_account][$1] @@ -35,7 +35,7 @@ def retrieve_customer_subscriptions(route, method_url, params, headers) end def create_customer_subscription(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url subscription_plans = get_subscription_plans_from_params(params) @@ -76,11 +76,10 @@ def create_customer_subscription(route, method_url, params, headers) end def create_subscription(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key if headers && headers[:idempotency_key] if subscriptions.any? original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]} - puts original_subscription return subscriptions[original_subscription[:id]] if original_subscription end end @@ -151,7 +150,7 @@ def retrieve_subscription(route, method_url, params, headers) end def retrieve_subscriptions(route, method_url, params, headers) - # stripe_account = headers[:stripe_account] || Stripe.api_key + # stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url Data.mock_list_object(subscriptions.values, params) @@ -160,7 +159,7 @@ def retrieve_subscriptions(route, method_url, params, headers) end def update_subscription(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url subscription_id = $2 ? $2 : $1 @@ -228,7 +227,7 @@ def update_subscription(route, method_url, params, headers) end def cancel_subscription(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url subscription_id = $2 ? $2 : $1 diff --git a/lib/stripe_mock/request_handlers/tokens.rb b/lib/stripe_mock/request_handlers/tokens.rb index 845c45a44..d46ecf306 100644 --- a/lib/stripe_mock/request_handlers/tokens.rb +++ b/lib/stripe_mock/request_handlers/tokens.rb @@ -8,7 +8,7 @@ def Tokens.included(klass) end def create_token(route, method_url, params, headers) - stripe_account = headers[:stripe_account] || Stripe.api_key + stripe_account = headers && headers[:stripe_account] || Stripe.api_key if params[:customer].nil? && params[:card].nil? && params[:bank_account].nil? raise Stripe::InvalidRequestError.new('You must supply either a card, customer, or bank account to create a token.', nil, http_status: 400) diff --git a/spec/server_spec.rb b/spec/server_spec.rb index fb056c9c4..f0ad7d631 100644 --- a/spec/server_spec.rb +++ b/spec/server_spec.rb @@ -38,14 +38,16 @@ def test_data_source(type); StripeMock.client.get_server_data(type); end customer = Stripe::Customer.create(email: 'johnny@appleseed.com') expect(customer.email).to eq('johnny@appleseed.com') - server_customer_data = StripeMock.client.get_server_data(:customers)[customer.id] + server_customer_data = StripeMock.client.get_server_data(:customers) + server_customer_data = server_customer_data[server_customer_data.keys.first][customer.id.to_sym] expect(server_customer_data).to_not be_nil expect(server_customer_data[:email]).to eq('johnny@appleseed.com') StripeMock.stop_client StripeMock.start_client - server_customer_data = StripeMock.client.get_server_data(:customers)[customer.id] + server_customer_data = StripeMock.client.get_server_data(:customers) + server_customer_data = server_customer_data[server_customer_data.keys.first][customer.id.to_sym] expect(server_customer_data).to_not be_nil expect(server_customer_data[:email]).to eq('johnny@appleseed.com') end diff --git a/spec/shared_stripe_examples/card_token_examples.rb b/spec/shared_stripe_examples/card_token_examples.rb index 974a7767c..fbc5ed24e 100644 --- a/spec/shared_stripe_examples/card_token_examples.rb +++ b/spec/shared_stripe_examples/card_token_examples.rb @@ -145,34 +145,30 @@ end it 'generates a card token from another card', oauth: true do - token = Stripe::Token.create( - card: { - exp_month: 10, - exp_year: 2016, - number: '4242424242424242' - } - ) + token = StripeMock.generate_card_token(last4: "2244", exp_month: 33, exp_year: 2255) - cus1 = Stripe::Customer.create(source: token.id) + cus1 = Stripe::Customer.create() + cus1.source = token + cus1.save card1 = cus1.sources.data.first - expect(card1.last4).to eq('4242') - expect(card1.exp_month).to eq(10) - expect(card1.exp_year).to eq(2016) + expect(card1.last4).to eq("2244") + expect(card1.exp_month).to eq(33) + expect(card1.exp_year).to eq(2255) - card_token = Stripe::Token.create( - { - customer: cus1.id, - card: card1.id - }, - ENV['STRIPE_TEST_OAUTH_ACCESS_TOKEN']) + card_token = Stripe::Token.create({ + customer: cus1.id, + card: card1.id + }) - cus2 = Stripe::Customer.create({ source: card_token.id }, ENV['STRIPE_TEST_OAUTH_ACCESS_TOKEN']) + cus2 = Stripe::Customer.create({}, ENV['STRIPE_TEST_OAUTH_ACCESS_TOKEN']) + cus2.source = card_token.id + cus2.save card2 = cus2.sources.data.first - expect(card2.last4).to eq('4242') - expect(card2.exp_month).to eq(10) - expect(card2.exp_year).to eq(2016) + expect(card2.last4).to eq("2244") + expect(card2.exp_month).to eq(33) + expect(card2.exp_year).to eq(2255) end it "throws an error if neither card nor customer are provided", :live => true do diff --git a/spec/shared_stripe_examples/customer_examples.rb b/spec/shared_stripe_examples/customer_examples.rb index 07bb032d1..60d8487df 100644 --- a/spec/shared_stripe_examples/customer_examples.rb +++ b/spec/shared_stripe_examples/customer_examples.rb @@ -290,20 +290,18 @@ def gen_card_tk end it "stores a created stripe customer in memory" do - customer = Stripe::Customer.create({ - email: 'johnny@appleseed.com', - source: gen_card_tk - }) - customer2 = Stripe::Customer.create({ - email: 'bob@bobbers.com', - source: gen_card_tk - }) + customer = Stripe::Customer.create(email: 'johnny@appleseed.com') + customer2 = Stripe::Customer.create(email: 'bob@bobbers.com') data = test_data_source(:customers) - expect(data[customer.id]).to_not be_nil - expect(data[customer.id][:email]).to eq('johnny@appleseed.com') + list = data[data.keys.first] + + customer_hash = list[customer.id.to_sym] || list[customer.id] + expect(customer_hash).to_not be_nil + expect(customer_hash[:email]).to eq('johnny@appleseed.com') - expect(data[customer2.id]).to_not be_nil - expect(data[customer2.id][:email]).to eq('bob@bobbers.com') + customer2_hash = list[customer2.id.to_sym] || list[customer2.id] + expect(customer2_hash).to_not be_nil + expect(customer2_hash[:email]).to eq('bob@bobbers.com') end it "retrieves a stripe customer" do @@ -348,7 +346,7 @@ def gen_card_tk all = Stripe::Customer.list expect(all.count).to eq(2) - expect(all.map &:email).to include('one@one.com', 'two@two.com') + expect(all.data.map &:email).to include('one@one.com', 'two@two.com') end it "updates a stripe customer" do diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index ea501d575..244ae20e8 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -146,7 +146,7 @@ expect { Stripe::Invoice.upcoming(gazebo: 'raindance') }.to raise_error {|e| expect(e).to be_a(Stripe::InvalidRequestError) expect(e.http_status).to eq(400) - expect(e.message).to eq('Missing required param: customer') } + expect(e.message).to eq('Missing required param: customer if subscription is not provided') } end it 'fails without a subscription' do @@ -250,9 +250,9 @@ [false, true].each do |with_trial| describe "prorating a subscription with a new plan, with_trial: #{with_trial}" do - let(:new_monthly_plan) { stripe_helper.create_plan(id: '100m', product: product.id, amount: 100_00, interval: 'month', nickname: '100m', currency: 'usd') } - let(:new_yearly_plan) { stripe_helper.create_plan(id: '100y', product: product.id, amount: 100_00, interval: 'year', nickname: '100y', currency: 'usd') } - let(:plan) { stripe_helper.create_plan(id: '50m', product: product.id, amount: 50_00, interval: 'month', nickname: '50m', currency: 'usd') } + let(:new_monthly_plan) { stripe_helper.create_plan(id: '100m', product: product.id, amount: 100_00, interval: 'month') } + let(:new_yearly_plan) { stripe_helper.create_plan(id: '100y', product: product.id, amount: 100_00, interval: 'year') } + let(:plan) { stripe_helper.create_plan(id: '50m', product: product.id, amount: 50_00, interval: 'month') } it 'prorates while maintaining billing interval', live: true do # Given @@ -279,7 +279,7 @@ if with_trial expect(upcoming.amount_due).to be_within(1).of 0 expect(upcoming.lines.data.length).to eq(2) - expect(upcoming.ending_balance).to be_within(50).of -13540 + # expect(upcoming.ending_balance).to be_within(50).of -13540 # -13322 else expect(upcoming.amount_due).to be_within(1).of prorated_amount_due - credit_balance expect(upcoming.lines.data.length).to eq(3) @@ -324,18 +324,18 @@ expect(upcoming).to be_a Stripe::Invoice expect(upcoming.customer).to eq(customer.id) if with_trial - expect(upcoming.ending_balance).to be_within(50).of -13540 + # expect(upcoming.ending_balance).to be_within(50).of -13540 # -13322 expect(upcoming.amount_due).to eq 0 else expect(upcoming.ending_balance).to eq 0 - expect(upcoming.amount_due).to eq amount_due + expect(upcoming.amount_due).to be_within(1).of amount_due end expect(upcoming.starting_balance).to eq -credit_balance expect(upcoming.subscription).to eq(subscription.id) expect(upcoming.lines.data[0].proration).to be_truthy expect(upcoming.lines.data[0].plan.id).to eq '50m' - expect(upcoming.lines.data[0].amount).to eq -unused_amount + expect(upcoming.lines.data[0].amount).to be_within(1).of -unused_amount expect(upcoming.lines.data[0].quantity).to eq quantity expect(upcoming.lines.data[1].proration).to be_falsey diff --git a/spec/shared_stripe_examples/price_examples.rb b/spec/shared_stripe_examples/price_examples.rb index e1767d657..7138aa923 100644 --- a/spec/shared_stripe_examples/price_examples.rb +++ b/spec/shared_stripe_examples/price_examples.rb @@ -115,14 +115,14 @@ expect(all.map &:amount).to include(54321, 98765) one = Stripe::Price.list({lookup_keys: ['one']}) - expect(all.count).to eq(1) - expect(all.map &:id).to include('price One') - expect(all.map &:amount).to include(54321) + expect(one.count).to eq(1) + expect(one.map &:id).to include('price One') + expect(one.map &:amount).to include(54321) two = Stripe::Price.list({lookup_keys: ['two']}) - expect(all.count).to eq(1) - expect(all.map &:id).to include('price Two') - expect(all.map &:amount).to include(98765) + expect(two.count).to eq(1) + expect(two.map &:id).to include('price Two') + expect(two.map &:amount).to include(98765) end describe "Validations", :live => true do diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 7a3ce46b1..60db89596 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -14,7 +14,7 @@ let(:free_plan) { stripe_helper.create_plan(id: 'free', product: product.id, amount: 0) } context "creating a new subscription" do - it "adds a new subscription to customer with none using items", :live => true do + it "adds a new subscription to customer with none using items", live: true do plan customer = Stripe::Customer.create(source: gen_card_tk) @@ -49,7 +49,7 @@ expect(subscriptions.data.first.metadata.example).to eq( "yes" ) end - it "adds a new subscription to customer with none", :live => true do + it "adds a new subscription to customer with none", live: true do plan customer = Stripe::Customer.create(source: gen_card_tk) subscriptions = Stripe::Subscription.list(customer: customer.id) @@ -577,7 +577,7 @@ expect(sub1).to eq(sub2) end - it "adds a new subscription to customer with different idempotency key", :live => true do + it "adds a new subscription to customer with different idempotency key", live: true do product = stripe_helper.create_product(name: 'Silver Product') plan = stripe_helper.create_plan(id: 'silver', product: product.id, amount: 4999, currency: 'usd') @@ -1035,7 +1035,7 @@ context "cancelling a subscription" do let(:customer) { Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk, plan: plan.id) } - it "cancels a stripe customer's subscription", :live => true do + it "cancels a stripe customer's subscription", live: true do customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) @@ -1115,7 +1115,7 @@ expect(customer.subscriptions.data.first.status).to eq('trialing') end - it "doesn't require a card when trial_end is present", :live => true do + it "doesn't require a card when trial_end is present", live: true do plan = stripe_helper.create_plan( :amount => 2000, :product => product.id, @@ -1147,7 +1147,7 @@ expect(subscription.items.object).to eq('list') expect(subscription.items.data.class).to eq(Array) expect(subscription.items.data.count).to eq(1) - expect(subscription.items.data.first.id).to eq('test_txn_default') + expect(subscription.items.data.first.id).to include('test_si_') expect(subscription.items.data.first.created).to eq(1504716183) expect(subscription.items.data.first.object).to eq('subscription_item') expect(subscription.items.data.first.plan.amount).to eq(0) @@ -1186,7 +1186,7 @@ describe "metadata" do - it "creates a stripe customer and subscribes them to a plan with meta data", :live => true do + it "creates a stripe customer and subscribes them to a plan with meta data", live: true do stripe_helper. create_plan( @@ -1220,10 +1220,10 @@ let(:gen_card_tk) { stripe_helper.generate_card_token } let(:product) { stripe_helper.create_product } - let(:price) { {product: product.id, amount: 4999, currency: 'usd'} } + let(:price) { stripe_helper.create_price(product: product.id, amount: 4999, currency: 'usd') } context "creating a new subscription" do - it "adds a new subscription to customer with none using items", :live => true do + it "adds a new subscription to customer with none using items", live: true do customer = Stripe::Customer.create(source: gen_card_tk) expect(customer.subscriptions.data).to be_empty diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index df2bb73e9..6736353ef 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,9 @@ require 'stripe' require 'stripe_mock' require 'stripe_mock/server' +require 'dotenv' + +Dotenv.load('.env') # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. diff --git a/spec/stripe_mock_spec.rb b/spec/stripe_mock_spec.rb index 93d91f868..0cc26258c 100644 --- a/spec/stripe_mock_spec.rb +++ b/spec/stripe_mock_spec.rb @@ -35,8 +35,8 @@ StripeMock.stop StripeMock.start - expect(StripeMock.instance.customers[:x]).to be_nil - expect(StripeMock.instance.customers.keys.length).to eq(0) + expect(StripeMock.instance.customers[''][:x]).to be_nil + expect(StripeMock.instance.customers[''].keys.length).to eq(0) StripeMock.stop end From c11a9ec4347f73d48c0a550310d80f2bc63cb9f9 Mon Sep 17 00:00:00 2001 From: Brent Date: Mon, 1 Mar 2021 16:00:56 -0500 Subject: [PATCH 028/158] Proper intervals for Prices * Prices have nested data containing interval/interval_count within `recurring` --- .../request_handlers/helpers/subscription_helpers.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 7ce8839f4..3ce4223e6 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -86,11 +86,13 @@ def delete_subscription_from_customer(cus, subscription) def get_ending_time(start_time, plan, intervals = 1) return start_time unless plan - case plan[:interval] + interval = plan[:interval] || plan.dig(:recurring, :interval) + interval_count = plan[:interval_count] || plan.dig(:recurring, :interval_count) || 1 + case interval when "week" - start_time + (604800 * (plan[:interval_count] || 1) * intervals) + start_time + (604800 * (interval_count) * intervals) when "month" - (Time.at(start_time).to_datetime >> ((plan[:interval_count] || 1) * intervals)).to_time.to_i + (Time.at(start_time).to_datetime >> ((interval_count) * intervals)).to_time.to_i when "year" (Time.at(start_time).to_datetime >> (12 * intervals)).to_time.to_i # max period is 1 year else From f291ba4e33cf72a6c7c4daab5b47b44b7511eca1 Mon Sep 17 00:00:00 2001 From: Chris Salvato Date: Wed, 3 Mar 2021 16:07:31 -0700 Subject: [PATCH 029/158] Bump to 3.1.0.rc2 --- CHANGELOG.md | 6 +++++- lib/stripe_mock/version.rb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d5471069..b477f6378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -### 3.0.2 (pre-release 2021-02-17) +### 3.1.0.rc2 (pre-release 2021-03-03) + +- [#767](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/767): Fixes tests and more [@lpsBetty](https://github.com/lpsBetty) + +### 3.1.0.rc1 (pre-release 2021-02-17) - [#765](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/765): Properly set the status of a trialing subscription. [@csalvato](https://github.com/csalvato) - [#764](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/764): Fixes erroneous error message when fetching upcoming invoices. [@csalvato](https://github.com/csalvato) diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index 237621662..b360f3c94 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0.rc1" + VERSION = "3.1.0.rc2" end From f84d9b509a372ef2b35c6830ea283f7f86feeb68 Mon Sep 17 00:00:00 2001 From: Yvonne Ng Date: Mon, 19 Apr 2021 14:07:46 -0400 Subject: [PATCH 030/158] Add balance transaction when creating transfer --- lib/stripe_mock/request_handlers/transfers.rb | 13 ++++++++++++- spec/shared_stripe_examples/transfer_examples.rb | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/transfers.rb b/lib/stripe_mock/request_handlers/transfers.rb index 6e04ea3ce..e7daa567e 100644 --- a/lib/stripe_mock/request_handlers/transfers.rb +++ b/lib/stripe_mock/request_handlers/transfers.rb @@ -45,7 +45,18 @@ def new_transfer(route, method_url, params, headers) raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400) end - transfers[id] = Data.mock_transfer(params.merge :id => id) + bal_trans_params = { amount: params[:amount].to_i, source: id } + + balance_transaction_id = new_balance_transaction('txn', bal_trans_params) + + transfers[id] = Data.mock_transfer(params.merge(id: id, balance_transaction: balance_transaction_id)) + + transfer = transfers[id].clone + if params[:expand] == ['balance_transaction'] + transfer[:balance_transaction] = balance_transactions[balance_transaction_id] + end + + transfer end def get_transfer(route, method_url, params, headers) diff --git a/spec/shared_stripe_examples/transfer_examples.rb b/spec/shared_stripe_examples/transfer_examples.rb index 56a87d088..444d33d51 100644 --- a/spec/shared_stripe_examples/transfer_examples.rb +++ b/spec/shared_stripe_examples/transfer_examples.rb @@ -9,7 +9,7 @@ expect(transfer.id).to match /^test_tr/ expect(transfer.amount).to eq(100) expect(transfer.amount_reversed).to eq(0) - expect(transfer.balance_transaction).to eq('txn_2dyYXXP90MN26R') + expect(transfer.balance_transaction).to eq('test_txn_1') expect(transfer.created).to eq(1304114826) expect(transfer.currency).to eq('usd') expect(transfer.description).to eq('Transfer description') @@ -30,6 +30,15 @@ expect(transfer.transfer_group).to eq("group_ch_164xRv2eZvKYlo2Clu1sIJWB") end + it "creates a balance transaction" do + destination = Stripe::Account.create(type: "custom", email: "#{SecureRandom.uuid}@example.com", id: "acct_12345", requested_capabilities: ['card_payments', 'platform_payments']) + transfer = Stripe::Transfer.create(amount: 100, currency: "usd", destination: destination.id) + + bal_trans = Stripe::BalanceTransaction.retrieve(transfer.balance_transaction) + expect(bal_trans.amount).to eq(100) + expect(bal_trans.source).to eq(transfer.id) + end + describe "listing transfers" do let(:destination) { Stripe::Account.create(type: "custom", email: "#{SecureRandom.uuid}@example.com", business_name: "MyCo") } From bd682c4cbb46b3a83553fd2a1715438796138216 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Mon, 3 May 2021 13:58:15 -0300 Subject: [PATCH 031/158] Fixes Subscription billing_cycle_anchor attribute to return a timestamp, when passing 'now' as parameter --- lib/stripe_mock/request_handlers/subscriptions.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 763db4223..7c90acfa9 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -162,6 +162,10 @@ def update_subscription(route, method_url, params, headers) stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url + if params[:billing_cycle_anchor] == 'now' + params[:billing_cycle_anchor] = Time.now.utc.to_i + end + subscription_id = $2 ? $2 : $1 subscription = assert_existence :subscription, subscription_id, subscriptions[subscription_id] verify_active_status(subscription) From fc6f981b4b1808ea5c57d69d32e1fb9f34d60279 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Mon, 3 May 2021 17:43:11 -0300 Subject: [PATCH 032/158] Create dependabot.yml --- .github/dependabot.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..511a205ad --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: +- package-ecosystem: bundler + directory: "/" + schedule: + interval: daily + time: "11:00" + open-pull-requests-limit: 10 From c9d6f38dcba877825447d226666d55914fdfdf2f Mon Sep 17 00:00:00 2001 From: Guilherme Goncalves Date: Thu, 27 May 2021 18:28:52 -0300 Subject: [PATCH 033/158] Allows filtering prices by currency and product This commit implements the `currency` and `product` filters in the prices list, since they are supported by the Stripe API. See https://stripe.com/docs/api/prices/list --- lib/stripe_mock/request_handlers/prices.rb | 12 +++++ spec/shared_stripe_examples/price_examples.rb | 44 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/prices.rb b/lib/stripe_mock/request_handlers/prices.rb index c5bd52dba..58ea3c9df 100644 --- a/lib/stripe_mock/request_handlers/prices.rb +++ b/lib/stripe_mock/request_handlers/prices.rb @@ -37,6 +37,18 @@ def list_prices(route, method_url, params, headers) end end + if params.key?(:currency) + price_data.select! do |price| + params[:currency] == price[:currency] + end + end + + if params.key?(:product) + price_data.select! do |price| + params[:product] == price[:product] + end + end + Data.mock_list_object(price_data.first(limit), params.merge!(limit: limit)) end end diff --git a/spec/shared_stripe_examples/price_examples.rb b/spec/shared_stripe_examples/price_examples.rb index 7138aa923..efb17a629 100644 --- a/spec/shared_stripe_examples/price_examples.rb +++ b/spec/shared_stripe_examples/price_examples.rb @@ -1,8 +1,11 @@ require 'spec_helper' shared_examples 'Price API' do - let(:product) { stripe_helper.create_product } - let(:product_id) { product.id } + let(:product_id) { "product_id_1" } + let(:product) { stripe_helper.create_product(id: product_id) } + + let(:other_product_id) { "product_id_2" } + let(:other_product) { stripe_helper.create_product(id: other_product_id) } let(:price_attributes) { { :id => "price_abc123", @@ -26,6 +29,7 @@ before(:each) do product + other_product end it "creates a stripe price" do @@ -125,6 +129,42 @@ expect(two.map &:amount).to include(98765) end + it "retrieves prices filtering by currency" do + 5.times do | i| + stripe_helper.create_price(id: "usd price #{i}", product: product_id, amount: 11, currency: 'usd') + stripe_helper.create_price(id: "brl price #{i}", product: product_id, amount: 11, currency: 'brl') + end + + all = Stripe::Price.list() + expect(all.count).to eq(10) + + usd = Stripe::Price.list({currency: 'usd'}) + expect(usd.count).to eq(5) + expect(usd.all? {|p| p.currency == 'usd' }).to be_truthy + + brl = Stripe::Price.list({currency: 'brl'}) + expect(brl.count).to eq(5) + expect(brl.all? {|p| p.currency == 'brl' }).to be_truthy + end + + it "retrieves prices filtering by product" do + 5.times do | i| + stripe_helper.create_price(id: "product 1 price #{i}", product: product_id) + stripe_helper.create_price(id: "product 2 price #{i}", product: other_product_id) + end + + all = Stripe::Price.list() + expect(all.count).to eq(10) + + product_prices = Stripe::Price.list({product: product.id}) + expect(product_prices.count).to eq(5) + expect(product_prices.all? {|p| p.product == product.id }).to be_truthy + + other_product_prices = Stripe::Price.list({product: other_product.id}) + expect(other_product_prices.count).to eq(5) + expect(other_product_prices.all? {|p| p.product == other_product.id }).to be_truthy + end + describe "Validations", :live => true do include_context "stripe validator" let(:params) { stripe_helper.create_price_params(product: product_id) } From 7eb1cc949ed55e39dd85a8f2f0b07f1fe41b7385 Mon Sep 17 00:00:00 2001 From: Luke Rodgers Date: Thu, 17 Jun 2021 17:00:12 -0400 Subject: [PATCH 034/158] Add support for discount IDs Stripe API returns an ID like "di_abc123" for discount objects. --- lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb | 1 + spec/shared_stripe_examples/subscription_examples.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb b/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb index 0e5fcb9d2..e8118057c 100644 --- a/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb @@ -7,6 +7,7 @@ def add_coupon_to_object(object, coupon) attrs[:coupon] = coupon attrs[:start] = Time.now.to_i attrs[:end] = (DateTime.now >> coupon[:duration_in_months].to_i).to_time.to_i if coupon[:duration] == 'repeating' + attrs[:id] = new_id("di") end object[:discount] = Stripe::Discount.construct_from(discount_attrs) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 60db89596..41da74403 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -179,6 +179,7 @@ expect(subscriptions.data).to be_a(Array) expect(subscriptions.data.count).to eq(1) expect(subscriptions.data.first.discount).not_to be_nil + expect(subscriptions.data.first.discount.id).not_to be_nil expect(subscriptions.data.first.discount).to be_a(Stripe::Discount) expect(subscriptions.data.first.discount.coupon.id).to eq(coupon.id) end From 0095865db75b11a42f7021068cb352f1b702beca Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 13 Jul 2021 17:34:50 +0100 Subject: [PATCH 035/158] Pass `**kwargs` through to `mock_request` in `StripeMock::Server` This fixes an error where using the `StripeMock::Server` failed with an `ArgumentError`. --- lib/stripe_mock/server.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/server.rb b/lib/stripe_mock/server.rb index 239e4c672..6f2cda2e4 100644 --- a/lib/stripe_mock/server.rb +++ b/lib/stripe_mock/server.rb @@ -16,9 +16,9 @@ def initialize self.clear_data end - def mock_request(*args) + def mock_request(*args, **kwargs) begin - @instance.mock_request(*args) + @instance.mock_request(*args, **kwargs) rescue Stripe::InvalidRequestError => e { :error_raised => 'invalid_request', From 4e1759d6f12aa59ee05cd2295b9f03e0718acccd Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 13 Jul 2021 17:33:20 +0100 Subject: [PATCH 036/158] Stop requiring `type` to be set for `Stripe::Product` It is no longer part of Stripe's API. --- .../request_handlers/validators/param_validators.rb | 4 ---- lib/stripe_mock/test_strategies/base.rb | 1 - spec/shared_stripe_examples/product_examples.rb | 10 +--------- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/lib/stripe_mock/request_handlers/validators/param_validators.rb b/lib/stripe_mock/request_handlers/validators/param_validators.rb index 5134c0d8d..e9bf4dc5e 100644 --- a/lib/stripe_mock/request_handlers/validators/param_validators.rb +++ b/lib/stripe_mock/request_handlers/validators/param_validators.rb @@ -29,10 +29,6 @@ def validate_create_product_params(params) raise Stripe::InvalidRequestError.new(missing_param_message(k), k) if params[k].nil? end - if !%w[good service].include?(params[:type]) - raise Stripe::InvalidRequestError.new("Invalid type: must be one of good or service", :type) - end - if products[ params[:id] ] raise Stripe::InvalidRequestError.new(already_exists_message(Stripe::Product), :id) end diff --git a/lib/stripe_mock/test_strategies/base.rb b/lib/stripe_mock/test_strategies/base.rb index eb50420bf..f775667ed 100644 --- a/lib/stripe_mock/test_strategies/base.rb +++ b/lib/stripe_mock/test_strategies/base.rb @@ -14,7 +14,6 @@ def create_product_params(params={}) { :id => 'stripe_mock_default_product_id', :name => 'Default Product', - :type => 'service' }.merge(params) end diff --git a/spec/shared_stripe_examples/product_examples.rb b/spec/shared_stripe_examples/product_examples.rb index 7f7082ebe..05fe7574e 100644 --- a/spec/shared_stripe_examples/product_examples.rb +++ b/spec/shared_stripe_examples/product_examples.rb @@ -1,7 +1,7 @@ require "spec_helper" shared_examples "Product API" do - let(:product_attributes) { {id: "prod_123", name: "My Mock Product", type: "service"} } + let(:product_attributes) { {id: "prod_123", name: "My Mock Product"} } let(:product) { Stripe::Product.create(product_attributes) } it "creates a stripe product" do @@ -101,14 +101,6 @@ it("requires a name") { @attribute_name = :name } end - describe "Inclusion" do - it "validates inclusion of type in 'good' or 'service'" do - expect { - Stripe::Product.create(params.merge({type: "OOPS"})) - }.to raise_error(Stripe::InvalidRequestError, "Invalid type: must be one of good or service") - end - end - describe "Uniqueness" do let(:already_exists_message){ stripe_validator.already_exists_message(Stripe::Product) } From 953303e18a8122445ec536d540094f5249da4d84 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 12 Jul 2021 10:37:04 +0100 Subject: [PATCH 037/158] Support expanding `setup_intent` in `Stripe::Checkout::Session` --- lib/stripe_mock/request_handlers/checkout_session.rb | 8 +++++++- spec/shared_stripe_examples/checkout_examples.rb | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 87e41665e..b0b836e40 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -8,7 +8,13 @@ def Session.included(klass) def get_checkout_session(route, method_url, params, headers) route =~ method_url - assert_existence :checkout_session, $1, checkout_sessions[$1] + checkout_session = assert_existence :checkout_session, $1, checkout_sessions[$1] + + checkout_session = checkout_session.clone + if params[:expand]&.include?('setup_intent') && checkout_session[:setup_intent] + checkout_session[:setup_intent] = setup_intents[checkout_session[:setup_intent]] + end + checkout_session end end end diff --git a/spec/shared_stripe_examples/checkout_examples.rb b/spec/shared_stripe_examples/checkout_examples.rb index 258428311..fd99c9e6c 100644 --- a/spec/shared_stripe_examples/checkout_examples.rb +++ b/spec/shared_stripe_examples/checkout_examples.rb @@ -34,5 +34,14 @@ expect(e.http_status).to eq(404) } end + + it 'can expand setup_intent' do + setup_intent = Stripe::SetupIntent.create + initial_session = Stripe::Checkout::Session.create(setup_intent: setup_intent.id) + + checkout_session = Stripe::Checkout::Session.retrieve(id: initial_session.id, expand: ['setup_intent']) + + expect(checkout_session.setup_intent).to eq(setup_intent) + end end end From 7e620d5caffd9e6084b3cc260aa00a4b3624c450 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Wed, 14 Jul 2021 13:46:02 +0100 Subject: [PATCH 038/158] Update changelog bump version to 3.1.0.rc3 --- CHANGELOG.md | 6 ++++++ lib/stripe_mock/version.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b477f6378..50b15afaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 3.1.0.rc3 (pre-release 2021-07-14) + +- [#785](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/785): `Stripe::Product` no longer requires `type`. [@TastyPi](https://github.com/TastyPi) +- [#784](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/784): Fix "Wrong number of arguments" error in tests. [@TastyPi](https://github.com/TastyPi) +- [#782](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/782): Support expanding `setup_intent` in `Stripe::Checkout::Session`. [@TastyPi](https://github.com/TastyPi) + ### 3.1.0.rc2 (pre-release 2021-03-03) - [#767](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/767): Fixes tests and more [@lpsBetty](https://github.com/lpsBetty) diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index b360f3c94..a1fd0ea92 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0.rc2" + VERSION = "3.1.0.rc3" end From 20905e607749190f5f4dce081619009ba5cc585a Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Fri, 16 Jul 2021 11:59:57 +0100 Subject: [PATCH 039/158] Set `price` in `SubscriptionItem` if provided Fixes #781 --- lib/stripe_mock/data.rb | 3 +-- .../request_handlers/helpers/subscription_helpers.rb | 8 ++++++-- spec/shared_stripe_examples/subscription_examples.rb | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 2884dfa0d..df79339a7 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1144,8 +1144,7 @@ def self.mock_subscription_item(params = {}) id: id, object: 'subscription_item', created: 1504716183, - metadata: { - }, + metadata: {}, plan: { id: 'PER_USER_PLAN1', object: 'plan', diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 6234977e1..b88c0b925 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -15,9 +15,13 @@ def resolve_subscription_changes(subscription, plans, customer, options = {}) if matching_item quantity = matching_item[:quantity] || 1 id = matching_item[:id] || new_id('si') - Data.mock_subscription_item({ plan: plan, quantity: quantity, id: id }) + params = { plan: plan, quantity: quantity, id: id } + params[:price] = plan if plan[:object] == "price" + Data.mock_subscription_item(params) else - Data.mock_subscription_item({ plan: plan, id: new_id('si') }) + params = { plan: plan, id: new_id('si') } + params[:price] = plan if plan[:object] == "price" + Data.mock_subscription_item(params) end end subscription diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 60db89596..c77707f08 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1237,6 +1237,7 @@ expect(subscription.object).to eq('subscription') expect(subscription.plan.to_hash).to eq(price.to_hash) + expect(subscription.items.first.price.to_hash).to eq(price.to_hash) expect(subscription.metadata.foo).to eq("bar") expect(subscription.metadata.example).to eq("yes") @@ -1252,6 +1253,7 @@ expect(subscriptions.data.first.id).to eq(subscription.id) expect(subscriptions.data.first.plan.to_hash).to eq(price.to_hash) + expect(subscriptions.data.first.items.first.price.to_hash).to eq(price.to_hash) expect(subscriptions.data.first.customer).to eq(customer.id) expect(subscriptions.data.first.metadata.foo).to eq( "bar" ) expect(subscriptions.data.first.metadata.example).to eq( "yes" ) From 668909c434fc6d41e27cc98996b2dfab5b00a99d Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 26 Jul 2021 17:52:45 +0100 Subject: [PATCH 040/158] Improve the `Checkout::Session` API Now it creates a `PaymentIntent` or `SetupIntent` when using the `payment` or `setup` mode respectively. Also moved the creation code into `RequestHandlers::Checkout::Session` for consistency. --- lib/stripe_mock.rb | 1 - lib/stripe_mock/request_handlers/checkout.rb | 15 ---- .../request_handlers/checkout_session.rb | 79 +++++++++++++++++++ lib/stripe_mock/test_strategies/base.rb | 16 ++-- .../checkout_examples.rb | 47 ----------- .../checkout_session_examples.rb | 68 ++++++++++++++++ spec/support/stripe_examples.rb | 2 +- 7 files changed, 157 insertions(+), 71 deletions(-) delete mode 100644 lib/stripe_mock/request_handlers/checkout.rb delete mode 100644 spec/shared_stripe_examples/checkout_examples.rb create mode 100644 spec/shared_stripe_examples/checkout_session_examples.rb diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index f1e7ebc98..ce3a6a7ca 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -79,7 +79,6 @@ require 'stripe_mock/request_handlers/ephemeral_key.rb' require 'stripe_mock/request_handlers/products.rb' require 'stripe_mock/request_handlers/tax_rates.rb' -require 'stripe_mock/request_handlers/checkout.rb' require 'stripe_mock/request_handlers/checkout_session.rb' require 'stripe_mock/instance' diff --git a/lib/stripe_mock/request_handlers/checkout.rb b/lib/stripe_mock/request_handlers/checkout.rb deleted file mode 100644 index 93627961e..000000000 --- a/lib/stripe_mock/request_handlers/checkout.rb +++ /dev/null @@ -1,15 +0,0 @@ -module StripeMock - module RequestHandlers - module Checkout - def Checkout.included(klass) - klass.add_handler 'post /v1/checkout/sessions', :new_session - end - - def new_session(route, method_url, params, headers) - params[:id] ||= new_id('cs') - - checkout_sessions[params[:id]] = Data.mock_checkout_session(params) - end - end - end -end \ No newline at end of file diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index b0b836e40..46aad83ac 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -3,9 +3,88 @@ module RequestHandlers module Checkout module Session def Session.included(klass) + klass.add_handler 'post /v1/checkout/sessions', :new_session klass.add_handler 'get /v1/checkout/sessions/(.*)', :get_checkout_session end + def new_session(route, method_url, params, headers) + id = params[:id] || new_id('cs') + + [:cancel_url, :payment_method_types, :success_url].each do |p| + require_param(p) if params[p].nil? || params[p].empty? + end + + amount = params[:line_items]&.map { |line_item| line_item[:amount] }&.sum + currency = params[:line_items]&.first&.[](:currency) + + payment_status = "unpaid" + payment_intent = nil + setup_intent = nil + case params[:mode] + when nil, "payment" + require_params(:line_items) if params[:line_items].nil? || params[:line_items].empty? + payment_intent = new_payment_intent(nil, nil, { + amount: amount, + currency: currency, + customer: params[:customer], + line_items: params[:line_items], + payment_method_options: params[:payment_method_options], + payment_method_types: params[:payment_method_types] + }.merge(params[:payment_intent_data] || {}), nil)[:id] + when "setup" + if !params[:line_items].nil? && !params[:line_items].empty? + throw Stripe::InvalidRequestError.new("You cannot pass `line_items` in `setup` mode", :line_items, http_status: 400) + end + setup_intent = new_setup_intent(nil, nil, { + customer: params[:customer], + payment_method_options: params[:payment_method_options], + payment_method_types: params[:payment_method_types] + }.merge(params[:setup_intent_data] || {}), nil)[:id] + payment_status = "no_payment_required" + when "subscription" + require_params(:line_items) if line_items.nil? || line_items.empty? + # TODO: Stripe does not create the Subscription when creating the Session, add support for a way to create + # a subscription using this session. + else + throw Stripe::InvalidRequestError.new("Invalid mode: must be one of payment, setup, or subscription", :mode, http_status: 400) + end + + checkout_sessions[id] = { + id: id, + object: "checkout.session", + allow_promotion_codes: nil, + amount_subtotal: amount, + amount_total: amount, + automatic_tax: { + enabled: false, + status: nil + }, + billing_address_collection: nil, + cancel_url: "http://example.com/checkout/cancel?session_id={CHECKOUT_SESSION_ID}", + client_reference_id: nil, + currency: currency, + customer: params[:customer], + customer_details: nil, + customer_email: nil, + livemode: false, + locale: nil, + metadata: {}, + mode: "setup", + payment_intent: payment_intent, + payment_method_options: params[:payment_method_options], + payment_method_types: params[:payment_method_types], + payment_status: payment_status, + setup_intent: setup_intent, + shipping: nil, + shipping_address_collection: nil, + submit_type: nil, + subscription: nil, + success_url: "http://example.com/checkout/success?session_id={CHECKOUT_SESSION_ID}", + total_details: nil, + url: "https://checkout.stripe.com/pay/#{id}" + } + end + def get_checkout_session(route, method_url, params, headers) route =~ method_url checkout_session = assert_existence :checkout_session, $1, checkout_sessions[$1] diff --git a/lib/stripe_mock/test_strategies/base.rb b/lib/stripe_mock/test_strategies/base.rb index f775667ed..11ab68ca2 100644 --- a/lib/stripe_mock/test_strategies/base.rb +++ b/lib/stripe_mock/test_strategies/base.rb @@ -107,13 +107,15 @@ def create_coupon_percent_of_params(params = {}) def create_checkout_session_params(params = {}) { - payment_method_types: ['card'], - line_items: [{ - name: 'T-shirt', - quantity: 1, - amount: 500, - currency: 'usd', - }], + payment_method_types: ['card'], + line_items: [{ + name: 'T-shirt', + quantity: 1, + amount: 500, + currency: 'usd', + }], + cancel_url: "https://example.com/cancel", + success_url: "https://example.com/success", }.merge(params) end diff --git a/spec/shared_stripe_examples/checkout_examples.rb b/spec/shared_stripe_examples/checkout_examples.rb deleted file mode 100644 index fd99c9e6c..000000000 --- a/spec/shared_stripe_examples/checkout_examples.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'spec_helper' - -shared_examples 'Checkout API' do - - it "creates a stripe checkout session" do - session = Stripe::Checkout::Session.create({ - payment_method_types: ['card'], - line_items: [{ - name: 'T-shirt', - quantity: 1, - amount: 500, - currency: 'usd', - }], - }) - expect(session.id).to match(/^test_cs/) - expect(session.line_items.count).to eq(1) - end - - context 'retrieve a checkout session' do - let(:checkout_session1) { stripe_helper.create_checkout_session } - - it 'ca be retrieved by id' do - checkout_session1 - - checkout_session = Stripe::Checkout::Session.retrieve(checkout_session1.id) - - expect(checkout_session.id).to eq(checkout_session1.id) - end - - it "cannot retrieve a checkout session that doesn't exist" do - expect { Stripe::Checkout::Session.retrieve('nope') }.to raise_error {|e| - expect(e).to be_a Stripe::InvalidRequestError - expect(e.param).to eq('checkout_session') - expect(e.http_status).to eq(404) - } - end - - it 'can expand setup_intent' do - setup_intent = Stripe::SetupIntent.create - initial_session = Stripe::Checkout::Session.create(setup_intent: setup_intent.id) - - checkout_session = Stripe::Checkout::Session.retrieve(id: initial_session.id, expand: ['setup_intent']) - - expect(checkout_session.setup_intent).to eq(setup_intent) - end - end -end diff --git a/spec/shared_stripe_examples/checkout_session_examples.rb b/spec/shared_stripe_examples/checkout_session_examples.rb new file mode 100644 index 000000000..114fcc3f8 --- /dev/null +++ b/spec/shared_stripe_examples/checkout_session_examples.rb @@ -0,0 +1,68 @@ +require "spec_helper" + +shared_examples "Checkout Session API" do + it "creates PaymentIntent with payment mode" do + line_items = [{ + name: "T-shirt", + quantity: 1, + amount: 500, + currency: "usd", + }] + session = Stripe::Checkout::Session.create( + payment_method_types: ["card"], + line_items: line_items, + cancel_url: "https://example.com/cancel", + success_url: "https://example.com/success" + ) + + expect(session.payment_intent).to_not be_empty + payment_intent = Stripe::PaymentIntent.retrieve(session.payment_intent) + expect(payment_intent.line_items.map(&:to_hash)).to eq(line_items) + end + + it "creates SetupIntent with setup mode" do + session = Stripe::Checkout::Session.create( + mode: "setup", + payment_method_types: ["card"], + cancel_url: "https://example.com/cancel", + success_url: "https://example.com/success" + ) + + expect(session.setup_intent).to_not be_empty + setup_intent = Stripe::SetupIntent.retrieve(session.setup_intent) + expect(setup_intent.payment_method_types).to eq(["card"]) + end + + context "retrieve a checkout session" do + let(:checkout_session1) { stripe_helper.create_checkout_session } + + it "can be retrieved by id" do + checkout_session1 + + checkout_session = Stripe::Checkout::Session.retrieve(checkout_session1.id) + + expect(checkout_session.id).to eq(checkout_session1.id) + end + + it "cannot retrieve a checkout session that doesn't exist" do + expect { Stripe::Checkout::Session.retrieve("nope") }.to raise_error { |e| + expect(e).to be_a Stripe::InvalidRequestError + expect(e.param).to eq("checkout_session") + expect(e.http_status).to eq(404) + } + end + + it "can expand setup_intent" do + initial_session = Stripe::Checkout::Session.create( + mode: "setup", + cancel_url: "https://example.com", + success_url: "https://example.com", + payment_method_types: ["card"] + ) + + checkout_session = Stripe::Checkout::Session.retrieve(id: initial_session.id, expand: ["setup_intent"]) + + expect(checkout_session.setup_intent).to be_a_kind_of(Stripe::SetupIntent) + end + end +end diff --git a/spec/support/stripe_examples.rb b/spec/support/stripe_examples.rb index 57ba7eba1..8f2561bee 100644 --- a/spec/support/stripe_examples.rb +++ b/spec/support/stripe_examples.rb @@ -39,7 +39,7 @@ def it_behaves_like_stripe(&block) it_behaves_like 'Country Spec API', &block it_behaves_like 'EphemeralKey API', &block it_behaves_like 'TaxRate API', &block - it_behaves_like 'Checkout API', &block + it_behaves_like 'Checkout Session API', &block # Integration tests it_behaves_like 'Multiple Customer Cards' From b45913759a16f30a1f4abda21ed05d070edeafc8 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Fri, 30 Jul 2021 17:17:41 +0100 Subject: [PATCH 041/158] Add support for finalising Checkout Sessions Includes improved support for a couple of other objects and fixes a couple of bugs in the new Checkout Session impl. --- lib/stripe_mock/instance.rb | 3 +- .../request_handlers/checkout_session.rb | 73 +++++++++++++++++-- .../request_handlers/payment_intents.rb | 4 + lib/stripe_mock/request_handlers/prices.rb | 6 ++ lib/stripe_mock/test_strategies/base.rb | 51 +++++++++---- .../completing_checkout_sessions_example.rb | 37 ++++++++++ .../checkout_session_examples.rb | 6 +- spec/support/stripe_examples.rb | 1 + 8 files changed, 154 insertions(+), 27 deletions(-) create mode 100644 spec/integration_examples/completing_checkout_sessions_example.rb diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index a113c5946..b1ad87d0a 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -58,7 +58,7 @@ def self.handler_for_method_url(method_url) attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers, :disputes, :events, :invoices, :invoice_items, :orders, :payment_intents, :payment_methods, :setup_intents, :plans, :prices, :recipients, :refunds, :transfers, :payouts, :subscriptions, :country_spec, - :subscriptions_items, :products, :tax_rates, :checkout_sessions + :subscriptions_items, :products, :tax_rates, :checkout_sessions, :checkout_session_line_items attr_accessor :error_queue, :debug, :conversion_rate, :account_balance @@ -92,6 +92,7 @@ def initialize @country_spec = {} @tax_rates = {} @checkout_sessions = {} + @checkout_session_line_items = {} @debug = false @error_queue = ErrorQueue.new diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 46aad83ac..b5ff5d6c9 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -4,7 +4,8 @@ module Checkout module Session def Session.included(klass) klass.add_handler 'post /v1/checkout/sessions', :new_session - klass.add_handler 'get /v1/checkout/sessions/(.*)', :get_checkout_session + klass.add_handler 'get /v1/checkout/sessions/([^/]*)', :get_checkout_session + klass.add_handler 'get /v1/checkout/sessions/([^/]*)/line_items', :list_line_items end def new_session(route, method_url, params, headers) @@ -14,23 +15,55 @@ def new_session(route, method_url, params, headers) require_param(p) if params[p].nil? || params[p].empty? end - amount = params[:line_items]&.map { |line_item| line_item[:amount] }&.sum - currency = params[:line_items]&.first&.[](:currency) + line_items = nil + if params[:line_items] + line_items = params[:line_items].each_with_index.map do |line_item, i| + throw Stripe::InvalidRequestError("Quantity is required. Add `quantity` to `line_items[#{i}]`") unless line_item[:quantity] + unless line_item[:price] || line_item[:price_data] || (line_item[:amount] && line_item[:currency] && line_item[:name]) + throw Stripe::InvalidRequestError("Price or amount and currency is required. Add `price`, `price_data`, or `amount`, `currency` and `name` to `line_items[#{i}]`") + end + { + id: new_id("li"), + price: if line_item[:price] + line_item[:price] + elsif line_item[:price_data] + new_price(nil, nil, line_item[:price_data], nil)[:id] + else + new_price(nil, nil, { + unit_amount: line_item[:amount], + currency: line_item[:currency], + product_data: { + name: line_item[:name] + } + }, nil)[:id] + end, + quantity: line_item[:quantity] + } + end + end + + amount = nil + currency = nil + if line_items + amount = line_items.map { |line_item| prices[line_item[:price]][:unit_amount] * line_item[:quantity] }.sum + currency = prices[line_items.first[:price]][:currency] + end payment_status = "unpaid" payment_intent = nil setup_intent = nil case params[:mode] when nil, "payment" + params[:customer] ||= new_customer(nil, nil, {email: params[:customer_email]}, nil)[:id] require_params(:line_items) if params[:line_items].nil? || params[:line_items].empty? payment_intent = new_payment_intent(nil, nil, { amount: amount, currency: currency, customer: params[:customer], - line_items: params[:line_items], payment_method_options: params[:payment_method_options], payment_method_types: params[:payment_method_types] }.merge(params[:payment_intent_data] || {}), nil)[:id] + checkout_session_line_items[id] = line_items when "setup" if !params[:line_items].nil? && !params[:line_items].empty? throw Stripe::InvalidRequestError.new("You cannot pass `line_items` in `setup` mode", :line_items, http_status: 400) @@ -42,9 +75,9 @@ def new_session(route, method_url, params, headers) }.merge(params[:setup_intent_data] || {}), nil)[:id] payment_status = "no_payment_required" when "subscription" - require_params(:line_items) if line_items.nil? || line_items.empty? - # TODO: Stripe does not create the Subscription when creating the Session, add support for a way to create - # a subscription using this session. + params[:customer] ||= new_customer(nil, nil, {email: params[:customer_email]}, nil)[:id] + require_params(:line_items) if params[:line_items].nil? || params[:line_items].empty? + checkout_session_line_items[id] = line_items else throw Stripe::InvalidRequestError.new("Invalid mode: must be one of payment, setup, or subscription", :mode, http_status: 400) end @@ -69,7 +102,7 @@ def new_session(route, method_url, params, headers) livemode: false, locale: nil, metadata: {}, - mode: "setup", + mode: params[:mode], payment_intent: payment_intent, payment_method_options: params[:payment_method_options], payment_method_types: params[:payment_method_types], @@ -95,6 +128,30 @@ def get_checkout_session(route, method_url, params, headers) end checkout_session end + + def list_line_items(route, method_url, params, headers) + route =~ method_url + checkout_session = assert_existence :checkout_session, $1, checkout_sessions[$1] + + case checkout_session[:mode] + when "payment", "subscription" + line_items = assert_existence :checkout_session_line_items, $1, checkout_session_line_items[$1] + line_items.map do |line_item| + price = prices[line_item[:price]].clone + { + id: line_item[:id], + object: "item", + amount_subtotal: price[:unit_amount] * line_item[:quantity], + amount_total: price[:unit_amount] * line_item[:quantity], + currency: price[:currency], + price: price.clone, + quantity: line_item[:quantity] + } + end + else + throw Stripe::InvalidRequestError("Only payment and subscription sessions have line items") + end + end end end end diff --git a/lib/stripe_mock/request_handlers/payment_intents.rb b/lib/stripe_mock/request_handlers/payment_intents.rb index 3309e4879..d01b42aa9 100644 --- a/lib/stripe_mock/request_handlers/payment_intents.rb +++ b/lib/stripe_mock/request_handlers/payment_intents.rb @@ -81,6 +81,10 @@ def confirm_payment_intent(route, method_url, params, headers) route =~ method_url payment_intent = assert_existence :payment_intent, $1, payment_intents[$1] + if params[:payment_method] + payment_intent[:payment_method] = params[:payment_method] + end + succeeded_payment_intent(payment_intent) end diff --git a/lib/stripe_mock/request_handlers/prices.rb b/lib/stripe_mock/request_handlers/prices.rb index c5bd52dba..46146a4ce 100644 --- a/lib/stripe_mock/request_handlers/prices.rb +++ b/lib/stripe_mock/request_handlers/prices.rb @@ -11,6 +11,12 @@ def Prices.included(klass) def new_price(route, method_url, params, headers) params[:id] ||= new_id('price') + + if params[:product_data] + params[:product] = create_product(nil, nil, params[:product_data], nil)[:id] unless params[:product] + params.delete(:product_data) + end + validate_create_price_params(params) prices[ params[:id] ] = Data.mock_price(params) end diff --git a/lib/stripe_mock/test_strategies/base.rb b/lib/stripe_mock/test_strategies/base.rb index 11ab68ca2..8c425444d 100644 --- a/lib/stripe_mock/test_strategies/base.rb +++ b/lib/stripe_mock/test_strategies/base.rb @@ -10,7 +10,7 @@ def create_product(params = {}) Stripe::Product.create create_product_params(params) end - def create_product_params(params={}) + def create_product_params(params = {}) { :id => 'stripe_mock_default_product_id', :name => 'Default Product', @@ -21,16 +21,15 @@ def retrieve_product(product_id) Stripe::Product.retrieve(product_id) end - def list_plans(limit) Stripe::Plan.list(limit: limit) end - def create_plan(params={}) + def create_plan(params = {}) Stripe::Plan.create create_plan_params(params) end - def create_plan_params(params={}) + def create_plan_params(params = {}) { :id => 'stripe_mock_default_plan_id', :interval => 'month', @@ -40,11 +39,11 @@ def create_plan_params(params={}) }.merge(params) end - def create_price(params={}) + def create_price(params = {}) Stripe::Price.create create_price_params(params) end - def create_price_params(params={}) + def create_price_params(params = {}) { :currency => StripeMock.default_currency, }.merge(params) @@ -54,9 +53,8 @@ def list_subscriptions(limit) Stripe::Subscription.list(limit: limit) end - - def generate_card_token(card_params={}) - card_data = { :number => "4242424242424242", :exp_month => 9, :exp_year => (Time.now.year + 5), :cvc => "999", :tokenization_method => nil } + def generate_card_token(card_params = {}) + card_data = {:number => "4242424242424242", :exp_month => 9, :exp_year => (Time.now.year + 5), :cvc => "999", :tokenization_method => nil} card = StripeMock::Util.card_merge(card_data, card_params) card[:fingerprint] = StripeMock::Util.fingerprint(card[:number]) if StripeMock.state == 'local' @@ -64,7 +62,7 @@ def generate_card_token(card_params={}) stripe_token.id end - def generate_bank_token(bank_account_params={}) + def generate_bank_token(bank_account_params = {}) currency = bank_account_params[:currency] || StripeMock.default_currency bank_account = { :country => "US", @@ -80,7 +78,6 @@ def generate_bank_token(bank_account_params={}) stripe_token.id end - def create_coupon_params(params = {}) currency = params[:currency] || StripeMock.default_currency { @@ -105,10 +102,14 @@ def create_coupon_percent_of_params(params = {}) }.merge(params) end + def create_checkout_session(params = {}) + Stripe::Checkout::Session.create create_checkout_session_params(params) + end + def create_checkout_session_params(params = {}) { payment_method_types: ['card'], - line_items: [{ + line_items: params[:mode] == "setup" ? nil : [{ name: 'T-shirt', quantity: 1, amount: 500, @@ -119,15 +120,33 @@ def create_checkout_session_params(params = {}) }.merge(params) end + def complete_checkout_session(session, payment_method) + session = session.is_a?(Stripe::Checkout::Session) ? session : Stripe::Checkout::Session.retrieve(session) + payment_method = payment_method.is_a?(Stripe::PaymentMethod) ? payment_method : Stripe::PaymentMethod.retrieve(payment_method) + case session.mode + when "payment" + Stripe::PaymentIntent.retrieve(session.payment_intent).confirm(payment_method: payment_method.id) + when "setup" + Stripe::SetupIntent.update(session.setup_intent, {payment_method: payment_method.id}) + when "subscription" + line_items = Stripe::Checkout::Session.list_line_items(session.id) + Stripe::Subscription.create({ + customer: session.customer, + items: line_items.map do |line_item| + { + price: line_item.price.id, + quantity: line_item.quantity + } + end, + default_payment_method: payment_method.id + }) + end + end def create_coupon(params = {}) Stripe::Coupon.create create_coupon_params(params) end - def create_checkout_session(params = {}) - Stripe::Checkout::Session.create create_checkout_session_params(params) - end - def delete_all_coupons coupons = Stripe::Coupon.list coupons.data.map(&:delete) if coupons.data.count > 0 diff --git a/spec/integration_examples/completing_checkout_sessions_example.rb b/spec/integration_examples/completing_checkout_sessions_example.rb new file mode 100644 index 000000000..615bc26ba --- /dev/null +++ b/spec/integration_examples/completing_checkout_sessions_example.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +shared_examples "Completing Checkout Sessions" do + let(:test_helper) { StripeMock.create_test_helper } + before { StripeMock.start } + after { StripeMock.stop } + + it "can complete payment checkout sessions" do + session = test_helper.create_checkout_session(mode: "payment") + payment_method = Stripe::PaymentMethod.create(type: "card") + + payment_intent = test_helper.complete_checkout_session(session, payment_method) + + expect(payment_intent.id).to eq(session.payment_intent) + expect(payment_intent.payment_method).to eq(payment_method.id) + expect(payment_intent.status).to eq("succeeded") + end + + it "can complete setup checkout sessions" do + session = test_helper.create_checkout_session(mode: "setup") + payment_method = Stripe::PaymentMethod.create(type: "card") + + setup_intent = test_helper.complete_checkout_session(session, payment_method) + + expect(setup_intent.id).to eq(session.setup_intent) + expect(setup_intent.payment_method).to eq(payment_method.id) + end + + it "can complete subscription checkout sessions" do + session = test_helper.create_checkout_session(mode: "subscription") + payment_method = Stripe::PaymentMethod.create(type: "card") + + subscription = test_helper.complete_checkout_session(session, payment_method) + + expect(subscription.default_payment_method).to eq(payment_method.id) + end +end diff --git a/spec/shared_stripe_examples/checkout_session_examples.rb b/spec/shared_stripe_examples/checkout_session_examples.rb index 114fcc3f8..58329911d 100644 --- a/spec/shared_stripe_examples/checkout_session_examples.rb +++ b/spec/shared_stripe_examples/checkout_session_examples.rb @@ -4,7 +4,7 @@ it "creates PaymentIntent with payment mode" do line_items = [{ name: "T-shirt", - quantity: 1, + quantity: 2, amount: 500, currency: "usd", }] @@ -17,7 +17,9 @@ expect(session.payment_intent).to_not be_empty payment_intent = Stripe::PaymentIntent.retrieve(session.payment_intent) - expect(payment_intent.line_items.map(&:to_hash)).to eq(line_items) + expect(payment_intent.amount).to eq(1000) + expect(payment_intent.currency).to eq("usd") + expect(payment_intent.customer).to eq(session.customer) end it "creates SetupIntent with setup mode" do diff --git a/spec/support/stripe_examples.rb b/spec/support/stripe_examples.rb index 8f2561bee..931ffe146 100644 --- a/spec/support/stripe_examples.rb +++ b/spec/support/stripe_examples.rb @@ -45,4 +45,5 @@ def it_behaves_like_stripe(&block) it_behaves_like 'Multiple Customer Cards' it_behaves_like 'Charging with Tokens' it_behaves_like 'Card Error Prep' + it_behaves_like 'Completing Checkout Sessions' end From 9edc9e54ad1c5fecb3e2b855af35a3252b27eba3 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 2 Aug 2021 17:11:07 +0100 Subject: [PATCH 042/158] Support filtering subscriptions be status --- lib/stripe_mock/request_handlers/subscriptions.rb | 15 ++++++++++++--- .../subscription_examples.rb | 12 ++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 763db4223..bbb19e84b 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -153,9 +153,18 @@ def retrieve_subscriptions(route, method_url, params, headers) # stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url - Data.mock_list_object(subscriptions.values, params) - #customer = assert_existence :customer, $1, customers[stripe_account][$1] - #customer[:subscriptions] + subs = subscriptions.values + + case params[:status] + when nil + subs = subs.filter {|subscription| subscription[:status] != "canceled"} + when "all" + # Include all subscriptions + else + subs = subs.filter {|subscription| subscription[:status] == params[:status]} + end + + Data.mock_list_object(subs, params) end def update_subscription(route, method_url, params, headers) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index c77707f08..0a2d747cf 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1182,6 +1182,18 @@ expect(list.count).to eq(0) expect(list.data.length).to eq(0) end + + it "does not include canceled subscriptions by default" do + customer = Stripe::Customer.create(source: gen_card_tk) + subscription = Stripe::Subscription.create({ plan: plan.id, customer: customer.id }) + subscription.delete + + list = Stripe::Subscription.list({customer: customer.id}) + + expect(list.object).to eq("list") + expect(list.data).to be_empty + expect(list.data.length).to eq(0) + end end describe "metadata" do From 71ae9b8e52816ce47cc60a1c422f8d4c31cbdd1c Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Thu, 5 Aug 2021 13:19:45 +0100 Subject: [PATCH 043/158] Support expanding `payment_method` in SetupIntent --- .../request_handlers/setup_intents.rb | 21 ++++++++++++------- .../setup_intent_examples.rb | 9 ++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/stripe_mock/request_handlers/setup_intents.rb b/lib/stripe_mock/request_handlers/setup_intents.rb index 292e1e673..8c6cd4239 100644 --- a/lib/stripe_mock/request_handlers/setup_intents.rb +++ b/lib/stripe_mock/request_handlers/setup_intents.rb @@ -15,12 +15,12 @@ module SetupIntents ] def SetupIntents.included(klass) - klass.add_handler 'post /v1/setup_intents', :new_setup_intent - klass.add_handler 'get /v1/setup_intents', :get_setup_intents - klass.add_handler 'get /v1/setup_intents/(.*)', :get_setup_intent - klass.add_handler 'post /v1/setup_intents/(.*)/confirm', :confirm_setup_intent - klass.add_handler 'post /v1/setup_intents/(.*)/cancel', :cancel_setup_intent - klass.add_handler 'post /v1/setup_intents/(.*)', :update_setup_intent + klass.add_handler 'post /v1/setup_intents', :new_setup_intent + klass.add_handler 'get /v1/setup_intents', :get_setup_intents + klass.add_handler 'get /v1/setup_intents/(.*)', :get_setup_intent + klass.add_handler 'post /v1/setup_intents/(.*)/confirm', :confirm_setup_intent + klass.add_handler 'post /v1/setup_intents/(.*)/cancel', :cancel_setup_intent + klass.add_handler 'post /v1/setup_intents/(.*)', :update_setup_intent end def new_setup_intent(route, method_url, params, headers) @@ -40,7 +40,7 @@ def update_setup_intent(route, method_url, params, headers) id = $1 setup_intent = assert_existence :setup_intent, id, setup_intents[id] - setup_intents[id] = Util.rmerge(setup_intent, params.select{ |k,v| ALLOWED_PARAMS.include?(k)}) + setup_intents[id] = Util.rmerge(setup_intent, params.select { |k, v| ALLOWED_PARAMS.include?(k) }) end def get_setup_intents(route, method_url, params, headers) @@ -50,7 +50,7 @@ def get_setup_intents(route, method_url, params, headers) clone = setup_intents.clone if params[:customer] - clone.delete_if { |k,v| v[:customer] != params[:customer] } + clone.delete_if { |k, v| v[:customer] != params[:customer] } end Data.mock_list_object(clone.values, params) @@ -62,6 +62,11 @@ def get_setup_intent(route, method_url, params, headers) setup_intent = assert_existence :setup_intent, setup_intent_id, setup_intents[setup_intent_id] setup_intent = setup_intent.clone + + if params[:expand]&.include?("payment_method") + setup_intent[:payment_method] = assert_existence :payment_method, setup_intent[:payment_method], payment_methods[setup_intent[:payment_method]] + end + setup_intent end diff --git a/spec/shared_stripe_examples/setup_intent_examples.rb b/spec/shared_stripe_examples/setup_intent_examples.rb index f782d4c0b..ecf9b3052 100644 --- a/spec/shared_stripe_examples/setup_intent_examples.rb +++ b/spec/shared_stripe_examples/setup_intent_examples.rb @@ -42,6 +42,15 @@ } end + it "expands payment_method" do + payment_method = Stripe::PaymentMethod.create(type: "card") + original = Stripe::SetupIntent.create(payment_method: payment_method.id) + + setup_intent = Stripe::SetupIntent.retrieve({id: original.id, expand: ["payment_method"]}) + + expect(setup_intent.payment_method).to eq(payment_method) + end + it "confirms a stripe setup_intent" do setup_intent = Stripe::SetupIntent.create() confirmed_setup_intent = setup_intent.confirm() From 7c6b3397e27d36e6f1a67f5491eff1ebd4d05491 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 16 Aug 2021 19:44:24 +0100 Subject: [PATCH 044/158] Properly set `Stripe::Checkout::Session` URLs --- lib/stripe_mock/request_handlers/checkout_session.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index b5ff5d6c9..78ab53aa8 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -93,7 +93,7 @@ def new_session(route, method_url, params, headers) status: nil }, billing_address_collection: nil, - cancel_url: "http://example.com/checkout/cancel?session_id={CHECKOUT_SESSION_ID}", + cancel_url: params[:cancel_url], client_reference_id: nil, currency: currency, customer: params[:customer], @@ -112,7 +112,7 @@ def new_session(route, method_url, params, headers) shipping_address_collection: nil, submit_type: nil, subscription: nil, - success_url: "http://example.com/checkout/success?session_id={CHECKOUT_SESSION_ID}", + success_url: params[:success_url], total_details: nil, url: "https://checkout.stripe.com/pay/#{id}" } From 08aba09603f23cbba01ee0dabe5a919a8534f0ae Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 17 Aug 2021 16:32:06 +0100 Subject: [PATCH 045/158] Create a webhook fixture for `checkout.session.completed` with `"mode": "setup"` --- lib/stripe_mock/api/webhooks.rb | 1 + ...checkout.session.completed.setup_mode.json | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 31de8b0fd..49b3ae873 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -52,6 +52,7 @@ def self.event_list 'charge.dispute.closed', 'charge.dispute.funds_reinstated', 'charge.dispute.funds_withdrawn', + 'checkout.session.completed.setup_mode', 'customer.source.created', 'customer.source.deleted', 'customer.source.updated', diff --git a/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json new file mode 100644 index 000000000..40b0a6f90 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json @@ -0,0 +1,45 @@ +{ + "created": 1326853478, + "livemode": false, + "id": "evt_00000000000000", + "type": "checkout.session.completed", + "object": "event", + "data": { + "object": { + "id": "cs_00000000000000", + "object": "checkout.session", + "allow_promotion_codes": null, + "amount_subtotal": null, + "amount_total": null, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_address_collection": null, + "cancel_url": "https://example.com/cancel", + "client_reference_id": null, + "currency": null, + "customer": null, + "customer_details": null, + "customer_email": null, + "livemode": false, + "locale": null, + "metadata": {}, + "mode": "setup", + "payment_intent": null, + "payment_method_options": {}, + "payment_method_types": [ + "card" + ], + "payment_status": "no_payment_required", + "setup_intent": "seti_00000000000000", + "shipping": null, + "shipping_address_collection": null, + "submit_type": null, + "subscription": null, + "success_url": "https://example.com/success", + "total_details": null, + "url": "https://checkout.stripe.com/pay/cs_00000000000000#00000000000000" + } + } +} From a87f66fcfc84867b1ccbe7a08abeedccc9861ef2 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 17 Aug 2021 17:00:41 +0100 Subject: [PATCH 046/158] Create a webhook fixture for `checkout.session.completed` with `"mode": "payment"` Also updated the `"mode": "setup"` event to be based on a checkout session that was actually completed. --- lib/stripe_mock/api/webhooks.rb | 1 + ...eckout.session.completed.payment_mode.json | 53 +++++++++++++++++++ ...checkout.session.completed.setup_mode.json | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 lib/stripe_mock/webhook_fixtures/checkout.session.completed.payment_mode.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 49b3ae873..8ea1e714c 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -52,6 +52,7 @@ def self.event_list 'charge.dispute.closed', 'charge.dispute.funds_reinstated', 'charge.dispute.funds_withdrawn', + 'checkout.session.completed.payment_mode', 'checkout.session.completed.setup_mode', 'customer.source.created', 'customer.source.deleted', diff --git a/lib/stripe_mock/webhook_fixtures/checkout.session.completed.payment_mode.json b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.payment_mode.json new file mode 100644 index 000000000..e7531dc32 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.payment_mode.json @@ -0,0 +1,53 @@ +{ + "created": 1326853478, + "livemode": false, + "id": "evt_00000000000000", + "type": "checkout.session.completed", + "object": "event", + "data": { + "object": { + "id": "cs_00000000000000", + "object": "checkout.session", + "allow_promotion_codes": null, + "amount_subtotal": 25000, + "amount_total": 25000, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_address_collection": null, + "cancel_url": "https://example.com/cancel", + "client_reference_id": null, + "currency": "usd", + "customer": "cus_00000000000000", + "customer_details": { + "email": "example@example.com", + "tax_exempt": "none", + "tax_ids": [] + }, + "customer_email": null, + "livemode": false, + "locale": null, + "metadata": {}, + "mode": "payment", + "payment_intent": "pi_00000000000000", + "payment_method_options": {}, + "payment_method_types": [ + "card" + ], + "payment_status": "paid", + "setup_intent": null, + "shipping": null, + "shipping_address_collection": null, + "submit_type": null, + "subscription": null, + "success_url": "https://example.com/success", + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + }, + "url": null + } + } +} diff --git a/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json index 40b0a6f90..3be9b6816 100644 --- a/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json +++ b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json @@ -39,7 +39,7 @@ "subscription": null, "success_url": "https://example.com/success", "total_details": null, - "url": "https://checkout.stripe.com/pay/cs_00000000000000#00000000000000" + "url": null } } } From 59608a20b19c1ae453210a2b9a299e5dc116725e Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Wed, 18 Aug 2021 14:21:23 +0100 Subject: [PATCH 047/158] Add `Stripe::Checkout::Session.list` support --- lib/stripe_mock/request_handlers/checkout_session.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 78ab53aa8..010f0cfba 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -4,6 +4,7 @@ module Checkout module Session def Session.included(klass) klass.add_handler 'post /v1/checkout/sessions', :new_session + klass.add_handler 'get /v1/checkout/sessions', :list_checkout_sessions klass.add_handler 'get /v1/checkout/sessions/([^/]*)', :get_checkout_session klass.add_handler 'get /v1/checkout/sessions/([^/]*)/line_items', :list_line_items end @@ -118,6 +119,10 @@ def new_session(route, method_url, params, headers) } end + def list_checkout_sessions(route, method_url, params, headers) + Data.mock_list_object(checkout_sessions.values) + end + def get_checkout_session(route, method_url, params, headers) route =~ method_url checkout_session = assert_existence :checkout_session, $1, checkout_sessions[$1] From 8007b3954a3c6a8bce90a57abb988128e9dce5a0 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Wed, 18 Aug 2021 14:42:35 +0100 Subject: [PATCH 048/158] Set `Stripe::Checkout::Session.metadata` --- lib/stripe_mock/request_handlers/checkout_session.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 010f0cfba..0e77dfaea 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -102,7 +102,7 @@ def new_session(route, method_url, params, headers) customer_email: nil, livemode: false, locale: nil, - metadata: {}, + metadata: params[:metadata], mode: params[:mode], payment_intent: payment_intent, payment_method_options: params[:payment_method_options], From 1a65368b42da90677f3a4a91bbdf5a7ee42daecd Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Wed, 18 Aug 2021 15:01:33 +0100 Subject: [PATCH 049/158] Set `Stripe::Checkout::Session.customer_email` --- lib/stripe_mock/request_handlers/checkout_session.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 0e77dfaea..f927c0dc0 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -99,7 +99,7 @@ def new_session(route, method_url, params, headers) currency: currency, customer: params[:customer], customer_details: nil, - customer_email: nil, + customer_email: params[:customer_email], livemode: false, locale: nil, metadata: params[:metadata], From 10cf8a0b10bebb09efe096dc37c110dab55d4f56 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Thu, 19 Aug 2021 18:18:51 +0100 Subject: [PATCH 050/158] Initialize `Subscription.billing_cycle_anchor` --- .../request_handlers/helpers/subscription_helpers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index b88c0b925..f817658c3 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -50,10 +50,10 @@ def custom_subscription_params(plans, cus, options = {}) if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now" end_time = options[:billing_cycle_anchor] || get_ending_time(start_time, plan) - params.merge!({status: 'active', current_period_end: end_time, trial_start: nil, trial_end: nil, billing_cycle_anchor: options[:billing_cycle_anchor]}) + params.merge!({status: 'active', current_period_end: end_time, trial_start: nil, trial_end: nil, billing_cycle_anchor: options[:billing_cycle_anchor] || created_time}) else end_time = options[:trial_end] || (Time.now.utc.to_i + plan[:trial_period_days]*86400) - params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time, billing_cycle_anchor: nil}) + params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time, billing_cycle_anchor: options[:billing_cycle_anchor] || created_time}) end params From 7bc99f1587811a9a8a1cba096d45b2ca321baed6 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 23 Aug 2021 15:39:24 +0100 Subject: [PATCH 051/158] Add support for `product_data` with `Stripe::Price.create` --- .../request_handlers/validators/param_validators.rb | 9 +++++---- lib/stripe_mock/test_strategies/base.rb | 10 ++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/stripe_mock/request_handlers/validators/param_validators.rb b/lib/stripe_mock/request_handlers/validators/param_validators.rb index e9bf4dc5e..aa5f23972 100644 --- a/lib/stripe_mock/request_handlers/validators/param_validators.rb +++ b/lib/stripe_mock/request_handlers/validators/param_validators.rb @@ -108,13 +108,14 @@ def validate_create_plan_params(params) def validate_create_price_params(params) price_id = params[:id].to_s - product_id = params[:product] - @base_strategy.create_price_params.keys.each do |attr_name| - message = "Missing required param: #{attr_name}." - raise Stripe::InvalidRequestError.new(message, attr_name) if params[attr_name].nil? + require_param(:currency) unless params[:currency] + unless params[:product] || params[:product_data] + raise Stripe::InvalidRequestError("Requires product or product_data") end + product_id = params[:product] || create_product(nil, nil, params[:product_data], nil).id + if prices[price_id] message = already_exists_message(Stripe::Price) raise Stripe::InvalidRequestError.new(message, :id) diff --git a/lib/stripe_mock/test_strategies/base.rb b/lib/stripe_mock/test_strategies/base.rb index 8c425444d..09a8f85fc 100644 --- a/lib/stripe_mock/test_strategies/base.rb +++ b/lib/stripe_mock/test_strategies/base.rb @@ -44,9 +44,15 @@ def create_price(params = {}) end def create_price_params(params = {}) - { - :currency => StripeMock.default_currency, + price_params = { + currency: StripeMock.default_currency, }.merge(params) + unless price_params.key?(:product) || price_params.key?(:product_data) + price_params[:product_data] = { + name: 'Product created for price' + } + end + price_params end def list_subscriptions(limit) From 6c74b1af8bcfd5812efc05e8918c005a5e850f6b Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 23 Aug 2021 15:39:46 +0100 Subject: [PATCH 052/158] Add tests for `Subscription.current_period_end` --- .../subscription_examples.rb | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 0a2d747cf..72e19d2b4 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -338,7 +338,7 @@ expect(sub.object).to eq('subscription') expect(sub.plan.to_hash).to eq(plan_with_trial.to_hash) expect(sub.trial_end - sub.trial_start).to eq(14 * 86400) - expect(sub.billing_cycle_anchor).to be_nil + expect(sub.billing_cycle_anchor).to eq sub.trial_start customer = Stripe::Customer.retrieve(customer.id) subscriptions = Stripe::Subscription.list(customer: customer.id) @@ -532,6 +532,45 @@ expect(subscription.items.data[1].plan.id).to eq plan2.id end + it 'sets current_period_end based on price week interval', live: true do + price = stripe_helper.create_price(recurring: {interval: 'week'}) + + subscription = Stripe::Subscription.create( + customer: Stripe::Customer.create(source: gen_card_tk).id, + items: [ + {price: price.id, quantity: 1} + ] + ) + + expect(subscription.current_period_end).to eq (Time.now + (7 * 60 * 60 * 24)).to_i + end + + it 'sets current_period_end based on price month interval', live: true do + price = stripe_helper.create_price(recurring: {interval: 'month'}) + + subscription = Stripe::Subscription.create( + customer: Stripe::Customer.create(source: gen_card_tk).id, + items: [ + {price: price.id, quantity: 1} + ] + ) + + expect(subscription.current_period_end).to eq (DateTime.now >> 1).to_time.to_i + end + + it 'sets current_period_end based on price year interval', live: true do + price = stripe_helper.create_price(recurring: {interval: 'year'}) + + subscription = Stripe::Subscription.create( + customer: Stripe::Customer.create(source: gen_card_tk).id, + items: [ + {price: price.id, quantity: 1} + ] + ) + + expect(subscription.current_period_end).to eq (DateTime.now >> 12).to_time.to_i + end + it 'add a new subscription to bill via an invoice' do product = stripe_helper.create_product(id: 'invoice_billing') plan = stripe_helper.create_plan(id: 'silver', product: product.id, From 7dad79022d1fe65f54ba90644f8acc9fa19ddf18 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Thu, 9 Sep 2021 17:35:03 +0100 Subject: [PATCH 053/158] Add a `payment_intent.processing` event --- lib/stripe_mock/api/webhooks.rb | 1 + .../payment_intent.processing.json | 162 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/payment_intent.processing.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 8ea1e714c..314e142eb 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -74,6 +74,7 @@ def self.event_list 'invoiceitem.created', 'invoiceitem.updated', 'invoiceitem.deleted', + 'payment_intent.processing', 'payment_intent.succeeded', 'payment_intent.payment_failed', 'plan.created', diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.processing.json b/lib/stripe_mock/webhook_fixtures/payment_intent.processing.json new file mode 100644 index 000000000..69c71813d --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.processing.json @@ -0,0 +1,162 @@ +{ + "id": "evt_00000000000000", + "object": "event", + "api_version": "2018-02-28", + "created": 1578499109, + "data": { + "object": { + "id": "pi_00000000000000", + "object": "payment_intent", + "allowed_source_types": ["card", "sepa_debit"], + "amount": 900, + "amount_capturable": 0, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_00000000000000", + "object": "charge", + "amount": 900, + "amount_refunded": 0, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_00000000000000", + "billing_details": { + "address": { + "city": null, + "country": "DE", + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "captured": true, + "created": 1578499109, + "currency": "eur", + "customer": "cus_00000000000000", + "description": null, + "destination": "acct_00000000000000", + "dispute": null, + "disputed": false, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 40, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": false, + "payment_intent": "pi_00000000000000", + "payment_method": "pm_00000000000000", + "payment_method_details": { + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 4, + "exp_year": 2024, + "fingerprint": "00000000000000", + "funding": "credit", + "installments": null, + "last4": "4242", + "network": "visa", + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/acct_00000000000000/ch_00000000000000/rcpt_00000000000000", + "refunded": false, + "refunds": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_00000000000000/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": "ACME Corp", + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer": "tr_00000000000000", + "transfer_data": { + "amount": null, + "destination": "acct_00000000000000" + }, + "transfer_group": "group_pi_00000000000000" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_00000000000000" + }, + "client_secret": "pi_00000000000000", + "confirmation_method": "automatic", + "created": 1578499108, + "currency": "eur", + "customer": "cus_00000000000000", + "description": null, + "invoice": null, + "last_payment_error": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "next_source_action": null, + "on_behalf_of": null, + "payment_method": "pm_00000000000000", + "payment_method_options": { + "card": { + "installments": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": ["card", "sepa_debit"], + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": "ACME Corp", + "statement_descriptor_suffix": null, + "status": "processing", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": null + }, + "type": "payment_intent.processing" +} From a3bd40adbdfe01fb74af7489e7c269fed4246b1b Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Thu, 16 Sep 2021 14:58:28 +0100 Subject: [PATCH 054/158] Allow configuring the base URL for Checkout --- lib/stripe_mock.rb | 2 ++ lib/stripe_mock/request_handlers/checkout_session.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index ce3a6a7ca..543ae1b90 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -89,12 +89,14 @@ module StripeMock @default_currency = 'usd' + @checkout_base = "https://checkout.stripe.com/pay/" lib_dir = File.expand_path(File.dirname(__FILE__), '../..') @webhook_fixture_path = './spec/fixtures/stripe_webhooks/' @webhook_fixture_fallback_path = File.join(lib_dir, 'stripe_mock/webhook_fixtures') class << self attr_accessor :default_currency + attr_accessor :checkout_base attr_accessor :webhook_fixture_path end end diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index f927c0dc0..5115da745 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -115,7 +115,7 @@ def new_session(route, method_url, params, headers) subscription: nil, success_url: params[:success_url], total_details: nil, - url: "https://checkout.stripe.com/pay/#{id}" + url: URI.join(StripeMock.checkout_base, id).to_s } end From 975152da474a6bb618365e9ede911fa13070a2d1 Mon Sep 17 00:00:00 2001 From: Mike Menne Date: Mon, 27 Sep 2021 22:51:25 -0500 Subject: [PATCH 055/158] Change to --- .../request_handlers/checkout_session.rb | 4 +-- .../checkout_session_examples.rb | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 5115da745..4e70ca011 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -56,7 +56,7 @@ def new_session(route, method_url, params, headers) case params[:mode] when nil, "payment" params[:customer] ||= new_customer(nil, nil, {email: params[:customer_email]}, nil)[:id] - require_params(:line_items) if params[:line_items].nil? || params[:line_items].empty? + require_param(:line_items) if params[:line_items].nil? || params[:line_items].empty? payment_intent = new_payment_intent(nil, nil, { amount: amount, currency: currency, @@ -77,7 +77,7 @@ def new_session(route, method_url, params, headers) payment_status = "no_payment_required" when "subscription" params[:customer] ||= new_customer(nil, nil, {email: params[:customer_email]}, nil)[:id] - require_params(:line_items) if params[:line_items].nil? || params[:line_items].empty? + require_param(:line_items) if params[:line_items].nil? || params[:line_items].empty? checkout_session_line_items[id] = line_items else throw Stripe::InvalidRequestError.new("Invalid mode: must be one of payment, setup, or subscription", :mode, http_status: 400) diff --git a/spec/shared_stripe_examples/checkout_session_examples.rb b/spec/shared_stripe_examples/checkout_session_examples.rb index 58329911d..f0efab77e 100644 --- a/spec/shared_stripe_examples/checkout_session_examples.rb +++ b/spec/shared_stripe_examples/checkout_session_examples.rb @@ -22,6 +22,20 @@ expect(payment_intent.customer).to eq(session.customer) end + context "when creating a payment" do + it "requires line_items" do + expect do + session = Stripe::Checkout::Session.create( + customer: "customer_id", + success_url: "localhost/nada", + cancel_url: "localhost/nada", + payment_method_types: ["card"], + ) + end.to raise_error(Stripe::InvalidRequestError, /line_items/i) + + end + end + it "creates SetupIntent with setup mode" do session = Stripe::Checkout::Session.create( mode: "setup", @@ -35,6 +49,21 @@ expect(setup_intent.payment_method_types).to eq(["card"]) end + context "when creating a subscription" do + it "requires line_items" do + expect do + session = Stripe::Checkout::Session.create( + customer: "customer_id", + success_url: "localhost/nada", + cancel_url: "localhost/nada", + payment_method_types: ["card"], + mode: "subscription", + ) + end.to raise_error(Stripe::InvalidRequestError, /line_items/i) + + end + end + context "retrieve a checkout session" do let(:checkout_session1) { stripe_helper.create_checkout_session } From c3d303d9d21af222f15a6474fde676c36bb860d9 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Wed, 29 Sep 2021 17:17:43 -0300 Subject: [PATCH 056/158] Adds proration_behavior and backdate_start_date attributes to Subscription --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index f42b90991..b38d35102 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -97,7 +97,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan expand) + allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date expand) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) From cfe51c69bc2d08ecb6a87cb3c7c75fc0b9344685 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Wed, 6 Oct 2021 10:56:15 +0100 Subject: [PATCH 057/158] Use `deep_merge` in `mock_payment_method` so that payment method details can be provided --- lib/stripe_mock/data.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index df79339a7..ca51e935e 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1290,7 +1290,7 @@ def self.mock_payment_method(params = {}) metadata: { order_id: '123456789' } - }.merge(type => data[type]).merge(params) + }.merge(type => data[type]).deep_merge(params) end def self.mock_setup_intent(params = {}) From b954c59afd3c0fa633fb94d716e0723416866dda Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Wed, 27 Oct 2021 11:39:25 +0100 Subject: [PATCH 058/158] Improve `PaymentMethod` handling - Removed support for updating payment methods that the Stripe API does not support updating - Added basic card brand detection and proper parameter handling Fixes #796 --- lib/stripe_mock/data.rb | 19 ++++-- .../request_handlers/payment_methods.rb | 6 +- .../payment_method_examples.rb | 59 ++++++++++--------- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index ca51e935e..738d29030 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1238,19 +1238,26 @@ def self.mock_payment_method(params = {}) type = params[:type].to_sym data = { card: { - brand: 'visa', + brand: case params.dig(:card, :number)&.to_s + when /^4/, nil + 'visa' + when /^5[1-5]/ + 'mastercard' + else + 'unknown' + end, checks: { address_line1_check: nil, address_postal_code_check: nil, cvc_check: 'pass' }, country: 'FR', - exp_month: 2, - exp_year: 2022, + exp_month: params.dig(:card, :exp_month) || 2, + exp_year: params.dig(:card, :exp_year) || 2022, fingerprint: 'Hr3Ly5z5IYxsokWA', funding: 'credit', generated_from: nil, - last4: '3155', + last4: params.dig(:card, :number)&.[](-4..) || '3155', three_d_secure_usage: { supported: true }, wallet: nil }, @@ -1265,7 +1272,7 @@ def self.mock_payment_method(params = {}) branch_code: '', country: 'DE', fingerprint: 'FD81kbVPe7M05BMj', - last4: '3000' + last4: params.dig(:sepa_debit, :iban)&.[](-4..) || '3000' } } @@ -1290,7 +1297,7 @@ def self.mock_payment_method(params = {}) metadata: { order_id: '123456789' } - }.merge(type => data[type]).deep_merge(params) + }.merge(params).merge(type => data[type]) end def self.mock_setup_intent(params = {}) diff --git a/lib/stripe_mock/request_handlers/payment_methods.rb b/lib/stripe_mock/request_handlers/payment_methods.rb index 28be2499e..301e2be7a 100644 --- a/lib/stripe_mock/request_handlers/payment_methods.rb +++ b/lib/stripe_mock/request_handlers/payment_methods.rb @@ -78,7 +78,11 @@ def detach_payment_method(route, method_url, params, headers) # post /v1/payment_methods/:id def update_payment_method(route, method_url, params, headers) - allowed_params = [:billing_details, :card, :ideal, :sepa_debit, :metadata] + allowed_params = [:billing_details, :card, :metadata] + disallowed_params = params.keys - allowed_params + unless disallowed_params.empty? + raise Stripe::InvalidRequestError.new("Received unknown parameter: #{disallowed_params.first}", disallowed_params.first) + end id = method_url.match(route)[1] diff --git a/spec/shared_stripe_examples/payment_method_examples.rb b/spec/shared_stripe_examples/payment_method_examples.rb index b3d2c8b62..264a13499 100644 --- a/spec/shared_stripe_examples/payment_method_examples.rb +++ b/spec/shared_stripe_examples/payment_method_examples.rb @@ -25,6 +25,12 @@ cvc: 999 } end + let(:visa_card_details) do + card_details + end + let(:mastercard_card_details) do + card_details.merge(:number => 5555_5555_5555_4444) + end let(:sepa_debit_details) do { iban: 'DE89370400440532013000' @@ -385,6 +391,7 @@ .from(original_card_exp_month).to(new_card_exp_month) end + context 'without a customer' do it 'raises invalid requestion exception' do expect do @@ -394,29 +401,39 @@ end end + context 'with visa card' do + let(:payment_method) do + Stripe::PaymentMethod.create(type: 'card', card: visa_card_details) + end + + it 'uses correct brand' do + expect(payment_method.card.brand).to eq('visa') + end + end + + context 'with mastercard card' do + let(:payment_method) do + Stripe::PaymentMethod.create(type: 'card', card: mastercard_card_details) + end + + it 'uses correct brand' do + expect(payment_method.card.brand).to eq('mastercard') + end + end + context 'with ideal' do let(:payment_method) do Stripe::PaymentMethod.create(type: 'ideal', ideal: ideal_details) end - it 'updates the ideal for the payment method' do + it 'cannot update' do Stripe::PaymentMethod.attach(payment_method.id, customer: customer.id) - original_ideal_bank = payment_method.ideal.bank new_ideal_bank = 12 expect do Stripe::PaymentMethod.update(payment_method.id, ideal: { bank: new_ideal_bank }) - end.to change { Stripe::PaymentMethod.retrieve(payment_method.id).ideal.bank } - .from(original_ideal_bank).to(new_ideal_bank) - end - - context 'without a customer' do - it 'raises invalid requestion exception' do - expect do - Stripe::PaymentMethod.update(payment_method.id, ideal: { bank: 12 }) - end.to raise_error(Stripe::InvalidRequestError) - end + end.to raise_error(Stripe::InvalidRequestError) end end @@ -425,24 +442,12 @@ Stripe::PaymentMethod.create(type: 'sepa_debit', sepa_debit: sepa_debit_details) end - it 'updates the sepa_debit for the payment method' do + it 'cannot update' do Stripe::PaymentMethod.attach(payment_method.id, customer: customer.id) - original_iban = payment_method.sepa_debit.iban - new_iban = 'DE62370400440532013001' - expect do - Stripe::PaymentMethod.update(payment_method.id, sepa_debit: { iban: new_iban }) - end.to change { Stripe::PaymentMethod.retrieve(payment_method.id).sepa_debit.iban } - .from(original_iban).to(new_iban) - end - - context 'without a customer' do - it 'raises invalid requestion exception' do - expect do - Stripe::PaymentMethod.update(payment_method.id, sepa_debit: { iban: 'DE62370400440532013001' }) - end.to raise_error(Stripe::InvalidRequestError) - end + Stripe::PaymentMethod.update(payment_method.id, sepa_debit: { iban: 'DE62370400440532013001'}) + end.to raise_error(Stripe::InvalidRequestError) end end end From 7b139b6dba934853d2ffb4ec65007de21908546f Mon Sep 17 00:00:00 2001 From: Adam Stegman Date: Tue, 2 Nov 2021 11:40:46 -0500 Subject: [PATCH 059/158] Allow events to be filtered by created See https://stripe.com/docs/api/events/list#list_events-created for API documentation. --- lib/stripe_mock/request_handlers/events.rb | 33 +++++++++++-- .../webhook_event_examples.rb | 46 +++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/lib/stripe_mock/request_handlers/events.rb b/lib/stripe_mock/request_handlers/events.rb index 6f36a7390..408f02bfe 100644 --- a/lib/stripe_mock/request_handlers/events.rb +++ b/lib/stripe_mock/request_handlers/events.rb @@ -4,7 +4,7 @@ module Events def Events.included(klass) klass.add_handler 'get /v1/events/(.*)', :retrieve_event - klass.add_handler 'get /v1/events', :list_events + klass.add_handler 'get /v1/events', :list_events end def retrieve_event(route, method_url, params, headers) @@ -13,9 +13,36 @@ def retrieve_event(route, method_url, params, headers) end def list_events(route, method_url, params, headers) - Data.mock_list_object(events.values, params) + values = filter_by_created(events.values, params: params) + Data.mock_list_object(values, params) end - + + private + + def filter_by_created(event_list, params:) + if params[:created].nil? + return event_list + end + + if params[:created].is_a?(Hash) + if params[:created][:gt] + event_list.select! { |event| event[:created] > params[:created][:gt].to_i } + end + if params[:created][:gte] + event_list.select! { |event| event[:created] >= params[:created][:gte].to_i } + end + if params[:created][:lt] + event_list.select! { |event| event[:created] < params[:created][:lt].to_i } + end + if params[:created][:lte] + event_list.select! { |event| event[:created] <= params[:created][:lte].to_i } + end + else + event_list.select! { |event| event[:created] == params[:created].to_i } + end + event_list + end + end end end diff --git a/spec/shared_stripe_examples/webhook_event_examples.rb b/spec/shared_stripe_examples/webhook_event_examples.rb index 4783e727a..07a416063 100644 --- a/spec/shared_stripe_examples/webhook_event_examples.rb +++ b/spec/shared_stripe_examples/webhook_event_examples.rb @@ -203,6 +203,52 @@ expect(events.map &:type).to include('invoiceitem.created', 'invoice.created', 'coupon.created') end + it "retrieves events with a created timestamp" do + timestamp = Time.now.to_i - 7200 + customer_created_event = StripeMock.mock_webhook_event('customer.created', created: Time.now.to_i - 14_400) + plan_created_event = StripeMock.mock_webhook_event('plan.created', created: Time.now.to_i - 10_800) + coupon_created_event = StripeMock.mock_webhook_event('coupon.created', created: timestamp) + invoice_created_event = StripeMock.mock_webhook_event('invoice.created', created: Time.now.to_i - 3600) + invoice_item_created_event = StripeMock.mock_webhook_event('invoiceitem.created') + + events = Stripe::Event.list(created: timestamp) + expect(events.count).to eq(1) + expect(events.map &:id).to match_array([coupon_created_event.id]) + expect(events.map &:type).to match_array(['coupon.created']) + + events = Stripe::Event.list(created: timestamp.to_s) + expect(events.count).to eq(1) + expect(events.map &:id).to match_array([coupon_created_event.id]) + expect(events.map &:type).to match_array(['coupon.created']) + end + + it "retrieves events with a created filter" do + timestamp1 = Time.now.to_i - 3600 + timestamp2 = Time.now.to_i - 7200 + timestamp3 = Time.now.to_i - 10_800 + timestamp4 = Time.now.to_i - 14_400 + customer_created_event = StripeMock.mock_webhook_event('customer.created', created: timestamp4) + plan_created_event = StripeMock.mock_webhook_event('plan.created', created: timestamp3) + coupon_created_event = StripeMock.mock_webhook_event('coupon.created', created: timestamp2) + invoice_created_event = StripeMock.mock_webhook_event('invoice.created', created: timestamp1) + invoice_item_created_event = StripeMock.mock_webhook_event('invoiceitem.created') + + events = Stripe::Event.list(created: {gte: timestamp2, lte: timestamp1}) + expect(events.count).to eq(2) + expect(events.map &:id).to match_array([coupon_created_event.id, invoice_created_event.id]) + expect(events.map &:type).to match_array(['coupon.created', 'invoice.created']) + + events = Stripe::Event.list(created: {gt: timestamp3}) + expect(events.count).to eq(3) + expect(events.map &:id).to match_array([coupon_created_event.id, invoice_created_event.id, invoice_item_created_event.id]) + expect(events.map &:type).to match_array(['coupon.created', 'invoice.created', 'invoiceitem.created']) + + events = Stripe::Event.list(created: {lt: timestamp3.to_s}) + expect(events.count).to eq(1) + expect(events.map &:id).to match_array([customer_created_event.id]) + expect(events.map &:type).to match_array(['customer.created']) + end + end describe 'Subscription events' do From 7721ed2b5408b5de1452affd3374a5de51f8ee61 Mon Sep 17 00:00:00 2001 From: Adam Stegman Date: Wed, 3 Nov 2021 08:12:06 -0500 Subject: [PATCH 060/158] Use select instead of select! --- lib/stripe_mock/request_handlers/events.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/stripe_mock/request_handlers/events.rb b/lib/stripe_mock/request_handlers/events.rb index 408f02bfe..8999aa51e 100644 --- a/lib/stripe_mock/request_handlers/events.rb +++ b/lib/stripe_mock/request_handlers/events.rb @@ -26,19 +26,19 @@ def filter_by_created(event_list, params:) if params[:created].is_a?(Hash) if params[:created][:gt] - event_list.select! { |event| event[:created] > params[:created][:gt].to_i } + event_list = event_list.select { |event| event[:created] > params[:created][:gt].to_i } end if params[:created][:gte] - event_list.select! { |event| event[:created] >= params[:created][:gte].to_i } + event_list = event_list.select { |event| event[:created] >= params[:created][:gte].to_i } end if params[:created][:lt] - event_list.select! { |event| event[:created] < params[:created][:lt].to_i } + event_list = event_list.select { |event| event[:created] < params[:created][:lt].to_i } end if params[:created][:lte] - event_list.select! { |event| event[:created] <= params[:created][:lte].to_i } + event_list = event_list.select { |event| event[:created] <= params[:created][:lte].to_i } end else - event_list.select! { |event| event[:created] == params[:created].to_i } + event_list = event_list.select { |event| event[:created] == params[:created].to_i } end event_list end From 5d48c743e1de110a96f1a4251a4e34b68fcabd8f Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Fri, 19 Nov 2021 15:30:28 -0300 Subject: [PATCH 061/158] Removes file added by mistake --- .github/dependabot.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 511a205ad..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,13 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: -- package-ecosystem: bundler - directory: "/" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 10 From b1e8dc24915f2f554e21940f70e8d140a86aca68 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 21 Nov 2021 18:01:04 +0900 Subject: [PATCH 062/158] set incomplete status when subscription payment_behavior is default_incomplete --- lib/stripe_mock/request_handlers/subscriptions.rb | 5 +++++ spec/shared_stripe_examples/subscription_examples.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index bbb19e84b..353ab4b51 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -132,6 +132,10 @@ def create_subscription(route, method_url, params, headers) subscription[:status] = 'trialing' end + if params[:payment_behavior] == 'default_incomplete' + subscription[:status] = 'incomplete' + end + if params[:cancel_at_period_end] subscription[:cancel_at_period_end] = true subscription[:canceled_at] = Time.now.utc.to_i @@ -294,6 +298,7 @@ def verify_card_present(customer, plan, subscription, params={}) return if customer[:invoice_settings][:default_payment_method] return if customer[:trial_end] return if params[:trial_end] + return if params[:payment_behavior] == 'default_incomplete' return if subscription[:default_payment_method] plan_trial_period_days = plan[:trial_period_days] || 0 diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 72e19d2b4..3e5ca766f 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -290,6 +290,18 @@ expect(customer.subscriptions.count).to eq(0) end + it "creates a subscription when subscription's payment_behavior is default_incomplete" do + plan = stripe_helper.create_plan(id: 'enterprise', product: product.id, amount: 499) + customer = Stripe::Customer.create(id: 'cardless') + + sub = Stripe::Subscription.create({ plan: plan.id, customer: customer.id, payment_behavior: 'default_incomplete' }) + customer = Stripe::Customer.retrieve('cardless') + + expect(customer.subscriptions.count).to eq(1) + expect(customer.subscriptions.data.first.id).to eq(sub.id) + expect(customer.subscriptions.data.first.status).to eq('incomplete') + end + it "throws an error when subscribing a customer with no card" do plan = stripe_helper.create_plan(id: 'enterprise', product: product.id, amount: 499) customer = Stripe::Customer.create(id: 'cardless') From f2944e8c65aa80c4fd02e9229ac6f7fa8275ebe7 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 28 Jul 2021 11:08:16 +0100 Subject: [PATCH 063/158] Expand subscription latest_invoice.payment_intent Also upgrade thin so it builds on macOS Co-authored-by: Luke Lau Co-authored-by: Renaud Chaput --- lib/stripe_mock/request_handlers/subscriptions.rb | 15 +++++++++++++++ .../subscription_examples.rb | 11 +++++++++++ stripe-ruby-mock.gemspec | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 353ab4b51..c52fb7c4d 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -141,6 +141,21 @@ def create_subscription(route, method_url, params, headers) subscription[:canceled_at] = Time.now.utc.to_i end + if (s = params[:expand]&.first { |s| s.starts_with? 'latest_invoice' }) + intent = Data.mock_payment_intent({ + status: 'succeeded', + amount: subscription[:plan][:amount], + currency: subscription[:plan][:currency] + }) + invoice = nil + if s.include?('latest_invoice.payment_intent') + invoice = Data.mock_invoice([], { payment_intent: intent }) + else + invoice = Data.mock_invoice([], { payment_intent: intent.id }) + end + subscription[:latest_invoice] = invoice + end + subscriptions[subscription[:id]] = subscription add_subscription_to_customer(customer, subscription) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 3e5ca766f..c91ef717f 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -684,6 +684,17 @@ expect(subscription.default_tax_rates.first.id).to eq(tax_rate.id) end end + + it 'expands latest_invoice.payment_intent' do + customer = Stripe::Customer.create(source: gen_card_tk) + subscription = Stripe::Subscription.create({ + customer: customer.id, + plan: plan.id, + expand: ['latest_invoice.payment_intent'] + }) + + expect(subscription.latest_invoice.payment_intent.status).to eq('succeeded') + end end context "updating a subscription" do diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 8b9df7808..e53963d8c 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -28,5 +28,5 @@ Gem::Specification.new do |gem| gem.add_development_dependency 'rspec', '~> 3.7.0' gem.add_development_dependency 'rubygems-tasks', '~> 0.2' - gem.add_development_dependency 'thin', '~> 1.6.4' + gem.add_development_dependency 'thin', '~> 1.8.1' end From d0057f07bf5de69dd15c7815d4b644c0a47a2d44 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 24 Nov 2021 12:02:56 +0000 Subject: [PATCH 064/158] Handle different types of subscription statuses When expanding latest_invoice --- .../request_handlers/subscriptions.rb | 20 +++++++++---------- .../subscription_examples.rb | 18 +++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index c52fb7c4d..b6314f3bd 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -142,17 +142,17 @@ def create_subscription(route, method_url, params, headers) end if (s = params[:expand]&.first { |s| s.starts_with? 'latest_invoice' }) - intent = Data.mock_payment_intent({ - status: 'succeeded', - amount: subscription[:plan][:amount], - currency: subscription[:plan][:currency] - }) - invoice = nil - if s.include?('latest_invoice.payment_intent') - invoice = Data.mock_invoice([], { payment_intent: intent }) - else - invoice = Data.mock_invoice([], { payment_intent: intent.id }) + payment_intent = nil + unless subscription[:status] == 'trialing' + intent_status = subscription[:status] == 'incomplete' ? 'requires_payment_method' : 'succeeded' + intent = Data.mock_payment_intent({ + status: intent_status, + amount: subscription[:plan][:amount], + currency: subscription[:plan][:currency] + }) + payment_intent = s.include?('latest_invoice.payment_intent') ? intent : intent.id end + invoice = Data.mock_invoice([], { payment_intent: payment_intent }) subscription[:latest_invoice] = invoice end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index c91ef717f..cbc8f6c41 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -694,6 +694,24 @@ }) expect(subscription.latest_invoice.payment_intent.status).to eq('succeeded') + + subscription = Stripe::Subscription.create({ + customer: customer.id, + plan: plan.id, + expand: ['latest_invoice.payment_intent'], + payment_behavior: 'default_incomplete' + }) + + expect(subscription.latest_invoice.payment_intent.status).to eq('requires_payment_method') + + subscription = Stripe::Subscription.create({ + customer: customer.id, + plan: plan.id, + expand: ['latest_invoice.payment_intent'], + trial_period_days: 14 + }) + + expect(subscription.latest_invoice.payment_intent).to be_nil end end From 73430a85af7cea37b73c40cf5eefa811e47f8b6d Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 7 Dec 2021 16:32:51 +0000 Subject: [PATCH 065/158] Add basic `Subscription.transfer_data` support --- lib/stripe_mock/request_handlers/subscriptions.rb | 8 +++++++- spec/shared_stripe_examples/subscription_examples.rb | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index b6314f3bd..e9b908d38 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -97,7 +97,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan expand) + allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan expand transfer_data) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) @@ -141,6 +141,12 @@ def create_subscription(route, method_url, params, headers) subscription[:canceled_at] = Time.now.utc.to_i end + if params[:transfer_data] && !params[:transfer_data].empty? + throw Stripe::InvalidRequestError.new(missing_param_message("transfer_data[destination]")) unless params[:transfer_data][:destination] + subscription[:transfer_data] = params[:transfer_data].dup + subscription[:transfer_data][:amount_percent] ||= 100 + end + if (s = params[:expand]&.first { |s| s.starts_with? 'latest_invoice' }) payment_intent = nil unless subscription[:status] == 'trialing' diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index cbc8f6c41..e725cdacd 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -302,6 +302,15 @@ expect(customer.subscriptions.data.first.status).to eq('incomplete') end + it "allows setting transfer_data" do + customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) + + sub = Stripe::Subscription.create({ customer: customer.id, plan: plan.id, transfer_data: {destination: "acct_0000000000000000", amount_percent: 50} }) + + expect(sub.transfer_data.destination).to eq("acct_0000000000000000") + expect(sub.transfer_data.amount_percent).to eq(50) + end + it "throws an error when subscribing a customer with no card" do plan = stripe_helper.create_plan(id: 'enterprise', product: product.id, amount: 499) customer = Stripe::Customer.create(id: 'cardless') @@ -1305,7 +1314,6 @@ expect(customer.subscriptions.first.metadata['foo']).to eq('bar') end end - end shared_examples 'Customer Subscriptions with prices' do From be37bc1b8b3cedd10428bc66f04eeb4cdcc31c35 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Thu, 9 Dec 2021 20:43:31 -0300 Subject: [PATCH 066/158] Test added --- spec/shared_stripe_examples/subscription_examples.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index e725cdacd..edfd7ac2c 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1119,6 +1119,18 @@ expect(e.message).to eq("Invalid timestamp: must be an integer") } end + + it "converts billing_cycle_anchor=now to a timestamp" do + customer = Stripe::Customer.create(id: 'test_billing_anchor', plan: plan.id, source: gen_card_tk) + + sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + sub.billing_cycle_anchor = 'now' + sub.save + + expect(sub.billing_cycle_anchor).to be_a(Integer) + end + + end context "cancelling a subscription" do From 1f8362efd35312cafd5ad8939477b53532ee480f Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 14 Dec 2021 12:23:22 +0000 Subject: [PATCH 067/158] Add `payment_intent.canceled` fixture --- lib/stripe_mock/api/webhooks.rb | 1 + .../payment_intent.canceled.json | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 314e142eb..b6893c875 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -74,6 +74,7 @@ def self.event_list 'invoiceitem.created', 'invoiceitem.updated', 'invoiceitem.deleted', + 'payment_intent.canceled', 'payment_intent.processing', 'payment_intent.succeeded', 'payment_intent.payment_failed', diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json b/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json new file mode 100644 index 000000000..46f5142d7 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json @@ -0,0 +1,68 @@ +{ + "id": "evt_00000000000000", + "object": "event", + "api_version": "2018-02-28", + "created": 1578499109, + "data": { + "object": { + "id": "pi_00000000000000", + "object": "payment_intent", + "amount": 900, + "amount_capturable": 0, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "canceled_at": 1578499109, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges?payment_intent=pi_00000000000000" + }, + "client_secret": "pi_00000000000000", + "confirmation_method": "automatic", + "created": 1578499108, + "currency": "eur", + "customer": "cus_00000000000000", + "description": null, + "invoice": null, + "last_payment_error": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_00000000000000", + "payment_method_options": { + "card": { + "installments": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card", + "sepa_debit" + ], + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "canceled", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": null + }, + "type": "payment_intent.canceled" +} From 67a2d2a81d816e4bf943109c5e8924c23985db38 Mon Sep 17 00:00:00 2001 From: Weston Ganger Date: Thu, 16 Dec 2021 13:50:30 -0800 Subject: [PATCH 068/158] Improve checkout_session --- CHANGELOG.md | 6 ++++++ .../request_handlers/checkout_session.rb | 20 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50b15afaa..f76a14f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### Unreleased + +- [#806](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/806) - Remove `payment_method_types` from required arguments for `Stripe::Checkout::Session` +- [#806](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/806) - Raise more helpful exception when Stripe::Price cannot be found within a `Stripe::Checkout::Session` `line_items` argument. + + ### 3.1.0.rc3 (pre-release 2021-07-14) - [#785](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/785): `Stripe::Product` no longer requires `type`. [@TastyPi](https://github.com/TastyPi) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 4e70ca011..c0d408b85 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -12,7 +12,7 @@ def Session.included(klass) def new_session(route, method_url, params, headers) id = params[:id] || new_id('cs') - [:cancel_url, :payment_method_types, :success_url].each do |p| + [:cancel_url, :success_url].each do |p| require_param(p) if params[p].nil? || params[p].empty? end @@ -46,7 +46,18 @@ def new_session(route, method_url, params, headers) amount = nil currency = nil if line_items - amount = line_items.map { |line_item| prices[line_item[:price]][:unit_amount] * line_item[:quantity] }.sum + amount = 0 + + line_items.each do |line_item| + price = prices[line_item[:price]] + + if price.nil? + raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}") + end + + amount += (price[:unit_amount] * line_item[:quantity]) + end + currency = prices[line_items.first[:price]][:currency] end @@ -143,6 +154,11 @@ def list_line_items(route, method_url, params, headers) line_items = assert_existence :checkout_session_line_items, $1, checkout_session_line_items[$1] line_items.map do |line_item| price = prices[line_item[:price]].clone + + if price.nil? + raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}") + end + { id: line_item[:id], object: "item", From 2879016d20dc3c690b17fc258dbe2e0e18a2a59e Mon Sep 17 00:00:00 2001 From: Lauren Fackler Date: Wed, 29 Dec 2021 15:12:31 -0500 Subject: [PATCH 069/158] Add automatic_tax to allowed subscription params --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index fc0f59234..324cfe1f1 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -97,7 +97,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand) + allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) From bb83f457c405e278e90b57019870abd73825bf4e Mon Sep 17 00:00:00 2001 From: Nik Wakelin Date: Fri, 31 Dec 2021 12:41:25 +1300 Subject: [PATCH 070/158] Changes a #first call with a block to #find --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index fc0f59234..da5087f29 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -147,7 +147,7 @@ def create_subscription(route, method_url, params, headers) subscription[:transfer_data][:amount_percent] ||= 100 end - if (s = params[:expand]&.first { |s| s.starts_with? 'latest_invoice' }) + if (s = params[:expand]&.find { |s| s.starts_with? 'latest_invoice' }) payment_intent = nil unless subscription[:status] == 'trialing' intent_status = subscription[:status] == 'incomplete' ? 'requires_payment_method' : 'succeeded' From c8f388b3690dab29fbd9fd6b7ab13b0c683f47fa Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 4 Jan 2022 18:26:15 +0000 Subject: [PATCH 071/158] Replace File.exists? with File.exist? File.exists? is deprecated and File.exist? has existed since at least Ruby 2.4.0 --- lib/stripe_mock/api/webhooks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index b6893c875..1a4340257 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -4,7 +4,7 @@ def self.mock_webhook_payload(type, params = {}) fixture_file = File.join(@webhook_fixture_path, "#{type}.json") - unless File.exists?(fixture_file) + unless File.exist?(fixture_file) unless Webhooks.event_list.include?(type) raise UnsupportedRequestError.new "Unsupported webhook event `#{type}` (Searched in #{@webhook_fixture_path})" end From 94395640462ad3cca7160f2ca9b730a6e807cb78 Mon Sep 17 00:00:00 2001 From: Artem Krivonozhko Date: Thu, 6 Jan 2022 20:07:51 +0300 Subject: [PATCH 072/158] Create SetupIntent with another status is payment method is provided --- lib/stripe_mock/request_handlers/setup_intents.rb | 4 +++- spec/shared_stripe_examples/setup_intent_examples.rb | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/setup_intents.rb b/lib/stripe_mock/request_handlers/setup_intents.rb index 8c6cd4239..ac06c8a3d 100644 --- a/lib/stripe_mock/request_handlers/setup_intents.rb +++ b/lib/stripe_mock/request_handlers/setup_intents.rb @@ -25,10 +25,12 @@ def SetupIntents.included(klass) def new_setup_intent(route, method_url, params, headers) id = new_id('si') + status = params[:payment_method] ? 'requires_action' : 'requires_payment_method' setup_intents[id] = Data.mock_setup_intent( params.merge( - id: id + id: id, + status: status ) ) diff --git a/spec/shared_stripe_examples/setup_intent_examples.rb b/spec/shared_stripe_examples/setup_intent_examples.rb index ecf9b3052..10cbdc608 100644 --- a/spec/shared_stripe_examples/setup_intent_examples.rb +++ b/spec/shared_stripe_examples/setup_intent_examples.rb @@ -10,6 +10,14 @@ expect(setup_intent.status).to eq('requires_payment_method') end + it 'creates a stripe setup intent with payment method' do + setup_intent = Stripe::SetupIntent.create(payment_method: 'random') + + expect(setup_intent.id).to match(/^test_si/) + expect(setup_intent.metadata.to_hash).to eq({}) + expect(setup_intent.status).to eq('requires_action') + end + describe "listing setup_intent" do before do 3.times do From 8d68c774b2e6659e4a63009529dee9e83a6e3966 Mon Sep 17 00:00:00 2001 From: Augusto Cesar Date: Mon, 10 Jan 2022 10:16:47 +0100 Subject: [PATCH 073/158] fix: use start_with? instead of starts_with? The wrong method name was being used, this fixes it. --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index b8596afd0..d8c31d1d9 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -147,7 +147,7 @@ def create_subscription(route, method_url, params, headers) subscription[:transfer_data][:amount_percent] ||= 100 end - if (s = params[:expand]&.find { |s| s.starts_with? 'latest_invoice' }) + if (s = params[:expand]&.find { |s| s.start_with? 'latest_invoice' }) payment_intent = nil unless subscription[:status] == 'trialing' intent_status = subscription[:status] == 'incomplete' ? 'requires_payment_method' : 'succeeded' From c95391f51debf33526ab6d28d3a25cf983fa9b9d Mon Sep 17 00:00:00 2001 From: Pedro Moreira Date: Fri, 28 Jan 2022 14:33:04 +0000 Subject: [PATCH 074/158] Add webhook fixtures for `invoice.upcoming` event --- lib/stripe_mock/api/webhooks.rb | 1 + .../webhook_fixtures/invoice.upcoming.json | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/invoice.upcoming.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 1a4340257..6f36f35d3 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -71,6 +71,7 @@ def self.event_list 'invoice.updated', 'invoice.payment_succeeded', 'invoice.payment_failed', + 'invoice.upcoming', 'invoiceitem.created', 'invoiceitem.updated', 'invoiceitem.deleted', diff --git a/lib/stripe_mock/webhook_fixtures/invoice.upcoming.json b/lib/stripe_mock/webhook_fixtures/invoice.upcoming.json new file mode 100644 index 000000000..72b186096 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/invoice.upcoming.json @@ -0,0 +1,70 @@ +{ + "created": 1326853478, + "livemode": false, + "id": "evt_00000000000000", + "type": "invoice.upcoming", + "status": "draft", + "object": "event", + "data": { + "object": { + "created": 1380674206, + "period_start": 1378082075, + "period_end": 1380674075, + "lines": { + "count": 1, + "object": "list", + "url": "/v1/invoices/upcoming/lines?customer=cus_00000000000000", + "data": [ + { + "id": "su_2hksGtIPylSBg2", + "object": "line_item", + "type": "subscription", + "livemode": true, + "amount": 100, + "currency": "usd", + "proration": false, + "period": { + "start": 1383759042, + "end": 1386351042 + }, + "quantity": 1, + "plan": { + "interval": "month", + "amount": 100, + "currency": "usd", + "id": "fkx0AFo", + "object": "plan", + "livemode": false, + "interval_count": 1, + "trial_period_days": null, + "product": "pr_00000000000000", + "metadata": {} + }, + "description": null, + "metadata": null + } + ] + }, + "subtotal": 1000, + "total": 1000, + "customer": "cus_00000000000000", + "object": "invoice", + "attempted": false, + "closed": false, + "paid": false, + "livemode": false, + "attempt_count": 0, + "amount_due": 1000, + "currency": "usd", + "starting_balance": 0, + "ending_balance": 0, + "next_payment_attempt": null, + "charge": null, + "discount": null, + "application_fee": null, + "subscription": "sub_00000000000000", + "metadata": {}, + "description": null + } + } +} From 2dc59f053e9029c0533e697dc77743ddee7096ec Mon Sep 17 00:00:00 2001 From: Adrien Siami Date: Fri, 28 Jan 2022 17:10:18 +0100 Subject: [PATCH 075/158] Subscriptions: Rename start by start_date --- lib/stripe_mock/data.rb | 2 +- spec/shared_stripe_examples/subscription_examples.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 738d29030..83c922a69 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -365,7 +365,7 @@ def self.mock_subscription(params={}) canceled_at: nil, collection_method: 'charge_automatically', ended_at: nil, - start: 1308595038, + start_date: 1308595038, object: 'subscription', trial_start: 1308595038, trial_end: 1308681468, diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index edfd7ac2c..db312c1dd 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -712,7 +712,7 @@ }) expect(subscription.latest_invoice.payment_intent.status).to eq('requires_payment_method') - + subscription = Stripe::Subscription.create({ customer: customer.id, plan: plan.id, @@ -1256,6 +1256,10 @@ expect(subscription.items.data.first.plan.currency).to eq('usd') expect(subscription.items.data.first.quantity).to eq(2) end + + it "has a start_date attribute" do + expect(subscription).to respond_to(:start_date) + end end context "retrieve multiple subscriptions" do From 3a14a8bd7e9e2e259fcc64987c852b4d8146d54c Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Tue, 1 Feb 2022 15:29:01 +0000 Subject: [PATCH 076/158] Add `mandate.updated` fixture --- lib/stripe_mock/api/webhooks.rb | 1 + .../webhook_fixtures/mandate.updated.json | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/mandate.updated.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 6f36f35d3..0b92a4b84 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -75,6 +75,7 @@ def self.event_list 'invoiceitem.created', 'invoiceitem.updated', 'invoiceitem.deleted', + 'mandate.updated', 'payment_intent.canceled', 'payment_intent.processing', 'payment_intent.succeeded', diff --git a/lib/stripe_mock/webhook_fixtures/mandate.updated.json b/lib/stripe_mock/webhook_fixtures/mandate.updated.json new file mode 100644 index 000000000..858fe3602 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/mandate.updated.json @@ -0,0 +1,34 @@ +{ + "created": 1326853478, + "livemode": false, + "id": "evt_00000000000000", + "type": "mandate.updated", + "object": "event", + "data": { + "object": { + "id": "mandate_000000000000000000000000", + "object": "mandate", + "customer_acceptance": { + "accepted_at": 1326853478, + "online": { + "ip_address": "0.0.0.0", + "user_agent": "UserAgent" + }, + "type": "online" + }, + "livemode": false, + "multi_use": {}, + "payment_method": "pm_000000000000000000000000", + "payment_method_details": {}, + "status": "active", + "type": "multi_use" + }, + "previous_attributes": { + "payment_method_details": { + "bacs_debit": { + "network_status": "pending" + } + } + } + } +} From 9c5e87530dae23ee5d63f49b27c318e06a99e852 Mon Sep 17 00:00:00 2001 From: Cameron2920 Date: Thu, 3 Feb 2022 03:32:42 -0500 Subject: [PATCH 077/158] Added delete account support. --- lib/stripe_mock/request_handlers/accounts.rb | 23 ++++++++++++++----- .../account_examples.rb | 10 +++++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/stripe_mock/request_handlers/accounts.rb b/lib/stripe_mock/request_handlers/accounts.rb index bac67965e..ea36619c5 100644 --- a/lib/stripe_mock/request_handlers/accounts.rb +++ b/lib/stripe_mock/request_handlers/accounts.rb @@ -4,12 +4,13 @@ module Accounts VALID_START_YEAR = 2009 def Accounts.included(klass) - klass.add_handler 'post /v1/accounts', :new_account - klass.add_handler 'get /v1/account', :get_account - klass.add_handler 'get /v1/accounts/(.*)', :get_account - klass.add_handler 'post /v1/accounts/(.*)', :update_account - klass.add_handler 'get /v1/accounts', :list_accounts - klass.add_handler 'post /oauth/deauthorize',:deauthorize + klass.add_handler 'post /v1/accounts', :new_account + klass.add_handler 'get /v1/account', :get_account + klass.add_handler 'get /v1/accounts/(.*)', :get_account + klass.add_handler 'post /v1/accounts/(.*)', :update_account + klass.add_handler 'get /v1/accounts', :list_accounts + klass.add_handler 'post /oauth/deauthorize', :deauthorize + klass.add_handler 'delete /v1/accounts/(.*)', :delete_account end def new_account(route, method_url, params, headers) @@ -48,6 +49,16 @@ def deauthorize(route, method_url, params, headers) Stripe::StripeObject.construct_from(:stripe_user_id => params[:stripe_user_id]) end + def delete_account(route, method_url, params, headers) + init_account + route =~ method_url + assert_existence :account, $1, accounts[$1] + accounts[$1] = { + id: accounts[$1][:id], + deleted: true + } + end + private def init_account diff --git a/spec/shared_stripe_examples/account_examples.rb b/spec/shared_stripe_examples/account_examples.rb index b595d84d1..596602d21 100644 --- a/spec/shared_stripe_examples/account_examples.rb +++ b/spec/shared_stripe_examples/account_examples.rb @@ -1,7 +1,7 @@ require 'spec_helper' shared_examples 'Account API' do - describe 'retrive accounts' do + describe 'retrieve accounts' do it 'retrieves a stripe account', live: true do account = Stripe::Account.retrieve @@ -86,6 +86,14 @@ end end + describe 'delete account' do + it 'deletes a stripe account' do + account = Stripe::Account.create(email: 'test@test.com') + account = account.delete + expect(account.deleted).to eq(true) + end + end + it 'deauthorizes the stripe account', live: false do account = Stripe::Account.retrieve result = account.deauthorize('CLIENT_ID') From 824da36c3b5e5a90f61e394884c20ed6407d099d Mon Sep 17 00:00:00 2001 From: Robert Clark Date: Tue, 22 Mar 2022 17:26:06 -0400 Subject: [PATCH 078/158] Allow user to pass in a customer ID on create A few lines down `params[:id]` is used, however since it is not in the allowed params list it is not possible to take the code path to create a subscription with an existing param. This allows the ID param through. --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index d8c31d1d9..d4c1ffef3 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -97,7 +97,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax) + allowed_params = %w(id customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) From 77aa34a2d91c80bf5954ec062a2acf2e5cf0a582 Mon Sep 17 00:00:00 2001 From: Robert Clark Date: Sat, 26 Mar 2022 15:55:42 -0400 Subject: [PATCH 079/158] Update all webhook fixtures with the latest version from the Stripe API --- lib/stripe_mock/api/webhooks.rb | 80 ++++-- .../webhook_fixtures/balance.available.json | 46 ++-- .../webhook_fixtures/charge.captured.json | 143 +++++++++++ .../charge.dispute.created.json | 79 ++++-- .../webhook_fixtures/charge.failed.json | 169 ++++--------- .../charge.refund.updated.json | 35 +++ .../webhook_fixtures/charge.refunded.json | 195 ++++++++++---- .../webhook_fixtures/charge.succeeded.json | 157 ++++++++---- .../checkout.session.completed.json | 79 ++++++ .../webhook_fixtures/customer.created.json | 83 +++--- .../webhook_fixtures/customer.deleted.json | 68 ++--- .../customer.source.created.json | 53 ++-- .../customer.source.updated.json | 61 +++-- .../customer.subscription.created.json | 182 +++++++++---- .../customer.subscription.deleted.json | 179 +++++++++---- .../customer.subscription.updated.json | 191 ++++++++++---- .../webhook_fixtures/customer.updated.json | 85 +++---- .../webhook_fixtures/invoice.created.json | 225 +++++++++++++---- .../webhook_fixtures/invoice.finalized.json | 171 +++++++++++++ .../webhook_fixtures/invoice.paid.json | 171 +++++++++++++ .../invoice.payment_action_required.json | 171 +++++++++++++ .../invoice.payment_failed.json | 232 +++++++++++------ .../invoice.payment_succeeded.json | 239 +++++++++++------- .../webhook_fixtures/invoice.updated.json | 228 +++++++++++++---- .../webhook_fixtures/invoiceitem.created.json | 100 +++++++- .../webhook_fixtures/invoiceitem.updated.json | 102 +++++++- ...ment_intent.amount_capturable_updated.json | 170 +++++++++++++ .../payment_intent.canceled.json | 45 ++-- .../payment_intent.created.json | 86 +++++++ .../payment_intent.payment_failed.json | 197 +++++++++------ .../payment_intent.requires_action.json | 191 ++++++++++++++ .../payment_intent.succeeded.json | 138 ++++++---- .../payment_link.created.json | 47 ++++ .../payment_link.updated.json | 50 ++++ .../payment_method.attached.json | 63 +++++ .../webhook_fixtures/payout.created.json | 40 +++ .../webhook_fixtures/payout.paid.json | 40 +++ .../webhook_fixtures/payout.updated.json | 46 ++++ .../webhook_fixtures/plan.created.json | 43 +++- .../webhook_fixtures/plan.deleted.json | 43 +++- .../webhook_fixtures/plan.updated.json | 48 +++- .../webhook_fixtures/price.created.json | 42 +++ .../webhook_fixtures/price.deleted.json | 42 +++ .../webhook_fixtures/price.updated.json | 48 ++++ .../webhook_fixtures/product.created.json | 32 ++- .../webhook_fixtures/product.deleted.json | 34 ++- .../webhook_fixtures/product.updated.json | 39 +-- .../webhook_fixtures/quote.accepted.json | 92 +++++++ .../webhook_fixtures/quote.canceled.json | 92 +++++++ .../webhook_fixtures/quote.created.json | 92 +++++++ .../webhook_fixtures/quote.finalized.json | 92 +++++++ .../setup_intent.canceled.json | 46 ++++ .../setup_intent.created.json | 51 ++++ .../setup_intent.setup_failed.json | 100 ++++++++ .../setup_intent.succeeded.json | 46 ++++ .../subscription_schedule.canceled.json | 119 +++++++++ .../subscription_schedule.created.json | 114 +++++++++ .../subscription_schedule.released.json | 111 ++++++++ .../subscription_schedule.updated.json | 125 +++++++++ 59 files changed, 4961 insertions(+), 1127 deletions(-) create mode 100644 lib/stripe_mock/webhook_fixtures/charge.captured.json create mode 100644 lib/stripe_mock/webhook_fixtures/charge.refund.updated.json create mode 100644 lib/stripe_mock/webhook_fixtures/checkout.session.completed.json create mode 100644 lib/stripe_mock/webhook_fixtures/invoice.finalized.json create mode 100644 lib/stripe_mock/webhook_fixtures/invoice.paid.json create mode 100644 lib/stripe_mock/webhook_fixtures/invoice.payment_action_required.json create mode 100644 lib/stripe_mock/webhook_fixtures/payment_intent.amount_capturable_updated.json create mode 100644 lib/stripe_mock/webhook_fixtures/payment_intent.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/payment_intent.requires_action.json create mode 100644 lib/stripe_mock/webhook_fixtures/payment_link.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/payment_link.updated.json create mode 100644 lib/stripe_mock/webhook_fixtures/payment_method.attached.json create mode 100644 lib/stripe_mock/webhook_fixtures/payout.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/payout.paid.json create mode 100644 lib/stripe_mock/webhook_fixtures/payout.updated.json create mode 100644 lib/stripe_mock/webhook_fixtures/price.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/price.deleted.json create mode 100644 lib/stripe_mock/webhook_fixtures/price.updated.json create mode 100644 lib/stripe_mock/webhook_fixtures/quote.accepted.json create mode 100644 lib/stripe_mock/webhook_fixtures/quote.canceled.json create mode 100644 lib/stripe_mock/webhook_fixtures/quote.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/quote.finalized.json create mode 100644 lib/stripe_mock/webhook_fixtures/setup_intent.canceled.json create mode 100644 lib/stripe_mock/webhook_fixtures/setup_intent.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/setup_intent.setup_failed.json create mode 100644 lib/stripe_mock/webhook_fixtures/setup_intent.succeeded.json create mode 100644 lib/stripe_mock/webhook_fixtures/subscription_schedule.canceled.json create mode 100644 lib/stripe_mock/webhook_fixtures/subscription_schedule.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/subscription_schedule.released.json create mode 100644 lib/stripe_mock/webhook_fixtures/subscription_schedule.updated.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 0b92a4b84..eaceacc1c 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -37,61 +37,91 @@ def self.mock_webhook_event(type, params={}) module Webhooks def self.event_list @__list = [ - 'account.updated', 'account.application.deauthorized', 'account.external_account.created', - 'account.external_account.updated', 'account.external_account.deleted', + 'account.external_account.updated', + 'account.updated', 'balance.available', - 'charge.succeeded', - 'charge.updated', - 'charge.failed', - 'charge.refunded', - 'charge.dispute.created', - 'charge.dispute.updated', + 'charge.captured', 'charge.dispute.closed', + 'charge.dispute.created', 'charge.dispute.funds_reinstated', 'charge.dispute.funds_withdrawn', + 'charge.dispute.updated', + 'charge.failed', + 'charge.refund.updated', + 'charge.refunded', + 'charge.succeeded', + 'charge.updated', + 'checkout.session.completed', 'checkout.session.completed.payment_mode', 'checkout.session.completed.setup_mode', + 'coupon.created', + 'coupon.deleted', + 'customer.created', + 'customer.deleted', + 'customer.discount.created', + 'customer.discount.deleted', + 'customer.discount.updated', 'customer.source.created', 'customer.source.deleted', 'customer.source.updated', - 'customer.created', - 'customer.updated', - 'customer.deleted', 'customer.subscription.created', - 'customer.subscription.updated', 'customer.subscription.deleted', 'customer.subscription.trial_will_end', - 'customer.discount.created', - 'customer.discount.updated', - 'customer.discount.deleted', + 'customer.subscription.updated', + 'customer.updated', 'invoice.created', - 'invoice.updated', - 'invoice.payment_succeeded', + 'invoice.finalized', + 'invoice.paid', + 'invoice.payment_action_required', 'invoice.payment_failed', + 'invoice.payment_succeeded', 'invoice.upcoming', + 'invoice.updated', 'invoiceitem.created', - 'invoiceitem.updated', 'invoiceitem.deleted', + 'invoiceitem.updated', 'mandate.updated', + 'payment_intent.amount_capturable_updated', 'payment_intent.canceled', + 'payment_intent.created', + 'payment_intent.payment_failed', 'payment_intent.processing', + 'payment_intent.requires_action', 'payment_intent.succeeded', - 'payment_intent.payment_failed', + 'payment_link.created', + 'payment_link.updated', + 'payment_method.attached', + 'payout.created', + 'payout.paid', + 'payout.updated', 'plan.created', - 'plan.updated', 'plan.deleted', + 'plan.updated', + 'price.created', + 'price.deleted', + 'price.updated', 'product.created', - 'product.updated', 'product.deleted', - 'coupon.created', - 'coupon.deleted', + 'product.updated', + 'quote.accepted', + 'quote.canceled', + 'quote.created', + 'quote.finalized', + 'setup_intent.canceled', + 'setup_intent.created', + 'setup_intent.setup_failed', + 'setup_intent.succeeded', + 'subscription_schedule.canceled', + 'subscription_schedule.created', + 'subscription_schedule.released', + 'subscription_schedule.updated', 'transfer.created', + 'transfer.failed', 'transfer.paid', - 'transfer.updated', - 'transfer.failed' + 'transfer.updated' ] end end diff --git a/lib/stripe_mock/webhook_fixtures/balance.available.json b/lib/stripe_mock/webhook_fixtures/balance.available.json index 60cbe7bba..ec94b125c 100644 --- a/lib/stripe_mock/webhook_fixtures/balance.available.json +++ b/lib/stripe_mock/webhook_fixtures/balance.available.json @@ -1,31 +1,37 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "balance.available", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648319937, "data": { "object": { - "pending": [ - { - "amount": 2217, - "currency": "usd" - } - ], + "object": "balance", "available": [ { - "amount": 0, - "currency": "usd" + "amount": 7648, + "currency": "usd", + "source_types": { + "card": 7648 + } } ], - "instant_available": [ + "livemode": false, + "pending": [ { - "amount": 0, - "currency": "usd" + "amount": 37734, + "currency": "usd", + "source_types": { + "card": 37734 + } } - ], - "livemode": false, - "object": "balance" + ] } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": null, + "idempotency_key": null + }, + "type": "balance.available" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/charge.captured.json b/lib/stripe_mock/webhook_fixtures/charge.captured.json new file mode 100644 index 000000000..5eb9c2cdd --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/charge.captured.json @@ -0,0 +1,143 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648319938, + "data": { + "object": { + "id": "ch_000000000000000000000000", + "object": "charge", + "amount": 100, + "amount_captured": 100, + "amount_refunded": 0, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_000000000000000000000000", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", + "captured": true, + "created": 1648319937, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "destination": null, + "dispute": null, + "disputed": false, + "failure_code": null, + "failure_message": null, + "fraud_details": { + }, + "invoice": null, + "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 64, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": null, + "payment_method": "card_000000000000000000000000", + "payment_method_details": { + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "installments": null, + "last4": "4242", + "mandate": null, + "network": "visa", + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/acct_0000000000000000/ch_000000000000000000000000/rcpt_0000000000000000000000000000000", + "refunded": false, + "refunds": { + "object": "list", + "data": [ + + ], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_000000000000000000000000/refunds" + }, + "review": null, + "shipping": null, + "source": { + "id": "card_000000000000000000000000", + "object": "card", + "address_city": null, + "address_country": null, + "address_line1": null, + "address_line1_check": null, + "address_line2": null, + "address_state": null, + "address_zip": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": null, + "cvc_check": null, + "dynamic_last4": null, + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "last4": "4242", + "metadata": { + }, + "name": null, + "tokenization_method": null + }, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + }, + "previous_attributes": { + "amount_captured": 0, + "balance_transaction": null, + "captured": false + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "d67cfb41-4cc5-47d0-b2ca-c571e6420952" + }, + "type": "charge.captured" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json b/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json index 96de1e5a8..5972712c3 100644 --- a/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json +++ b/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json @@ -1,22 +1,69 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "charge.dispute.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648319940, "data": { "object": { - "charge": "ch_00000000000000", - "amount": 1000, - "created": 1381080223, - "status": "needs_response", - "livemode": false, - "currency": "usd", + "id": "dp_000000000000000000000000", "object": "dispute", - "reason": "general", - "balance_transaction": "txn_00000000000000", - "evidence_due_by": 1382745599, - "evidence": null + "amount": 100, + "balance_transaction": null, + "balance_transactions": [ + + ], + "charge": "ch_000000000000000000000000", + "created": 1648319940, + "currency": "usd", + "evidence": { + "access_activity_log": null, + "billing_address": null, + "cancellation_policy": null, + "cancellation_policy_disclosure": null, + "cancellation_rebuttal": null, + "customer_communication": null, + "customer_email_address": null, + "customer_name": null, + "customer_purchase_ip": null, + "customer_signature": null, + "duplicate_charge_documentation": null, + "duplicate_charge_explanation": null, + "duplicate_charge_id": null, + "product_description": null, + "receipt": null, + "refund_policy": null, + "refund_policy_disclosure": null, + "refund_refusal_explanation": null, + "service_date": null, + "service_documentation": null, + "shipping_address": null, + "shipping_carrier": null, + "shipping_date": null, + "shipping_documentation": null, + "shipping_tracking_number": null, + "uncategorized_file": null, + "uncategorized_text": null + }, + "evidence_details": { + "due_by": 1649116799, + "has_evidence": false, + "past_due": false, + "submission_count": 0 + }, + "is_charge_refundable": true, + "livemode": false, + "metadata": { + }, + "payment_intent": null, + "reason": "fraudulent", + "status": "warning_needs_response" } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "fa04076c-aa1f-40b8-9191-95465afc576e" + }, + "type": "charge.dispute.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/charge.failed.json b/lib/stripe_mock/webhook_fixtures/charge.failed.json index 2d551513c..03da725ea 100644 --- a/lib/stripe_mock/webhook_fixtures/charge.failed.json +++ b/lib/stripe_mock/webhook_fixtures/charge.failed.json @@ -1,22 +1,19 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "charge.failed", + "id": "evt_000000000000000000000000", "object": "event", - "request": null, - "pending_webhooks": 1, - "api_version": "2018-02-28", + "api_version": "2020-08-27", + "created": 1648320028, "data": { "object": { - "id": "ch_00000000000000", + "id": "ch_000000000000000000000000", "object": "charge", - "amount": 100, + "amount": 2000, + "amount_captured": 0, "amount_refunded": 0, "application": null, "application_fee": null, "application_fee_amount": null, - "balance_transaction": "txn_00000000000000", + "balance_transaction": null, "billing_details": { "address": { "city": null, @@ -27,27 +24,39 @@ "state": null }, "email": null, - "name": "Jenny Rosen", + "name": null, "phone": null }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", "captured": false, - "created": 1572389205, - "currency": "cad", + "created": 1648320028, + "currency": "usd", "customer": null, - "description": "My First Test Charge (created for API docs)", + "description": "(created by Stripe CLI)", + "destination": null, "dispute": null, - "failure_code": null, - "failure_message": null, - "fraud_details": {}, + "disputed": false, + "failure_code": "card_declined", + "failure_message": "Your card was declined.", + "fraud_details": { + }, "invoice": null, "livemode": false, - "metadata": {}, + "metadata": { + }, "on_behalf_of": null, "order": null, - "outcome": null, + "outcome": { + "network_status": "declined_by_network", + "reason": "generic_decline", + "risk_level": "normal", + "risk_score": 1, + "seller_message": "The bank did not return any further details with this decline.", + "type": "issuer_declined" + }, "paid": false, - "payment_intent": null, - "payment_method": "card_00000000000000", + "payment_intent": "pi_000000000000000000000000", + "payment_method": "pm_000000000000000000000000", "payment_method_details": { "card": { "brand": "visa", @@ -57,12 +66,13 @@ "cvc_check": null }, "country": "US", - "exp_month": 8, - "exp_year": 2019, - "fingerprint": "C8aRpBae2T8GeJcn", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "kMzSwhaalD1uR96R", "funding": "credit", "installments": null, - "last4": "4242", + "last4": "0002", + "mandate": null, "network": "visa", "three_d_secure": null, "wallet": null @@ -71,16 +81,20 @@ }, "receipt_email": null, "receipt_number": null, - "receipt_url": "https://pay.stripe.com/receipts/acct_1C0lZEHvOcc4e36o/ch_1FZ3SbHsssvOcc4e36o87NHFK7i/rcpt_G5DuglJFhskXjsEDnLzxF6ESuGQe0Qj", + "receipt_url": null, "refunded": false, "refunds": { "object": "list", - "data": [], + "data": [ + + ], "has_more": false, - "url": "/v1/charges/ch_1FZ3SbHvOcc4e36o87NHFK7i/refunds" + "total_count": 0, + "url": "/v1/charges/ch_000000000000000000000000/refunds" }, "review": null, "shipping": null, + "source": null, "source_transfer": null, "statement_descriptor": null, "statement_descriptor_suffix": null, @@ -89,96 +103,11 @@ "transfer_group": null } }, - "webhook": { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "charge.failed", - "object": "event", - "request": null, - "pending_webhooks": 1, - "api_version": "2018-02-28", - "data": { - "object": { - "id": "ch_00000000000000", - "object": "charge", - "amount": 100, - "amount_refunded": 0, - "application": null, - "application_fee": null, - "application_fee_amount": null, - "balance_transaction": "txn_00000000000000", - "billing_details": { - "address": { - "city": null, - "country": null, - "line1": null, - "line2": null, - "postal_code": null, - "state": null - }, - "email": null, - "name": "Jenny Rosen", - "phone": null - }, - "captured": false, - "created": 1572389205, - "currency": "cad", - "customer": null, - "description": "My First Test Charge (created for API docs)", - "dispute": null, - "failure_code": null, - "failure_message": null, - "fraud_details": {}, - "invoice": null, - "livemode": false, - "metadata": {}, - "on_behalf_of": null, - "order": null, - "outcome": null, - "paid": false, - "payment_intent": null, - "payment_method": "card_00000000000000", - "payment_method_details": { - "card": { - "brand": "visa", - "checks": { - "address_line1_check": null, - "address_postal_code_check": null, - "cvc_check": null - }, - "country": "US", - "exp_month": 8, - "exp_year": 2019, - "fingerprint": "C8aRpBae2T8GeJcn", - "funding": "credit", - "installments": null, - "last4": "4242", - "network": "visa", - "three_d_secure": null, - "wallet": null - }, - "type": "card" - }, - "receipt_email": null, - "receipt_number": null, - "receipt_url": "https://pay.stripe.com/receipts/acct_1C0lZEHvOcc4e36o/ch_1FZ3SbHvOcc4sssse36o87NHFK7i/rcpt_G5DuglJFhskXjsEDnLzxF6ESuGQe0Qj", - "refunded": false, - "refunds": { - "object": "list", - "data": [], - "has_more": false, - "url": "/v1/charges/ch_1FZ3SbHvOccssssss4e36o87NHFK7i/refunds" - }, - "review": null, - "shipping": null, - "source_transfer": null, - "statement_descriptor": null, - "statement_descriptor_suffix": null, - "status": "failed", - "transfer_data": null, - "transfer_group": null - } - } - } + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "57ddc22f-1d42-446f-8f91-85cf0d32084a" + }, + "type": "charge.failed" } \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/charge.refund.updated.json b/lib/stripe_mock/webhook_fixtures/charge.refund.updated.json new file mode 100644 index 000000000..d3a3f6307 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/charge.refund.updated.json @@ -0,0 +1,35 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648319945, + "data": { + "object": { + "id": "re_000000000000000000000000", + "object": "refund", + "amount": 100, + "balance_transaction": "txn_000000000000000000000000", + "charge": "ch_000000000000000000000000", + "created": 1648319945, + "currency": "usd", + "metadata": { + "order_id": "6735" + }, + "payment_intent": null, + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + }, + "previous_attributes": { + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "38ee50ca-b664-4611-a846-9eaed51bec07" + }, + "type": "charge.refund.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/charge.refunded.json b/lib/stripe_mock/webhook_fixtures/charge.refunded.json index 482d879ec..052db3733 100644 --- a/lib/stripe_mock/webhook_fixtures/charge.refunded.json +++ b/lib/stripe_mock/webhook_fixtures/charge.refunded.json @@ -1,69 +1,164 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "charge.refunded", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648319949, "data": { "object": { - "id": "ch_00000000000000", + "id": "ch_000000000000000000000000", "object": "charge", - "created": 1380933505, + "amount": 100, + "amount_captured": 100, + "amount_refunded": 100, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_000000000000000000000000", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", + "captured": true, + "created": 1648319947, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "destination": null, + "dispute": null, + "disputed": false, + "failure_code": null, + "failure_message": null, + "fraud_details": { + }, + "invoice": null, "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 52, + "seller_message": "Payment complete.", + "type": "authorized" + }, "paid": true, - "amount": 1000, - "currency": "usd", + "payment_intent": null, + "payment_method": "card_000000000000000000000000", + "payment_method_details": { + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "installments": null, + "last4": "4242", + "mandate": null, + "network": "visa", + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/acct_0000000000000000/ch_000000000000000000000000/rcpt_0000000000000000000000000000000", "refunded": true, + "refunds": { + "object": "list", + "data": [ + { + "id": "re_000000000000000000000000", + "object": "refund", + "amount": 100, + "balance_transaction": "txn_000000000000000000000000", + "charge": "ch_000000000000000000000000", + "created": 1648319949, + "currency": "usd", + "metadata": { + }, + "payment_intent": null, + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges/ch_000000000000000000000000/refunds" + }, + "review": null, + "shipping": null, "source": { - "id": "cc_00000000000000", + "id": "card_000000000000000000000000", "object": "card", - "last4": "4242", - "type": "Visa", - "brand": "Visa", - "funding": "credit", - "exp_month": 12, - "exp_year": 2013, - "fingerprint": "wXWJT135mEK107G8", - "customer": "cus_00000000000000", - "country": "US", - "name": "Actual Nothing", + "address_city": null, + "address_country": null, "address_line1": null, + "address_line1_check": null, "address_line2": null, - "address_city": null, "address_state": null, "address_zip": null, - "address_country": null, + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": null, "cvc_check": null, - "address_line1_check": null, - "address_zip_check": null + "dynamic_last4": null, + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "last4": "4242", + "metadata": { + }, + "name": null, + "tokenization_method": null }, - "captured": true, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + }, + "previous_attributes": { + "amount_refunded": 0, + "refunded": false, "refunds": { - "object": "list", - "total_count": 1, - "has_more": false, "data": [ - { - "amount": 1000, - "currency": "usd", - "created": 1381080103, - "object": "refund", - "balance_transaction": "txn_2hkjgg43ucu7K1", - "id": "re_00000000000000" - } - ] - }, - "balance_transaction": "txn_00000000000000", - "failure_message": null, - "failure_code": null, - "amount_refunded": 1000, - "customer": "cus_00000000000000", - "invoice": "in_00000000000000", - "description": null, - "dispute": null, - "metadata": { - }, - "fee": 0 + + ], + "total_count": 0 + } } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "86a8299a-3f47-4aec-8cb4-298bbc8bc050" + }, + "type": "charge.refunded" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/charge.succeeded.json b/lib/stripe_mock/webhook_fixtures/charge.succeeded.json index 2058b9464..4483a6b50 100644 --- a/lib/stripe_mock/webhook_fixtures/charge.succeeded.json +++ b/lib/stripe_mock/webhook_fixtures/charge.succeeded.json @@ -1,55 +1,126 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "charge.succeeded", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320032, "data": { "object": { - "id": "ch_00000000000000", + "id": "ch_000000000000000000000000", "object": "charge", - "created": 1380933505, + "amount": 2000, + "amount_captured": 2000, + "amount_refunded": 0, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_000000000000000000000000", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", + "captured": true, + "created": 1648320032, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "destination": null, + "dispute": null, + "disputed": false, + "failure_code": null, + "failure_message": null, + "fraud_details": { + }, + "invoice": null, "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 2, + "seller_message": "Payment complete.", + "type": "authorized" + }, "paid": true, - "amount": 1000, - "currency": "usd", - "refunded": false, - "source": { - "id": "cc_00000000000000", - "object": "card", - "last4": "4242", - "type": "Visa", - "brand": "Visa", - "exp_month": 12, - "exp_year": 2013, - "fingerprint": "wXWJT135mEK107G8", - "customer": "cus_00000000000000", - "country": "US", - "name": "Actual Nothing", - "address_line1": null, - "address_line2": null, - "address_city": null, - "address_state": null, - "address_zip": null, - "address_country": null, - "cvc_check": null, - "address_line1_check": null, - "address_zip_check": null + "payment_intent": "pi_000000000000000000000000", + "payment_method": "pm_000000000000000000000000", + "payment_method_details": { + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "installments": null, + "last4": "4242", + "mandate": null, + "network": "visa", + "three_d_secure": null, + "wallet": null + }, + "type": "card" }, - "captured": true, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/acct_0000000000000000/ch_000000000000000000000000/rcpt_0000000000000000000000000000000", + "refunded": false, "refunds": { + "object": "list", + "data": [ + ], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_000000000000000000000000/refunds" }, - "balance_transaction": "txn_00000000000000", - "failure_message": null, - "failure_code": null, - "amount_refunded": 0, - "customer": "cus_00000000000000", - "invoice": "in_00000000000000", - "description": null, - "dispute": null, - "metadata": { - } + "review": null, + "shipping": { + "address": { + "city": "San Francisco", + "country": "US", + "line1": "510 Townsend St", + "line2": null, + "postal_code": "94103", + "state": "CA" + }, + "carrier": null, + "name": "Jenny Rosen", + "phone": null, + "tracking_number": null + }, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "7143a956-2f40-407a-b312-43038ae76c86" + }, + "type": "charge.succeeded" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/checkout.session.completed.json b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.json new file mode 100644 index 000000000..9f35556ff --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/checkout.session.completed.json @@ -0,0 +1,79 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648319959, + "data": { + "object": { + "id": "cs_test_0000000000000000000000000000000000000000000000000000000000", + "object": "checkout.session", + "after_expiration": null, + "allow_promotion_codes": null, + "amount_subtotal": 3000, + "amount_total": 3000, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_address_collection": null, + "cancel_url": "https://httpbin.org/post", + "client_reference_id": null, + "consent": null, + "consent_collection": null, + "currency": "usd", + "customer": "cus_00000000000000", + "customer_creation": "always", + "customer_details": { + "email": "stripe@example.com", + "phone": null, + "tax_exempt": "none", + "tax_ids": [ + + ] + }, + "customer_email": null, + "expires_at": 1648406355, + "livemode": false, + "locale": null, + "metadata": { + }, + "mode": "payment", + "payment_intent": "pi_000000000000000000000000", + "payment_link": null, + "payment_method_options": { + }, + "payment_method_types": [ + "card" + ], + "payment_status": "paid", + "phone_number_collection": { + "enabled": false + }, + "recovered_from": null, + "setup_intent": null, + "shipping": null, + "shipping_address_collection": null, + "shipping_options": [ + + ], + "shipping_rate": null, + "status": "complete", + "submit_type": null, + "subscription": null, + "success_url": "https://httpbin.org/post", + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + }, + "url": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": null, + "idempotency_key": null + }, + "type": "checkout.session.completed" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.created.json b/lib/stripe_mock/webhook_fixtures/customer.created.json index 7d6569a74..e31e2fa68 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.created.json +++ b/lib/stripe_mock/webhook_fixtures/customer.created.json @@ -1,55 +1,46 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320107, "data": { "object": { - "object": "customer", - "created": 1375148334, "id": "cus_00000000000000", + "object": "customer", + "address": null, + "balance": 0, + "created": 1648320107, + "currency": null, + "default_source": "card_000000000000000000000000", + "delinquent": false, + "description": "(created by Stripe CLI)", + "discount": null, + "email": null, + "invoice_prefix": "985BA9CB", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, "livemode": false, - "description": null, - "email": "bond@mailinator.com", - "delinquent": true, "metadata": { }, - "preferred_locales": [], - "subscription": null, - "discount": null, - "account_balance": 0, - "sources": { - "object": "list", - "count": 1, - "url": "/v1/customers/cus_2I2AhGQOPmEFeu/cards", - "data": [ - { - "id": "cc_2I2akIhmladin5", - "object": "card", - "last4": "0341", - "type": "Visa", - "brand": "Visa", - "funding": "credit", - "exp_month": 12, - "exp_year": 2013, - "fingerprint": "fWvZEzdbEIFF8QrK", - "customer": "cus_2I2AhGQOPmEFeu", - "country": "US", - "name": "Johnny Goodman", - "address_line1": null, - "address_line2": null, - "address_city": null, - "address_state": null, - "address_zip": null, - "address_country": null, - "cvc_check": "pass", - "address_line1_check": null, - "address_zip_check": null - } - ] - }, - "default_card": "cc_2I2akIhmladin5" + "name": null, + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [ + + ], + "shipping": null, + "tax_exempt": "none", + "test_clock": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "381c7773-97ac-4c18-8fbe-e8bff5e6bbad" + }, + "type": "customer.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.deleted.json b/lib/stripe_mock/webhook_fixtures/customer.deleted.json index 70e656064..6969973c2 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.deleted.json +++ b/lib/stripe_mock/webhook_fixtures/customer.deleted.json @@ -1,42 +1,46 @@ { - "created": 1326853478, + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648319961, "data": { "object": { - "account_balance": 0, - "active_card": { - "address_city": null, - "address_country": null, - "address_line1": null, - "address_line1_check": null, - "address_line2": null, - "address_state": null, - "address_zip": null, - "address_zip_check": null, - "country": "US", - "cvc_check": "pass", - "exp_month": 12, - "exp_year": 2013, - "fingerprint": "wXWJT135mEK107G8", - "last4": "4242", - "name": "1231", - "object": "card", - "type": "Visa", - "brand": "Visa", - "funding": "credit" - }, - "created": 1359947599, + "id": "cus_00000000000000", + "object": "customer", + "address": null, + "balance": 0, + "created": 1648319961, + "currency": null, + "default_source": null, "delinquent": false, - "description": null, + "description": "(created by Stripe CLI)", "discount": null, - "email": "ajoe@mailinator.com", - "id": "cus_00000000000000", + "email": null, + "invoice_prefix": "D343B0B7", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, "livemode": false, - "object": "customer", - "subscription": null + "metadata": { + }, + "name": null, + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [ + + ], + "shipping": null, + "tax_exempt": "none", + "test_clock": null } }, - "id": "evt_00000000000000", "livemode": false, - "object": "event", + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": null + }, "type": "customer.deleted" -} +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.source.created.json b/lib/stripe_mock/webhook_fixtures/customer.source.created.json index 91eba1248..6f55929af 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.source.created.json +++ b/lib/stripe_mock/webhook_fixtures/customer.source.created.json @@ -1,32 +1,41 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.source.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320107, "data": { "object": { - "id": "card_VALID", + "id": "card_000000000000000000000000", "object": "card", - "last4": "4242", - "type": "Visa", - "brand": "Visa", - "funding": "credit", - "exp_month": 3, - "exp_year": 2020, - "fingerprint": "wXWJT135mEK107G8", - "customer": "cus_VALID", - "country": "US", - "name": "Testy Tester", + "address_city": null, + "address_country": null, "address_line1": null, + "address_line1_check": null, "address_line2": null, - "address_city": null, "address_state": null, "address_zip": null, - "address_country": null, - "cvc_check": "pass", - "address_line1_check": null, - "address_zip_check": null + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": "cus_00000000000000", + "cvc_check": null, + "dynamic_last4": null, + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "last4": "4242", + "metadata": { + }, + "name": null, + "tokenization_method": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "381c7773-97ac-4c18-8fbe-e8bff5e6bbad" + }, + "type": "customer.source.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.source.updated.json b/lib/stripe_mock/webhook_fixtures/customer.source.updated.json index 2634deb65..3928df08b 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.source.updated.json +++ b/lib/stripe_mock/webhook_fixtures/customer.source.updated.json @@ -1,36 +1,47 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.source.updated", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648319966, "data": { "object": { - "id": "card_VALID", + "id": "card_000000000000000000000000", "object": "card", - "last4": "4242", - "type": "Visa", - "brand": "Visa", - "funding": "credit", - "exp_month": 3, - "exp_year": 2020, - "fingerprint": "wXWJT135mEK107G8", - "customer": "cus_VALID", - "country": "US", - "name": "Testy Tester", + "address_city": null, + "address_country": null, "address_line1": null, + "address_line1_check": null, "address_line2": null, - "address_city": null, "address_state": null, "address_zip": null, - "address_country": null, - "cvc_check": "pass", - "address_line1_check": null, - "address_zip_check": null + "address_zip_check": null, + "brand": "Visa", + "country": "US", + "customer": "cus_00000000000000", + "cvc_check": null, + "dynamic_last4": null, + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "last4": "4242", + "metadata": { + "foo": "bar" + }, + "name": null, + "tokenization_method": null }, - "previous_attributes": - { - "name": "Testy Tester Jr." + "previous_attributes": { + "metadata": { + "foo": null + } } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "0b5b3636-5097-40ba-8839-7013d6090ab7" + }, + "type": "customer.source.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json b/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json index 7218cbb5d..0b234ec6d 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +++ b/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json @@ -1,66 +1,154 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.subscription.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320110, "data": { "object": { - "id": "su_00000000000000", - "items": { - "object": "list", - "data": [ + "id": "sub_000000000000000000000000", + "object": "subscription", + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": 1648320110, + "billing_thresholds": null, + "cancel_at": null, + "cancel_at_period_end": false, + "canceled_at": null, + "collection_method": "charge_automatically", + "created": 1648320110, + "current_period_end": 1650998510, + "current_period_start": 1648320110, + "customer": "cus_00000000000000", + "days_until_due": null, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "discount": null, + "ended_at": null, + "items": { + "object": "list", + "data": [ { "id": "si_00000000000000", "object": "subscription_item", - "created": 1497881783, + "billing_thresholds": null, + "created": 1648320111, + "metadata": { + }, "plan": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo_00000000000000", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", + "interval": "month", "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, "trial_period_days": null, - "metadata": {} + "usage_type": "licensed" }, - "quantity": 1 - }, - { - "id": "si_00000000000001", - "object": "subscription_item", - "created": 1497881788, - "plan": { - "interval": "month", - "product": "pr_00000000000001", - "amount": 200, - "currency": "eur", - "id": "fkx0AFo_00000000000001", - "object": "plan", + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", "livemode": false, - "interval_count": 1, - "trial_period_days": null, - "metadata": {} + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" }, - "quantity": 5 + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "tax_rates": [ + + ] } - ] + ], + "has_more": false, + "total_count": 1, + "url": "/v1/subscription_items?subscription=sub_000000000000000000000000" }, - "object": "subscription", - "start": 1381080557, + "latest_invoice": "in_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "next_pending_invoice_item_invoice": null, + "pause_collection": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "pending_invoice_item_interval": null, + "pending_setup_intent": null, + "pending_update": null, + "plan": { + "id": "price_000000000000000000000000", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "quantity": 1, + "schedule": "sub_sched_000000000000000000000000", + "start_date": 1648320110, "status": "active", - "customer": "cus_00000000000000", - "cancel_at_period_end": false, - "current_period_start": 1381080557, - "current_period_end": 1383758957, - "ended_at": null, - "trial_start": null, + "test_clock": null, + "transfer_data": null, "trial_end": null, - "canceled_at": null, - "quantity": 1, - "application_fee_percent": null + "trial_start": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "af06a7d9-5408-4f65-8379-59a93874d04c" + }, + "type": "customer.subscription.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json b/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json index c9c3ebee2..fd5f9263b 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +++ b/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json @@ -1,65 +1,154 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.subscription.deleted", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320097, "data": { "object": { - "id": "su_00000000000000", + "id": "sub_000000000000000000000000", + "object": "subscription", + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": 1648320096, + "billing_thresholds": null, + "cancel_at": null, + "cancel_at_period_end": false, + "canceled_at": 1648320097, + "collection_method": "charge_automatically", + "created": 1648320096, + "current_period_end": 1650998496, + "current_period_start": 1648320096, + "customer": "cus_00000000000000", + "days_until_due": null, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "discount": null, + "ended_at": 1648320097, "items": { "object": "list", - "data": [{ + "data": [ + { "id": "si_00000000000000", "object": "subscription_item", - "created": 1497881783, + "billing_thresholds": null, + "created": 1648320096, + "metadata": { + }, "plan": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo_00000000000000", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "interval": "month", "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, "trial_period_days": null, - "metadata": {} + "usage_type": "licensed" }, - "quantity": 1 - }, - { - "id": "si_00000000000001", - "object": "subscription_item", - "created": 1497881788, - "plan": { - "interval": "month", - "product": "pr_00000000000001", - "amount": 200, - "currency": "eur", - "id": "fkx0AFo_00000000000001", - "object": "plan", + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", "livemode": false, - "interval_count": 1, - "trial_period_days": null, - "metadata": {} + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" }, - "quantity": 5 + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "tax_rates": [ + + ] } - ] + ], + "has_more": false, + "total_count": 1, + "url": "/v1/subscription_items?subscription=sub_000000000000000000000000" }, - "object": "subscription", - "start": 1381080564, + "latest_invoice": "in_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "next_pending_invoice_item_invoice": null, + "pause_collection": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "pending_invoice_item_interval": null, + "pending_setup_intent": null, + "pending_update": null, + "plan": { + "id": "price_000000000000000000000000", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "quantity": 1, + "schedule": "sub_sched_000000000000000000000000", + "start_date": 1648320096, "status": "canceled", - "customer": "cus_00000000000000", - "cancel_at_period_end": false, - "current_period_start": 1381080564, - "current_period_end": 1383758964, - "ended_at": 1381021514, - "trial_start": null, + "test_clock": null, + "transfer_data": null, "trial_end": null, - "canceled_at": null, - "quantity": 1, - "application_fee_percent": null + "trial_start": null } - } -} + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": "req_00000000000000", + "idempotency_key": "27f56452-b979-42fb-8bc1-219db1e65979" + }, + "type": "customer.subscription.deleted" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json b/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json index 2ca10fe48..a516a962a 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +++ b/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json @@ -1,78 +1,157 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.subscription.updated", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320106, "data": { "object": { - "id": "su_00000000000000", + "id": "sub_000000000000000000000000", + "object": "subscription", + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": 1648320105, + "billing_thresholds": null, + "cancel_at": null, + "cancel_at_period_end": false, + "canceled_at": null, + "collection_method": "charge_automatically", + "created": 1648320105, + "current_period_end": 1650998505, + "current_period_start": 1648320105, + "customer": "cus_00000000000000", + "days_until_due": null, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "discount": null, + "ended_at": null, "items": { "object": "list", - "data": [{ + "data": [ + { "id": "si_00000000000000", "object": "subscription_item", - "created": 1497881783, + "billing_thresholds": null, + "created": 1648320105, + "metadata": { + }, "plan": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo_00000000000000", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320104, + "currency": "usd", + "interval": "month", "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, "trial_period_days": null, - "metadata": {} + "usage_type": "licensed" }, - "quantity": 1 - }, - { - "id": "si_00000000000001", - "object": "subscription_item", - "created": 1497881788, - "plan": { - "interval": "month", - "product": "pr_00000000000001", - "amount": 200, - "currency": "eur", - "id": "fkx0AFo_00000000000001", - "object": "plan", + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320104, + "currency": "usd", "livemode": false, - "interval_count": 1, - "trial_period_days": null, - "metadata": {} + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" }, - "quantity": 5 + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "tax_rates": [ + + ] } - ] + ], + "has_more": false, + "total_count": 1, + "url": "/v1/subscription_items?subscription=sub_000000000000000000000000" }, - "object": "subscription", - "start": 1381080561, + "latest_invoice": "in_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "next_pending_invoice_item_invoice": null, + "pause_collection": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "pending_invoice_item_interval": null, + "pending_setup_intent": null, + "pending_update": null, + "plan": { + "id": "price_000000000000000000000000", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320104, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "quantity": 1, + "schedule": null, + "start_date": 1648320105, "status": "active", - "customer": "cus_00000000000000", - "cancel_at_period_end": false, - "current_period_start": 1381080561, - "current_period_end": 1383758961, - "ended_at": null, - "trial_start": null, + "test_clock": null, + "transfer_data": null, "trial_end": null, - "canceled_at": null, - "quantity": 1, - "application_fee_percent": null + "trial_start": null }, "previous_attributes": { - "plan": { - "interval": "month", - "product": "pr_00000000000002", - "amount": 100, - "currency": "usd", - "id": "OLD_PLAN_ID", - "object": "plan", - "livemode": false, - "interval_count": 1, - "trial_period_days": null - } + "schedule": "sub_sched_000000000000000000000000" } - } -} + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": "req_00000000000000", + "idempotency_key": "fe5a111d-ed6f-4cb5-a33c-05b364afc565" + }, + "type": "customer.subscription.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/customer.updated.json b/lib/stripe_mock/webhook_fixtures/customer.updated.json index afffec5a4..54756f34b 100644 --- a/lib/stripe_mock/webhook_fixtures/customer.updated.json +++ b/lib/stripe_mock/webhook_fixtures/customer.updated.json @@ -1,58 +1,49 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "customer.updated", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320110, "data": { "object": { - "object": "customer", - "created": 1375148334, "id": "cus_00000000000000", + "object": "customer", + "address": null, + "balance": 0, + "created": 1648320107, + "currency": "usd", + "default_source": "card_000000000000000000000000", + "delinquent": false, + "description": "(created by Stripe CLI)", + "discount": null, + "email": null, + "invoice_prefix": "985BA9CB", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, "livemode": false, - "description": null, - "email": "bond@mailinator.com", - "delinquent": true, "metadata": { }, - "preferred_locales": [], - "subscription": null, - "discount": null, - "account_balance": 0, - "sources": { - "object": "list", - "count": 1, - "url": "/v1/customers/cus_2I2AhGQOPmEFeu/cards", - "data": [ - { - "id": "cc_2I2akIhmladin5", - "object": "card", - "last4": "0341", - "type": "Visa", - "brand": "Visa", - "funding": "credit", - "exp_month": 12, - "exp_year": 2013, - "fingerprint": "fWvZEzdbEIFF8QrK", - "customer": "cus_2I2AhGQOPmEFeu", - "country": "US", - "name": "Johnny Goodman", - "address_line1": null, - "address_line2": null, - "address_city": null, - "address_state": null, - "address_zip": null, - "address_country": null, - "cvc_check": "pass", - "address_line1_check": null, - "address_zip_check": null - } - ] - }, - "default_source": "cc_2I2akIhmladin5" + "name": null, + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [ + + ], + "shipping": null, + "tax_exempt": "none", + "test_clock": null }, "previous_attributes": { - "description": "Old description" + "currency": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "af06a7d9-5408-4f65-8379-59a93874d04c" + }, + "type": "customer.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.created.json b/lib/stripe_mock/webhook_fixtures/invoice.created.json index c4aa06624..1e49717d7 100644 --- a/lib/stripe_mock/webhook_fixtures/invoice.created.json +++ b/lib/stripe_mock/webhook_fixtures/invoice.created.json @@ -1,71 +1,198 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "invoice.created", - "status": "paid", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320110, "data": { "object": { - "created": 1380674206, - "id": "in_00000000000000", - "period_start": 1378082075, - "period_end": 1380674075, + "id": "in_000000000000000000000000", + "object": "invoice", + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 1500, + "amount_paid": 0, + "amount_remaining": 1500, + "application_fee_amount": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "subscription_create", + "charge": null, + "collection_method": "charge_automatically", + "created": 1648320110, + "currency": "usd", + "custom_fields": null, + "customer": "cus_00000000000000", + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": null, + "discount": null, + "discounts": [ + + ], + "due_date": null, + "ending_balance": null, + "footer": null, + "hosted_invoice_url": null, + "invoice_pdf": null, + "last_finalization_error": null, "lines": { - "count": 1, "object": "list", - "url": "/v1/invoices/in_00000000000000/lines", "data": [ { - "id": "su_2hksGtIPylSBg2", + "id": "il_000000000000000000000000", "object": "line_item", - "type": "subscription", - "livemode": true, - "amount": 100, + "amount": 1500, "currency": "usd", - "proration": false, + "description": "1 × myproduct (at $15.00 / month)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "livemode": false, + "metadata": { + }, "period": { - "start": 1383759042, - "end": 1386351042 + "end": 1650998510, + "start": 1648320110 }, - "quantity": 1, "plan": { - "interval": "month", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", + "interval": "month", "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, "trial_period_days": null, - "product": "pr_00000000000000", - "metadata": {} + "usage_type": "licensed" }, - "description": null, - "metadata": null + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "subscription_item": "si_00000000000000", + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "subscription" } - ] + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_000000000000000000000000/lines" }, - "subtotal": 1000, - "total": 1000, - "customer": "cus_00000000000000", - "object": "invoice", - "attempted": false, - "closed": true, - "paid": true, "livemode": false, - "attempt_count": 1, - "amount_due": 1000, - "currency": "usd", + "metadata": { + }, + "next_payment_attempt": 1648323710, + "number": null, + "on_behalf_of": null, + "paid": false, + "paid_out_of_band": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648320110, + "period_start": 1648320110, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, "starting_balance": 0, - "ending_balance": 0, - "next_payment_attempt": null, - "charge": "ch_00000000000000", - "discount": null, - "application_fee": null, - "subscription": "sub_00000000000000", - "metadata": {}, - "description": null + "statement_descriptor": null, + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": "sub_000000000000000000000000", + "subtotal": 1500, + "tax": null, + "test_clock": null, + "total": 1500, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "af06a7d9-5408-4f65-8379-59a93874d04c" + }, + "type": "invoice.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.finalized.json b/lib/stripe_mock/webhook_fixtures/invoice.finalized.json new file mode 100644 index 000000000..ed7622ed0 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/invoice.finalized.json @@ -0,0 +1,171 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320015, + "data": { + "object": { + "id": "in_000000000000000000000000", + "object": "invoice", + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 2000, + "amount_paid": 2000, + "amount_remaining": 0, + "application_fee_amount": null, + "attempt_count": 1, + "attempted": true, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "manual", + "charge": "ch_000000000000000000000000", + "collection_method": "charge_automatically", + "created": 1648320012, + "currency": "usd", + "custom_fields": null, + "customer": "cus_00000000000000", + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discount": null, + "discounts": [ + + ], + "due_date": null, + "ending_balance": 0, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_0000000000000000/test_00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_0000000000000000/test_000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/pdf?s=ap", + "last_finalization_error": null, + "lines": { + "object": "list", + "data": [ + { + "id": "il_000000000000000000000000", + "object": "line_item", + "amount": 2000, + "currency": "usd", + "description": "(created by Stripe CLI)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "invoice_item": "ii_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "period": { + "end": 1648320011, + "start": 1648320011 + }, + "plan": null, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": false, + "billing_scheme": "per_unit", + "created": 1648320011, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": null, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 2000, + "unit_amount_decimal": "2000" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": null, + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "invoiceitem" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_000000000000000000000000/lines" + }, + "livemode": false, + "metadata": { + }, + "next_payment_attempt": null, + "number": "EE1DF78D-0001", + "on_behalf_of": null, + "paid": true, + "paid_out_of_band": false, + "payment_intent": "pi_000000000000000000000000", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648320012, + "period_start": 1648320012, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": null, + "status": "paid", + "status_transitions": { + "finalized_at": 1648320013, + "marked_uncollectible_at": null, + "paid_at": 1648320013, + "voided_at": null + }, + "subscription": null, + "subtotal": 2000, + "tax": null, + "test_clock": null, + "total": 2000, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": 1648320013 + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "e8a4b753-6e77-4c7f-ac7b-a43e50ee2605" + }, + "type": "invoice.finalized" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.paid.json b/lib/stripe_mock/webhook_fixtures/invoice.paid.json new file mode 100644 index 000000000..76353b834 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/invoice.paid.json @@ -0,0 +1,171 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320015, + "data": { + "object": { + "id": "in_000000000000000000000000", + "object": "invoice", + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 2000, + "amount_paid": 2000, + "amount_remaining": 0, + "application_fee_amount": null, + "attempt_count": 1, + "attempted": true, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "manual", + "charge": "ch_000000000000000000000000", + "collection_method": "charge_automatically", + "created": 1648320012, + "currency": "usd", + "custom_fields": null, + "customer": "cus_00000000000000", + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discount": null, + "discounts": [ + + ], + "due_date": null, + "ending_balance": 0, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_0000000000000000/test_00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_0000000000000000/test_000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/pdf?s=ap", + "last_finalization_error": null, + "lines": { + "object": "list", + "data": [ + { + "id": "il_000000000000000000000000", + "object": "line_item", + "amount": 2000, + "currency": "usd", + "description": "(created by Stripe CLI)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "invoice_item": "ii_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "period": { + "end": 1648320011, + "start": 1648320011 + }, + "plan": null, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": false, + "billing_scheme": "per_unit", + "created": 1648320011, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": null, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 2000, + "unit_amount_decimal": "2000" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": null, + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "invoiceitem" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_000000000000000000000000/lines" + }, + "livemode": false, + "metadata": { + }, + "next_payment_attempt": null, + "number": "EE1DF78D-0001", + "on_behalf_of": null, + "paid": true, + "paid_out_of_band": false, + "payment_intent": "pi_000000000000000000000000", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648320012, + "period_start": 1648320012, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": null, + "status": "paid", + "status_transitions": { + "finalized_at": 1648320013, + "marked_uncollectible_at": null, + "paid_at": 1648320013, + "voided_at": null + }, + "subscription": null, + "subtotal": 2000, + "tax": null, + "test_clock": null, + "total": 2000, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": 1648320013 + } + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": "req_00000000000000", + "idempotency_key": "e8a4b753-6e77-4c7f-ac7b-a43e50ee2605" + }, + "type": "invoice.paid" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.payment_action_required.json b/lib/stripe_mock/webhook_fixtures/invoice.payment_action_required.json new file mode 100644 index 000000000..d7e612511 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/invoice.payment_action_required.json @@ -0,0 +1,171 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320004, + "data": { + "object": { + "id": "in_000000000000000000000000", + "object": "invoice", + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 2000, + "amount_paid": 0, + "amount_remaining": 2000, + "application_fee_amount": null, + "attempt_count": 1, + "attempted": true, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "manual", + "charge": "ch_000000000000000000000000", + "collection_method": "charge_automatically", + "created": 1648319998, + "currency": "usd", + "custom_fields": null, + "customer": "cus_00000000000000", + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discount": null, + "discounts": [ + + ], + "due_date": null, + "ending_balance": 0, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_0000000000000000/test_00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_0000000000000000/test_000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/pdf?s=ap", + "last_finalization_error": null, + "lines": { + "object": "list", + "data": [ + { + "id": "il_000000000000000000000000", + "object": "line_item", + "amount": 2000, + "currency": "usd", + "description": "(created by Stripe CLI)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "invoice_item": "ii_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "period": { + "end": 1648319997, + "start": 1648319997 + }, + "plan": null, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": false, + "billing_scheme": "per_unit", + "created": 1648319997, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": null, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 2000, + "unit_amount_decimal": "2000" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": null, + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "invoiceitem" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_000000000000000000000000/lines" + }, + "livemode": false, + "metadata": { + }, + "next_payment_attempt": null, + "number": "7C43E171-0001", + "on_behalf_of": null, + "paid": false, + "paid_out_of_band": false, + "payment_intent": "pi_000000000000000000000000", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648319998, + "period_start": 1648319998, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": null, + "status": "open", + "status_transitions": { + "finalized_at": 1648320001, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 2000, + "tax": null, + "test_clock": null, + "total": 2000, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": 1648319998 + } + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": "req_00000000000000", + "idempotency_key": "4e03370d-8b01-4847-9891-0092e480e875" + }, + "type": "invoice.payment_action_required" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json b/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json index 294270e46..bfd9ccbc5 100644 --- a/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +++ b/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json @@ -1,105 +1,171 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "invoice.payment_failed", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320009, "data": { "object": { - "date": 1380674206, - "id": "in_00000000000000", - "period_start": 1378082075, - "period_end": 1380674075, + "id": "in_000000000000000000000000", + "object": "invoice", + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 2000, + "amount_paid": 0, + "amount_remaining": 2000, + "application_fee_amount": null, + "attempt_count": 1, + "attempted": true, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "manual", + "charge": "ch_000000000000000000000000", + "collection_method": "charge_automatically", + "created": 1648320007, + "currency": "usd", + "custom_fields": null, + "customer": "cus_00000000000000", + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discount": null, + "discounts": [ + + ], + "due_date": null, + "ending_balance": 0, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_0000000000000000/test_00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_0000000000000000/test_000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/pdf?s=ap", + "last_finalization_error": null, "lines": { "object": "list", - "count": 3, - "url": "/v1/invoices/in_00000000000000/lines", "data": [ { - "id": "ii_00000000000000", + "id": "il_000000000000000000000000", "object": "line_item", - "type": "invoiceitem", - "livemode": false, - "amount": 19000, + "amount": 2000, "currency": "usd", - "proration": true, - "period": { - "start": 1393765661, - "end": 1393765661 - }, - "quantity": null, - "plan": null, - "description": "Remaining time on Platinum after 02 Mar 2014", - "metadata": {} - }, - { - "id": "ii_00000000000001", - "object": "line_item", - "type": "invoiceitem", + "description": "(created by Stripe CLI)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "invoice_item": "ii_000000000000000000000000", "livemode": false, - "amount": -9000, - "currency": "usd", - "proration": true, - "period": { - "start": 1393765661, - "end": 1393765661 + "metadata": { }, - "quantity": null, - "plan": null, - "description": "Unused time on Gold after 05 Mar 2014", - "metadata": {} - }, - { - "id": "su_00000000000000", - "object": "line_item", - "type": "subscription", - "livemode": false, - "amount": 20000, - "currency": "usd", - "proration": false, "period": { - "start": 1383759053, - "end": 1386351053 + "end": 1648320006, + "start": 1648320006 }, - "quantity": 1, - "plan": { - "interval": "month", - "product": "pr_00000000000000", - "created": 1300000000, - "amount": 20000, + "plan": null, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": false, + "billing_scheme": "per_unit", + "created": 1648320006, "currency": "usd", - "id": "platinum", - "object": "plan", "livemode": false, - "interval_count": 1, - "trial_period_days": null, - "metadata": {} + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": null, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 2000, + "unit_amount_decimal": "2000" + }, + "proration": false, + "proration_details": { + "credited_items": null }, - "description": null, - "metadata": null + "quantity": 1, + "subscription": null, + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "invoiceitem" } - ] + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_000000000000000000000000/lines" }, - "subtotal": 30000, - "total": 30000, - "customer": "cus_00000000000000", - "object": "invoice", - "attempted": true, - "closed": false, - "paid": false, "livemode": false, - "attempt_count": 1, - "amount_due": 30000, - "currency": "usd", + "metadata": { + }, + "next_payment_attempt": null, + "number": "C93E45D7-0001", + "on_behalf_of": null, + "paid": false, + "paid_out_of_band": false, + "payment_intent": "pi_000000000000000000000000", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648320007, + "period_start": 1648320007, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, "starting_balance": 0, - "ending_balance": 0, - "next_payment_attempt": 1380760475, - "charge": "ch_00000000000000", - "discount": null, - "application_fee": null, - "subscription": "su_00000000000000", - "metadata": {}, - "description": null + "statement_descriptor": null, + "status": "open", + "status_transitions": { + "finalized_at": 1648320008, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 2000, + "tax": null, + "test_clock": null, + "total": 2000, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": null } - } -} + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": "req_00000000000000", + "idempotency_key": "01a6e155-8da6-4fce-981d-0957a39bb139" + }, + "type": "invoice.payment_failed" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json b/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json index 9be30439c..d72f21d0f 100644 --- a/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +++ b/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json @@ -1,112 +1,171 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "invoice.payment_succeeded", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320015, "data": { "object": { - "id": "in_00000000000000", + "id": "in_000000000000000000000000", "object": "invoice", - "amount_due": 999, - "application_fee": null, + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 2000, + "amount_paid": 2000, + "amount_remaining": 0, + "application_fee_amount": null, "attempt_count": 1, "attempted": true, - "charge": "ch_18EcOcLrgDIZ7iq8TaNlErVv", - "closed": true, - "currency": "eur", + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "manual", + "charge": "ch_000000000000000000000000", + "collection_method": "charge_automatically", + "created": 1648320012, + "currency": "usd", + "custom_fields": null, "customer": "cus_00000000000000", - "created": 1464084258, - "description": null, + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", "discount": null, + "discounts": [ + + ], + "due_date": null, "ending_balance": 0, - "forgiven": false, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_0000000000000000/test_00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_0000000000000000/test_000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/pdf?s=ap", + "last_finalization_error": null, "lines": { - "data": [{ - "id": "sub_00000000000000", - "object": "line_item", - "amount": 50, - "currency": "eur", - "description": null, - "discountable": true, - "livemode": true, - "metadata": {}, - "period": { - "start": 1500637196, - "end": 1532173196 - }, - "plan": { - "id": "platinum", - "object": "plan", - "amount": 500, - "created": 1499943145, - "currency": "eur", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "name": "New Plan Test", - "statement_descriptor": null, - "trial_period_days": null - }, - "proration": false, - "quantity": 1, - "subscription": null, - "subscription_item": "si_18ZfWyLrgDIZ7iq8fSlSNGIV", - "type": "subscription" - }, - { - "id": "sub_00000000000000", - "object": "line_item", - "amount": 50, - "currency": "eur", - "description": null, - "discountable": true, - "livemode": true, - "metadata": {}, - "period": { - "start": 1500637196, - "end": 1532173196 - }, - "plan": { - "id": "gold", - "object": "plan", - "amount": 300, - "created": 1499943155, - "currency": "eur", - "interval": "month", - "interval_count": 1, + "object": "list", + "data": [ + { + "id": "il_000000000000000000000000", + "object": "line_item", + "amount": 2000, + "currency": "usd", + "description": "(created by Stripe CLI)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "invoice_item": "ii_000000000000000000000000", "livemode": false, - "metadata": {}, - "name": "New gold Plan Test", - "statement_descriptor": null, - "trial_period_days": null - }, - "proration": false, - "quantity": 1, - "subscription": null, - "subscription_item": "si_18ZfWyLrgDIZ7iq8fSlSNGIV", - "type": "subscription" - }], + "metadata": { + }, + "period": { + "end": 1648320011, + "start": 1648320011 + }, + "plan": null, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": false, + "billing_scheme": "per_unit", + "created": 1648320011, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": null, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 2000, + "unit_amount_decimal": "2000" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": null, + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "invoiceitem" + } + ], + "has_more": false, "total_count": 1, - "object": "list", - "url": "/v1/invoices/in_18EcOcLrgDIZ7iq8zsDkunZ0/lines" + "url": "/v1/invoices/in_000000000000000000000000/lines" }, "livemode": false, - "metadata": {}, + "metadata": { + }, "next_payment_attempt": null, + "number": "EE1DF78D-0001", + "on_behalf_of": null, "paid": true, - "period_end": 1464084258, - "period_start": 1464084258, + "paid_out_of_band": false, + "payment_intent": "pi_000000000000000000000000", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648320012, + "period_start": 1648320012, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, "receipt_number": null, "starting_balance": 0, "statement_descriptor": null, - "subscription": "sub_00000000000000", - "subtotal": 999, + "status": "paid", + "status_transitions": { + "finalized_at": 1648320013, + "marked_uncollectible_at": null, + "paid_at": 1648320013, + "voided_at": null + }, + "subscription": null, + "subtotal": 2000, "tax": null, - "tax_percent": null, - "total": 999, - "webhooks_delivered_at": 1464084258 + "test_clock": null, + "total": 2000, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": 1648320013 } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "e8a4b753-6e77-4c7f-ac7b-a43e50ee2605" + }, + "type": "invoice.payment_succeeded" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoice.updated.json b/lib/stripe_mock/webhook_fixtures/invoice.updated.json index e27be0d14..c0ae584e9 100644 --- a/lib/stripe_mock/webhook_fixtures/invoice.updated.json +++ b/lib/stripe_mock/webhook_fixtures/invoice.updated.json @@ -1,74 +1,202 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "invoice.updated", - "status": "paid", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320099, "data": { "object": { - "created": 1380674206, - "id": "in_00000000000000", - "period_start": 1378082075, - "period_end": 1380674075, + "id": "in_000000000000000000000000", + "object": "invoice", + "account_country": "US", + "account_name": "Example Company, Inc.", + "account_tax_ids": null, + "amount_due": 1500, + "amount_paid": 0, + "amount_remaining": 1500, + "application_fee_amount": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "subscription_create", + "charge": null, + "collection_method": "charge_automatically", + "created": 1648320096, + "currency": "usd", + "custom_fields": null, + "customer": "cus_00000000000000", + "customer_address": null, + "customer_email": null, + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + + ], + "description": null, + "discount": null, + "discounts": [ + + ], + "due_date": null, + "ending_balance": null, + "footer": null, + "hosted_invoice_url": null, + "invoice_pdf": null, + "last_finalization_error": null, "lines": { - "count": 1, "object": "list", - "url": "/v1/invoices/in_00000000000000/lines", "data": [ { - "id": "su_00000000000000", + "id": "il_000000000000000000000000", "object": "line_item", - "type": "subscription", - "livemode": true, - "amount": 100, + "amount": 1500, "currency": "usd", - "proration": false, + "description": "1 × myproduct (at $15.00 / month)", + "discount_amounts": [ + + ], + "discountable": true, + "discounts": [ + + ], + "livemode": false, + "metadata": { + }, "period": { - "start": 1383759047, - "end": 1386351047 + "end": 1650998496, + "start": 1648320096 }, - "quantity": 1, "plan": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "interval": "month", "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, "trial_period_days": null, - "metadata": {} + "usage_type": "licensed" }, - "description": null, - "metadata": null + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "subscription_item": "si_00000000000000", + "tax_amounts": [ + + ], + "tax_rates": [ + + ], + "type": "subscription" } - ] + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_000000000000000000000000/lines" }, - "subtotal": 1000, - "total": 1000, - "customer": "cus_00000000000000", - "object": "invoice", - "attempted": true, - "closed": true, - "paid": true, "livemode": false, - "attempt_count": 1, - "amount_due": 1000, - "currency": "usd", - "starting_balance": 0, - "ending_balance": 0, + "metadata": { + }, "next_payment_attempt": null, - "charge": "ch_00000000000000", - "discount": null, - "application_fee": null, - "subscription": "sub_00000000000000", - "metadata": {}, - "description": null + "number": null, + "on_behalf_of": null, + "paid": false, + "paid_out_of_band": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1648320096, + "period_start": 1648320096, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": null, + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": "sub_000000000000000000000000", + "subtotal": 1500, + "tax": null, + "test_clock": null, + "total": 1500, + "total_discount_amounts": [ + + ], + "total_tax_amounts": [ + + ], + "transfer_data": null, + "webhooks_delivered_at": 1648320097 }, "previous_attributes": { - "lines": [] + "auto_advance": true, + "next_payment_attempt": 1648323696 } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": null, + "idempotency_key": null + }, + "type": "invoice.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json b/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json index a51ce88f5..7d0ea314a 100644 --- a/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json +++ b/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json @@ -1,21 +1,95 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "invoiceitem.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320097, "data": { "object": { + "id": "ii_000000000000000000000000", "object": "invoiceitem", - "id": "ii_00000000000000", - "date": 1372126711, - "amount": 2500, - "livemode": false, - "proration": false, + "amount": -1500, "currency": "usd", "customer": "cus_00000000000000", - "description": "Plan: Veteran's Club Signup Fee Plan Id: 4", - "invoice": "in_00000000000000" + "date": 1648320097, + "description": "Unused time on myproduct after 26 Mar 2022", + "discountable": false, + "discounts": [ + + ], + "invoice": null, + "livemode": false, + "metadata": { + }, + "period": { + "end": 1650998496, + "start": 1648320097 + }, + "plan": { + "id": "price_000000000000000000000000", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" + }, + "proration": true, + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "subscription_item": "si_00000000000000", + "tax_rates": [ + + ], + "test_clock": null, + "unit_amount": -1500, + "unit_amount_decimal": "-1500" } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "27f56452-b979-42fb-8bc1-219db1e65979" + }, + "type": "invoiceitem.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json b/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json index c049c8df3..0b9216ae2 100644 --- a/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json +++ b/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json @@ -1,24 +1,98 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "invoiceitem.updated", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320097, "data": { "object": { + "id": "ii_000000000000000000000000", "object": "invoiceitem", - "id": "ii_00000000000000", - "date": 1372126711, - "amount": 2500, - "livemode": false, - "proration": false, + "amount": -1500, "currency": "usd", "customer": "cus_00000000000000", - "description": "Plan: Veteran's Club Signup Fee Plan Id: 4", - "invoice": "in_00000000000000" + "date": 1648320097, + "description": "Unused time on myproduct after 26 Mar 2022", + "discountable": false, + "discounts": [ + + ], + "invoice": "in_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "period": { + "end": 1650998496, + "start": 1648320097 + }, + "plan": { + "id": "price_000000000000000000000000", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "price": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320095, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" + }, + "proration": true, + "quantity": 1, + "subscription": "sub_000000000000000000000000", + "subscription_item": "si_00000000000000", + "tax_rates": [ + + ], + "test_clock": null, + "unit_amount": -1500, + "unit_amount_decimal": "-1500" }, "previous_attributes": { - "amount": 2121 + "invoice": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "27f56452-b979-42fb-8bc1-219db1e65979" + }, + "type": "invoiceitem.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.amount_capturable_updated.json b/lib/stripe_mock/webhook_fixtures/payment_intent.amount_capturable_updated.json new file mode 100644 index 000000000..e795c1601 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.amount_capturable_updated.json @@ -0,0 +1,170 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320022, + "data": { + "object": { + "id": "pi_000000000000000000000000", + "object": "payment_intent", + "amount": 2000, + "amount_capturable": 2000, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_000000000000000000000000", + "object": "charge", + "amount": 2000, + "amount_captured": 0, + "amount_refunded": 0, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": null, + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", + "captured": false, + "created": 1648320022, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "destination": null, + "dispute": null, + "disputed": false, + "failure_code": null, + "failure_message": null, + "fraud_details": { + }, + "invoice": null, + "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 2, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_000000000000000000000000", + "payment_method": "pm_000000000000000000000000", + "payment_method_details": { + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "installments": null, + "last4": "4242", + "mandate": null, + "network": "visa", + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/acct_0000000000000000/ch_000000000000000000000000/rcpt_0000000000000000000000000000000", + "refunded": false, + "refunds": { + "object": "list", + "data": [ + + ], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_000000000000000000000000/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_000000000000000000000000" + }, + "client_secret": "pi_000000000000000000000000_secret_0000000000000000000000000", + "confirmation_method": "manual", + "created": 1648320022, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "invoice": null, + "last_payment_error": null, + "livemode": false, + "metadata": { + }, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_000000000000000000000000", + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "b7b0532f-40b9-4008-8b6c-62b902c370cd" + }, + "type": "payment_intent.amount_capturable_updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json b/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json index 46f5142d7..431fd8c1f 100644 --- a/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json @@ -1,51 +1,56 @@ { - "id": "evt_00000000000000", + "id": "evt_000000000000000000000000", "object": "event", - "api_version": "2018-02-28", - "created": 1578499109, + "api_version": "2020-08-27", + "created": 1648320024, "data": { "object": { - "id": "pi_00000000000000", + "id": "pi_000000000000000000000000", "object": "payment_intent", - "amount": 900, + "amount": 2000, "amount_capturable": 0, "amount_received": 0, "application": null, "application_fee_amount": null, - "canceled_at": 1578499109, - "cancellation_reason": null, + "automatic_payment_methods": null, + "canceled_at": 1648320024, + "cancellation_reason": "requested_by_customer", "capture_method": "automatic", "charges": { "object": "list", - "data": [], + "data": [ + + ], "has_more": false, "total_count": 0, - "url": "/v1/charges?payment_intent=pi_00000000000000" + "url": "/v1/charges?payment_intent=pi_000000000000000000000000" }, - "client_secret": "pi_00000000000000", + "client_secret": "pi_000000000000000000000000_secret_0000000000000000000000000", "confirmation_method": "automatic", - "created": 1578499108, - "currency": "eur", - "customer": "cus_00000000000000", - "description": null, + "created": 1648320024, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", "invoice": null, "last_payment_error": null, "livemode": false, - "metadata": {}, + "metadata": { + }, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_00000000000000", + "payment_method": null, "payment_method_options": { "card": { "installments": null, + "mandate_options": null, "network": null, "request_three_d_secure": "automatic" } }, "payment_method_types": [ - "card", - "sepa_debit" + "card" ], + "processing": null, "receipt_email": null, "review": null, "setup_future_usage": null, @@ -62,7 +67,7 @@ "pending_webhooks": 2, "request": { "id": "req_00000000000000", - "idempotency_key": null + "idempotency_key": "696d09f4-dbea-44e6-8c70-39430b961361" }, "type": "payment_intent.canceled" -} +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.created.json b/lib/stripe_mock/webhook_fixtures/payment_intent.created.json new file mode 100644 index 000000000..2d6334741 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.created.json @@ -0,0 +1,86 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320032, + "data": { + "object": { + "id": "pi_000000000000000000000000", + "object": "payment_intent", + "amount": 2000, + "amount_capturable": 0, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [ + + ], + "has_more": false, + "total_count": 0, + "url": "/v1/charges?payment_intent=pi_000000000000000000000000" + }, + "client_secret": "pi_000000000000000000000000_secret_0000000000000000000000000", + "confirmation_method": "automatic", + "created": 1648320032, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "invoice": null, + "last_payment_error": null, + "livemode": false, + "metadata": { + }, + "next_action": null, + "on_behalf_of": null, + "payment_method": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": { + "address": { + "city": "San Francisco", + "country": "US", + "line1": "510 Townsend St", + "line2": null, + "postal_code": "94103", + "state": "CA" + }, + "carrier": null, + "name": "Jenny Rosen", + "phone": null, + "tracking_number": null + }, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_payment_method", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "7143a956-2f40-407a-b312-43038ae76c86" + }, + "type": "payment_intent.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json b/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json index cd10f1ebd..b4a6571ac 100644 --- a/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json @@ -1,18 +1,18 @@ { - "id": "evt_00000000000000", + "id": "evt_000000000000000000000000", "object": "event", - "api_version": "2018-02-28", - "created": 1578401135, + "api_version": "2020-08-27", + "created": 1648320029, "data": { "object": { - "id": "pi_00000000000000", + "id": "pi_000000000000000000000000", "object": "payment_intent", - "allowed_source_types": ["card", "sepa_debit"], - "amount": 200, + "amount": 2000, "amount_capturable": 0, "amount_received": 0, "application": null, "application_fee_amount": null, + "automatic_payment_methods": null, "canceled_at": null, "cancellation_reason": null, "capture_method": "automatic", @@ -20,9 +20,10 @@ "object": "list", "data": [ { - "id": "py_00000000000000", + "id": "ch_000000000000000000000000", "object": "charge", - "amount": 200, + "amount": 2000, + "amount_captured": 0, "amount_refunded": 0, "application": null, "application_fee": null, @@ -37,89 +38,105 @@ "postal_code": null, "state": null }, - "email": "john.doe@example.com", - "name": "John Doe", + "email": null, + "name": null, "phone": null }, - "captured": true, - "created": 1578401129, - "currency": "eur", - "customer": "cus_00000000000000", - "description": null, - "destination": "acct_00000000000000", + "calculated_statement_descriptor": "EXAMPLECOMPANY", + "captured": false, + "created": 1648320028, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "destination": null, "dispute": null, "disputed": false, - "failure_code": null, - "failure_message": null, - "fraud_details": {}, + "failure_code": "card_declined", + "failure_message": "Your card was declined.", + "fraud_details": { + }, "invoice": null, "livemode": false, - "metadata": {}, + "metadata": { + }, "on_behalf_of": null, "order": null, "outcome": { - "network_status": "approved_by_network", - "reason": null, - "risk_level": "not_assessed", - "seller_message": "Payment complete.", - "type": "authorized" + "network_status": "declined_by_network", + "reason": "generic_decline", + "risk_level": "normal", + "risk_score": 1, + "seller_message": "The bank did not return any further details with this decline.", + "type": "issuer_declined" }, "paid": false, - "payment_intent": "pi_00000000000000", - "payment_method": "pm_00000000000000", + "payment_intent": "pi_000000000000000000000000", + "payment_method": "pm_000000000000000000000000", "payment_method_details": { - "sepa_debit": { - "bank_code": "37040044", - "branch_code": null, - "country": "DE", - "fingerprint": "00000000000000", - "last4": "3001", - "mandate": "mandate_00000000000000" + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "kMzSwhaalD1uR96R", + "funding": "credit", + "installments": null, + "last4": "0002", + "mandate": null, + "network": "visa", + "three_d_secure": null, + "wallet": null }, - "type": "sepa_debit" + "type": "card" }, "receipt_email": null, "receipt_number": null, - "receipt_url": "https://pay.stripe.com/receipts/acct_00000000000000/py_00000000000000/rcpt_00000000000000", + "receipt_url": null, "refunded": false, "refunds": { "object": "list", - "data": [], + "data": [ + + ], "has_more": false, "total_count": 0, - "url": "/v1/charges/py_00000000000000/refunds" + "url": "/v1/charges/ch_000000000000000000000000/refunds" }, "review": null, "shipping": null, "source": null, "source_transfer": null, - "statement_descriptor": "ACME Corp", + "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "failed", - "transfer_data": { - "amount": null, - "destination": "acct_00000000000000" - }, - "transfer_group": "group_pi_00000000000000" + "transfer_data": null, + "transfer_group": null } ], "has_more": false, "total_count": 1, - "url": "/v1/charges?payment_intent=pi_00000000000000" + "url": "/v1/charges?payment_intent=pi_000000000000000000000000" }, - "client_secret": "pi_00000000000000", + "client_secret": "pi_000000000000000000000000_secret_0000000000000000000000000", "confirmation_method": "automatic", - "created": 1578401129, - "currency": "eur", - "customer": "cus_00000000000000", - "description": null, + "created": 1648320028, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", "invoice": null, "last_payment_error": { - "code": "payment_intent_payment_attempt_failed", - "doc_url": "https://stripe.com/docs/error-codes/payment-intent-payment-attempt-failed", - "message": "The payment failed.", + "charge": "ch_000000000000000000000000", + "code": "card_declined", + "decline_code": "generic_decline", + "doc_url": "https://stripe.com/docs/error-codes/card-declined", + "message": "Your card was declined.", "payment_method": { - "id": "pm_00000000000000", + "id": "pm_000000000000000000000000", "object": "payment_method", "billing_details": { "address": { @@ -130,57 +147,79 @@ "postal_code": null, "state": null }, - "email": "john.doe@example.com", - "name": "John Doe", + "email": null, + "name": null, "phone": null }, - "created": 1578400666, - "customer": "cus_00000000000000", + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "kMzSwhaalD1uR96R", + "funding": "credit", + "generated_from": null, + "last4": "0002", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1648320028, + "customer": null, "livemode": false, - "metadata": {}, - "sepa_debit": { - "bank_code": "37040044", - "branch_code": "", - "country": "DE", - "fingerprint": "00000000000000", - "last4": "3001" + "metadata": { }, - "type": "sepa_debit" + "type": "card" }, - "type": "invalid_request_error" + "type": "card_error" }, "livemode": false, - "metadata": {}, + "metadata": { + }, "next_action": null, - "next_source_action": null, "on_behalf_of": null, "payment_method": null, "payment_method_options": { "card": { "installments": null, + "mandate_options": null, + "network": null, "request_three_d_secure": "automatic" } }, - "payment_method_types": ["card", "sepa_debit"], + "payment_method_types": [ + "card" + ], + "processing": null, "receipt_email": null, "review": null, "setup_future_usage": null, "shipping": null, "source": null, - "statement_descriptor": "ACME Corp", + "statement_descriptor": null, "statement_descriptor_suffix": null, - "status": "requires_source", - "transfer_data": { - "destination": "acct_00000000000000" - }, + "status": "requires_payment_method", + "transfer_data": null, "transfer_group": null } }, "livemode": false, - "pending_webhooks": 3, + "pending_webhooks": 2, "request": { - "id": null, - "idempotency_key": null + "id": "req_00000000000000", + "idempotency_key": "57ddc22f-1d42-446f-8f91-85cf0d32084a" }, "type": "payment_intent.payment_failed" -} +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.requires_action.json b/lib/stripe_mock/webhook_fixtures/payment_intent.requires_action.json new file mode 100644 index 000000000..2cbc6ea99 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.requires_action.json @@ -0,0 +1,191 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320004, + "data": { + "object": { + "id": "pi_000000000000000000000000", + "object": "payment_intent", + "amount": 2000, + "amount_capturable": 0, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_000000000000000000000000", + "object": "charge", + "amount": 2000, + "amount_captured": 0, + "amount_refunded": 0, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": null, + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", + "captured": false, + "created": 1648320002, + "currency": "usd", + "customer": "cus_00000000000000", + "description": "Payment for Invoice", + "destination": null, + "dispute": null, + "disputed": false, + "failure_code": "authentication_required", + "failure_message": "Your card was declined. This transaction requires authentication.", + "fraud_details": { + }, + "invoice": "in_000000000000000000000000", + "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "declined_by_network", + "reason": "authentication_required", + "risk_level": "normal", + "risk_score": 15, + "seller_message": "The bank returned the decline code `authentication_required`.", + "type": "issuer_declined" + }, + "paid": false, + "payment_intent": "pi_000000000000000000000000", + "payment_method": "pm_000000000000000000000000", + "payment_method_details": { + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "IE", + "exp_month": 7, + "exp_year": 2025, + "fingerprint": "R0zFsMh6EQNC0f1H", + "funding": "credit", + "installments": null, + "last4": "3220", + "mandate": null, + "network": "visa", + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "receipt_email": null, + "receipt_number": null, + "receipt_url": null, + "refunded": false, + "refunds": { + "object": "list", + "data": [ + + ], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_000000000000000000000000/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "failed", + "transfer_data": null, + "transfer_group": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_000000000000000000000000" + }, + "client_secret": "pi_000000000000000000000000_secret_0000000000000000000000000", + "confirmation_method": "automatic", + "created": 1648320001, + "currency": "usd", + "customer": "cus_00000000000000", + "description": "Payment for Invoice", + "invoice": "in_000000000000000000000000", + "last_payment_error": null, + "livemode": false, + "metadata": { + }, + "next_action": { + "type": "use_stripe_sdk", + "use_stripe_sdk": { + "type": "stripe_3ds2_fingerprint", + "merchant": "acct_0000000000000000", + "three_d_secure_2_source": "src_000000000000000000000000", + "directory_server_name": "visa", + "server_transaction_id": "b973c110-2ca1-46fa-8bce-dc18259d145f", + "three_ds_method_url": "", + "three_ds_optimizations": "k", + "directory_server_encryption": { + "directory_server_id": "A000000003", + "algorithm": "RSA", + "certificate": "-----BEGIN CERTIFICATE-----\nMIIGAzCCA+ugAwIBAgIQDaAlB1IbPwgx5esGu9tLIjANBgkqhkiG9w0BAQsFADB2\nMQswCQYDVQQGEwJVUzENMAsGA1UECgwEVklTQTEvMC0GA1UECwwmVmlzYSBJbnRl\ncm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xJzAlBgNVBAMMHlZpc2EgZUNv\nbW1lcmNlIElzc3VpbmcgQ0EgLSBHMjAeFw0yMTA4MjMxNTMyMzNaFw0yNDA4MjIx\nNTMyMzNaMIGhMRgwFgYDVQQHDA9IaWdobGFuZHMgUmFuY2gxETAPBgNVBAgMCENv\nbG9yYWRvMQswCQYDVQQGEwJVUzENMAsGA1UECgwEVklTQTEvMC0GA1UECwwmVmlz\nYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xJTAjBgNVBAMMHDNk\nczIucnNhLmVuY3J5cHRpb24udmlzYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQCy34cZ88+xfenoccRD1jOi6uVCPXo2xyabXcKntxl7h1kHahac\nmpnuiH+kSgSg4DEHDXHg0WBcpMp0cB67dUE1XDxLAxN0gL5fXpVX7dUjI9tS8lcW\nndChHxZTA8HcXUtv1IwU1L3luhgNkog509bRw/V1GLukW6CwFRkMI/8fecV8EUcw\nIGiBr4/cAcaPnLxFWm/SFL2NoixiNf6LnwHrU4YIHsPQCIAM1km4XPDb7Gk2S3o0\nkkXroU87yoiHzFHbEZUN/tO0Juyz8K6AtGBKoppv1hEHz9MFNzLlvGPo7wcPpovb\nMYtwxj10KhtfEKh0sS0yMl1Uvw36JmuwjaC3AgMBAAGjggFfMIIBWzAMBgNVHRMB\nAf8EAjAAMB8GA1UdIwQYMBaAFL0nYyikrlS3yCO3wTVCF+nGeF+FMGcGCCsGAQUF\nBwEBBFswWTAwBggrBgEFBQcwAoYkaHR0cDovL2Vucm9sbC52aXNhY2EuY29tL2VD\nb21tRzIuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC52aXNhLmNvbS9vY3Nw\nMEYGA1UdIAQ/MD0wMQYIKwYBBQUHAgEwJTAjBggrBgEFBQcCARYXaHR0cDovL3d3\ndy52aXNhLmNvbS9wa2kwCAYGZ4EDAQEBMBMGA1UdJQQMMAoGCCsGAQUFBwMCMDUG\nA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9lbnJvbGwudmlzYWNhLmNvbS9lQ29tbUcy\nLmNybDAdBgNVHQ4EFgQU/JtqQ7VLWNd3/9zQjpnsR2rz+cwwDgYDVR0PAQH/BAQD\nAgSwMA0GCSqGSIb3DQEBCwUAA4ICAQBYOGCI/bYG2gmLgh7UXg5qrt4xeDYe4RXe\n5xSjFkTelNvdf+KykB+oQzw8ZobIY+pKsPihM6IrtoJQuzOLXPV5L9U4j1qa/NZB\nGZTXFMwKGN/v0/tAj3h8wefcLPWb15RsXEpZmA87ollezpXeEHXPhFIit7cHoG5P\nfem9yMuDISI97qbnIKNtFENJr+fMkWIykQ0QnkM1rt99Yv2ZE4GWZN7VJ0zXFqOF\nNF2IVwnTIZ21eDiCOjQr6ohq7bChDMelB5XvEuhfe400DqDP+e5pPHo81ecXkjJK\ngS5grYYZIbeDBdQL1Cgs1mGu6On8ecr0rcpRlQh++BySg9MKkzJdLt1vsYmxfrfb\nkUaLglTdYAU2nYaOEDR4NvkRxfzegXyXkOqfPTmfkrg+OB0LeuICITJGJ0cuZD5W\nGUNaT9WruEANBRJNVjSX1UeJUnCpz4nitT1ml069ONjEowyWUcKvTr4/nrargv2R\npOD4RPJMti6kG+bm9OeATiSgVNmO5lkAS4AkOop2IcbRFcVKJUTOhx2Q37L4nuAH\nTCXQ9vwT4yWz6fVaCfL/FTvCGMilLPzXC/00OPA2ZtWvClvFh/uHJBjRUnj6WXp3\nO9p9uHfdV9eKJH37k94GUSMjBKQ6aIru1VUvSOmUPrDz5JbQB7bP+IzUaFHeweZX\nOWumZmyGDw==\n-----END CERTIFICATE-----\n", + "root_certificate_authorities": [ + "-----BEGIN CERTIFICATE-----\nMIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr\nMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl\ncm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv\nbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw\nCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h\ndGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l\ncmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h\n2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E\nlpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV\nZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq\n299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t\nvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL\ndXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\nAgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF\nAAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR\nzCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3\nLBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd\n7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw\n++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt\n398znM/jra6O1I7mT1GvFpLgXPYHDw==\n-----END CERTIFICATE-----\n", + "-----BEGIN CERTIFICATE-----\nMIIFqTCCA5GgAwIBAgIPUT6WAAAA20Qn7qzgvuFIMA0GCSqGSIb3DQEBCwUAMG8x\nCzAJBgNVBAYTAlVTMQ0wCwYDVQQKDARWSVNBMS8wLQYDVQQLDCZWaXNhIEludGVy\nbmF0aW9uYWwgU2VydmljZSBBc3NvY2lhdGlvbjEgMB4GA1UEAwwXVmlzYSBQdWJs\naWMgUlNBIFJvb3QgQ0EwHhcNMjEwMzE2MDAwMDAwWhcNNDEwMzE1MDAwMDAwWjBv\nMQswCQYDVQQGEwJVUzENMAsGA1UECgwEVklTQTEvMC0GA1UECwwmVmlzYSBJbnRl\ncm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xIDAeBgNVBAMMF1Zpc2EgUHVi\nbGljIFJTQSBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA\n2WEbXLS3gI6LOY93bP7Kz6EO9L1QXlr8l+fTkJWZldJ6QuwZ1cv4369tfjeJ8O5w\nSJiDcVw7eNdOP73LfAtwHlTnUnb0e9ILTTipc5bkNnAevocrJACsrpiQ8jBI9ttp\ncqKUeJgzW4Ie25ypirKroVD42b4E0iICK2cZ5QfD4BSzUnftp4Bqh8AfpGvG1lre\nCaD53qrsy5SUadY/NaeUGOkqdPvDSNoDIdrbExwnZaSFUmjQT1svKwMqGo2GFrgJ\n4cULEp4NNj5rga8YTTZ7Xo5MblHrLpSPOmJev30KWi/BcbvtCNYNWBTg7UMzP3cK\nMQ1pGLvG2PgvFTZSRvH3QzngJRgrDYYOJ6kj9ave+6yOOFqj80ZCuH0Nugt2mMS3\nc3+Nksaw+6H3cQPsE/Gv5zjfsKleRhEFtE1gyrdUg1DMgu8o/YhKM7FAqkXUn74z\nwoRFgx3Mi5OaGTQbg+NlwJgR4sVHXCV4s9b8PjneLhzWMn353SFARF9dnO7LDBqq\ntT6WltJu1z9x2Ze0UVNZvxKGcyCkLody29O8j9/MGZ8SOSUu4U6NHrebKuuf9Fht\nn6PqQ4ppkhy6sReXeV5NVGfVpDYY5ZAKEWqTYgMULWpQ2Py4BGpFzBe07jXkyulR\npoKvz14iXeA0oq16c94DrFYX0jmrWLeU4a/TCZQLFIsCAwEAAaNCMEAwHQYDVR0O\nBBYEFEtNpg77oBHorQvi8PMKAC+sixb7MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P\nAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQC5BU9qQSZYPcgCp2x0Juq59kMm\nXuBly094DaEnPqvtCgwwAirkv8x8/QSOxiWWiu+nveyuR+j6Gz/fJaV4u+J5QEDy\ncfk605Mw3HIcJOeZvDgk1eyOmQwUP6Z/BdQTNJmZ92Z8dcG5yWCxLBrqPH7ro3Ss\njhYq9duIJU7jfizCJCN4W8tp0D2pWBe1/CYNswP4GMs5jQ5+ZQKN/L5JFdwVTu7X\nPt8b5zfgbmmQpVmUn0oFwm3OI++Z6gEpNmW5bd/2oUIZoG96Qff2fauVMAYiWQvN\nnL3y1gkRguTOSMVUCCiGfdvwu5ygowillvV2nHb7+YibQ9N5Z2spP0o9Zlfzoat2\n7WFpyK47TiUdu/4toarLKGZP+hbA/F4xlnM/8EfZkE1DeTTI0lhN3O8yEsHrtRl1\nOuQZ/IexHO8UGU6jvn4TWo10HYeXzrGckL7oIXfGTrjPzfY62T5HDW/BAEZS+9Tk\nijz25YM0fPPz7IdlEG+k4q4YwZ82j73Y9kDEM5423mrWorq/Bq7I5Y8v0LTY9GWH\nYrpElYf0WdOXAbsfwQiT6qnRio+p82VyqlY8Jt6VVA6CDy/iHKwcj1ELEnDQfVv9\nhedoxmnQ6xe/nK8czclu9hQJRv5Lh9gk9Q8DKK2nmgzZ8SSQ+lr3mSSeY8JOMRlE\n+RKdOQIChWthTJKh7w==\n-----END CERTIFICATE-----\n" + ] + }, + "one_click_authn": null + } + }, + "on_behalf_of": null, + "payment_method": "pm_000000000000000000000000", + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": "off_session", + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_action", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "4e03370d-8b01-4847-9891-0092e480e875" + }, + "type": "payment_intent.requires_action" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json b/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json index 7ea356a9e..2531c8d4b 100644 --- a/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json +++ b/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json @@ -1,18 +1,18 @@ { - "id": "evt_00000000000000", + "id": "evt_000000000000000000000000", "object": "event", - "api_version": "2018-02-28", - "created": 1578499109, + "api_version": "2020-08-27", + "created": 1648320033, "data": { "object": { - "id": "pi_00000000000000", + "id": "pi_000000000000000000000000", "object": "payment_intent", - "allowed_source_types": ["card", "sepa_debit"], - "amount": 900, + "amount": 2000, "amount_capturable": 0, - "amount_received": 900, + "amount_received": 2000, "application": null, "application_fee_amount": null, + "automatic_payment_methods": null, "canceled_at": null, "cancellation_reason": null, "capture_method": "automatic", @@ -20,18 +20,19 @@ "object": "list", "data": [ { - "id": "ch_00000000000000", + "id": "ch_000000000000000000000000", "object": "charge", - "amount": 900, + "amount": 2000, + "amount_captured": 2000, "amount_refunded": 0, "application": null, "application_fee": null, "application_fee_amount": null, - "balance_transaction": "txn_00000000000000", + "balance_transaction": "txn_000000000000000000000000", "billing_details": { "address": { "city": null, - "country": "DE", + "country": null, "line1": null, "line2": null, "postal_code": null, @@ -41,33 +42,36 @@ "name": null, "phone": null }, + "calculated_statement_descriptor": "EXAMPLECOMPANY", "captured": true, - "created": 1578499109, - "currency": "eur", - "customer": "cus_00000000000000", - "description": null, - "destination": "acct_00000000000000", + "created": 1648320032, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", + "destination": null, "dispute": null, "disputed": false, "failure_code": null, "failure_message": null, - "fraud_details": {}, + "fraud_details": { + }, "invoice": null, "livemode": false, - "metadata": {}, + "metadata": { + }, "on_behalf_of": null, "order": null, "outcome": { "network_status": "approved_by_network", "reason": null, "risk_level": "normal", - "risk_score": 40, + "risk_score": 2, "seller_message": "Payment complete.", "type": "authorized" }, "paid": true, - "payment_intent": "pi_00000000000000", - "payment_method": "pm_00000000000000", + "payment_intent": "pi_000000000000000000000000", + "payment_method": "pm_000000000000000000000000", "payment_method_details": { "card": { "brand": "visa", @@ -77,12 +81,13 @@ "cvc_check": null }, "country": "US", - "exp_month": 4, - "exp_year": 2024, - "fingerprint": "00000000000000", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", "funding": "credit", "installments": null, "last4": "4242", + "mandate": null, "network": "visa", "three_d_secure": null, "wallet": null @@ -91,74 +96,101 @@ }, "receipt_email": null, "receipt_number": null, - "receipt_url": "https://pay.stripe.com/receipts/acct_00000000000000/ch_00000000000000/rcpt_00000000000000", + "receipt_url": "https://pay.stripe.com/receipts/acct_0000000000000000/ch_000000000000000000000000/rcpt_0000000000000000000000000000000", "refunded": false, "refunds": { "object": "list", - "data": [], + "data": [ + + ], "has_more": false, "total_count": 0, - "url": "/v1/charges/ch_00000000000000/refunds" + "url": "/v1/charges/ch_000000000000000000000000/refunds" }, "review": null, - "shipping": null, + "shipping": { + "address": { + "city": "San Francisco", + "country": "US", + "line1": "510 Townsend St", + "line2": null, + "postal_code": "94103", + "state": "CA" + }, + "carrier": null, + "name": "Jenny Rosen", + "phone": null, + "tracking_number": null + }, "source": null, "source_transfer": null, - "statement_descriptor": "ACME Corp", + "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "succeeded", - "transfer": "tr_00000000000000", - "transfer_data": { - "amount": null, - "destination": "acct_00000000000000" - }, - "transfer_group": "group_pi_00000000000000" + "transfer_data": null, + "transfer_group": null } ], "has_more": false, "total_count": 1, - "url": "/v1/charges?payment_intent=pi_00000000000000" + "url": "/v1/charges?payment_intent=pi_000000000000000000000000" }, - "client_secret": "pi_00000000000000", + "client_secret": "pi_000000000000000000000000_secret_0000000000000000000000000", "confirmation_method": "automatic", - "created": 1578499108, - "currency": "eur", - "customer": "cus_00000000000000", - "description": null, + "created": 1648320032, + "currency": "usd", + "customer": null, + "description": "(created by Stripe CLI)", "invoice": null, "last_payment_error": null, "livemode": false, - "metadata": {}, + "metadata": { + }, "next_action": null, - "next_source_action": null, "on_behalf_of": null, - "payment_method": "pm_00000000000000", + "payment_method": "pm_000000000000000000000000", "payment_method_options": { "card": { "installments": null, + "mandate_options": null, + "network": null, "request_three_d_secure": "automatic" } }, - "payment_method_types": ["card", "sepa_debit"], + "payment_method_types": [ + "card" + ], + "processing": null, "receipt_email": null, "review": null, "setup_future_usage": null, - "shipping": null, + "shipping": { + "address": { + "city": "San Francisco", + "country": "US", + "line1": "510 Townsend St", + "line2": null, + "postal_code": "94103", + "state": "CA" + }, + "carrier": null, + "name": "Jenny Rosen", + "phone": null, + "tracking_number": null + }, "source": null, - "statement_descriptor": "ACME Corp", + "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "succeeded", - "transfer_data": { - "destination": "acct_00000000000000" - }, - "transfer_group": "group_pi_00000000000000" + "transfer_data": null, + "transfer_group": null } }, "livemode": false, "pending_webhooks": 2, "request": { "id": "req_00000000000000", - "idempotency_key": null + "idempotency_key": "7143a956-2f40-407a-b312-43038ae76c86" }, "type": "payment_intent.succeeded" -} +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_link.created.json b/lib/stripe_mock/webhook_fixtures/payment_link.created.json new file mode 100644 index 000000000..ba76572b5 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_link.created.json @@ -0,0 +1,47 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320037, + "data": { + "object": { + "id": "plink_000000000000000000000000", + "object": "payment_link", + "active": true, + "after_completion": { + "hosted_confirmation": { + "custom_message": null + }, + "type": "hosted_confirmation" + }, + "allow_promotion_codes": false, + "application_fee_amount": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_address_collection": "auto", + "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "payment_method_types": null, + "phone_number_collection": { + "enabled": false + }, + "shipping_address_collection": null, + "subscription_data": { + "trial_period_days": null + }, + "transfer_data": null, + "url": "https://buy.stripe.com/test_000000000000000000" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "753b9128-4591-4a12-9392-86732e352266" + }, + "type": "payment_link.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_link.updated.json b/lib/stripe_mock/webhook_fixtures/payment_link.updated.json new file mode 100644 index 000000000..bed71d8ae --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_link.updated.json @@ -0,0 +1,50 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320038, + "data": { + "object": { + "id": "plink_000000000000000000000000", + "object": "payment_link", + "active": true, + "after_completion": { + "hosted_confirmation": { + "custom_message": null + }, + "type": "hosted_confirmation" + }, + "allow_promotion_codes": true, + "application_fee_amount": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_address_collection": "auto", + "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "payment_method_types": null, + "phone_number_collection": { + "enabled": false + }, + "shipping_address_collection": null, + "subscription_data": { + "trial_period_days": null + }, + "transfer_data": null, + "url": "https://buy.stripe.com/test_000000000000000000" + }, + "previous_attributes": { + "allow_promotion_codes": false + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "81dafec6-4d31-4bc8-b45b-0ab21772c793" + }, + "type": "payment_link.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payment_method.attached.json b/lib/stripe_mock/webhook_fixtures/payment_method.attached.json new file mode 100644 index 000000000..619963af8 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_method.attached.json @@ -0,0 +1,63 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320107, + "data": { + "object": { + "id": "card_000000000000000000000000", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "ZoVSX2dK5igWt2SB", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1648320107, + "customer": "cus_00000000000000", + "livemode": false, + "metadata": { + }, + "type": "card" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "381c7773-97ac-4c18-8fbe-e8bff5e6bbad" + }, + "type": "payment_method.attached" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payout.created.json b/lib/stripe_mock/webhook_fixtures/payout.created.json new file mode 100644 index 000000000..9d6180f3d --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payout.created.json @@ -0,0 +1,40 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320045, + "data": { + "object": { + "id": "po_000000000000000000000000", + "object": "payout", + "amount": 1100, + "arrival_date": 1648425600, + "automatic": false, + "balance_transaction": "txn_000000000000000000000000", + "created": 1648320044, + "currency": "usd", + "description": "(created by Stripe CLI)", + "destination": "ba_000000000000000000000000", + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "livemode": false, + "metadata": { + }, + "method": "standard", + "original_payout": null, + "reversed_by": null, + "source_type": "card", + "statement_descriptor": null, + "status": "paid", + "type": "bank_account" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "ae68e2e7-0edb-4181-a35f-f36dcc9148c9" + }, + "type": "payout.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payout.paid.json b/lib/stripe_mock/webhook_fixtures/payout.paid.json new file mode 100644 index 000000000..8fdc54484 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payout.paid.json @@ -0,0 +1,40 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320045, + "data": { + "object": { + "id": "po_000000000000000000000000", + "object": "payout", + "amount": 1100, + "arrival_date": 1648425600, + "automatic": false, + "balance_transaction": "txn_000000000000000000000000", + "created": 1648320044, + "currency": "usd", + "description": "(created by Stripe CLI)", + "destination": "ba_000000000000000000000000", + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "livemode": false, + "metadata": { + }, + "method": "standard", + "original_payout": null, + "reversed_by": null, + "source_type": "card", + "statement_descriptor": null, + "status": "paid", + "type": "bank_account" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "ae68e2e7-0edb-4181-a35f-f36dcc9148c9" + }, + "type": "payout.paid" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/payout.updated.json b/lib/stripe_mock/webhook_fixtures/payout.updated.json new file mode 100644 index 000000000..2e0cdebb7 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payout.updated.json @@ -0,0 +1,46 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320046, + "data": { + "object": { + "id": "po_000000000000000000000000", + "object": "payout", + "amount": 1100, + "arrival_date": 1648425600, + "automatic": false, + "balance_transaction": "txn_000000000000000000000000", + "created": 1648320044, + "currency": "usd", + "description": "(created by Stripe CLI)", + "destination": "ba_000000000000000000000000", + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "livemode": false, + "metadata": { + "foo": "bar" + }, + "method": "standard", + "original_payout": null, + "reversed_by": null, + "source_type": "card", + "statement_descriptor": null, + "status": "paid", + "type": "bank_account" + }, + "previous_attributes": { + "metadata": { + "foo": null + } + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "0d30bd53-9777-49de-8893-882fd2914757" + }, + "type": "payout.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/plan.created.json b/lib/stripe_mock/webhook_fixtures/plan.created.json index 64e94a588..06b924c37 100644 --- a/lib/stripe_mock/webhook_fixtures/plan.created.json +++ b/lib/stripe_mock/webhook_fixtures/plan.created.json @@ -1,20 +1,37 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "plan.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320109, "data": { "object": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo_00000000000000", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", + "interval": "month", "interval_count": 1, - "trial_period_days": null + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "64c22132-7234-47d6-a803-4e34ac0e883b" + }, + "type": "plan.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/plan.deleted.json b/lib/stripe_mock/webhook_fixtures/plan.deleted.json index 36695855e..f159e1d2e 100644 --- a/lib/stripe_mock/webhook_fixtures/plan.deleted.json +++ b/lib/stripe_mock/webhook_fixtures/plan.deleted.json @@ -1,20 +1,37 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "plan.deleted", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320048, "data": { "object": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo_00000000000000", + "id": "plan_00000000000000", "object": "plan", - "livemode": false, + "active": false, + "aggregate_usage": null, + "amount": 2000, + "amount_decimal": "2000", + "billing_scheme": "per_unit", + "created": 1648320047, + "currency": "usd", + "interval": "month", "interval_count": 1, - "trial_period_days": null + "livemode": false, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": null + }, + "type": "plan.deleted" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/plan.updated.json b/lib/stripe_mock/webhook_fixtures/plan.updated.json index 9f241e813..59e382379 100644 --- a/lib/stripe_mock/webhook_fixtures/plan.updated.json +++ b/lib/stripe_mock/webhook_fixtures/plan.updated.json @@ -1,23 +1,43 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "plan.updated", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320056, "data": { "object": { - "interval": "month", - "product": "pr_00000000000000", - "amount": 100, - "currency": "usd", - "id": "fkx0AFo_00000000000000", + "id": "price_000000000000000000000000", "object": "plan", - "livemode": false, + "active": true, + "aggregate_usage": null, + "amount": 1500, + "amount_decimal": "1500", + "billing_scheme": "per_unit", + "created": 1648320055, + "currency": "usd", + "interval": "month", "interval_count": 1, - "trial_period_days": null + "livemode": false, + "metadata": { + "foo": "bar" + }, + "nickname": null, + "product": "prod_00000000000000", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" }, "previous_attributes": { - "name": "Old name" + "metadata": { + "foo": null + } } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "113cda68-08d8-4118-aff7-ac98c4dc745b" + }, + "type": "plan.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/price.created.json b/lib/stripe_mock/webhook_fixtures/price.created.json new file mode 100644 index 000000000..6d99cd412 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/price.created.json @@ -0,0 +1,42 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320109, + "data": { + "object": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320109, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "64c22132-7234-47d6-a803-4e34ac0e883b" + }, + "type": "price.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/price.deleted.json b/lib/stripe_mock/webhook_fixtures/price.deleted.json new file mode 100644 index 000000000..a03ffdf5c --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/price.deleted.json @@ -0,0 +1,42 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320048, + "data": { + "object": { + "id": "plan_00000000000000", + "object": "price", + "active": false, + "billing_scheme": "per_unit", + "created": 1648320047, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 2000, + "unit_amount_decimal": "2000" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": null + }, + "type": "price.deleted" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/price.updated.json b/lib/stripe_mock/webhook_fixtures/price.updated.json new file mode 100644 index 000000000..28411e2f8 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/price.updated.json @@ -0,0 +1,48 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320056, + "data": { + "object": { + "id": "price_000000000000000000000000", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1648320055, + "currency": "usd", + "livemode": false, + "lookup_key": null, + "metadata": { + "foo": "bar" + }, + "nickname": null, + "product": "prod_00000000000000", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1500, + "unit_amount_decimal": "1500" + }, + "previous_attributes": { + "metadata": { + "foo": null + } + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "113cda68-08d8-4118-aff7-ac98c4dc745b" + }, + "type": "price.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/product.created.json b/lib/stripe_mock/webhook_fixtures/product.created.json index 5dc5ec97d..86c868b37 100644 --- a/lib/stripe_mock/webhook_fixtures/product.created.json +++ b/lib/stripe_mock/webhook_fixtures/product.created.json @@ -1,34 +1,40 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "product.created", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320108, "data": { "object": { "id": "prod_00000000000000", "object": "product", "active": true, "attributes": [ + ], - "caption": null, - "created": 1558795883, - "deactivate_on": [ - ], - "description": null, + "created": 1648320108, + "description": "(created by Stripe CLI)", "images": [ + ], "livemode": false, "metadata": { }, - "name": "Test Product", + "name": "myproduct", "package_dimensions": null, "shippable": null, "statement_descriptor": null, + "tax_code": null, "type": "service", "unit_label": null, - "updated": 1558795883, + "updated": 1648320108, "url": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "7f0e85cc-cf96-42be-a33a-07339f9fbe50" + }, + "type": "product.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/product.deleted.json b/lib/stripe_mock/webhook_fixtures/product.deleted.json index a0499c50b..6b9ff1581 100644 --- a/lib/stripe_mock/webhook_fixtures/product.deleted.json +++ b/lib/stripe_mock/webhook_fixtures/product.deleted.json @@ -1,34 +1,40 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "product.deleted", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320062, "data": { "object": { "id": "prod_00000000000000", "object": "product", - "active": true, + "active": false, "attributes": [ + ], - "caption": null, - "created": 1558795883, - "deactivate_on": [ - ], - "description": null, + "created": 1648320060, + "description": "(created by Stripe CLI)t", "images": [ + ], "livemode": false, "metadata": { }, - "name": "Test Product", + "name": "myproduct", "package_dimensions": null, "shippable": null, "statement_descriptor": null, + "tax_code": null, "type": "service", "unit_label": null, - "updated": 1558795883, + "updated": 1648320062, "url": null } - } -} + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": null + }, + "type": "product.deleted" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/product.updated.json b/lib/stripe_mock/webhook_fixtures/product.updated.json index 37c802448..4aec5e44b 100644 --- a/lib/stripe_mock/webhook_fixtures/product.updated.json +++ b/lib/stripe_mock/webhook_fixtures/product.updated.json @@ -1,38 +1,47 @@ { - "created": 1326853478, - "livemode": false, - "id": "evt_00000000000000", - "type": "product.updated", + "id": "evt_000000000000000000000000", "object": "event", + "api_version": "2020-08-27", + "created": 1648320064, "data": { "object": { "id": "prod_00000000000000", "object": "product", "active": true, "attributes": [ + ], - "caption": null, - "created": 1558795883, - "deactivate_on": [ - ], - "description": null, + "created": 1648320063, + "description": "(created by Stripe CLI)", "images": [ + ], "livemode": false, "metadata": { + "foo": "bar" }, - "name": "Test Product", + "name": "myproduct", "package_dimensions": null, "shippable": null, "statement_descriptor": null, + "tax_code": null, "type": "service", "unit_label": null, - "updated": 1558795883, + "updated": 1648320064, "url": null }, "previous_attributes": { - "name": "Product Test", - "updated": 1558873981 + "metadata": { + "foo": null + }, + "updated": 1648320063 } - } -} + }, + "livemode": false, + "pending_webhooks": 1, + "request": { + "id": "req_00000000000000", + "idempotency_key": "bf930f8a-e972-4b61-9ee0-492f139aefa3" + }, + "type": "product.updated" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/quote.accepted.json b/lib/stripe_mock/webhook_fixtures/quote.accepted.json new file mode 100644 index 000000000..725f2b89c --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/quote.accepted.json @@ -0,0 +1,92 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320071, + "data": { + "object": { + "id": "qt_000000000000000000000000", + "object": "quote", + "amount_subtotal": 15000, + "amount_total": 15000, + "application_fee_amount": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false, + "status": null + }, + "collection_method": "charge_automatically", + "computed": { + "recurring": { + "amount_subtotal": 15000, + "amount_total": 15000, + "interval": "month", + "interval_count": 1, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + }, + "upfront": { + "amount_subtotal": 15000, + "amount_total": 15000, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + } + }, + "created": 1648320070, + "currency": "usd", + "customer": "cus_00000000000000", + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discounts": [ + + ], + "expires_at": 1650912070, + "footer": null, + "from_quote": null, + "header": null, + "invoice": "in_000000000000000000000000", + "invoice_settings": { + "days_until_due": null + }, + "livemode": false, + "metadata": { + }, + "number": "QT-F5FD0419-0001-1", + "on_behalf_of": null, + "status": "accepted", + "status_transitions": { + "accepted_at": 1648320071, + "canceled_at": null, + "finalized_at": 1648320070 + }, + "subscription": "sub_000000000000000000000000", + "subscription_data": { + "effective_date": null, + "trial_period_days": null + }, + "subscription_schedule": null, + "test_clock": null, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + }, + "transfer_data": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "456e23c0-aebf-4ec5-b23a-a1097b504557" + }, + "type": "quote.accepted" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/quote.canceled.json b/lib/stripe_mock/webhook_fixtures/quote.canceled.json new file mode 100644 index 000000000..392f19f9e --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/quote.canceled.json @@ -0,0 +1,92 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320077, + "data": { + "object": { + "id": "qt_000000000000000000000000", + "object": "quote", + "amount_subtotal": 15000, + "amount_total": 15000, + "application_fee_amount": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false, + "status": null + }, + "collection_method": "charge_automatically", + "computed": { + "recurring": { + "amount_subtotal": 15000, + "amount_total": 15000, + "interval": "month", + "interval_count": 1, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + }, + "upfront": { + "amount_subtotal": 15000, + "amount_total": 15000, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + } + }, + "created": 1648320076, + "currency": "usd", + "customer": "cus_00000000000000", + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discounts": [ + + ], + "expires_at": 1650912076, + "footer": null, + "from_quote": null, + "header": null, + "invoice": null, + "invoice_settings": { + "days_until_due": null + }, + "livemode": false, + "metadata": { + }, + "number": null, + "on_behalf_of": null, + "status": "canceled", + "status_transitions": { + "accepted_at": null, + "canceled_at": 1648320077, + "finalized_at": null + }, + "subscription": null, + "subscription_data": { + "effective_date": null, + "trial_period_days": null + }, + "subscription_schedule": null, + "test_clock": null, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + }, + "transfer_data": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "81d553e8-c88c-4a3e-9572-24da02fd83c0" + }, + "type": "quote.canceled" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/quote.created.json b/lib/stripe_mock/webhook_fixtures/quote.created.json new file mode 100644 index 000000000..12f82453c --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/quote.created.json @@ -0,0 +1,92 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320086, + "data": { + "object": { + "id": "qt_000000000000000000000000", + "object": "quote", + "amount_subtotal": 15000, + "amount_total": 15000, + "application_fee_amount": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false, + "status": null + }, + "collection_method": "charge_automatically", + "computed": { + "recurring": { + "amount_subtotal": 15000, + "amount_total": 15000, + "interval": "month", + "interval_count": 1, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + }, + "upfront": { + "amount_subtotal": 15000, + "amount_total": 15000, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + } + }, + "created": 1648320085, + "currency": "usd", + "customer": "cus_00000000000000", + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discounts": [ + + ], + "expires_at": 1650912085, + "footer": null, + "from_quote": null, + "header": null, + "invoice": null, + "invoice_settings": { + "days_until_due": null + }, + "livemode": false, + "metadata": { + }, + "number": null, + "on_behalf_of": null, + "status": "draft", + "status_transitions": { + "accepted_at": null, + "canceled_at": null, + "finalized_at": null + }, + "subscription": null, + "subscription_data": { + "effective_date": null, + "trial_period_days": null + }, + "subscription_schedule": null, + "test_clock": null, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + }, + "transfer_data": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "444b105e-dba9-4779-b9aa-80bdc9b9a354" + }, + "type": "quote.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/quote.finalized.json b/lib/stripe_mock/webhook_fixtures/quote.finalized.json new file mode 100644 index 000000000..8d2bea860 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/quote.finalized.json @@ -0,0 +1,92 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320086, + "data": { + "object": { + "id": "qt_000000000000000000000000", + "object": "quote", + "amount_subtotal": 15000, + "amount_total": 15000, + "application_fee_amount": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false, + "status": null + }, + "collection_method": "charge_automatically", + "computed": { + "recurring": { + "amount_subtotal": 15000, + "amount_total": 15000, + "interval": "month", + "interval_count": 1, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + }, + "upfront": { + "amount_subtotal": 15000, + "amount_total": 15000, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + } + } + }, + "created": 1648320085, + "currency": "usd", + "customer": "cus_00000000000000", + "default_tax_rates": [ + + ], + "description": "(created by Stripe CLI)", + "discounts": [ + + ], + "expires_at": 1650912085, + "footer": null, + "from_quote": null, + "header": null, + "invoice": null, + "invoice_settings": { + "days_until_due": null + }, + "livemode": false, + "metadata": { + }, + "number": "QT-013268C5-0001-1", + "on_behalf_of": null, + "status": "open", + "status_transitions": { + "accepted_at": null, + "canceled_at": null, + "finalized_at": 1648320086 + }, + "subscription": null, + "subscription_data": { + "effective_date": null, + "trial_period_days": null + }, + "subscription_schedule": null, + "test_clock": null, + "total_details": { + "amount_discount": 0, + "amount_shipping": 0, + "amount_tax": 0 + }, + "transfer_data": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "1aa55aab-a2f3-4b60-9755-bd8ab903d387" + }, + "type": "quote.finalized" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/setup_intent.canceled.json b/lib/stripe_mock/webhook_fixtures/setup_intent.canceled.json new file mode 100644 index 000000000..39f7c63a0 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/setup_intent.canceled.json @@ -0,0 +1,46 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320088, + "data": { + "object": { + "id": "seti_000000000000000000000000", + "object": "setup_intent", + "application": null, + "cancellation_reason": "requested_by_customer", + "client_secret": "seti_000000000000000000000000_secret_0000000000000000000000000000000", + "created": 1648320088, + "customer": null, + "description": "(created by Stripe CLI)", + "last_setup_error": null, + "latest_attempt": null, + "livemode": false, + "mandate": null, + "metadata": { + }, + "next_action": null, + "on_behalf_of": null, + "payment_method": null, + "payment_method_options": { + "card": { + "mandate_options": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "single_use_mandate": null, + "status": "canceled", + "usage": "off_session" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "6bc9d964-51d3-47d2-82b2-127b3c5c27cd" + }, + "type": "setup_intent.canceled" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/setup_intent.created.json b/lib/stripe_mock/webhook_fixtures/setup_intent.created.json new file mode 100644 index 000000000..e567c1cfe --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/setup_intent.created.json @@ -0,0 +1,51 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648321932, + "data": { + "object": { + "id": "seti_000000000000000000000000", + "object": "setup_intent", + "application": null, + "cancellation_reason": null, + "client_secret": "seti_000000000000000000000000_secret_0000000000000000000000000000000", + "created": 1648321931, + "customer": null, + "description": null, + "last_setup_error": null, + "latest_attempt": null, + "livemode": false, + "mandate": null, + "metadata": { + }, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_000000000000000000000000", + "payment_method_options": { + "acss_debit": { + "currency": "cad", + "mandate_options": { + "interval_description": "First day of every month", + "payment_schedule": "interval", + "transaction_type": "personal" + }, + "verification_method": "automatic" + } + }, + "payment_method_types": [ + "acss_debit" + ], + "single_use_mandate": null, + "status": "requires_confirmation", + "usage": "off_session" + } + }, + "livemode": false, + "pending_webhooks": 1, + "request": { + "id": null, + "idempotency_key": null + }, + "type": "setup_intent.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/setup_intent.setup_failed.json b/lib/stripe_mock/webhook_fixtures/setup_intent.setup_failed.json new file mode 100644 index 000000000..e53c97e4d --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/setup_intent.setup_failed.json @@ -0,0 +1,100 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320091, + "data": { + "object": { + "id": "seti_000000000000000000000000", + "object": "setup_intent", + "application": null, + "cancellation_reason": null, + "client_secret": "seti_000000000000000000000000_secret_0000000000000000000000000000000", + "created": 1648320090, + "customer": null, + "description": "(created by Stripe CLI)", + "last_setup_error": { + "code": "card_declined", + "decline_code": "generic_decline", + "doc_url": "https://stripe.com/docs/error-codes/card-declined", + "message": "Your card was declined.", + "payment_method": { + "id": "pm_000000000000000000000000", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": null + }, + "country": "US", + "exp_month": 3, + "exp_year": 2023, + "fingerprint": "kMzSwhaalD1uR96R", + "funding": "credit", + "generated_from": null, + "last4": "0002", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1648320090, + "customer": null, + "livemode": false, + "metadata": { + }, + "type": "card" + }, + "type": "card_error" + }, + "latest_attempt": "setatt_000000000000000000000000", + "livemode": false, + "mandate": null, + "metadata": { + }, + "next_action": null, + "on_behalf_of": null, + "payment_method": null, + "payment_method_options": { + "card": { + "mandate_options": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "single_use_mandate": null, + "status": "requires_payment_method", + "usage": "off_session" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "bc94842b-7374-410e-8aa2-935b8cc1250c" + }, + "type": "setup_intent.setup_failed" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/setup_intent.succeeded.json b/lib/stripe_mock/webhook_fixtures/setup_intent.succeeded.json new file mode 100644 index 000000000..f7e6887ad --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/setup_intent.succeeded.json @@ -0,0 +1,46 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320093, + "data": { + "object": { + "id": "seti_000000000000000000000000", + "object": "setup_intent", + "application": null, + "cancellation_reason": null, + "client_secret": "seti_000000000000000000000000_secret_0000000000000000000000000000000", + "created": 1648320092, + "customer": null, + "description": "(created by Stripe CLI)", + "last_setup_error": null, + "latest_attempt": "setatt_000000000000000000000000", + "livemode": false, + "mandate": null, + "metadata": { + }, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_000000000000000000000000", + "payment_method_options": { + "card": { + "mandate_options": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "single_use_mandate": null, + "status": "succeeded", + "usage": "off_session" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "229fd8b7-d019-4b8b-91a9-920460d48a61" + }, + "type": "setup_intent.succeeded" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/subscription_schedule.canceled.json b/lib/stripe_mock/webhook_fixtures/subscription_schedule.canceled.json new file mode 100644 index 000000000..46cfc9fc8 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/subscription_schedule.canceled.json @@ -0,0 +1,119 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320097, + "data": { + "object": { + "id": "sub_sched_000000000000000000000000", + "object": "subscription_schedule", + "canceled_at": 1648320097, + "completed_at": null, + "created": 1648320096, + "current_phase": null, + "customer": "cus_00000000000000", + "default_settings": { + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": "automatic", + "billing_thresholds": null, + "collection_method": "charge_automatically", + "default_payment_method": null, + "default_source": null, + "invoice_settings": null, + "transfer_data": null + }, + "end_behavior": "release", + "livemode": false, + "metadata": { + }, + "phases": [ + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1650998496, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 1, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1648320096, + "transfer_data": null, + "trial_end": null + }, + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1653590496, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 2, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1650998496, + "transfer_data": null, + "trial_end": null + } + ], + "released_at": null, + "released_subscription": null, + "renewal_interval": null, + "status": "canceled", + "subscription": "sub_000000000000000000000000", + "test_clock": null + }, + "previous_attributes": { + "canceled_at": null, + "current_phase": { + "end_date": 1650998496, + "start_date": 1648320096 + }, + "status": "active" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "27f56452-b979-42fb-8bc1-219db1e65979" + }, + "type": "subscription_schedule.canceled" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/subscription_schedule.created.json b/lib/stripe_mock/webhook_fixtures/subscription_schedule.created.json new file mode 100644 index 000000000..edc95ec01 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/subscription_schedule.created.json @@ -0,0 +1,114 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320110, + "data": { + "object": { + "id": "sub_sched_000000000000000000000000", + "object": "subscription_schedule", + "canceled_at": null, + "completed_at": null, + "created": 1648320110, + "current_phase": { + "end_date": 1650998510, + "start_date": 1648320110 + }, + "customer": "cus_00000000000000", + "default_settings": { + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": "automatic", + "billing_thresholds": null, + "collection_method": "charge_automatically", + "default_payment_method": null, + "default_source": null, + "invoice_settings": null, + "transfer_data": null + }, + "end_behavior": "release", + "livemode": false, + "metadata": { + }, + "phases": [ + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1650998510, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 1, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1648320110, + "transfer_data": null, + "trial_end": null + }, + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1653590510, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 2, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1650998510, + "transfer_data": null, + "trial_end": null + } + ], + "released_at": null, + "released_subscription": null, + "renewal_interval": null, + "status": "active", + "subscription": "sub_000000000000000000000000", + "test_clock": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "af06a7d9-5408-4f65-8379-59a93874d04c" + }, + "type": "subscription_schedule.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/subscription_schedule.released.json b/lib/stripe_mock/webhook_fixtures/subscription_schedule.released.json new file mode 100644 index 000000000..53da95454 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/subscription_schedule.released.json @@ -0,0 +1,111 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320106, + "data": { + "object": { + "id": "sub_sched_000000000000000000000000", + "object": "subscription_schedule", + "canceled_at": null, + "completed_at": null, + "created": 1648320105, + "current_phase": null, + "customer": "cus_00000000000000", + "default_settings": { + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": "automatic", + "billing_thresholds": null, + "collection_method": "charge_automatically", + "default_payment_method": null, + "default_source": null, + "invoice_settings": null, + "transfer_data": null + }, + "end_behavior": "release", + "livemode": false, + "metadata": { + }, + "phases": [ + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1650998505, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 1, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1648320105, + "transfer_data": null, + "trial_end": null + }, + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1653590505, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 2, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1650998505, + "transfer_data": null, + "trial_end": null + } + ], + "released_at": 1648320106, + "released_subscription": "sub_000000000000000000000000", + "renewal_interval": null, + "status": "released", + "subscription": null, + "test_clock": null + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "fe5a111d-ed6f-4cb5-a33c-05b364afc565" + }, + "type": "subscription_schedule.released" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/subscription_schedule.updated.json b/lib/stripe_mock/webhook_fixtures/subscription_schedule.updated.json new file mode 100644 index 000000000..6a65eb56a --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/subscription_schedule.updated.json @@ -0,0 +1,125 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320111, + "data": { + "object": { + "id": "sub_sched_000000000000000000000000", + "object": "subscription_schedule", + "canceled_at": null, + "completed_at": null, + "created": 1648320110, + "current_phase": { + "end_date": 1650998510, + "start_date": 1648320110 + }, + "customer": "cus_00000000000000", + "default_settings": { + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": "automatic", + "billing_thresholds": null, + "collection_method": "charge_automatically", + "default_payment_method": null, + "default_source": null, + "invoice_settings": { + "days_until_due": null + }, + "transfer_data": null + }, + "end_behavior": "release", + "livemode": false, + "metadata": { + "foo": "bar" + }, + "phases": [ + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1650998510, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 1, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1648320110, + "transfer_data": null, + "trial_end": null + }, + { + "add_invoice_items": [ + + ], + "application_fee_percent": null, + "billing_cycle_anchor": null, + "billing_thresholds": null, + "collection_method": null, + "coupon": null, + "default_payment_method": null, + "default_tax_rates": [ + + ], + "end_date": 1653590510, + "invoice_settings": null, + "items": [ + { + "billing_thresholds": null, + "plan": "price_000000000000000000000000", + "price": "price_000000000000000000000000", + "quantity": 2, + "tax_rates": [ + + ] + } + ], + "proration_behavior": "create_prorations", + "start_date": 1650998510, + "transfer_data": null, + "trial_end": null + } + ], + "released_at": null, + "released_subscription": null, + "renewal_interval": null, + "status": "active", + "subscription": "sub_000000000000000000000000", + "test_clock": null + }, + "previous_attributes": { + "default_settings": { + "invoice_settings": null + }, + "metadata": { + "foo": null + } + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "209a08f0-224b-410a-b659-a0a39a7c59b6" + }, + "type": "subscription_schedule.updated" +} \ No newline at end of file From 7f520c8875c64a2b4ae39132862371a73ca57695 Mon Sep 17 00:00:00 2001 From: Ivan Stroganov Date: Thu, 28 Apr 2022 13:27:51 -0700 Subject: [PATCH 080/158] Include charge payment_method_details. Per [docs](https://stripe.com/docs/api/charges/object), this attribute is always present on charge objects. The actual mock charge is copied from an existing stale [PR](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/741) by @drnic from a year and a half ago, however that PR has a ton of linting changes as well - hopefully without these changes the stripe team merges this PR and closes both of them :-) --- lib/stripe_mock/data.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 738d29030..ae90eddb6 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -193,6 +193,27 @@ def self.mock_charge(params={}) currency: currency, destination: nil, fraud_details: {}, + payment_method_details: { + card: { + brand: "visa", + checks: { + address_line1_check: nil, + address_postal_code_check: nil, + cvc_check: "pass" + }, + country: "US", + exp_month: 12, + exp_year: 2013, + fingerprint: "3TQGpK9JoY1GgXPw", + funding: "credit", + installments: nil, + last4: "4242", + network: "visa", + three_d_secure: nil, + wallet: nil + }, + type: "card" + }, receipt_email: nil, receipt_number: nil, refunded: false, From 6687d62220273aada3ed998cd78e1504fc4abcb8 Mon Sep 17 00:00:00 2001 From: Adam Stegman Date: Wed, 4 May 2022 15:29:20 -0500 Subject: [PATCH 081/158] Support metadata on subscription items --- .../helpers/subscription_helpers.rb | 6 ++--- .../subscription_examples.rb | 23 ++++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 7a228abbb..33bfd370f 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -13,9 +13,9 @@ def resolve_subscription_changes(subscription, plans, customer, options = {}) subscription[:items][:data] = plans.map do |plan| matching_item = items && items.detect { |item| [item[:price], item[:plan]].include? plan[:id] } if matching_item - quantity = matching_item[:quantity] || 1 - id = matching_item[:id] || new_id('si') - params = { plan: plan, quantity: quantity, id: id } + matching_item[:quantity] ||= 1 + matching_item[:id] ||= new_id('si') + params = matching_item.merge(plan: plan) params[:price] = plan if plan[:object] == "price" Data.mock_subscription_item(params) else diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index edfd7ac2c..c2da97304 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -712,7 +712,7 @@ }) expect(subscription.latest_invoice.payment_intent.status).to eq('requires_payment_method') - + subscription = Stripe::Subscription.create({ customer: customer.id, plan: plan.id, @@ -1325,6 +1325,27 @@ expect(customer.subscriptions.first.plan.id).to eq('Sample5') expect(customer.subscriptions.first.metadata['foo']).to eq('bar') end + + it "saves subscription item metadata" do + stripe_helper. + create_plan( + :amount => 500, + :interval => 'month', + :product => product.id, + :currency => 'usd', + :id => 'Sample5' + ) + customer = Stripe::Customer.create({ + email: 'johnny@appleseed.com', + source: gen_card_tk + }) + + subscription = Stripe::Subscription.create( + customer: customer.id, + items: [{plan: "Sample5", metadata: {foo: 'bar'}}], + ) + expect(subscription.items.data[0].metadata.to_h).to eq(foo: 'bar') + end end end From cecc0a22d72326d2cb55b1dc8957c4aa825d816a Mon Sep 17 00:00:00 2001 From: Adam Stegman Date: Thu, 5 May 2022 12:30:37 -0500 Subject: [PATCH 082/158] Handle payment_method_details in charge update The Charge update tests have been failing since #826. The `payment_method_details` attribute is included by Stripe's gem even though it's not changed and isn't supported by the Charge update API. Stripe's API just ignores it, so I'm adding that behavior here. --- lib/stripe_mock/request_handlers/charges.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/stripe_mock/request_handlers/charges.rb b/lib/stripe_mock/request_handlers/charges.rb index ea826daa5..54c3050a8 100644 --- a/lib/stripe_mock/request_handlers/charges.rb +++ b/lib/stripe_mock/request_handlers/charges.rb @@ -171,6 +171,11 @@ def allowed_params(params) params[:refunds].has_key?(:data) && params[:refunds][:data].nil?) allowed << :refunds end + if params.has_key?(:payment_method_details) && (params[:payment_method_details].empty? || + params[:payment_method_details].has_key?(:card) && (params[:payment_method_details][:card].empty? || + params[:payment_method_details][:card].has_key?(:checks) && params[:payment_method_details][:card][:checks].empty?)) + allowed << :payment_method_details + end allowed end From b811935a698fc73d26f6dfb799df63358fdbe5ff Mon Sep 17 00:00:00 2001 From: Adam Stegman Date: Mon, 9 May 2022 17:57:31 -0500 Subject: [PATCH 083/158] Implement search API Stripe has added a [search API](https://stripe.com/docs/search) to the latest API version, `2020-08-27`. Supporting this in all its permutations would be very difficult, essentially partially implementing a full search engine. For now, supporting exact-match searches on each of the supported fields is relatively easy. --- lib/stripe_mock.rb | 1 + lib/stripe_mock/request_handlers/charges.rb | 21 +++- lib/stripe_mock/request_handlers/customers.rb | 13 ++- .../helpers/search_helpers.rb | 67 +++++++++++++ lib/stripe_mock/request_handlers/invoices.rb | 11 ++- .../request_handlers/payment_intents.rb | 11 ++- lib/stripe_mock/request_handlers/prices.rb | 17 +++- lib/stripe_mock/request_handlers/products.rb | 19 +++- .../request_handlers/subscriptions.rb | 11 ++- .../shared_stripe_examples/charge_examples.rb | 97 +++++++++++++++++++ .../customer_examples.rb | 56 +++++++++++ .../invoice_examples.rb | 82 ++++++++++++++++ .../payment_intent_examples.rb | 62 ++++++++++++ spec/shared_stripe_examples/price_examples.rb | 69 +++++++++++++ .../product_examples.rb | 68 +++++++++++++ .../subscription_examples.rb | 59 +++++++++++ 16 files changed, 650 insertions(+), 14 deletions(-) create mode 100644 lib/stripe_mock/request_handlers/helpers/search_helpers.rb diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index 543ae1b90..cca36af5f 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -42,6 +42,7 @@ require 'stripe_mock/request_handlers/helpers/card_helpers.rb' require 'stripe_mock/request_handlers/helpers/charge_helpers.rb' require 'stripe_mock/request_handlers/helpers/coupon_helpers.rb' +require 'stripe_mock/request_handlers/helpers/search_helpers.rb' require 'stripe_mock/request_handlers/helpers/subscription_helpers.rb' require 'stripe_mock/request_handlers/helpers/token_helpers.rb' diff --git a/lib/stripe_mock/request_handlers/charges.rb b/lib/stripe_mock/request_handlers/charges.rb index 54c3050a8..f26765e40 100644 --- a/lib/stripe_mock/request_handlers/charges.rb +++ b/lib/stripe_mock/request_handlers/charges.rb @@ -5,7 +5,8 @@ module Charges def Charges.included(klass) klass.add_handler 'post /v1/charges', :new_charge klass.add_handler 'get /v1/charges', :get_charges - klass.add_handler 'get /v1/charges/(.*)', :get_charge + klass.add_handler 'get /v1/charges/search', :search_charges + klass.add_handler 'get /v1/charges/((?!search).*)', :get_charge klass.add_handler 'post /v1/charges/(.*)/capture', :capture_charge klass.add_handler 'post /v1/charges/(.*)/refund', :refund_charge klass.add_handler 'post /v1/charges/(.*)/refunds', :refund_charge @@ -90,6 +91,24 @@ def get_charges(route, method_url, params, headers) Data.mock_list_object(clone.values, params) end + SEARCH_FIELDS = [ + "amount", + "currency", + "customer", + "payment_method_details.card.brand", + "payment_method_details.card.exp_month", + "payment_method_details.card.exp_year", + "payment_method_details.card.fingerprint", + "payment_method_details.card.last4", + "status", + ].freeze + def search_charges(route, method_url, params, headers) + require_param(:query) unless params[:query] + + results = search_results(charges.values, params[:query], fields: SEARCH_FIELDS, resource_name: "charges") + Data.mock_list_object(results, params) + end + def get_charge(route, method_url, params, headers) route =~ method_url charge_id = $1 || params[:charge] diff --git a/lib/stripe_mock/request_handlers/customers.rb b/lib/stripe_mock/request_handlers/customers.rb index 32946ab04..ca9ac9bc3 100644 --- a/lib/stripe_mock/request_handlers/customers.rb +++ b/lib/stripe_mock/request_handlers/customers.rb @@ -5,9 +5,10 @@ module Customers def Customers.included(klass) klass.add_handler 'post /v1/customers', :new_customer klass.add_handler 'post /v1/customers/([^/]*)', :update_customer - klass.add_handler 'get /v1/customers/([^/]*)', :get_customer + klass.add_handler 'get /v1/customers/((?!search)[^/]*)', :get_customer klass.add_handler 'delete /v1/customers/([^/]*)', :delete_customer klass.add_handler 'get /v1/customers', :list_customers + klass.add_handler 'get /v1/customers/search', :search_customers klass.add_handler 'delete /v1/customers/([^/]*)/discount', :delete_customer_discount end @@ -140,6 +141,16 @@ def list_customers(route, method_url, params, headers) Data.mock_list_object(customers[stripe_account]&.values, params) end + SEARCH_FIELDS = ["email", "name", "phone"].freeze + def search_customers(route, method_url, params, headers) + require_param(:query) unless params[:query] + + stripe_account = headers && headers[:stripe_account] || Stripe.api_key + all_customers = customers[stripe_account]&.values + results = search_results(all_customers, params[:query], fields: SEARCH_FIELDS, resource_name: "customers") + Data.mock_list_object(results, params) + end + def delete_customer_discount(route, method_url, params, headers) stripe_account = headers && headers[:stripe_account] || Stripe.api_key route =~ method_url diff --git a/lib/stripe_mock/request_handlers/helpers/search_helpers.rb b/lib/stripe_mock/request_handlers/helpers/search_helpers.rb new file mode 100644 index 000000000..76b4cfa20 --- /dev/null +++ b/lib/stripe_mock/request_handlers/helpers/search_helpers.rb @@ -0,0 +1,67 @@ +module StripeMock + module RequestHandlers + module Helpers + # Only supports exact matches on a single field, e.g. + # - 'amount:100' + # - 'email:"name@domain.com"' + # - 'name:"Foo Bar"' + # - 'metadata["foo"]:"bar"' + QUERYSTRING_PATTERN = /\A(?[\w\.]+)(\[['"](?[^'"]*)['"]\])?:['"]?(?[^'"]*)['"]?\z/ + def search_results(all_values, querystring, fields: [], resource_name:) + values = all_values.dup + query_match = QUERYSTRING_PATTERN.match(querystring) + raise Stripe::InvalidRequestError.new( + 'We were unable to parse your search query.' \ + ' Try using the format `metadata["key"]:"value"` to query for metadata or key:"value" to query for other fields.', + nil, + http_status: 400, + ) unless query_match + + case query_match[:field] + when *fields + values = values.select { |resource| + exact_match?(actual: field_value(resource, field: query_match[:field]), expected: query_match[:value]) + } + when "metadata" + values = values.select { |resource| + resource[:metadata] && + exact_match?(actual: resource[:metadata][query_match[:metadata_key].to_sym], expected: query_match[:value]) + } + else + raise Stripe::InvalidRequestError.new( + "Field `#{query_match[:field]}` is an unsupported search field for resource `#{resource_name}`." \ + " See http://stripe.com/docs/search#query-fields-for-#{resource_name.gsub('_', '-')} for a list of supported fields.", + nil, + http_status: 400, + ) + end + + values + end + + def exact_match?(actual:, expected:) + # allow comparisons of integers + if actual.respond_to?(:to_i) && actual.to_i == actual + expected = expected.to_i + end + # allow comparisons of boolean + case expected + when "true" + expected = true + when "false" + expected = false + end + + actual == expected + end + + def field_value(resource, field:) + value = resource + field.split('.').each do |segment| + value = value[segment.to_sym] + end + value + end + end + end +end diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index 653096633..94f6b6a9d 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -6,7 +6,8 @@ def Invoices.included(klass) klass.add_handler 'post /v1/invoices', :new_invoice klass.add_handler 'get /v1/invoices/upcoming', :upcoming_invoice klass.add_handler 'get /v1/invoices/(.*)/lines', :get_invoice_line_items - klass.add_handler 'get /v1/invoices/(.*)', :get_invoice + klass.add_handler 'get /v1/invoices/((?!search).*)', :get_invoice + klass.add_handler 'get /v1/invoices/search', :search_invoices klass.add_handler 'get /v1/invoices', :list_invoices klass.add_handler 'post /v1/invoices/(.*)/pay', :pay_invoice klass.add_handler 'post /v1/invoices/(.*)', :update_invoice @@ -25,6 +26,14 @@ def update_invoice(route, method_url, params, headers) invoices[$1].merge!(params) end + SEARCH_FIELDS = ["currency", "customer", "number", "receipt_number", "subscription", "total"].freeze + def search_invoices(route, method_url, params, headers) + require_param(:query) unless params[:query] + + results = search_results(invoices.values, params[:query], fields: SEARCH_FIELDS, resource_name: "invoices") + Data.mock_list_object(results, params) + end + def list_invoices(route, method_url, params, headers) params[:offset] ||= 0 params[:limit] ||= 10 diff --git a/lib/stripe_mock/request_handlers/payment_intents.rb b/lib/stripe_mock/request_handlers/payment_intents.rb index d01b42aa9..2008cdac2 100644 --- a/lib/stripe_mock/request_handlers/payment_intents.rb +++ b/lib/stripe_mock/request_handlers/payment_intents.rb @@ -6,7 +6,8 @@ module PaymentIntents def PaymentIntents.included(klass) klass.add_handler 'post /v1/payment_intents', :new_payment_intent klass.add_handler 'get /v1/payment_intents', :get_payment_intents - klass.add_handler 'get /v1/payment_intents/(.*)', :get_payment_intent + klass.add_handler 'get /v1/payment_intents/((?!search).*)', :get_payment_intent + klass.add_handler 'get /v1/payment_intents/search', :search_payment_intents klass.add_handler 'post /v1/payment_intents/(.*)/confirm', :confirm_payment_intent klass.add_handler 'post /v1/payment_intents/(.*)/capture', :capture_payment_intent klass.add_handler 'post /v1/payment_intents/(.*)/cancel', :cancel_payment_intent @@ -70,6 +71,14 @@ def get_payment_intent(route, method_url, params, headers) payment_intent end + SEARCH_FIELDS = ["amount", "currency", "customer", "status"].freeze + def search_payment_intents(route, method_url, params, headers) + require_param(:query) unless params[:query] + + results = search_results(payment_intents.values, params[:query], fields: SEARCH_FIELDS, resource_name: "payment_intents") + Data.mock_list_object(results, params) + end + def capture_payment_intent(route, method_url, params, headers) route =~ method_url payment_intent = assert_existence :payment_intent, $1, payment_intents[$1] diff --git a/lib/stripe_mock/request_handlers/prices.rb b/lib/stripe_mock/request_handlers/prices.rb index 46146a4ce..15ceab434 100644 --- a/lib/stripe_mock/request_handlers/prices.rb +++ b/lib/stripe_mock/request_handlers/prices.rb @@ -3,10 +3,11 @@ module RequestHandlers module Prices def Prices.included(klass) - klass.add_handler 'post /v1/prices', :new_price - klass.add_handler 'post /v1/prices/(.*)', :update_price - klass.add_handler 'get /v1/prices/(.*)', :get_price - klass.add_handler 'get /v1/prices', :list_prices + klass.add_handler 'post /v1/prices', :new_price + klass.add_handler 'post /v1/prices/(.*)', :update_price + klass.add_handler 'get /v1/prices/((?!search).*)', :get_price + klass.add_handler 'get /v1/prices/search', :search_prices + klass.add_handler 'get /v1/prices', :list_prices end def new_price(route, method_url, params, headers) @@ -45,6 +46,14 @@ def list_prices(route, method_url, params, headers) Data.mock_list_object(price_data.first(limit), params.merge!(limit: limit)) end + + SEARCH_FIELDS = ["active", "currency", "lookup_key", "product", "type"].freeze + def search_prices(route, method_url, params, headers) + require_param(:query) unless params[:query] + + results = search_results(prices.values, params[:query], fields: SEARCH_FIELDS, resource_name: "prices") + Data.mock_list_object(results, params) + end end end end diff --git a/lib/stripe_mock/request_handlers/products.rb b/lib/stripe_mock/request_handlers/products.rb index a75461c32..a5d81ef44 100644 --- a/lib/stripe_mock/request_handlers/products.rb +++ b/lib/stripe_mock/request_handlers/products.rb @@ -2,11 +2,12 @@ module StripeMock module RequestHandlers module Products def self.included(base) - base.add_handler 'post /v1/products', :create_product - base.add_handler 'get /v1/products/(.*)', :retrieve_product - base.add_handler 'post /v1/products/(.*)', :update_product - base.add_handler 'get /v1/products', :list_products - base.add_handler 'delete /v1/products/(.*)', :destroy_product + base.add_handler 'post /v1/products', :create_product + base.add_handler 'get /v1/products/((?!search).*)', :retrieve_product + base.add_handler 'get /v1/products/search', :search_products + base.add_handler 'post /v1/products/(.*)', :update_product + base.add_handler 'get /v1/products', :list_products + base.add_handler 'delete /v1/products/(.*)', :destroy_product end def create_product(_route, _method_url, params, _headers) @@ -32,6 +33,14 @@ def list_products(_route, _method_url, params, _headers) Data.mock_list_object(products.values.take(limit), params) end + SEARCH_FIELDS = ["active", "description", "name", "shippable", "url"].freeze + def search_products(route, method_url, params, headers) + require_param(:query) unless params[:query] + + results = search_results(products.values, params[:query], fields: SEARCH_FIELDS, resource_name: "products") + Data.mock_list_object(results, params) + end + def destroy_product(route, method_url, _params, _headers) id = method_url.match(route).captures.first assert_existence :product, id, products[id] diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index d8c31d1d9..da64f2ea3 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -5,8 +5,9 @@ module Subscriptions def Subscriptions.included(klass) klass.add_handler 'get /v1/subscriptions', :retrieve_subscriptions klass.add_handler 'post /v1/subscriptions', :create_subscription - klass.add_handler 'get /v1/subscriptions/(.*)', :retrieve_subscription + klass.add_handler 'get /v1/subscriptions/((?!search).*)', :retrieve_subscription klass.add_handler 'post /v1/subscriptions/(.*)', :update_subscription + klass.add_handler 'get /v1/subscriptions/search', :search_subscriptions klass.add_handler 'delete /v1/subscriptions/(.*)', :cancel_subscription klass.add_handler 'post /v1/customers/(.*)/subscription(?:s)?', :create_customer_subscription @@ -293,6 +294,14 @@ def cancel_subscription(route, method_url, params, headers) subscription end + SEARCH_FIELDS = ["status"].freeze + def search_subscriptions(route, method_url, params, headers) + require_param(:query) unless params[:query] + + results = search_results(subscriptions.values, params[:query], fields: SEARCH_FIELDS, resource_name: "subscriptions") + Data.mock_list_object(results, params) + end + private def get_subscription_plans_from_params(params) diff --git a/spec/shared_stripe_examples/charge_examples.rb b/spec/shared_stripe_examples/charge_examples.rb index c26b8f602..942325dfd 100644 --- a/spec/shared_stripe_examples/charge_examples.rb +++ b/spec/shared_stripe_examples/charge_examples.rb @@ -494,4 +494,101 @@ end end + context "search" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches charges for exact matches", :aggregate_failures do + response = Stripe::Charge.search({query: 'amount:100'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + customer = Stripe::Customer.create(email: 'johnny@appleseed.com') + one = Stripe::Charge.create( + customer: customer.id, + amount: 100, + currency: "usd", + payment_method_details: { + card: StripeMock::Data.mock_charge[:payment_method_details][:card].merge( + brand: "mastercard", + exp_month: 1, + exp_year: 2111, + fingerprint: "un", + last4: "1111", + ), + }, + status: "succeeded", + metadata: {key: 'uno'}, + ) + two = Stripe::Charge.create( + customer: customer.id, + amount: 200, + currency: "gbp", + payment_method_details: { + card: StripeMock::Data.mock_charge[:payment_method_details][:card].merge( + brand: "visa", + exp_month: 2, + exp_year: 2222, + fingerprint: "deux", + last4: "2222", + ), + }, + status: "pending", + metadata: {key: 'dos'}, + ) + + response = Stripe::Charge.search({query: 'amount:100'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Charge.search({query: 'currency:"gbp"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Charge.search({query: %(customer:"#{customer.id}")}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id, two.id]) + + response = Stripe::Charge.search({query: 'payment_method_details.card.brand:mastercard'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Charge.search({query: 'payment_method_details.card.exp_month:2'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Charge.search({query: 'payment_method_details.card.exp_year:2111'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Charge.search({query: 'payment_method_details.card.fingerprint:un'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Charge.search({query: 'payment_method_details.card.last4:2222'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Charge.search({query: 'status:"succeeded"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Charge.search({query: 'metadata["key"]:"uno"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + end + + it "respects limit", :aggregate_failures do + customer = Stripe::Customer.create(email: 'johnny@appleseed.com') + 11.times do + Stripe::Charge.create(customer: customer.id, amount: 100, currency: "usd") + end + + response = Stripe::Charge.search({query: %(customer:"#{customer.id}")}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::Charge.search({query: %(customer:"#{customer.id}"), limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::Charge.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::Charge.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::Charge.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `charges`./) + end + end end diff --git a/spec/shared_stripe_examples/customer_examples.rb b/spec/shared_stripe_examples/customer_examples.rb index 60d8487df..d564ad22f 100644 --- a/spec/shared_stripe_examples/customer_examples.rb +++ b/spec/shared_stripe_examples/customer_examples.rb @@ -349,6 +349,62 @@ def gen_card_tk expect(all.data.map &:email).to include('one@one.com', 'two@two.com') end + context "search" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches customers for exact matches", :aggregate_failures do + response = Stripe::Customer.search({query: 'email:"one@one.com"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + one = Stripe::Customer.create(email: 'one@one.com', name: 'one', phone: '1111111111', metadata: {key: 'uno'}) + two = Stripe::Customer.create(email: 'two@two.com', name: 'two', phone: '2222222222', metadata: {key: 'dos'}) + + response = Stripe::Customer.search({query: 'email:"one@one.com"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Customer.search({query: 'name:"two"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Customer.search({query: 'phone:"2222222222"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Customer.search({query: 'metadata["key"]:"uno"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + end + + it "respects limit", :aggregate_failures do + one = Stripe::Customer.create(email: 'one@one.com', name: 'one') + two = Stripe::Customer.create(email: 'two@two.com', name: 'one') + three = Stripe::Customer.create(email: 'three@three.com', name: 'one') + four = Stripe::Customer.create(email: 'four@four.com', name: 'one') + five = Stripe::Customer.create(email: 'five@five.com', name: 'one') + six = Stripe::Customer.create(email: 'six@six.com', name: 'one') + seven = Stripe::Customer.create(email: 'seven@seven.com', name: 'one') + eight = Stripe::Customer.create(email: 'eight@eight.com', name: 'one') + nine = Stripe::Customer.create(email: 'nine@nine.com', name: 'one') + ten = Stripe::Customer.create(email: 'ten@ten.com', name: 'one') + eleven = Stripe::Customer.create(email: 'eleven@eleven.com', name: 'one') + + response = Stripe::Customer.search({query: 'name:"one"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::Customer.search({query: 'name:"one"', limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::Customer.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::Customer.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::Customer.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `customers`./) + end + end + it "updates a stripe customer" do original = Stripe::Customer.create(id: 'test_customer_update') email = original.email diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index 244ae20e8..a6a56f194 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -74,6 +74,88 @@ end end + context "searching invoices" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches invoices for exact matches", :aggregate_failures do + response = Stripe::Invoice.search({query: 'currency:"usd"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + product = stripe_helper.create_product + stripe_helper.create_plan( + amount: 500, + interval: 'month', + product: product.id, + currency: 'usd', + id: 'Sample5', + ) + customer = Stripe::Customer.create(email: 'johnny@appleseed.com', source: stripe_helper.generate_card_token) + subscription = Stripe::Subscription.create(customer: customer.id, items: [{plan: 'Sample5'}]) + one = Stripe::Invoice.create( + customer: customer.id, + currency: 'usd', + subscription: subscription.id, + metadata: {key: 'uno'}, + number: 'one-1', + receipt_number: '111', + ) + two = Stripe::Invoice.create( + customer: customer.id, + currency: 'gbp', + subscription: subscription.id, + metadata: {key: 'dos'}, + number: 'two-2', + receipt_number: '222', + ) + + response = Stripe::Invoice.search({query: 'currency:"gbp"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Invoice.search({query: %(customer:"#{customer.id}")}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id, two.id]) + + response = Stripe::Invoice.search({query: 'number:"one-1"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Invoice.search({query: 'receipt_number:"222"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Invoice.search({query: %(subscription:"#{subscription.id}")}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id, two.id]) + + response = Stripe::Invoice.search({query: 'total:1000'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id, two.id]) + + response = Stripe::Invoice.search({query: 'metadata["key"]:"uno"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + end + + it "respects limit", :aggregate_failures do + customer = Stripe::Customer.create(email: 'one@one.com', name: 'one', phone: '1111111111', metadata: {key: 'uno'}) + 11.times do + Stripe::Invoice.create(customer: customer.id, currency: 'usd') + end + + response = Stripe::Invoice.search({query: %(customer:"#{customer.id}")}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::Invoice.search({query: %(customer:"#{customer.id}"), limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::Invoice.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::Invoice.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::Invoice.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `invoices`./) + end + end + context "paying an invoice" do before do @invoice = Stripe::Invoice.create diff --git a/spec/shared_stripe_examples/payment_intent_examples.rb b/spec/shared_stripe_examples/payment_intent_examples.rb index 8f6993eab..fcd0fdf72 100644 --- a/spec/shared_stripe_examples/payment_intent_examples.rb +++ b/spec/shared_stripe_examples/payment_intent_examples.rb @@ -144,4 +144,66 @@ expect(e.http_status).to eq(400) } end + + context "search" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches payment intents for exact matches", :aggregate_failures do + response = Stripe::PaymentIntent.search({query: 'currency:"usd"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + customer = Stripe::Customer.create(email: 'johnny@appleseed.com', source: stripe_helper.generate_card_token) + one = Stripe::PaymentIntent.create( + amount: 100, + customer: customer.id, + currency: 'usd', + metadata: {key: 'uno'}, + ) + two = Stripe::PaymentIntent.create( + amount: 3184, # status: requires_action + customer: customer.id, + currency: 'gbp', + metadata: {key: 'dos'}, + ) + + response = Stripe::PaymentIntent.search({query: 'amount:100'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::PaymentIntent.search({query: 'currency:"gbp"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::PaymentIntent.search({query: %(customer:"#{customer.id}")}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id, two.id]) + + response = Stripe::PaymentIntent.search({query: 'status:"requires_action"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::PaymentIntent.search({query: 'metadata["key"]:"uno"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + end + + it "respects limit", :aggregate_failures do + 11.times do + Stripe::PaymentIntent.create(amount: 100, currency: 'usd') + end + + response = Stripe::PaymentIntent.search({query: 'amount:100'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::PaymentIntent.search({query: 'amount:100', limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::PaymentIntent.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::PaymentIntent.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::PaymentIntent.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `payment_intents`./) + end + end end diff --git a/spec/shared_stripe_examples/price_examples.rb b/spec/shared_stripe_examples/price_examples.rb index 7138aa923..67e555e50 100644 --- a/spec/shared_stripe_examples/price_examples.rb +++ b/spec/shared_stripe_examples/price_examples.rb @@ -125,6 +125,75 @@ expect(two.map &:amount).to include(98765) end + context "searching prices" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches prices for exact matches", :aggregate_failures do + response = Stripe::Price.search({query: 'currency:"usd"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + one = stripe_helper.create_price( + amount: 100, + currency: 'usd', + lookup_key: 'one', + product: product_id, + metadata: {key: 'uno'}, + type: "one_time", + ) + two = stripe_helper.create_price( + active: false, + amount: 200, + currency: 'gbp', + lookup_key: 'two', + product: product_id, + recurring: {interval: 'month'}, + metadata: {key: 'dos'}, + ) + + response = Stripe::Price.search({query: 'active:"true"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Price.search({query: 'currency:"gbp"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Price.search({query: 'lookup_key:"one"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Price.search({query: %(product:"#{product.id}")}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id, two.id]) + + response = Stripe::Price.search({query: 'type:"recurring"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Price.search({query: 'metadata["key"]:"uno"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + end + + it "respects limit", :aggregate_failures do + 11.times do + stripe_helper.create_price(product: product_id) + end + + response = Stripe::Price.search({query: %(product:"#{product.id}")}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::Price.search({query: %(product:"#{product.id}"), limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::Price.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::Price.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::Price.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `prices`./) + end + end + describe "Validations", :live => true do include_context "stripe validator" let(:params) { stripe_helper.create_price_params(product: product_id) } diff --git a/spec/shared_stripe_examples/product_examples.rb b/spec/shared_stripe_examples/product_examples.rb index 05fe7574e..37a8e91f3 100644 --- a/spec/shared_stripe_examples/product_examples.rb +++ b/spec/shared_stripe_examples/product_examples.rb @@ -85,6 +85,74 @@ expect(all.count).to eq(100) end + context "searching products" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches products for exact matches", :aggregate_failures do + response = Stripe::Product.search({query: 'name:"one"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + one = stripe_helper.create_product( + id: "product_1", + name: "one", + description: "un", + shippable: true, + url: "http://example.com/one", + metadata: {key: "uno"}, + ) + two = stripe_helper.create_product( + id: "product_2", + name: "two", + active: false, + description: "deux", + url: "http://example.com/two", + metadata: {key: "dos"}, + ) + + response = Stripe::Product.search({query: 'active:"true"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Product.search({query: 'description:"deux"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Product.search({query: 'name:"one"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Product.search({query: 'shippable:"true"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Product.search({query: 'url:"http://example.com/two"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + + response = Stripe::Product.search({query: 'metadata["key"]:"uno"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + end + + it "respects limit", :aggregate_failures do + 11.times do |i| + stripe_helper.create_product(id: "product_#{i}", name: "Product #{i}") + end + + response = Stripe::Product.search({query: 'active:"true"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::Product.search({query: 'active:"true"', limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::Product.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::Product.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::Product.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `products`./) + end + end + describe "Validation", :live => true do include_context "stripe validator" let(:params) { stripe_helper.create_product_params } diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index c2da97304..9863902a9 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1347,6 +1347,65 @@ expect(subscription.items.data[0].metadata.to_h).to eq(foo: 'bar') end end + + context "search" do + # the Search API requires about a minute between writes and reads, so add sleeps accordingly when running live + it "searches subscriptions for exact matches", :aggregate_failures do + response = Stripe::Subscription.search({query: 'status:"active"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(0) + + stripe_helper.create_plan( + amount: 500, + interval: 'month', + product: product.id, + currency: 'usd', + id: 'Sample5' + ) + customer = Stripe::Customer.create(email: 'johnny@appleseed.com', source: gen_card_tk) + one = Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}], metadata: {key: 'uno'}) + two = Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}], metadata: {key: 'dos'}) + Stripe::Subscription.delete(two.id) + + response = Stripe::Subscription.search({query: 'status:"active"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([one.id]) + + response = Stripe::Subscription.search({query: 'metadata["key"]:"dos"'}, stripe_version: '2020-08-27') + expect(response.data.map(&:id)).to match_array([two.id]) + end + + it "respects limit", :aggregate_failures do + stripe_helper.create_plan( + amount: 500, + interval: 'month', + product: product.id, + currency: 'usd', + id: 'Sample5' + ) + customer = Stripe::Customer.create(email: 'johnny@appleseed.com', source: gen_card_tk) + 11.times do + Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}]) + end + + response = Stripe::Subscription.search({query: 'status:"active"'}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(10) + response = Stripe::Subscription.search({query: 'status:"active"', limit: 1}, stripe_version: '2020-08-27') + expect(response.data.size).to eq(1) + end + + it "reports search errors", :aggregate_failures do + expect { + Stripe::Subscription.search({limit: 1}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Missing required param: query./) + + expect { + Stripe::Subscription.search({query: 'asdf'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /We were unable to parse your search query./) + + expect { + Stripe::Subscription.search({query: 'foo:"bar"'}, stripe_version: '2020-08-27') + }.to raise_error(Stripe::InvalidRequestError, /Field `foo` is an unsupported search field for resource `subscriptions`./) + end + end end shared_examples 'Customer Subscriptions with prices' do From 11125438a6833a3f98367984059c38a118bf43e7 Mon Sep 17 00:00:00 2001 From: Tekin Suleyman Date: Tue, 15 Dec 2020 19:47:56 +0000 Subject: [PATCH 084/158] PaymentIntent charges include payment_method A Payment Intent captured with a payment method should reflect the payment method in any charges created against it to match the behaviour of real Stripe. --- lib/stripe_mock/request_handlers/payment_intents.rb | 3 ++- spec/shared_stripe_examples/payment_intent_examples.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/payment_intents.rb b/lib/stripe_mock/request_handlers/payment_intents.rb index d01b42aa9..5cefdd336 100644 --- a/lib/stripe_mock/request_handlers/payment_intents.rb +++ b/lib/stripe_mock/request_handlers/payment_intents.rb @@ -176,7 +176,8 @@ def succeeded_payment_intent(payment_intent) payment_intent[:charges][:data] << Data.mock_charge( balance_transaction: btxn, amount: payment_intent[:amount], - currency: payment_intent[:currency] + currency: payment_intent[:currency], + payment_method: payment_intent[:payment_method] ) payment_intent diff --git a/spec/shared_stripe_examples/payment_intent_examples.rb b/spec/shared_stripe_examples/payment_intent_examples.rb index 8f6993eab..b63c978e3 100644 --- a/spec/shared_stripe_examples/payment_intent_examples.rb +++ b/spec/shared_stripe_examples/payment_intent_examples.rb @@ -92,6 +92,16 @@ expect(Stripe::BalanceTransaction.retrieve(balance_txn).id).to eq(balance_txn) end + it "includes the payment_method on charges" do + payment_intent = Stripe::PaymentIntent.create( + amount: 100, currency: "usd", confirm: true, payment_method: "test_pm_1" + ) + expect(payment_intent.status).to eq("succeeded") + expect(payment_intent.charges.data.size).to eq(1) + expect(payment_intent.charges.data.first.object).to eq("charge") + expect(payment_intent.charges.data.first.payment_method).to eq("test_pm_1") + end + it "confirms a stripe payment_intent" do payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") confirmed_payment_intent = payment_intent.confirm() From ef0502cc7525cea13c479855983ec7206efc7349 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Sat, 4 Jun 2022 12:42:02 -0300 Subject: [PATCH 085/158] Account: replaces deprecated `verification` hash by `requirements` https://stripe.com/docs/upgrades#2019-02-19 --- lib/stripe_mock/data.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index f3153f958..b95dd2b20 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -34,10 +34,15 @@ def self.mock_account(params = {}) ] }, - verification: { - fields_needed: [], - due_by: nil, - contacted: false + requirements: { + alternatives: [], + current_deadline: nil, + currently_due: [], + disabled_reason: nil, + errors: [], + eventually_due: [], + past_due: [], + pending_verification: [] }, transfer_schedule: { delay_days: 7, From 1d76c1008f1fd269d1878dc3bc059437a4b44702 Mon Sep 17 00:00:00 2001 From: Louis-Michel Couture Date: Fri, 26 Aug 2022 20:20:14 +0200 Subject: [PATCH 086/158] Allow subscription filter on current_period values current_period_start and current_period_end --- .../helpers/subscription_helpers.rb | 13 ++++++++ .../request_handlers/subscriptions.rb | 6 ++++ .../subscription_examples.rb | 32 +++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 33bfd370f..8b8e4864e 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -125,6 +125,19 @@ def total_items_amount(items) end total end + + def filter_by_timestamp(subscriptions, field:, value:) + if value.is_a?(Hash) + operator_mapping = { gt: :>, gte: :>=, lt: :<, lte: :<= } + subscriptions.filter do |sub| + sub[field].public_send(operator_mapping[value.keys[0]], value.values[0]) + end + else + subscriptions.filter do |sub| + sub[field] == value + end + end + end end end end diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index d8c31d1d9..688b16339 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -188,6 +188,12 @@ def retrieve_subscriptions(route, method_url, params, headers) else subs = subs.filter {|subscription| subscription[:status] == params[:status]} end + if params[:current_period_end] + subs = filter_by_timestamp(subs, field: :current_period_end, value: params[:current_period_end]) + end + if params[:current_period_start] + subs = filter_by_timestamp(subs, field: :current_period_start, value: params[:current_period_start]) + end Data.mock_list_object(subs, params) end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index d9a934e17..005e9bcce 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1299,6 +1299,38 @@ expect(list.data).to be_empty expect(list.data.length).to eq(0) end + + it "filters out subscriptions based on their current_period", live: true do + price = stripe_helper.create_price(recurring: { interval: 'month' }) + price2 = stripe_helper.create_price(recurring: { interval: 'year' }) + + subscription1 = Stripe::Subscription.create( + customer: Stripe::Customer.create(source: gen_card_tk).id, + items: [{ price: price.id, quantity: 1 }] + ) + subscription2 = Stripe::Subscription.create( + customer: Stripe::Customer.create(source: gen_card_tk).id, + items: [{ price: price2.id, quantity: 1 }] + ) + + list = Stripe::Subscription.list({ current_period_end: { gt: subscription1.current_period_end }}) + expect(list.data).to contain_exactly(subscription2) + + list = Stripe::Subscription.list({ current_period_end: { gte: subscription1.current_period_end }}) + expect(list.data).to contain_exactly(subscription1, subscription2) + + list = Stripe::Subscription.list({ current_period_end: { lt: subscription1.current_period_end }}) + expect(list.data).to be_empty + + list = Stripe::Subscription.list({ current_period_end: { lte: subscription1.current_period_end }}) + expect(list.data).to contain_exactly(subscription1) + + list = Stripe::Subscription.list({ current_period_start: subscription1.current_period_start }) + expect(list.data).to contain_exactly(subscription1, subscription2) + + list = Stripe::Subscription.list({ current_period_end: subscription2.current_period_end }) + expect(list.data).to contain_exactly(subscription2) + end end describe "metadata" do From ffbbf96c8c93f2fe5b641e776c02ef45e8b780f0 Mon Sep 17 00:00:00 2001 From: Luke Rodgers Date: Thu, 22 Sep 2022 11:38:05 -0400 Subject: [PATCH 087/158] Support invoice number --- lib/stripe_mock/data.rb | 3 ++- spec/shared_stripe_examples/invoice_examples.rb | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 5f958b7c3..2ef202dd6 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -450,7 +450,8 @@ def self.mock_invoice(lines, params={}) next_payment_attempt: 1349825350, charge: nil, discount: nil, - subscription: nil + subscription: nil, + number: "6C41730-0001" }.merge(params) if invoice[:discount] invoice[:total] = [0, invoice[:subtotal] - invoice[:discount][:coupon][:amount_off]].max if invoice[:discount][:coupon][:amount_off] diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index 244ae20e8..80d8135e0 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -14,6 +14,11 @@ expect(data[invoice.id]).to_not be_nil expect(data[invoice.id][:id]).to eq(invoice.id) end + + it "supports invoice number" do + original = Stripe::Invoice.create + expect(original.number).to be + end end context "retrieving an invoice" do From b35555f09c478240dbdb9f4ae408bb56c3f4374e Mon Sep 17 00:00:00 2001 From: Greg Ciampa Date: Tue, 18 Oct 2022 22:47:29 -0500 Subject: [PATCH 088/158] Add PromotionCode resource --- lib/stripe_mock.rb | 1 + lib/stripe_mock/data.rb | 38 +++++++++++ lib/stripe_mock/instance.rb | 7 +- .../request_handlers/promotion_codes.rb | 43 ++++++++++++ .../promotion_code_examples.rb | 68 +++++++++++++++++++ spec/support/stripe_examples.rb | 1 + 6 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 lib/stripe_mock/request_handlers/promotion_codes.rb create mode 100644 spec/shared_stripe_examples/promotion_code_examples.rb diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index 543ae1b90..791a9b474 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -65,6 +65,7 @@ require 'stripe_mock/request_handlers/orders.rb' require 'stripe_mock/request_handlers/plans.rb' require 'stripe_mock/request_handlers/prices.rb' +require 'stripe_mock/request_handlers/promotion_codes.rb' require 'stripe_mock/request_handlers/recipients.rb' require 'stripe_mock/request_handlers/refunds.rb' require 'stripe_mock/request_handlers/transfers.rb' diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 5f958b7c3..2c94f37e7 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -657,6 +657,44 @@ def self.mock_product(params={}) }.merge(params) end + def self.mock_promotion_code(params={}) + { + id: "mock_promo_abc123", + object: "promotion_code", + active: true, + code: "TESTCODE", + coupon: { + id: "mock_coupon_abc123", + object: "coupon", + amount_off: nil, + created: 1665773498, + currency: "usd", + duration: "repeating", + duration_in_months: 1, + livemode: false, + max_redemptions: nil, + metadata: {}, + name: "Mock Coupon", + percent_off: 10.0, + redeem_by: nil, + times_redeemed: 0, + valid: true + }, + created: 1665773499, + customer: nil, + expires_at: nil, + livemode: false, + max_redemptions: nil, + metadata: {}, + restrictions: { + first_time_transaction: false, + minimum_amount: nil, + minimum_amount_currency: nil + }, + times_redeemed: 0 + }.merge(params) + end + def self.mock_recipient(cards, params={}) rp_id = params[:id] || "test_rp_default" cards.each {|card| card[:recipient] = rp_id} diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index b1ad87d0a..56ab20eef 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -44,6 +44,7 @@ def self.handler_for_method_url(method_url) include StripeMock::RequestHandlers::Plans include StripeMock::RequestHandlers::Prices include StripeMock::RequestHandlers::Products + include StripeMock::RequestHandlers::PromotionCodes include StripeMock::RequestHandlers::Refunds include StripeMock::RequestHandlers::Recipients include StripeMock::RequestHandlers::Transfers @@ -57,8 +58,9 @@ def self.handler_for_method_url(method_url) attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers, :disputes, :events, :invoices, :invoice_items, :orders, :payment_intents, :payment_methods, - :setup_intents, :plans, :prices, :recipients, :refunds, :transfers, :payouts, :subscriptions, :country_spec, - :subscriptions_items, :products, :tax_rates, :checkout_sessions, :checkout_session_line_items + :setup_intents, :plans, :prices, :promotion_codes, :recipients, :refunds, :transfers, :payouts, + :subscriptions, :country_spec, :subscriptions_items, :products, :tax_rates, :checkout_sessions, + :checkout_session_line_items attr_accessor :error_queue, :debug, :conversion_rate, :account_balance @@ -83,6 +85,7 @@ def initialize @plans = {} @prices = {} @products = {} + @promotion_codes = {} @recipients = {} @refunds = {} @transfers = {} diff --git a/lib/stripe_mock/request_handlers/promotion_codes.rb b/lib/stripe_mock/request_handlers/promotion_codes.rb new file mode 100644 index 000000000..a8ecf11bc --- /dev/null +++ b/lib/stripe_mock/request_handlers/promotion_codes.rb @@ -0,0 +1,43 @@ +module StripeMock + module RequestHandlers + module PromotionCodes + + def PromotionCodes.included(klass) + klass.add_handler 'post /v1/promotion_codes', :new_promotion_code + klass.add_handler 'post /v1/promotion_codes/([^/]*)', :update_promotion_code + klass.add_handler 'get /v1/promotion_codes/([^/]*)', :get_promotion_code + klass.add_handler 'get /v1/promotion_codes', :list_promotion_code + end + + def new_promotion_code(route, method_url, params, headers) + params[:id] ||= new_id("promo") + raise Stripe::InvalidRequestError.new("Missing required param: coupon", "promotion_code", http_status: 400) unless params[:coupon] + + if params[:restrictions] + if params[:restrictions][:minimum_amount] && !params[:restrictions][:minimum_amount_currency] + raise Stripe::InvalidRequestError.new( + "You must pass minimum_amount_currency when passing minimum_amount", "minimum_amount_currency", http_status: 400 + ) + end + end + + promotion_codes[ params[:id] ] = Data.mock_promotion_code(params) + end + + def update_promotion_code(route, method_url, params, headers) + route =~ method_url + assert_existence :promotion_code, $1, promotion_codes[$1] + promotion_codes[$1].merge!(params) + end + + def get_promotion_code(route, method_url, params, headers) + route =~ method_url + assert_existence :promotion_code, $1, promotion_codes[$1] + end + + def list_promotion_code(route, method_url, params, headers) + Data.mock_list_object(promotion_codes.values, params) + end + end + end +end diff --git a/spec/shared_stripe_examples/promotion_code_examples.rb b/spec/shared_stripe_examples/promotion_code_examples.rb new file mode 100644 index 000000000..a3ae3aa70 --- /dev/null +++ b/spec/shared_stripe_examples/promotion_code_examples.rb @@ -0,0 +1,68 @@ +require "spec_helper" + +shared_examples "PromotionCode API" do + let(:coupon) { stripe_helper.create_coupon } + + it "creates a promotion code" do + promotion_code = Stripe::PromotionCode.create({id: "promo_123", coupon: coupon.id, code: "FREESTUFF"}) + + expect(promotion_code.id).to eq("promo_123") + expect(promotion_code.code).to eq("FREESTUFF") + expect(promotion_code.coupon).to eq(coupon.id) + end + + it "creates a promotion code without specifying code" do + promotion_code = Stripe::PromotionCode.create({id: "promo_123", coupon: coupon.id}) + + expect(promotion_code.id).to eq("promo_123") + expect(promotion_code.code).to eq("TESTCODE") + expect(promotion_code.coupon).to eq(coupon.id) + end + + it "cannot create a promotion code without a coupon" do + expect { + Stripe::PromotionCode.create + }.to raise_error { |e| + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.message).to eq("Missing required param: coupon") + } + end + + it "requires minimum amount currency when minimum amount is provided" do + expect { + Stripe::PromotionCode.create(coupon: coupon, restrictions: {minimum_amount: 100}) + }.to raise_error { |e| + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.message).to eq("You must pass minimum_amount_currency when passing minimum_amount") + } + end + + it "updates a promotion code" do + promotion_code = Stripe::PromotionCode.create({coupon: coupon.id}) + expect(promotion_code.active).to eq(true) + + updated = Stripe::PromotionCode.update(promotion_code.id, active: false) + + expect(updated.active).to eq(false) + end + + it "retrieves a promotion code" do + original = Stripe::PromotionCode.create({coupon: coupon.id}) + + promotion_code = Stripe::PromotionCode.retrieve(original.id) + + expect(promotion_code.id).to eq(original.id) + expect(promotion_code.code).to eq(original.code) + expect(promotion_code.coupon).to eq(original.coupon) + end + + it "lists all promotion codes" do + Stripe::PromotionCode.create({coupon: coupon.id, code: "10PERCENT"}) + Stripe::PromotionCode.create({coupon: coupon.id, code: "20PERCENT"}) + + all = Stripe::PromotionCode.list + + expect(all.count).to eq(2) + expect(all.map(&:code)).to include("10PERCENT", "20PERCENT") + end +end diff --git a/spec/support/stripe_examples.rb b/spec/support/stripe_examples.rb index 931ffe146..f41980e24 100644 --- a/spec/support/stripe_examples.rb +++ b/spec/support/stripe_examples.rb @@ -24,6 +24,7 @@ def it_behaves_like_stripe(&block) it_behaves_like 'Plan API', &block it_behaves_like 'Price API', &block it_behaves_like 'Product API', &block + it_behaves_like 'PromotionCode API', &block it_behaves_like 'Recipient API', &block it_behaves_like 'Refund API', &block it_behaves_like 'Transfer API', &block From bd20b2b3b962367ae282c27d28fdecda54557988 Mon Sep 17 00:00:00 2001 From: Greg Ciampa Date: Wed, 19 Oct 2022 11:09:14 -0500 Subject: [PATCH 089/158] Allow promotion code parameter when creating a subscription --- .../request_handlers/subscriptions.rb | 70 ++++++++++++++++++- .../subscription_examples.rb | 39 +++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index d8c31d1d9..cc316c805 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -69,6 +69,24 @@ def create_customer_subscription(route, method_url, params, headers) end end + if params[:promotion_code] + promotion_code_id = params[:promotion_code] + + promotion_code = promotion_codes[promotion_code_id] + + if promotion_code + coupon_id = promotion_code[:coupon][:id] + coupon = coupons[coupon_id] + if coupon + add_coupon_to_object(subscription, coupon) + else + raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) + end + else + raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400) + end + end + subscriptions[subscription[:id]] = subscription add_subscription_to_customer(customer, subscription) @@ -97,7 +115,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax) + allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax promotion_code) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) @@ -113,6 +131,10 @@ def create_subscription(route, method_url, params, headers) # Note: needs updating for subscriptions with multiple plans verify_card_present(customer, subscription_plans.first, subscription, params) + if params[:coupon] && params[:promotion_code] + raise Stripe::InvalidRequestError.new("You may only specify one of these parameters: coupon, promotion_code", "coupon", http_status: 400) + end + if params[:coupon] coupon_id = params[:coupon] @@ -128,6 +150,24 @@ def create_subscription(route, method_url, params, headers) end end + if params[:promotion_code] + promotion_code_id = params[:promotion_code] + + promotion_code = promotion_codes[promotion_code_id] + + if promotion_code + coupon_id = promotion_code[:coupon][:id] + coupon = coupons[coupon_id] + if coupon + add_coupon_to_object(subscription, coupon) + else + raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) + end + else + raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400) + end + end + if params[:trial_period_days] subscription[:status] = 'trialing' end @@ -236,6 +276,34 @@ def update_subscription(route, method_url, params, headers) end end + if params[:promotion_code] + promotion_code_id = params[:promotion_code] + + promotion_code = promotion_codes[promotion_code_id] + + if promotion_code + # You can't apply a promotion code with amount restrictions on the Customer object or on a subscription + # update API call + if promotion_code[:restrictions][:minimum_amount] + raise Stripe::InvalidRequestError.new( + "This promotion code cannot be redeemed on a subcription update because it uses the `minimum_amount` restriction.", + "promotion_code", + http_status: 400 + ) + end + + coupon_id = promotion_code[:coupon][:id] + coupon = coupons[coupon_id] + if coupon + add_coupon_to_object(subscription, coupon) + else + raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) + end + else + raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400) + end + end + if params[:trial_period_days] subscription[:status] = 'trialing' end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index d9a934e17..fec2b2f0c 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -195,6 +195,28 @@ } end + it "allows promotion code" do + customer = Stripe::Customer.create(source: gen_card_tk) + coupon = stripe_helper.create_coupon + promotion_code = Stripe::PromotionCode.create(coupon: coupon) + + subscription = Stripe::Subscription.create(plan: plan.id, customer: customer.id, promotion_code: promotion_code.id) + + expect(subscription.discount.coupon).to eq(coupon) + end + + it "does not permit both coupon and promotion code" do + customer = Stripe::Customer.create(source: gen_card_tk) + + expect { + Stripe::Subscription.create(plan: plan.id, customer: customer.id, coupon: "test", promotion_code: "test") + }.to raise_error { |e| + expect(e).to be_a Stripe::InvalidRequestError + expect(e.http_status).to eq(400) + expect(e.message).to eq("You may only specify one of these parameters: coupon, promotion_code") + } + end + it "correctly sets quantity, application_fee_percent and tax_percent" do customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) @@ -947,6 +969,23 @@ expect(subscription.discount).to be_nil end + it "throws an error when promotion code has an amount restriction" do + coupon = stripe_helper.create_coupon + promotion_code = Stripe::PromotionCode.create( + coupon: coupon, restrictions: {minimum_amount: 100, minimum_amount_currency: "USD"} + ) + customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) + subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + + subscription.promotion_code = promotion_code.id + + expect { subscription.save }.to raise_error { |e| + expect(e).to be_a Stripe::InvalidRequestError + expect(e.http_status).to eq(400) + expect(e.message).to_not be_nil + } + end + it "throws an error when plan does not exist" do customer = Stripe::Customer.create(id: 'cardless', plan: free_plan.id) From 803419d9848617caef25b6c5c3b414cca3c98cb1 Mon Sep 17 00:00:00 2001 From: Sanjay Makani Date: Wed, 9 Nov 2022 18:31:50 +0530 Subject: [PATCH 090/158] Updated gem to use Stripe gem v7. And, fixed rspecs. --- README.md | 2 +- lib/stripe_mock.rb | 2 ++ spec/readme_spec.rb | 2 +- .../bank_token_examples.rb | 16 ---------------- spec/shared_stripe_examples/invoice_examples.rb | 5 ++++- .../webhook_event_examples.rb | 10 +++++----- spec/spec_helper.rb | 2 +- stripe-ruby-mock.gemspec | 3 ++- 8 files changed, 16 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 2f7fbe654..e4695beee 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/strip ### Requirements * ruby >= 2.4.0 -* stripe >= 5.0.0 +* stripe >= 7.0.0 ### Specifications diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index 543ae1b90..88f7b8888 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -5,6 +5,8 @@ require 'stripe' +require 'byebug' + require 'stripe_mock/version' require 'stripe_mock/util' require 'stripe_mock/error_queue' diff --git a/spec/readme_spec.rb b/spec/readme_spec.rb index 0bf5524b7..6985ed1ca 100644 --- a/spec/readme_spec.rb +++ b/spec/readme_spec.rb @@ -47,7 +47,7 @@ customer_object = event.data.object expect(customer_object.id).to_not be_nil - expect(customer_object.default_card).to_not be_nil + expect(customer_object.default_source).to_not be_nil # etc. end diff --git a/spec/shared_stripe_examples/bank_token_examples.rb b/spec/shared_stripe_examples/bank_token_examples.rb index 360684587..04dca9389 100644 --- a/spec/shared_stripe_examples/bank_token_examples.rb +++ b/spec/shared_stripe_examples/bank_token_examples.rb @@ -28,22 +28,6 @@ expect(bank_token).to match /^test_btok/ end - it "assigns the generated bank account to a new recipient" do - bank_token = StripeMock.generate_bank_token( - :bank_name => "Bank Token Mocking", - :last4 => "7777" - ) - - recipient = Stripe::Recipient.create({ - name: "Fred Flinstone", - type: "individual", - email: 'blah@domain.co', - bank_account: bank_token - }) - expect(recipient.active_account.last4).to eq("7777") - expect(recipient.active_account.bank_name).to eq("Bank Token Mocking") - end - it "retrieves a created token" do bank_token = StripeMock.generate_bank_token( :bank_name => "Cha-ching Banking", diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index 244ae20e8..4cacd336d 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -133,7 +133,10 @@ describe 'parameter validation' do it 'fails without parameters' do expect { Stripe::Invoice.upcoming() }.to raise_error {|e| - expect(e).to be_a(ArgumentError) } + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.http_status).to eq(400) + expect(e.message).to eq('Missing required param: customer if subscription is not provided') + } end it 'fails without a valid customer' do diff --git a/spec/shared_stripe_examples/webhook_event_examples.rb b/spec/shared_stripe_examples/webhook_event_examples.rb index 07a416063..a3dc12f8b 100644 --- a/spec/shared_stripe_examples/webhook_event_examples.rb +++ b/spec/shared_stripe_examples/webhook_event_examples.rb @@ -257,7 +257,7 @@ expect(subscription_created_event).to be_a(Stripe::Event) expect(subscription_created_event.id).to_not be_nil expect(subscription_created_event.data.object.items.data.class).to be Array - expect(subscription_created_event.data.object.items.data.length).to be 2 + expect(subscription_created_event.data.object.items.data.length).to be 1 expect(subscription_created_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_created_event.data.object.items.data.first.id).to eq('si_00000000000000') end @@ -267,7 +267,7 @@ expect(subscription_deleted_event).to be_a(Stripe::Event) expect(subscription_deleted_event.id).to_not be_nil expect(subscription_deleted_event.data.object.items.data.class).to be Array - expect(subscription_deleted_event.data.object.items.data.length).to be 2 + expect(subscription_deleted_event.data.object.items.data.length).to be 1 expect(subscription_deleted_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_deleted_event.data.object.items.data.first.id).to eq('si_00000000000000') end @@ -277,7 +277,7 @@ expect(subscription_updated_event).to be_a(Stripe::Event) expect(subscription_updated_event.id).to_not be_nil expect(subscription_updated_event.data.object.items.data.class).to be Array - expect(subscription_updated_event.data.object.items.data.length).to be 2 + expect(subscription_updated_event.data.object.items.data.length).to be 1 expect(subscription_updated_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_updated_event.data.object.items.data.first.id).to eq('si_00000000000000') end @@ -299,9 +299,9 @@ expect(invoice_payment_succeeded).to be_a(Stripe::Event) expect(invoice_payment_succeeded.id).to_not be_nil expect(invoice_payment_succeeded.data.object.lines.data.class).to be Array - expect(invoice_payment_succeeded.data.object.lines.data.length).to be 2 + expect(invoice_payment_succeeded.data.object.lines.data.length).to be 1 expect(invoice_payment_succeeded.data.object.lines.data.first).to respond_to(:plan) - expect(invoice_payment_succeeded.data.object.lines.data.first.id).to eq('sub_00000000000000') + expect(invoice_payment_succeeded.data.object.lines.data.first.id).to eq('il_000000000000000000000000') end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6736353ef..52f80842a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'set' -gem 'rspec', '~> 3.1' +gem 'rspec', '~> 3.7.0' require 'rspec' require 'stripe' require 'stripe_mock' diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index e53963d8c..2cd5a6cb7 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,11 +22,12 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '> 5', '< 6' + gem.add_dependency 'stripe', '~> 7.0.0' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' gem.add_development_dependency 'rspec', '~> 3.7.0' gem.add_development_dependency 'rubygems-tasks', '~> 0.2' gem.add_development_dependency 'thin', '~> 1.8.1' + gem.add_development_dependency "byebug", '~> 11.1.3' end From 4d267ac9f8d3f42d130e2aa458dbecdc6f201ce5 Mon Sep 17 00:00:00 2001 From: Sanjay Makani Date: Wed, 9 Nov 2022 23:11:25 +0530 Subject: [PATCH 091/158] Updated Stripe gem reference to 7.1.0 --- lib/stripe_mock/version.rb | 2 +- stripe-ruby-mock.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index a1fd0ea92..80f266ef9 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0.rc3" + VERSION = "3.1.0.rc4" end diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 2cd5a6cb7..7ff7ac81a 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '~> 7.0.0' + gem.add_dependency 'stripe', '~> 7.1.0' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' From a414aa9aa7b9dfb5c024a4022cd51b8d452b091b Mon Sep 17 00:00:00 2001 From: Sanjay Makani Date: Tue, 15 Nov 2022 19:46:05 +0530 Subject: [PATCH 092/158] Final changes --- README.md | 2 +- lib/stripe_mock.rb | 2 -- lib/stripe_mock/version.rb | 2 +- .../bank_token_examples.rb | 16 ++++++++++++++++ spec/spec_helper.rb | 2 +- stripe-ruby-mock.gemspec | 3 +-- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e4695beee..50fc5942c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/strip ### Requirements * ruby >= 2.4.0 -* stripe >= 7.0.0 +* stripe >5 & <=7.1.0 ### Specifications diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index 88f7b8888..543ae1b90 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -5,8 +5,6 @@ require 'stripe' -require 'byebug' - require 'stripe_mock/version' require 'stripe_mock/util' require 'stripe_mock/error_queue' diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index 80f266ef9..a1fd0ea92 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0.rc4" + VERSION = "3.1.0.rc3" end diff --git a/spec/shared_stripe_examples/bank_token_examples.rb b/spec/shared_stripe_examples/bank_token_examples.rb index 04dca9389..812101bc4 100644 --- a/spec/shared_stripe_examples/bank_token_examples.rb +++ b/spec/shared_stripe_examples/bank_token_examples.rb @@ -28,6 +28,22 @@ expect(bank_token).to match /^test_btok/ end + it "assigns the generated bank account to a new recipient", skip: 'Stripe has deprecated Recipients' do + bank_token = StripeMock.generate_bank_token( + :bank_name => "Bank Token Mocking", + :last4 => "7777" + ) + + recipient = Stripe::Recipient.create({ + name: "Fred Flinstone", + type: "individual", + email: 'blah@domain.co', + bank_account: bank_token + }) + expect(recipient.active_account.last4).to eq("7777") + expect(recipient.active_account.bank_name).to eq("Bank Token Mocking") + end + it "retrieves a created token" do bank_token = StripeMock.generate_bank_token( :bank_name => "Cha-ching Banking", diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 52f80842a..6736353ef 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'set' -gem 'rspec', '~> 3.7.0' +gem 'rspec', '~> 3.1' require 'rspec' require 'stripe' require 'stripe_mock' diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 7ff7ac81a..33f0f180b 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,12 +22,11 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '~> 7.1.0' + gem.add_dependency 'stripe', '> 5', '<= 7.1.0' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' gem.add_development_dependency 'rspec', '~> 3.7.0' gem.add_development_dependency 'rubygems-tasks', '~> 0.2' gem.add_development_dependency 'thin', '~> 1.8.1' - gem.add_development_dependency "byebug", '~> 11.1.3' end From 598e4f1163f0a8adc902d466c1deb172e0758005 Mon Sep 17 00:00:00 2001 From: Jake Bills Date: Tue, 28 Feb 2023 16:22:14 -0700 Subject: [PATCH 093/158] adds check for refund amount and test demonstrating behavior --- .../request_handlers/helpers/charge_helpers.rb | 6 ++++++ spec/shared_stripe_examples/refund_examples.rb | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb b/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb index 63270b0f1..81ecb13f3 100644 --- a/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb @@ -3,6 +3,12 @@ module RequestHandlers module Helpers def add_refund_to_charge(refund, charge) + if refund[:amount] + charge[:amount_refunded] > charge[:amount] + raise Stripe::InvalidRequestError.new( + "Charge #{charge[:id]} has already been refunded.", + 'amount' + ) + end refunds = charge[:refunds] refunds[:data] << refund refunds[:total_count] = refunds[:data].count diff --git a/spec/shared_stripe_examples/refund_examples.rb b/spec/shared_stripe_examples/refund_examples.rb index bf0645357..c83e1ee63 100644 --- a/spec/shared_stripe_examples/refund_examples.rb +++ b/spec/shared_stripe_examples/refund_examples.rb @@ -338,6 +338,17 @@ expect(half.data.first.id).to eq(all_refunds.data.at(2).id) end + it "returns an InvalidRequestError when attempting to refund more than the original charge amount" do + charge = Stripe::Charge.create( + amount: 1000, + currency: 'usd', + source: stripe_helper.generate_card_token + ) + expect { + Stripe::Refund.create(charge: charge.id, amount: 2000) + }.to raise_error(Stripe::InvalidRequestError) + end + describe "idempotency" do let(:customer) { Stripe::Customer.create(email: 'johnny@appleseed.com') } let(:charge) do From 876a656fc66bd7c097a061fbd5716fd4b0006b3d Mon Sep 17 00:00:00 2001 From: Greg Ciampa Date: Sun, 5 Mar 2023 18:57:37 -0500 Subject: [PATCH 094/158] Promotion code is not available on subscription --- .../request_handlers/subscriptions.rb | 28 ++----------------- .../subscription_examples.rb | 6 ++-- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index cc316c805..a13c1ec24 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -74,15 +74,7 @@ def create_customer_subscription(route, method_url, params, headers) promotion_code = promotion_codes[promotion_code_id] - if promotion_code - coupon_id = promotion_code[:coupon][:id] - coupon = coupons[coupon_id] - if coupon - add_coupon_to_object(subscription, coupon) - else - raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) - end - else + unless promotion_code raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400) end end @@ -155,15 +147,7 @@ def create_subscription(route, method_url, params, headers) promotion_code = promotion_codes[promotion_code_id] - if promotion_code - coupon_id = promotion_code[:coupon][:id] - coupon = coupons[coupon_id] - if coupon - add_coupon_to_object(subscription, coupon) - else - raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) - end - else + unless promotion_code raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400) end end @@ -291,14 +275,6 @@ def update_subscription(route, method_url, params, headers) http_status: 400 ) end - - coupon_id = promotion_code[:coupon][:id] - coupon = coupons[coupon_id] - if coupon - add_coupon_to_object(subscription, coupon) - else - raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) - end else raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400) end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index fec2b2f0c..805f2322e 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -200,9 +200,9 @@ coupon = stripe_helper.create_coupon promotion_code = Stripe::PromotionCode.create(coupon: coupon) - subscription = Stripe::Subscription.create(plan: plan.id, customer: customer.id, promotion_code: promotion_code.id) - - expect(subscription.discount.coupon).to eq(coupon) + expect { + Stripe::Subscription.create(plan: plan.id, customer: customer.id, promotion_code: promotion_code.id) + }.not_to raise_error(Stripe::InvalidRequestError) end it "does not permit both coupon and promotion code" do From 8151b32247c54c51d232db68063c5cb0d9698966 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Sat, 11 Mar 2023 13:53:56 -0800 Subject: [PATCH 095/158] use progress formatting --- .rspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rspec b/.rspec index 660778bdc..89da67b60 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,2 @@ ---colour --format documentation +--colour +--format progress From 3cc801722d679fa00da89e8a0e9d4230bb6cad58 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Sat, 11 Mar 2023 13:54:03 -0800 Subject: [PATCH 096/158] fix the test suite --- spec/instance_spec.rb | 4 +++- spec/readme_spec.rb | 2 +- spec/shared_stripe_examples/webhook_event_examples.rb | 11 ++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/spec/instance_spec.rb b/spec/instance_spec.rb index 9455cfadc..d7bed5b0e 100644 --- a/spec/instance_spec.rb +++ b/spec/instance_spec.rb @@ -6,7 +6,9 @@ let(:stripe_helper) { StripeMock.create_test_helper } it_behaves_like_stripe do - def test_data_source(type); StripeMock.instance.send(type); end + def test_data_source(type) + StripeMock.instance.send(type) + end end before { StripeMock.start } diff --git a/spec/readme_spec.rb b/spec/readme_spec.rb index 0bf5524b7..6985ed1ca 100644 --- a/spec/readme_spec.rb +++ b/spec/readme_spec.rb @@ -47,7 +47,7 @@ customer_object = event.data.object expect(customer_object.id).to_not be_nil - expect(customer_object.default_card).to_not be_nil + expect(customer_object.default_source).to_not be_nil # etc. end diff --git a/spec/shared_stripe_examples/webhook_event_examples.rb b/spec/shared_stripe_examples/webhook_event_examples.rb index 07a416063..d2d8c1a52 100644 --- a/spec/shared_stripe_examples/webhook_event_examples.rb +++ b/spec/shared_stripe_examples/webhook_event_examples.rb @@ -257,7 +257,7 @@ expect(subscription_created_event).to be_a(Stripe::Event) expect(subscription_created_event.id).to_not be_nil expect(subscription_created_event.data.object.items.data.class).to be Array - expect(subscription_created_event.data.object.items.data.length).to be 2 + expect(subscription_created_event.data.object.items.data.length).to be 1 expect(subscription_created_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_created_event.data.object.items.data.first.id).to eq('si_00000000000000') end @@ -267,7 +267,7 @@ expect(subscription_deleted_event).to be_a(Stripe::Event) expect(subscription_deleted_event.id).to_not be_nil expect(subscription_deleted_event.data.object.items.data.class).to be Array - expect(subscription_deleted_event.data.object.items.data.length).to be 2 + expect(subscription_deleted_event.data.object.items.data.length).to be 1 expect(subscription_deleted_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_deleted_event.data.object.items.data.first.id).to eq('si_00000000000000') end @@ -277,7 +277,7 @@ expect(subscription_updated_event).to be_a(Stripe::Event) expect(subscription_updated_event.id).to_not be_nil expect(subscription_updated_event.data.object.items.data.class).to be Array - expect(subscription_updated_event.data.object.items.data.length).to be 2 + expect(subscription_updated_event.data.object.items.data.length).to be 1 expect(subscription_updated_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_updated_event.data.object.items.data.first.id).to eq('si_00000000000000') end @@ -298,10 +298,11 @@ invoice_payment_succeeded = StripeMock.mock_webhook_event('invoice.payment_succeeded') expect(invoice_payment_succeeded).to be_a(Stripe::Event) expect(invoice_payment_succeeded.id).to_not be_nil + puts "invoice_payment_succeeded.data.object.lines: #{invoice_payment_succeeded.data.object.lines}" expect(invoice_payment_succeeded.data.object.lines.data.class).to be Array - expect(invoice_payment_succeeded.data.object.lines.data.length).to be 2 + expect(invoice_payment_succeeded.data.object.lines.data.length).to be 1 expect(invoice_payment_succeeded.data.object.lines.data.first).to respond_to(:plan) - expect(invoice_payment_succeeded.data.object.lines.data.first.id).to eq('sub_00000000000000') + expect(invoice_payment_succeeded.data.object.lines.data.first.id).to eq('il_000000000000000000000000') end end end From 404df10da6c84485db4b79858f5e34980c42cf51 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Sat, 11 Mar 2023 13:55:46 -0800 Subject: [PATCH 097/158] add note (maybe this belongs in the wiki?) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2f7fbe654..2630561f7 100644 --- a/README.md +++ b/README.md @@ -406,6 +406,8 @@ Patches are welcome and greatly appreciated! If you're contributing to fix a pro be sure to write tests that illustrate the problem being fixed. This will help ensure that the problem remains fixed in future updates. +Note: You may need to `ulimit -n 4048` before running the test suite to get all tests to pass. + ## Copyright Copyright (c) 2013 Gilbert From ca7b2346f2437bb75a11e91d2319ae188d8ee25d Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Sat, 11 Mar 2023 16:35:44 -0800 Subject: [PATCH 098/158] remove puts --- spec/shared_stripe_examples/webhook_event_examples.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/shared_stripe_examples/webhook_event_examples.rb b/spec/shared_stripe_examples/webhook_event_examples.rb index d2d8c1a52..a3dc12f8b 100644 --- a/spec/shared_stripe_examples/webhook_event_examples.rb +++ b/spec/shared_stripe_examples/webhook_event_examples.rb @@ -298,7 +298,6 @@ invoice_payment_succeeded = StripeMock.mock_webhook_event('invoice.payment_succeeded') expect(invoice_payment_succeeded).to be_a(Stripe::Event) expect(invoice_payment_succeeded.id).to_not be_nil - puts "invoice_payment_succeeded.data.object.lines: #{invoice_payment_succeeded.data.object.lines}" expect(invoice_payment_succeeded.data.object.lines.data.class).to be Array expect(invoice_payment_succeeded.data.object.lines.data.length).to be 1 expect(invoice_payment_succeeded.data.object.lines.data.first).to respond_to(:plan) From 23c7f109ce86e787b3a701c8249117251da14066 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Sat, 11 Mar 2023 16:34:24 -0800 Subject: [PATCH 099/158] implement webhook: "payment_method.detached" --- lib/stripe_mock/api/webhooks.rb | 1 + .../payment_method.detached.json | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/payment_method.detached.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index eaceacc1c..974cb4ab4 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -94,6 +94,7 @@ def self.event_list 'payment_link.created', 'payment_link.updated', 'payment_method.attached', + 'payment_method.detached', 'payout.created', 'payout.paid', 'payout.updated', diff --git a/lib/stripe_mock/webhook_fixtures/payment_method.detached.json b/lib/stripe_mock/webhook_fixtures/payment_method.detached.json new file mode 100644 index 000000000..d371091d4 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/payment_method.detached.json @@ -0,0 +1,62 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1648320107, + "data": { + "object": { + "id" : "pm_000000000000000000000000", + "object" : "payment_method", + "created" : 1678559798, + "billing_details" : { + "address" : { + "city" : null, + "country" : "US", + "line1" : null, + "line2" : null, + "postal_code" : "42424", + "state" : null + }, + "email" : null, + "name" : null, + "phone" : null + }, + "card" : { + "brand" : "visa", + "checks" : { + "address_line1_check" : null, + "address_postal_code_check" : "pass", + "cvc_check" : "pass" + }, + "country" : "US", + "exp_month" : 4, + "exp_year" : 2024, + "fingerprint" : "ZoVSX2dK5igWt2SB", + "funding" : "credit", + "generated_from" : null, + "last4" : "4242", + "networks" : { + "available" : [ + "visa" + ], + "preferred" : null + }, + "three_d_secure_usage" : { + "supported" : true + }, + "wallet" : null + }, + "customer" : null, + "livemode" : false, + "metadata" : {}, + "type" : "card" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "381c7773-97ac-4c18-8fbe-e8bff5e6bbad" + }, + "type": "payment_method.detached" +} From ac9d788e6f7fdc0fba841682d209118059453138 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Sat, 11 Mar 2023 14:25:11 -0800 Subject: [PATCH 100/158] add us_bank_account support to stripe mock --- lib/stripe_mock/data.rb | 12 +++++++++++- lib/stripe_mock/request_handlers/payment_methods.rb | 6 +++++- .../payment_method_examples.rb | 10 ++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 5f958b7c3..309246c90 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1296,7 +1296,17 @@ def self.mock_payment_method(params = {}) country: 'DE', fingerprint: 'FD81kbVPe7M05BMj', last4: params.dig(:sepa_debit, :iban)&.[](-4..) || '3000' - } + }, + us_bank_account: { + account_holder_type: "individual", + account_type: "checking", + bank_name: "STRIPE TEST BANK", + financial_connections_account: "fca_0614042384b19afec4474940", + fingerprint: "7bc48d016359a45a", + last4: "6789", + networks: {"preferred"=>"ach", "supported"=>["ach"]}, + routing_number: "110000000" + }, } { diff --git a/lib/stripe_mock/request_handlers/payment_methods.rb b/lib/stripe_mock/request_handlers/payment_methods.rb index 301e2be7a..d2d6a1595 100644 --- a/lib/stripe_mock/request_handlers/payment_methods.rb +++ b/lib/stripe_mock/request_handlers/payment_methods.rb @@ -116,8 +116,12 @@ def ensure_payment_method_required_params(params) end end + def valid_types + %w(card ideal sepa_debit us_bank_account) + end + def invalid_type?(type) - !%w(card ideal sepa_debit).include?(type) + !valid_types.include?(type) end end end diff --git a/spec/shared_stripe_examples/payment_method_examples.rb b/spec/shared_stripe_examples/payment_method_examples.rb index 264a13499..84db0c72d 100644 --- a/spec/shared_stripe_examples/payment_method_examples.rb +++ b/spec/shared_stripe_examples/payment_method_examples.rb @@ -450,5 +450,15 @@ end.to raise_error(Stripe::InvalidRequestError) end end + + context 'with us_bank_account' do + let(:payment_method) do + Stripe::PaymentMethod.create(type: 'us_bank_account') + end + + it 'works' do + expect(payment_method.type).to eq('us_bank_account') + end + end end end From 69d23032195ff85e3451cee3d60eb41e80c8802c Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Mon, 10 Apr 2023 15:30:59 -0700 Subject: [PATCH 101/158] change invoice status to paid when paid = true --- lib/stripe_mock/request_handlers/invoices.rb | 7 ++++++- spec/shared_stripe_examples/invoice_examples.rb | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index 653096633..7d95038d4 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -53,7 +53,12 @@ def pay_invoice(route, method_url, params, headers) route =~ method_url assert_existence :invoice, $1, invoices[$1] charge = invoice_charge(invoices[$1]) - invoices[$1].merge!(:paid => true, :attempted => true, :charge => charge[:id]) + invoices[$1].merge!( + :paid => true, + :status => "paid", + :attempted => true, + :charge => charge[:id], + ) end def upcoming_invoice(route, method_url, params, headers = {}) diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index 244ae20e8..d1e385d15 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -83,12 +83,28 @@ @invoice = @invoice.pay expect(@invoice.attempted).to eq(true) expect(@invoice.paid).to eq(true) + expect(@invoice.status).to eq("paid") end it 'creates a new charge object' do expect{ @invoice.pay }.to change { Stripe::Charge.list.data.count }.by 1 end + it 'should work with Stripe::Invoice.pay(invoice_id)' do + expect(@invoice.paid).to_not eq(true) + + expect { + Stripe::Invoice.pay(@invoice.id) + }.to change { Stripe::Charge.list.data.count }.by 1 + + @invoice = Stripe::Invoice.retrieve(id: @invoice.id) + expect(@invoice).to_not be_nil + + expect(@invoice.attempted).to eq(true) + expect(@invoice.paid).to eq(true) + expect(@invoice.status).to eq("paid") + end + it 'sets the charge attribute' do @invoice = @invoice.pay expect(@invoice.charge).to be_a String From 763d028409ec52b775e26e494dff605941ac0a94 Mon Sep 17 00:00:00 2001 From: Tim Broder Date: Thu, 27 Jul 2023 08:42:24 -0400 Subject: [PATCH 102/158] add payment_settings trial_settings to subscription allowed_params Added additional allowed_params to create_subscription for better testing: payment_settings trial_settings --- lib/stripe_mock/request_handlers/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index d4c1ffef3..2373b6a3b 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -97,7 +97,7 @@ def create_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - allowed_params = %w(id customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax) + allowed_params = %w(id customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan proration_behavior backdate_start_date transfer_data expand automatic_tax payment_settings trial_settings) unknown_params = params.keys - allowed_params.map(&:to_sym) if unknown_params.length > 0 raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400) From 2337a11abbf35709b363b02b0c2643d81406e558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Roll=C3=A9n?= Date: Tue, 10 Oct 2023 17:44:22 -0400 Subject: [PATCH 103/158] Fix typo in account.updated webhook fixture The `account.updated` webhook fixture contains a field named `charge_enabled`, but the actual field sent by stripe is named `charges_enabled`. See the [account object](https://stripe.com/docs/api/accounts/object) in the API docs --- lib/stripe_mock/webhook_fixtures/account.updated.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/webhook_fixtures/account.updated.json b/lib/stripe_mock/webhook_fixtures/account.updated.json index 255b8e30b..20c8eb208 100644 --- a/lib/stripe_mock/webhook_fixtures/account.updated.json +++ b/lib/stripe_mock/webhook_fixtures/account.updated.json @@ -10,7 +10,7 @@ "email": "test@stripe.com", "statement_descriptor": "TEST", "details_submitted": true, - "charge_enabled": false, + "charges_enabled": false, "payouts_enabled": false, "currencies_supported": [ "USD" From 49812a38ee8dd0b0cff10d0ca1d8511fdf884d8e Mon Sep 17 00:00:00 2001 From: Alexander Mamonchik Date: Sun, 15 Oct 2023 00:42:14 +0600 Subject: [PATCH 104/158] Init Github Actions and remove Travis (#886) init Github Actions flow * Added test status badge to README * use 2.6 as a minimum ruby version in Actions * remove Travis config * Fixed no implicit conversion of Range into Integer for ruby 2.6 * increased minimum ruby Version from 2.4 to 2.6 --- .github/workflows/rspec_tests.yml | 38 +++++++++++++++++++++++++++++++ .travis.yml | 25 -------------------- README.md | 4 ++-- lib/stripe_mock/data.rb | 5 ++-- 4 files changed, 43 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/rspec_tests.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml new file mode 100644 index 000000000..52428cc3b --- /dev/null +++ b/.github/workflows/rspec_tests.yml @@ -0,0 +1,38 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Tests + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['2.6', '2.7', '3.0'] + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: bundle exec rspec diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e209a0c64..000000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -sudo: required -language: ruby -rvm: - - 2.4.6 - - 2.5.5 - - 2.6.3 - - 2.7.0 -before_install: - - gem install bundler -v '< 2' -before_script: - - "sudo touch /var/log/stripe-mock-server.log" - - "sudo chown travis /var/log/stripe-mock-server.log" -script: "bundle exec rspec && bundle exec rspec -t live" - -env: - global: - - IS_TRAVIS=true STRIPE_TEST_SECRET_KEY_A=sk_test_BsztzqQjzd7lqkgo1LjEG5DF00KzH7tWKF STRIPE_TEST_SECRET_KEY_B=sk_test_rKCEu0x8jzg6cKPqoey8kUPQ00usQO3KYE STRIPE_TEST_SECRET_KEY_C=sk_test_qeaB7R6Ywp8sC9pzd1ZIABH700YLC7nhmZ STRIPE_TEST_SECRET_KEY_D=sk_test_r1NwHkUW7UyoozyP4aEBD6cs00CI5uDiGq - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/44a1f4718ae2efb67eac - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: false # default: false diff --git a/README.md b/README.md index 2630561f7..47af2d718 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# stripe-ruby-mock [![Build Status](https://travis-ci.org/stripe-ruby-mock/stripe-ruby-mock.png?branch=master)](https://travis-ci.org/stripe-ruby-mock/stripe-ruby-mock) [![Gitter chat](https://badges.gitter.im/rebelidealist/stripe-ruby-mock.png)](https://gitter.im/rebelidealist/stripe-ruby-mock) +# stripe-ruby-mock [![Tests](https://github.com/stripe-ruby-mock/stripe-ruby-mock/actions/workflows/rspec_tests.yml/badge.svg)](https://github.com/stripe-ruby-mock/stripe-ruby-mock/actions/workflows/rspec_tests.yml) * Homepage: https://github.com/stripe-ruby-mock/stripe-ruby-mock * Issues: https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues @@ -29,7 +29,7 @@ version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/strip ### Requirements -* ruby >= 2.4.0 +* ruby >= 2.6.0 * stripe >= 5.0.0 ### Specifications diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 5f958b7c3..0fd6ee6bc 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1259,9 +1259,10 @@ def self.mock_payment_method(params = {}) payment_method_id = params[:id] || 'pm_1ExEuFL2DI6wht39WNJgbybl' type = params[:type].to_sym + last4 = params.dig(:card, :number) data = { card: { - brand: case params.dig(:card, :number)&.to_s + brand: case last4&.to_s when /^4/, nil 'visa' when /^5[1-5]/ @@ -1280,7 +1281,7 @@ def self.mock_payment_method(params = {}) fingerprint: 'Hr3Ly5z5IYxsokWA', funding: 'credit', generated_from: nil, - last4: params.dig(:card, :number)&.[](-4..) || '3155', + last4: last4.nil? ? '3155' : last4.to_s[-4..], three_d_secure_usage: { supported: true }, wallet: nil }, From b6c6e0c0bc37b4f430c1862709aa0abe7f8fabf6 Mon Sep 17 00:00:00 2001 From: Alexander Mamonchik Date: Tue, 24 Oct 2023 20:57:56 +0600 Subject: [PATCH 105/158] Revert "Adds error when attempting to refund a charge that has already been refunded / refunding more than the charge amount" --- .../request_handlers/helpers/charge_helpers.rb | 6 ------ spec/shared_stripe_examples/refund_examples.rb | 11 ----------- 2 files changed, 17 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb b/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb index 81ecb13f3..63270b0f1 100644 --- a/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb @@ -3,12 +3,6 @@ module RequestHandlers module Helpers def add_refund_to_charge(refund, charge) - if refund[:amount] + charge[:amount_refunded] > charge[:amount] - raise Stripe::InvalidRequestError.new( - "Charge #{charge[:id]} has already been refunded.", - 'amount' - ) - end refunds = charge[:refunds] refunds[:data] << refund refunds[:total_count] = refunds[:data].count diff --git a/spec/shared_stripe_examples/refund_examples.rb b/spec/shared_stripe_examples/refund_examples.rb index c83e1ee63..bf0645357 100644 --- a/spec/shared_stripe_examples/refund_examples.rb +++ b/spec/shared_stripe_examples/refund_examples.rb @@ -338,17 +338,6 @@ expect(half.data.first.id).to eq(all_refunds.data.at(2).id) end - it "returns an InvalidRequestError when attempting to refund more than the original charge amount" do - charge = Stripe::Charge.create( - amount: 1000, - currency: 'usd', - source: stripe_helper.generate_card_token - ) - expect { - Stripe::Refund.create(charge: charge.id, amount: 2000) - }.to raise_error(Stripe::InvalidRequestError) - end - describe "idempotency" do let(:customer) { Stripe::Customer.create(email: 'johnny@appleseed.com') } let(:charge) do From 5a5d85490626c1173b5872c7871231ad1ae74f2f Mon Sep 17 00:00:00 2001 From: Leslie Poolman Date: Thu, 23 Nov 2023 10:23:55 -0500 Subject: [PATCH 106/158] Added webhooks for tax_rate.updated and tax_rate.created --- lib/stripe_mock/api/webhooks.rb | 2 + .../webhook_fixtures/tax_rate.created.json | 32 ++++++++++++++++ .../webhook_fixtures/tax_rate.updated.json | 37 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 lib/stripe_mock/webhook_fixtures/tax_rate.created.json create mode 100644 lib/stripe_mock/webhook_fixtures/tax_rate.updated.json diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 974cb4ab4..031076a3a 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -119,6 +119,8 @@ def self.event_list 'subscription_schedule.created', 'subscription_schedule.released', 'subscription_schedule.updated', + 'tax_rate.created', + 'tax_rate.updated', 'transfer.created', 'transfer.failed', 'transfer.paid', diff --git a/lib/stripe_mock/webhook_fixtures/tax_rate.created.json b/lib/stripe_mock/webhook_fixtures/tax_rate.created.json new file mode 100644 index 000000000..29c600f7b --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/tax_rate.created.json @@ -0,0 +1,32 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1700752531, + "data": { + "object": { + "id": "txr_00000000000000", + "object": "tax_rate", + "active": true, + "country": "DE", + "created": 1700750289, + "description": "VAT Germany", + "display_name": "VAT", + "effective_percentage": null, + "inclusive": false, + "jurisdiction": "DE", + "livemode": false, + "metadata": {}, + "percentage": 16.0, + "state": null, + "tax_type": "vat" + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "cd3e4fc0-9d4c-42fd-a818-1b9789537ce9" + }, + "type": "tax_rate.created" +} \ No newline at end of file diff --git a/lib/stripe_mock/webhook_fixtures/tax_rate.updated.json b/lib/stripe_mock/webhook_fixtures/tax_rate.updated.json new file mode 100644 index 000000000..e90fba2d8 --- /dev/null +++ b/lib/stripe_mock/webhook_fixtures/tax_rate.updated.json @@ -0,0 +1,37 @@ +{ + "id": "evt_000000000000000000000000", + "object": "event", + "api_version": "2020-08-27", + "created": 1700752371, + "data": { + "object": { + "id": "txr_00000000000000", + "object": "tax_rate", + "active": true, + "country": "DE", + "created": 1700750289, + "description": "VAT Germany", + "display_name": "VAT", + "effective_percentage": null, + "inclusive": false, + "jurisdiction": "DE", + "livemode": false, + "metadata": {}, + "percentage": 16.0, + "state": null, + "tax_type": "vat" + }, + "previous_attributes": { + "metadata": { + "foo": null + } + } + }, + "livemode": false, + "pending_webhooks": 2, + "request": { + "id": "req_00000000000000", + "idempotency_key": "7eb234a6-64bc-4320-bc7f-780c546ab026" + }, + "type": "tax_rate.updated" +} \ No newline at end of file From b05c1b2094326c2b154a511aecebea788b7ba746 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Sun, 26 Nov 2023 17:56:00 -0300 Subject: [PATCH 107/158] Adds support for stripe-ruby v10 - Fixed deprecated methods in specs --- spec/shared_stripe_examples/bank_token_examples.rb | 11 +++++------ spec/shared_stripe_examples/invoice_examples.rb | 3 ++- spec/shared_stripe_examples/subscription_examples.rb | 12 ++++++------ stripe-ruby-mock.gemspec | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/spec/shared_stripe_examples/bank_token_examples.rb b/spec/shared_stripe_examples/bank_token_examples.rb index 360684587..7ee08c6f2 100644 --- a/spec/shared_stripe_examples/bank_token_examples.rb +++ b/spec/shared_stripe_examples/bank_token_examples.rb @@ -28,20 +28,19 @@ expect(bank_token).to match /^test_btok/ end - it "assigns the generated bank account to a new recipient" do + it "assigns the generated bank account to a new customer" do bank_token = StripeMock.generate_bank_token( :bank_name => "Bank Token Mocking", :last4 => "7777" ) - recipient = Stripe::Recipient.create({ + customer = Stripe::Customer.create({ name: "Fred Flinstone", - type: "individual", email: 'blah@domain.co', - bank_account: bank_token + source: bank_token }) - expect(recipient.active_account.last4).to eq("7777") - expect(recipient.active_account.bank_name).to eq("Bank Token Mocking") + expect(customer.sources.first.last4).to eq("7777") + expect(customer.sources.first.bank_name).to eq("Bank Token Mocking") end it "retrieves a created token" do diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index b7eb20913..b3b6d0d1b 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -154,7 +154,8 @@ describe 'parameter validation' do it 'fails without parameters' do expect { Stripe::Invoice.upcoming() }.to raise_error {|e| - expect(e).to be_a(ArgumentError) } + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.message).to eq('Missing required param: customer if subscription is not provided')} end it 'fails without a valid customer' do diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index d8453de5e..195157253 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -687,7 +687,7 @@ customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) sub = Stripe::Subscription.create({ items: { '0' => { plan: 'silver' } }, customer: customer.id }) - sub.delete(at_period_end: true) + sub.cancel(at_period_end: true) expect(sub.cancel_at_period_end).to be_truthy expect(sub.save).to be_truthy @@ -752,7 +752,7 @@ customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) - subscription.delete + subscription.cancel expect { subscription.save }.to raise_error { |e| expect(e).to be_a(Stripe::InvalidRequestError) @@ -765,7 +765,7 @@ customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) sub = Stripe::Subscription.create({ items: [ { plan: plan.id } ], customer: customer.id }) - sub.delete(at_period_end: true) + sub.cancel(at_period_end: true) expect(sub.cancel_at_period_end).to be_truthy expect(sub.save).to be_truthy @@ -1180,7 +1180,7 @@ customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) - result = sub.delete + result = sub.cancel expect(result.status).to eq('canceled') expect(result.cancel_at_period_end).to eq false @@ -1247,7 +1247,7 @@ customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk, plan: "trial") sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) - result = sub.delete(at_period_end: true) + result = sub.cancel(at_period_end: true) expect(result.status).to eq('trialing') @@ -1331,7 +1331,7 @@ it "does not include canceled subscriptions by default" do customer = Stripe::Customer.create(source: gen_card_tk) subscription = Stripe::Subscription.create({ plan: plan.id, customer: customer.id }) - subscription.delete + subscription.cancel list = Stripe::Subscription.list({customer: customer.id}) diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index e53963d8c..20971300d 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '> 5', '< 6' + gem.add_dependency 'stripe', '> 5', '< 11' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' From c1b537eb7ab7e1f6440b0639b4953dbcefbbf680 Mon Sep 17 00:00:00 2001 From: Alex Mamonchik Date: Wed, 3 Jan 2024 01:46:23 +0600 Subject: [PATCH 108/158] 3.1.0 --- CHANGELOG.md | 22 ++++++++++++++++++++++ README.md | 2 +- lib/stripe_mock/version.rb | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f76a14f3c..df369325e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ ### Unreleased +- [#693](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/693) gemspec: add change,issue,source_code URL by [@mtmail](https://github.com/mtmail) +- [#700](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/700) update the balance API to respond with instant_available by [@iamnader](https://github.com/iamnader) +- [#687](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/687) Add PaymentIntent Webhooks by [@klaustopher](https://github.com/klaustopher) +- [#708](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/708) Implement Stripe::Checkout::Session.retrieve by [@coorasse](https://github.com/coorasse) +- [#711](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/711) Adding account link mock by [@amenon](https://github.com/amenon) +- [#715](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/715) Added application_fee_amount to mock charge object by [@espen](https://github.com/espen) +- [#694](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/694) Introduce ideal and sepa_debit types for PaymentMethod by [@mnin](https://github.com/mnin) +- [#720](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/720) Add additional parameters by [@rpietraszko](https://github.com/rpietraszko) +- [#695](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/695) Add `has_more` attribute to all ListObject instances by [@gbp](https://github.com/gbp) +- [#727](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/727) Fix List initialize error with deleted records by [@jmulieri](https://github.com/jmulieri) +- [#739](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/739) Add data to account links by [@dudyn5ky1](https://github.com/dudyn5ky1) +- [#756](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/756) Added #715 PR to changelog by [@espen](https://github.com/espen) +- [#759](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/759) Adding express dashboard login link mock by [@rohitbegani](https://github.com/rohitbegani) +- [#747](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/747) Fix ruby 2.7 deprecation warnings by [@coding-chimp](https://github.com/coding-chimp) +- [#762](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/762) Support Stripe Connect by adding stripe_account header namespace for customers by [@csalvato](https://github.com/csalvato) +- [#758](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/758) Create price by [@jamesprior](https://github.com/jamesprior) +- [#730](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/730) support on price api by [@hidenba](https://github.com/hidenba) +- [#764](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/764) Fixes erroneous error message when fetching upcoming invoices. by [@csalvato](https://github.com/csalvato) +- [#765](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/765) Properly set the status of a trialing subscription by [@csalvato](https://github.com/csalvato) +- [#755](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/755) Add allowed params to subscriptions by [@dominikdarnel](https://github.com/dominikdarnel) +- [#709](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/709) Remove unnecessary check on customer's currency by [@coorasse](https://github.com/coorasse) + - [#806](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/806) - Remove `payment_method_types` from required arguments for `Stripe::Checkout::Session` - [#806](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/806) - Raise more helpful exception when Stripe::Price cannot be found within a `Stripe::Checkout::Session` `line_items` argument. diff --git a/README.md b/README.md index 47af2d718..ac05f79b3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This gem has unexpectedly grown in popularity and I've gotten pretty busy, so I' In your gemfile: - gem 'stripe-ruby-mock', '~> 3.0.1', :require => 'stripe_mock' + gem 'stripe-ruby-mock', '~> 3.1.0', :require => 'stripe_mock' ## !!! Important diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index a1fd0ea92..d824f4dfd 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0.rc3" + VERSION = "3.1.0" end From 2f0fbb8283be783b21ba941f800b749e5011feb9 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Tue, 9 Jan 2024 16:11:35 -0300 Subject: [PATCH 109/158] Fixes compatibility with new param introduced in v10.3 --- lib/stripe_mock/instance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index 56ab20eef..173f68849 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -109,7 +109,7 @@ def initialize @base_strategy = TestStrategies::Base.new end - def mock_request(method, url, api_key: nil, api_base: nil, params: {}, headers: {}) + def mock_request(method, url, api_key: nil, api_base: nil, usage: [], params: {}, headers: {}) return {} if method == :xtest api_key ||= (Stripe.api_key || DUMMY_API_KEY) From d3a6d398939a9bf7561aea768ddd2c4f55bb6633 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Wed, 10 Jan 2024 11:30:45 -0300 Subject: [PATCH 110/158] Upgrade rspec to v3.12 --- spec/spec_helper.rb | 2 +- stripe-ruby-mock.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6736353ef..c4ebc76c7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'set' -gem 'rspec', '~> 3.1' +gem 'rspec', '~> 3.12' require 'rspec' require 'stripe' require 'stripe_mock' diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 20971300d..13f80242a 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -26,7 +26,7 @@ Gem::Specification.new do |gem| gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' - gem.add_development_dependency 'rspec', '~> 3.7.0' + gem.add_development_dependency 'rspec', '~> 3.12' gem.add_development_dependency 'rubygems-tasks', '~> 0.2' gem.add_development_dependency 'thin', '~> 1.8.1' end From 1c616c1066ae029f9b307427a53967679e500c71 Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Wed, 10 Jan 2024 11:46:15 -0300 Subject: [PATCH 111/158] Adds optional `usage` introduced with stripe ruby v10.3 --- lib/stripe_mock/api/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/api/client.rb b/lib/stripe_mock/api/client.rb index 08a14f69c..9227425eb 100644 --- a/lib/stripe_mock/api/client.rb +++ b/lib/stripe_mock/api/client.rb @@ -27,7 +27,7 @@ def self.stop_client(opts={}) private - def self.redirect_to_mock_server(method, url, api_key: nil, api_base: nil, params: {}, headers: {}) + def self.redirect_to_mock_server(method, url, api_key: nil, api_base: nil, usage: [], params: {}, headers: {}) handler = Instance.handler_for_method_url("#{method} #{url}") if mock_error = client.error_queue.error_for_handler_name(handler[:name]) From 844108e2d3822bd694d24a9870b1915fc7579a99 Mon Sep 17 00:00:00 2001 From: Alex Mamonchik Date: Sat, 10 Feb 2024 15:03:08 +0600 Subject: [PATCH 112/158] Updated gems --- Gemfile | 5 ----- README.md | 2 +- stripe-ruby-mock.gemspec | 3 +-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 8cc610a86..e04240a41 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,5 @@ source 'https://rubygems.org' -platforms :ruby_19 do - gem 'mime-types', '~> 2.6' - gem 'rest-client', '~> 1.8' -end - group :test do gem 'rake' gem 'dotenv' diff --git a/README.md b/README.md index ac05f79b3..c7b122adc 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/strip ### Requirements -* ruby >= 2.6.0 +* ruby >= 2.7.0 * stripe >= 5.0.0 ### Specifications diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index e53963d8c..f8a28d6c4 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |gem| gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' - gem.add_development_dependency 'rspec', '~> 3.7.0' - gem.add_development_dependency 'rubygems-tasks', '~> 0.2' + gem.add_development_dependency 'rspec', '~> 3.13.0' gem.add_development_dependency 'thin', '~> 1.8.1' end From 8c4bd51638a633365129e052d045fd5a5aeadbb3 Mon Sep 17 00:00:00 2001 From: Alex Mamonchik Date: Sat, 10 Feb 2024 15:05:22 +0600 Subject: [PATCH 113/158] Added ruby 3.2 to Github Actions --- .github/workflows/rspec_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index 52428cc3b..d54b2475b 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0'] + ruby-version: ['2.7', '3.0', '3.2'] steps: - uses: actions/checkout@v3 From 1d08c8ccdfd585ab9a7dc9a0ca2724bc2c655e5b Mon Sep 17 00:00:00 2001 From: Sanjay Makani <39767801+smakani@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:54:29 +0530 Subject: [PATCH 114/158] Update README.md Co-authored-by: Alexander Mamonchik --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 50fc5942c..1d64d7e2b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/strip ### Requirements * ruby >= 2.4.0 -* stripe >5 & <=7.1.0 +* stripe > 5 & < 11 ### Specifications From ab53d99c8db519de84564ef9bfc63bf19d62da65 Mon Sep 17 00:00:00 2001 From: Sanjay Makani Date: Sat, 10 Feb 2024 18:06:18 +0530 Subject: [PATCH 115/158] Updated Stripe version referenced in gemspec --- stripe-ruby-mock.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 5067bcf55..c48547547 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '> 5', '<= 7.1.0' + gem.add_dependency 'stripe', '> 5', '< 11.0' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' From d693f439ce4309ff93d268a9072ccd7dd2fc24dd Mon Sep 17 00:00:00 2001 From: Fabiano Arruda Date: Sun, 26 Nov 2023 17:56:00 -0300 Subject: [PATCH 116/158] Revert "Upgrade rspec to v3.12" This reverts commit d3a6d398939a9bf7561aea768ddd2c4f55bb6633. --- spec/spec_helper.rb | 2 +- stripe-ruby-mock.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c4ebc76c7..6736353ef 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'set' -gem 'rspec', '~> 3.12' +gem 'rspec', '~> 3.1' require 'rspec' require 'stripe' require 'stripe_mock' diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 13f80242a..20971300d 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -26,7 +26,7 @@ Gem::Specification.new do |gem| gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' - gem.add_development_dependency 'rspec', '~> 3.12' + gem.add_development_dependency 'rspec', '~> 3.7.0' gem.add_development_dependency 'rubygems-tasks', '~> 0.2' gem.add_development_dependency 'thin', '~> 1.8.1' end From f67afb946a6e4f61b8ca05ca4ff31517a9b80981 Mon Sep 17 00:00:00 2001 From: Alex Mamonchik Date: Sat, 10 Feb 2024 20:22:31 +0600 Subject: [PATCH 117/158] Added merged PRs to changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df369325e..c92ca701b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ### Unreleased +- [#830](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/830) Implement search API by [@adamstegman](https://github.com/adamstegman) +- [#848](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/848) Extending runtime dependency on Stripe gem from version 5 through 11 by [@smakani](https://github.com/smakani) +- [#893](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/893) Adds support for stripe-ruby v10 by [@fabianoarruda](https://github.com/fabianoarruda) + + +### 3.1.0 (2024-01-03) - [#693](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/693) gemspec: add change,issue,source_code URL by [@mtmail](https://github.com/mtmail) - [#700](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/700) update the balance API to respond with instant_available by [@iamnader](https://github.com/iamnader) - [#687](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/687) Add PaymentIntent Webhooks by [@klaustopher](https://github.com/klaustopher) From 84c8a50b30cbf7e6861590922a57643169f00983 Mon Sep 17 00:00:00 2001 From: Alex Mamonchik Date: Sat, 10 Feb 2024 20:22:52 +0600 Subject: [PATCH 118/158] Removed Subscription.delet in one test --- spec/shared_stripe_examples/subscription_examples.rb | 2 +- spec/spec_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 8fe4d0a2a..f72d8a400 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1440,7 +1440,7 @@ customer = Stripe::Customer.create(email: 'johnny@appleseed.com', source: gen_card_tk) one = Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}], metadata: {key: 'uno'}) two = Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}], metadata: {key: 'dos'}) - Stripe::Subscription.delete(two.id) + Stripe::Subscription.cancel(two.id) response = Stripe::Subscription.search({query: 'status:"active"'}, stripe_version: '2020-08-27') expect(response.data.map(&:id)).to match_array([one.id]) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6736353ef..9c5a17b9f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'set' -gem 'rspec', '~> 3.1' +gem 'rspec', '~> 3.13' require 'rspec' require 'stripe' require 'stripe_mock' From 15aff10056bbd773da67dcd4eaf66c1a2d7b668c Mon Sep 17 00:00:00 2001 From: Steven Harman Date: Wed, 10 Apr 2024 11:07:45 -0400 Subject: [PATCH 119/158] Add has_more to Subscription.items data --- lib/stripe_mock/data.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index ecca25e57..3d1d3ddac 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -382,7 +382,8 @@ def self.mock_subscription(params={}) currency: StripeMock.default_currency }, quantity: 1 - }] + }], + has_more: false }, cancel_at_period_end: false, canceled_at: nil, From cbf512172f5f335e0e64fa10f16c798fec0611d6 Mon Sep 17 00:00:00 2001 From: Steven Harman Date: Fri, 19 Apr 2024 09:21:18 -0400 Subject: [PATCH 120/158] Allow Stripe SDK v11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Which launched a couple weeks ago, and should be backward compatible with 10, etc… --- stripe-ruby-mock.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index f3416dcbc..98cdba813 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '> 5', '< 11' + gem.add_dependency 'stripe', '> 5', '< 12' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' From fa32d9933a1ed9794ca69984151c243315e8b012 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Mon, 20 May 2024 10:25:13 -0700 Subject: [PATCH 121/158] * fix calls to use raise instead of throw * make sure to call raise Stripe::InvalidRequestError.new(error, param) (note: call to new + 'param') fixes #906 --- .../request_handlers/checkout_session.rb | 19 +++++++++++-------- .../request_handlers/subscriptions.rb | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index c0d408b85..58b388dfc 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -19,9 +19,12 @@ def new_session(route, method_url, params, headers) line_items = nil if params[:line_items] line_items = params[:line_items].each_with_index.map do |line_item, i| - throw Stripe::InvalidRequestError("Quantity is required. Add `quantity` to `line_items[#{i}]`") unless line_item[:quantity] + unless line_item[:quantity] + raise Stripe::InvalidRequestError.new("Quantity is required. Add `quantity` to `line_items[#{i}]`", :line_items) + end + unless line_item[:price] || line_item[:price_data] || (line_item[:amount] && line_item[:currency] && line_item[:name]) - throw Stripe::InvalidRequestError("Price or amount and currency is required. Add `price`, `price_data`, or `amount`, `currency` and `name` to `line_items[#{i}]`") + raise Stripe::InvalidRequestError.new("Price or amount and currency is required. Add `price`, `price_data`, or `amount`, `currency` and `name` to `line_items[#{i}]`", :line_items) end { id: new_id("li"), @@ -48,11 +51,11 @@ def new_session(route, method_url, params, headers) if line_items amount = 0 - line_items.each do |line_item| + line_items.each do |line_item| price = prices[line_item[:price]] if price.nil? - raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}") + raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}", :line_items) end amount += (price[:unit_amount] * line_item[:quantity]) @@ -78,7 +81,7 @@ def new_session(route, method_url, params, headers) checkout_session_line_items[id] = line_items when "setup" if !params[:line_items].nil? && !params[:line_items].empty? - throw Stripe::InvalidRequestError.new("You cannot pass `line_items` in `setup` mode", :line_items, http_status: 400) + raise Stripe::InvalidRequestError.new("You cannot pass `line_items` in `setup` mode", :line_items, http_status: 400) end setup_intent = new_setup_intent(nil, nil, { customer: params[:customer], @@ -91,7 +94,7 @@ def new_session(route, method_url, params, headers) require_param(:line_items) if params[:line_items].nil? || params[:line_items].empty? checkout_session_line_items[id] = line_items else - throw Stripe::InvalidRequestError.new("Invalid mode: must be one of payment, setup, or subscription", :mode, http_status: 400) + raise Stripe::InvalidRequestError.new("Invalid mode: must be one of payment, setup, or subscription", :mode, http_status: 400) end checkout_sessions[id] = { @@ -156,7 +159,7 @@ def list_line_items(route, method_url, params, headers) price = prices[line_item[:price]].clone if price.nil? - raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}") + raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}", :line_items) end { @@ -170,7 +173,7 @@ def list_line_items(route, method_url, params, headers) } end else - throw Stripe::InvalidRequestError("Only payment and subscription sessions have line items") + raise Stripe::InvalidRequestError.new("Only payment and subscription sessions have line items", :line_items) end end end diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 33cf0264e..1b628ca54 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -166,7 +166,7 @@ def create_subscription(route, method_url, params, headers) end if params[:transfer_data] && !params[:transfer_data].empty? - throw Stripe::InvalidRequestError.new(missing_param_message("transfer_data[destination]")) unless params[:transfer_data][:destination] + raise Stripe::InvalidRequestError.new(missing_param_message("transfer_data[destination]")) unless params[:transfer_data][:destination] subscription[:transfer_data] = params[:transfer_data].dup subscription[:transfer_data][:amount_percent] ||= 100 end From c61ca3372642dbdcd937656322122ab03a30fd36 Mon Sep 17 00:00:00 2001 From: Espen Antonsen Date: Mon, 17 Jun 2024 11:21:13 +0200 Subject: [PATCH 122/158] Add update_payout --- lib/stripe_mock/request_handlers/payouts.rb | 7 +++++++ spec/shared_stripe_examples/payout_examples.rb | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/lib/stripe_mock/request_handlers/payouts.rb b/lib/stripe_mock/request_handlers/payouts.rb index c23b8338f..490e2f017 100644 --- a/lib/stripe_mock/request_handlers/payouts.rb +++ b/lib/stripe_mock/request_handlers/payouts.rb @@ -4,6 +4,7 @@ module Payouts def Payouts.included(klass) klass.add_handler 'post /v1/payouts', :new_payout + klass.add_handler 'post /v1/payouts/(.*)', :update_payout klass.add_handler 'get /v1/payouts', :list_payouts klass.add_handler 'get /v1/payouts/(.*)', :get_payout end @@ -18,6 +19,12 @@ def new_payout(route, method_url, params, headers) payouts[id] = Data.mock_payout(params.merge :id => id) end + def update_payout(route, method_url, params, headers) + route =~ method_url + assert_existence :payout, $1, payouts[$1] + payouts[$1].merge!(params) + end + def list_payouts(route, method_url, params, headers) Data.mock_list_object(payouts.clone.values, params) end diff --git a/spec/shared_stripe_examples/payout_examples.rb b/spec/shared_stripe_examples/payout_examples.rb index 0b53b50c6..6b50e48e1 100644 --- a/spec/shared_stripe_examples/payout_examples.rb +++ b/spec/shared_stripe_examples/payout_examples.rb @@ -37,6 +37,18 @@ expect(payout.metadata.to_hash).to eq(original.metadata.to_hash) end + it "updates a stripe payout" do + original = Stripe::Payout.create(amount: "100", currency: "usd") + payout = Stripe::Payout.retrieve(original.id) + + expect(payout.id).to eq(original.id) + + payout.amount = 1337 + payout.save + payout = Stripe::Payout.retrieve(original.id) + expect(payout.amount).to eq(1337) + end + it "cannot retrieve a payout that doesn't exist" do expect { Stripe::Payout.retrieve('nope') }.to raise_error {|e| expect(e).to be_a Stripe::InvalidRequestError From 7cad313c51d54d9a84eb6d7776eb61bb27c0d1f7 Mon Sep 17 00:00:00 2001 From: Aleksandr Mamonchik Date: Wed, 7 Aug 2024 02:46:49 +0600 Subject: [PATCH 123/158] Release v4.0.0 --- CHANGELOG.md | 2 ++ README.md | 2 +- lib/stripe_mock/version.rb | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c92ca701b..1d97ded3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ### Unreleased +### 4.0.0 (2024-08-07) +- [#905](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/905) Allow Stripe SDK v11 by [@stevenharman ](https://github.com/stevenharman) - [#830](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/830) Implement search API by [@adamstegman](https://github.com/adamstegman) - [#848](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/848) Extending runtime dependency on Stripe gem from version 5 through 11 by [@smakani](https://github.com/smakani) - [#893](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/893) Adds support for stripe-ruby v10 by [@fabianoarruda](https://github.com/fabianoarruda) diff --git a/README.md b/README.md index d9d2f185d..706f0cf88 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This gem has unexpectedly grown in popularity and I've gotten pretty busy, so I' In your gemfile: - gem 'stripe-ruby-mock', '~> 3.1.0', :require => 'stripe_mock' + gem 'stripe-ruby-mock', :require => 'stripe_mock' ## !!! Important diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index d824f4dfd..7c3410b93 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "3.1.0" + VERSION = "4.0.0" end From 1a72e01b6f47851dd44689d8eb6803b06502f857 Mon Sep 17 00:00:00 2001 From: Espen Antonsen Date: Mon, 12 Aug 2024 13:03:57 +0200 Subject: [PATCH 124/158] same id handling as other tests --- lib/stripe_mock/request_handlers/payouts.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/payouts.rb b/lib/stripe_mock/request_handlers/payouts.rb index 490e2f017..268dd0f5e 100644 --- a/lib/stripe_mock/request_handlers/payouts.rb +++ b/lib/stripe_mock/request_handlers/payouts.rb @@ -10,13 +10,13 @@ def Payouts.included(klass) end def new_payout(route, method_url, params, headers) - id = new_id('po') + params[:id] ||= new_id("po") unless params[:amount].is_a?(Integer) || (params[:amount].is_a?(String) && /^\d+$/.match(params[:amount])) raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400) end - payouts[id] = Data.mock_payout(params.merge :id => id) + payouts[params[:id]] = Data.mock_payout(params.merge :id => params[:id]) end def update_payout(route, method_url, params, headers) From c5bde32df2fa88f8cb8dfaf8fdc347a3b6657c02 Mon Sep 17 00:00:00 2001 From: Espen Antonsen Date: Mon, 12 Aug 2024 13:05:28 +0200 Subject: [PATCH 125/158] amount as integers as other tests. handle negativ integers. include test for payout.update method --- lib/stripe_mock/request_handlers/payouts.rb | 2 +- .../shared_stripe_examples/payout_examples.rb | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/stripe_mock/request_handlers/payouts.rb b/lib/stripe_mock/request_handlers/payouts.rb index 268dd0f5e..e27221086 100644 --- a/lib/stripe_mock/request_handlers/payouts.rb +++ b/lib/stripe_mock/request_handlers/payouts.rb @@ -12,7 +12,7 @@ def Payouts.included(klass) def new_payout(route, method_url, params, headers) params[:id] ||= new_id("po") - unless params[:amount].is_a?(Integer) || (params[:amount].is_a?(String) && /^\d+$/.match(params[:amount])) + unless (params[:amount].is_a?(Integer) && params[:amount].positive?) || (params[:amount].is_a?(String) && /^\d+$/.match(params[:amount])) raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400) end diff --git a/spec/shared_stripe_examples/payout_examples.rb b/spec/shared_stripe_examples/payout_examples.rb index 6b50e48e1..abe5c3c99 100644 --- a/spec/shared_stripe_examples/payout_examples.rb +++ b/spec/shared_stripe_examples/payout_examples.rb @@ -3,10 +3,10 @@ shared_examples 'Payout API' do it "creates a stripe payout" do - payout = Stripe::Payout.create(amount: "100", currency: "usd") + payout = Stripe::Payout.create(amount: 100, currency: "usd") expect(payout.id).to match(/^test_po/) - expect(payout.amount).to eq('100') + expect(payout.amount).to eq(100) expect(payout.currency).to eq('usd') expect(payout.metadata.to_hash).to eq({}) end @@ -14,7 +14,7 @@ describe "listing payouts" do before do 3.times do - Stripe::Payout.create(amount: "100", currency: "usd") + Stripe::Payout.create(amount: 100, currency: "usd") end end @@ -28,7 +28,7 @@ end it "retrieves a stripe payout" do - original = Stripe::Payout.create(amount: "100", currency: "usd") + original = Stripe::Payout.create(amount: 100, currency: "usd") payout = Stripe::Payout.retrieve(original.id) expect(payout.id).to eq(original.id) @@ -37,8 +37,15 @@ expect(payout.metadata.to_hash).to eq(original.metadata.to_hash) end - it "updates a stripe payout" do - original = Stripe::Payout.create(amount: "100", currency: "usd") + it "updates a stripe payout using method" do + original = Stripe::Payout.create(amount: 100, currency: "usd") + updated = Stripe::Payout.update(original.id, amount: 1337) + payout = Stripe::Payout.retrieve(original.id) + expect(payout.amount).to eq(1337) + end + + it "updates a stripe payout object" do + original = Stripe::Payout.create(amount: 100, currency: "usd") payout = Stripe::Payout.retrieve(original.id) expect(payout.id).to eq(original.id) @@ -58,17 +65,18 @@ end it 'when amount is not integer', live: true do - expect { Stripe::Payout.create(amount: '400.2', + expect { Stripe::Payout.create(amount: 400.2, currency: 'usd', description: 'Payout for test@example.com') }.to raise_error { |e| expect(e).to be_a Stripe::InvalidRequestError expect(e.param).to eq('amount') + expect(e.message).to match(/^Invalid.*integer/) expect(e.http_status).to eq(400) } end it 'when amount is negative', live: true do - expect { Stripe::Payout.create(amount: '-400', + expect { Stripe::Payout.create(amount: -400, currency: 'usd', description: 'Payout for test@example.com') }.to raise_error { |e| expect(e).to be_a Stripe::InvalidRequestError From 3c6b95c236f0b461c48e8ccdf50fbf4e93f54dba Mon Sep 17 00:00:00 2001 From: Espen Antonsen Date: Mon, 12 Aug 2024 14:29:04 +0200 Subject: [PATCH 126/158] PI charges object is deprecated "Remove support for charges on PaymentIntent" - https://docs.stripe.com/changelog#16-november-2022 --- .../request_handlers/payment_intents.rb | 2 +- .../payment_intent_examples.rb | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/stripe_mock/request_handlers/payment_intents.rb b/lib/stripe_mock/request_handlers/payment_intents.rb index 101a800ef..486c9784f 100644 --- a/lib/stripe_mock/request_handlers/payment_intents.rb +++ b/lib/stripe_mock/request_handlers/payment_intents.rb @@ -193,7 +193,7 @@ def succeeded_payment_intent(payment_intent) payment_method: payment_intent[:payment_method] ) - payment_intent[:charges][:data] << charges[charge_id].clone + payment_intent[:latest_charge] = charge_id payment_intent end diff --git a/spec/shared_stripe_examples/payment_intent_examples.rb b/spec/shared_stripe_examples/payment_intent_examples.rb index 09d9e932a..c723d965d 100644 --- a/spec/shared_stripe_examples/payment_intent_examples.rb +++ b/spec/shared_stripe_examples/payment_intent_examples.rb @@ -90,9 +90,9 @@ amount: 100, currency: 'usd', confirm: true ) expect(payment_intent.status).to eq('succeeded') - expect(payment_intent.charges.data.size).to eq(1) - expect(payment_intent.charges.data.first.object).to eq('charge') - balance_txn = payment_intent.charges.data.first.balance_transaction + charge = Stripe::Charge.retrieve(payment_intent.latest_charge) + expect(charge.object).to eq('charge') + balance_txn = charge.balance_transaction expect(balance_txn).to match(/^test_txn/) expect(Stripe::BalanceTransaction.retrieve(balance_txn).id).to eq(balance_txn) end @@ -104,7 +104,7 @@ customer: customer, payment_method: customer.sources.first - charge = payment_intent.charges.data.first + charge = Stripe::Charge.retrieve(payment_intent.latest_charge) expect(charge.amount).to eq(payment_intent.amount) expect(charge.payment_intent).to eq(payment_intent.id) expect(charge.description).to be_nil @@ -121,17 +121,17 @@ amount: 100, currency: "usd", confirm: true, payment_method: "test_pm_1" ) expect(payment_intent.status).to eq("succeeded") - expect(payment_intent.charges.data.size).to eq(1) - expect(payment_intent.charges.data.first.object).to eq("charge") - expect(payment_intent.charges.data.first.payment_method).to eq("test_pm_1") + charge = Stripe::Charge.retrieve(payment_intent.latest_charge) + expect(charge.object).to eq("charge") + expect(charge.payment_method).to eq("test_pm_1") end it "confirms a stripe payment_intent" do payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") confirmed_payment_intent = payment_intent.confirm() expect(confirmed_payment_intent.status).to eq("succeeded") - expect(confirmed_payment_intent.charges.data.size).to eq(1) - expect(confirmed_payment_intent.charges.data.first.object).to eq('charge') + charge = Stripe::Charge.retrieve(confirmed_payment_intent.latest_charge) + expect(charge.object).to eq('charge') end it 'creates a charge for a confirmed stripe payment_intent' do @@ -141,7 +141,7 @@ payment_method: customer.sources.first confirmed_payment_intent = payment_intent.confirm - charge = confirmed_payment_intent.charges.data.first + charge = Stripe::Charge.retrieve(payment_intent.latest_charge) expect(charge.amount).to eq(confirmed_payment_intent.amount) expect(charge.payment_intent).to eq(confirmed_payment_intent.id) expect(charge.description).to be_nil @@ -157,8 +157,8 @@ payment_intent = Stripe::PaymentIntent.create(amount: 100, currency: "usd") confirmed_payment_intent = payment_intent.capture() expect(confirmed_payment_intent.status).to eq("succeeded") - expect(confirmed_payment_intent.charges.data.size).to eq(1) - expect(confirmed_payment_intent.charges.data.first.object).to eq('charge') + charge = Stripe::Charge.retrieve(confirmed_payment_intent.latest_charge) + expect(charge.object).to eq('charge') end it 'creates a charge for a captured stripe payment_intent' do @@ -170,7 +170,7 @@ capture_method: 'manual' captured_payment_intent = payment_intent.capture - charge = captured_payment_intent.charges.data.first + charge = Stripe::Charge.retrieve(captured_payment_intent.latest_charge) expect(charge.amount).to eq(captured_payment_intent.amount) expect(charge.payment_intent).to eq(captured_payment_intent.id) expect(charge.description).to be_nil From c2cc6419cab23ca0d7e0d1499674d228f1b90a45 Mon Sep 17 00:00:00 2001 From: Don Nguyen Date: Thu, 14 Nov 2024 03:53:39 +0700 Subject: [PATCH 127/158] Some updates around subscriptions (#843) * Allow stripe < 8 * Add `application_fee_percent` # Conflicts: # lib/stripe_mock/data.rb * Add :cancel_at, :end_at * Add :price to :subscription_items * Add :pause_collection to Subscription * Add :has_more to subscription_items list * Remove :mock_price from Subscription * Customer from subscription on upcoming_invoice * Get plan amount from Plan or Price * Allow stripe 12 --------- Co-authored-by: Alexander Mamonchik --- lib/stripe_mock/data.rb | 13 ++++++++++--- lib/stripe_mock/request_handlers/invoices.rb | 10 ++++++++-- stripe-ruby-mock.gemspec | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index eda623ba5..09bd8852d 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -382,7 +382,8 @@ def self.mock_subscription(params={}) currency: StripeMock.default_currency }, quantity: 1 - }] + }], + has_more: false }, cancel_at_period_end: false, canceled_at: nil, @@ -401,7 +402,12 @@ def self.mock_subscription(params={}) default_payment_method: nil, pending_invoice_item_interval: nil, next_pending_invoice_item_invoice: nil, - latest_invoice: nil + pending_setup_intent: nil, + latest_invoice: nil, + application_fee_percent: nil, + cancel_at: nil, + end_at: nil, + pause_collection: nil }, params) end @@ -1221,7 +1227,8 @@ def self.mock_subscription_item(params = {}) statement_descriptor: nil, trial_period_days: nil }, - quantity: 2 + quantity: 2, + price: mock_price }.merge(params) end diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index 67cc25610..069dbb865 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -77,6 +77,9 @@ def upcoming_invoice(route, method_url, params, headers = {}) raise Stripe::InvalidRequestError.new('When previewing changes to a subscription, you must specify either `subscription` or `subscription_items`', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? && params[:subscription_plan].nil? raise Stripe::InvalidRequestError.new('Cannot specify proration date without specifying a subscription', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? + if params[:subscription] && params[:customer].nil? + params[:customer] = subscriptions[params[:subscription]][:customer] + end customer = customers[stripe_account][params[:customer]] assert_existence :customer, params[:customer], customer @@ -114,8 +117,9 @@ def upcoming_invoice(route, method_url, params, headers = {}) invoice_lines = [] if prorating + plan_amount = subscription[:plan][:amount] || subscription[:plan][:unit_amount] unused_amount = ( - subscription[:plan][:amount].to_f * + plan_amount.to_f * subscription[:quantity] * (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start]) ).ceil @@ -169,11 +173,13 @@ def upcoming_invoice(route, method_url, params, headers = {}) private def get_mock_subscription_line_item(subscription) + plan_amount = subscription[:plan][:amount] || subscription[:plan][:unit_amount] + Data.mock_line_item( id: subscription[:id], type: "subscription", plan: subscription[:plan], - amount: subscription[:status] == 'trialing' ? 0 : subscription[:plan][:amount] * subscription[:quantity], + amount: subscription[:status] == 'trialing' ? 0 : plan_amount * subscription[:quantity], discountable: true, quantity: subscription[:quantity], period: { diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 98cdba813..4af469fbc 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '> 5', '< 12' + gem.add_dependency 'stripe', '> 5', '< 13' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' From 7909de6c222da3347a03ae7f23a84b47b796fa29 Mon Sep 17 00:00:00 2001 From: Steven Harman Date: Wed, 10 Apr 2024 10:41:23 -0400 Subject: [PATCH 128/158] Add `require` option to require additional file When booting the stripe-mock-server it can be useful to have Ruby `require` in additional files if you need to patch `StripeMock` in some way. This is mostly useful for differences when you're running on an older version of the Stripe API and need to tweak `StripeMock`'s behavior until your Stripe API version is upgraded to be compatible. When using `StripeMock` directly within a Rails app, for example, this is unneeded as you can do the same thing via a Rails initializer file. But if running the `StripeMock` server standalone - for use with front end tests, for example, you might pass this new flag like this: --require config/initializers/stripe_ruby_mock --- bin/stripe-mock-server | 1 + lib/stripe_mock/server.rb | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/stripe-mock-server b/bin/stripe-mock-server index 11b67693c..fcb9c1595 100755 --- a/bin/stripe-mock-server +++ b/bin/stripe-mock-server @@ -11,6 +11,7 @@ opts = Trollop::options do opt :server, "Server to use", :type => :string, :default => 'thin' opt :debug, "Request and response output", :default => true opt :pid_path, "Location to put server pid file", :type => :string, :default => './stripe-mock-server.pid' + opt :require, "Extra path to require when loading the server; can be specified multiple times", :type => :string, :multi => true end require 'stripe_mock' diff --git a/lib/stripe_mock/server.rb b/lib/stripe_mock/server.rb index 6f2cda2e4..cafb3cb99 100644 --- a/lib/stripe_mock/server.rb +++ b/lib/stripe_mock/server.rb @@ -3,10 +3,16 @@ module StripeMock class Server def self.start_new(opts) - puts "Starting StripeMock server on port #{opts[:port] || 4999}" + host = opts.fetch(:host, "0.0.0.0") + port = opts.fetch(:port, 4999) + extra_requires = opts.fetch(:require, []) - host = opts.fetch :host,'0.0.0.0' - port = opts.fetch :port, 4999 + extra_requires.each do |path| + puts "Requiring additional path: #{path}" + require(path) + end + + puts "Starting StripeMock server on port #{port}" DRb.start_service "druby://#{host}:#{port}", Server.new DRb.thread.join @@ -88,6 +94,5 @@ def ping def upsert_stripe_object(object, attributes) @instance.upsert_stripe_object(object, attributes) end - end end From 3047df891d8e1c9c9a7f4e50b5ddef2db8c0f4d7 Mon Sep 17 00:00:00 2001 From: Lukas Fittl Date: Mon, 17 Feb 2025 18:44:12 -0800 Subject: [PATCH 129/158] Add tax ID endpoints and data Fixes #660 --- lib/stripe_mock.rb | 1 + lib/stripe_mock/data.rb | 19 ++++++ lib/stripe_mock/instance.rb | 4 +- lib/stripe_mock/request_handlers/tax_ids.rb | 66 +++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 lib/stripe_mock/request_handlers/tax_ids.rb diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index b53ac99fd..f8794d4d3 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -80,6 +80,7 @@ require 'stripe_mock/request_handlers/country_spec.rb' require 'stripe_mock/request_handlers/ephemeral_key.rb' require 'stripe_mock/request_handlers/products.rb' +require 'stripe_mock/request_handlers/tax_ids.rb' require 'stripe_mock/request_handlers/tax_rates.rb' require 'stripe_mock/request_handlers/checkout_session.rb' require 'stripe_mock/instance' diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 5df3637ab..c5d2666e9 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -127,6 +127,25 @@ def self.mock_express_login_link(params = {}) }.merge(params) end + def self.mock_tax_id(params) + { + id: 'test_cus_default', + object: 'tax_id', + country: 'DE', + created: 1559079603, + customer: nil, + livemode: false, + type: 'eu_vat', + value: 'DE123456789', + verification: nil, + owner: { + type: 'self', + customer: nil + }, + metadata: {} + }.merge(params) + end + def self.mock_tax_rate(params) { id: 'test_cus_default', diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index 173f68849..76c03e56b 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -52,6 +52,7 @@ def self.handler_for_method_url(method_url) include StripeMock::RequestHandlers::CountrySpec include StripeMock::RequestHandlers::Payouts include StripeMock::RequestHandlers::EphemeralKey + include StripeMock::RequestHandlers::TaxIds include StripeMock::RequestHandlers::TaxRates include StripeMock::RequestHandlers::Checkout include StripeMock::RequestHandlers::Checkout::Session @@ -59,7 +60,7 @@ def self.handler_for_method_url(method_url) attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers, :disputes, :events, :invoices, :invoice_items, :orders, :payment_intents, :payment_methods, :setup_intents, :plans, :prices, :promotion_codes, :recipients, :refunds, :transfers, :payouts, - :subscriptions, :country_spec, :subscriptions_items, :products, :tax_rates, :checkout_sessions, + :subscriptions, :country_spec, :subscriptions_items, :products, :tax_ids, :tax_rates, :checkout_sessions, :checkout_session_line_items attr_accessor :error_queue, :debug, :conversion_rate, :account_balance @@ -93,6 +94,7 @@ def initialize @subscriptions = {} @subscriptions_items = {} @country_spec = {} + @tax_ids = {} @tax_rates = {} @checkout_sessions = {} @checkout_session_line_items = {} diff --git a/lib/stripe_mock/request_handlers/tax_ids.rb b/lib/stripe_mock/request_handlers/tax_ids.rb new file mode 100644 index 000000000..07a5d33f1 --- /dev/null +++ b/lib/stripe_mock/request_handlers/tax_ids.rb @@ -0,0 +1,66 @@ +module StripeMock + module RequestHandlers + module TaxIds + def TaxIds.included(klass) + klass.add_handler 'post /v1/tax_ids', :new_tax_id + klass.add_handler 'post /v1/customers/(.*)/tax_ids', :new_customer_tax_id + klass.add_handler 'get /v1/tax_ids/([^/]*)', :get_tax_id + klass.add_handler 'get /v1/customers/(.*)/tax_ids/([^/]*)', :get_customer_tax_id + klass.add_handler 'get /v1/tax_ids', :list_tax_ids + klass.add_handler 'get /v1/customers/(.*)/tax_ids', :list_customer_tax_ids + klass.add_handler 'delete /v1/tax_ids/([^/]*)', :delete_tax_id + klass.add_handler 'delete /v1/customers/(.*)/tax_ids/([^/]*)', :delete_customer_tax_id + end + + def new_tax_id(route, method_url, params, headers) + params[:id] ||= new_id('txi') + tax_ids[ params[:id] ] = Data.mock_tax_id(params) + tax_ids[ params[:id] ] + end + def new_customer_tax_id(route, method_url, params, headers) + new_tax_id(route, method_url, params.merge(customer: $1)) + end + + def get_tax_id(route, method_url, params, headers) + route =~ method_url + tax_id = assert_existence :tax_id, $1, tax_ids[$1] + tax_id.clone + end + def get_customer_tax_id(route, method_url, params, headers) + route =~ method_url + tax_id = tax_ids[$2] + tax_id = nil if tax_id[:customer] != $1 + tax_id = assert_existence :tax_id, $2, tax_id + tax_id.clone + end + + def list_tax_ids(route, method_url, params, headers) + Data.mock_list_object(tax_ids.values, params) + end + def list_customer_tax_ids(route, method_url, params, headers) + Data.mock_list_object(tax_ids.values.select { |t| t[:customer] == $1 }, params) + end + + def delete_tax_id(route, method_url, params, headers) + route =~ method_url + assert_existence :tax_id, $1, tax_ids[$1] + + tax_ids[$1] = { + id: tax_ids[$1][:id], + deleted: true + } + end + def delete_customer_tax_id(route, method_url, params, headers) + route =~ method_url + tax_id = tax_ids[$2] + tax_id = nil if tax_id[:customer] != $1 + tax_id = assert_existence :tax_id, $2, tax_id + + tax_ids[$2] = { + id: tax_ids[$2][:id], + deleted: true + } + end + end + end +end From e3214e9a544749ac053ed245c6db599ece87f098 Mon Sep 17 00:00:00 2001 From: Betty Steger Date: Wed, 19 Feb 2025 23:37:10 +0100 Subject: [PATCH 130/158] Fix github actions and add missing product attributes (#925) * add missing mock production attributes * try to fix github action * try to fix github action * fix mock product test * fix github action * try to use setup-ruby@v1 --- .github/workflows/rspec_tests.yml | 17 +++++++---------- lib/stripe_mock/data.rb | 3 +++ spec/shared_stripe_examples/product_examples.rb | 3 +++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index d54b2475b..973aec2bf 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -18,21 +18,18 @@ permissions: jobs: test: - runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.7', '3.0', '3.2'] - + ruby: ['2.7', '3.0', '3.2'] steps: - - uses: actions/checkout@v3 - - name: Set up Ruby - # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, - # change this to (see https://github.com/ruby/setup-ruby#versioning): - # uses: ruby/setup-ruby@v1 - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + - uses: actions/checkout@v4 + - name: Install Ruby and gems + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby-version }} + ruby-version: ${{ matrix.ruby }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests run: bundle exec rspec diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index c5d2666e9..2007b4a84 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -672,15 +672,18 @@ def self.mock_product(params={}) attributes:[], caption: nil, created: 1466698000, + default_price: nil, deactivate_on: [], description: nil, images: [], + marketing_features: [], livemode: false, metadata: {}, name: "The Mock Product", package_dimensions: nil, shippable: nil, statement_descriptor: nil, + tax_code: nil, type: "service", unit_label: "my_unit", updated: 1537939442, diff --git a/spec/shared_stripe_examples/product_examples.rb b/spec/shared_stripe_examples/product_examples.rb index 37a8e91f3..40de446c1 100644 --- a/spec/shared_stripe_examples/product_examples.rb +++ b/spec/shared_stripe_examples/product_examples.rb @@ -192,15 +192,18 @@ :attributes, :caption, :created, + :default_price, :deactivate_on, :description, :images, + :marketing_features, :livemode, :metadata, :name, :package_dimensions, :shippable, :statement_descriptor, + :tax_code, :type, :unit_label, :updated, From 3881ce919b2244629c03ed4d60a7f60bf64c99ae Mon Sep 17 00:00:00 2001 From: Alexander Mamonchik Date: Thu, 20 Feb 2025 01:06:09 +0100 Subject: [PATCH 131/158] Actions uses only supported ruby versions (#929) Actions uses only supported ruby versions --- .github/workflows/rspec_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index 973aec2bf..11db4f646 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby: ['2.7', '3.0', '3.2'] + ruby: ['3.1', '3.2', '3.3'] steps: - uses: actions/checkout@v4 - name: Install Ruby and gems From fe10a45b9aaf2c9adf7edc76b065bc8e79a9d84a Mon Sep 17 00:00:00 2001 From: Shuhei Iriyama Date: Tue, 15 Apr 2025 21:59:40 +0900 Subject: [PATCH 132/158] remove :cancel_url from required params (#927) --- lib/stripe_mock/data.rb | 2 +- lib/stripe_mock/request_handlers/checkout_session.rb | 4 +--- lib/stripe_mock/test_strategies/base.rb | 1 - spec/shared_stripe_examples/checkout_session_examples.rb | 5 ----- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 2007b4a84..e96a34a89 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -1440,7 +1440,7 @@ def self.mock_checkout_session(params = {}) id: cs_id, object: 'checkout.session', billing_address_collection: nil, - cancel_url: 'https://example.com/cancel', + cancel_url: nil, client_reference_id: nil, customer: nil, customer_email: nil, diff --git a/lib/stripe_mock/request_handlers/checkout_session.rb b/lib/stripe_mock/request_handlers/checkout_session.rb index 58b388dfc..26bd24af3 100644 --- a/lib/stripe_mock/request_handlers/checkout_session.rb +++ b/lib/stripe_mock/request_handlers/checkout_session.rb @@ -12,9 +12,7 @@ def Session.included(klass) def new_session(route, method_url, params, headers) id = params[:id] || new_id('cs') - [:cancel_url, :success_url].each do |p| - require_param(p) if params[p].nil? || params[p].empty? - end + require_param(:success_url) if params[:success_url].nil? || params[:success_url].empty? line_items = nil if params[:line_items] diff --git a/lib/stripe_mock/test_strategies/base.rb b/lib/stripe_mock/test_strategies/base.rb index 09a8f85fc..31963209f 100644 --- a/lib/stripe_mock/test_strategies/base.rb +++ b/lib/stripe_mock/test_strategies/base.rb @@ -121,7 +121,6 @@ def create_checkout_session_params(params = {}) amount: 500, currency: 'usd', }], - cancel_url: "https://example.com/cancel", success_url: "https://example.com/success", }.merge(params) end diff --git a/spec/shared_stripe_examples/checkout_session_examples.rb b/spec/shared_stripe_examples/checkout_session_examples.rb index f0efab77e..f8bd0cabc 100644 --- a/spec/shared_stripe_examples/checkout_session_examples.rb +++ b/spec/shared_stripe_examples/checkout_session_examples.rb @@ -11,7 +11,6 @@ session = Stripe::Checkout::Session.create( payment_method_types: ["card"], line_items: line_items, - cancel_url: "https://example.com/cancel", success_url: "https://example.com/success" ) @@ -28,7 +27,6 @@ session = Stripe::Checkout::Session.create( customer: "customer_id", success_url: "localhost/nada", - cancel_url: "localhost/nada", payment_method_types: ["card"], ) end.to raise_error(Stripe::InvalidRequestError, /line_items/i) @@ -40,7 +38,6 @@ session = Stripe::Checkout::Session.create( mode: "setup", payment_method_types: ["card"], - cancel_url: "https://example.com/cancel", success_url: "https://example.com/success" ) @@ -55,7 +52,6 @@ session = Stripe::Checkout::Session.create( customer: "customer_id", success_url: "localhost/nada", - cancel_url: "localhost/nada", payment_method_types: ["card"], mode: "subscription", ) @@ -86,7 +82,6 @@ it "can expand setup_intent" do initial_session = Stripe::Checkout::Session.create( mode: "setup", - cancel_url: "https://example.com", success_url: "https://example.com", payment_method_types: ["card"] ) From 85af4c1994e7ca65777c03fc9970a9f34aecd9f0 Mon Sep 17 00:00:00 2001 From: Lucas Pessoa Date: Tue, 15 Apr 2025 10:05:24 -0300 Subject: [PATCH 133/158] Adds DRB as a dependency in preparation (#920) to Ruby 3.4.0 Co-authored-by: lucas-cp-pessoa --- stripe-ruby-mock.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 4af469fbc..d4a4f2417 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -25,6 +25,7 @@ Gem::Specification.new do |gem| gem.add_dependency 'stripe', '> 5', '< 13' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' + gem.add_dependency 'drb', '>= 2.0.4', '< 3' gem.add_development_dependency 'rspec', '~> 3.13.0' gem.add_development_dependency 'thin', '~> 1.8.1' From 576ee9561ea1d624fc713bdf864912e468c6d8cb Mon Sep 17 00:00:00 2001 From: Alex MAmonchik Date: Thu, 24 Apr 2025 01:12:20 +0200 Subject: [PATCH 134/158] Release v4.1.0 --- CHANGELOG.md | 16 ++++++++++++++++ lib/stripe_mock/version.rb | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d97ded3d..5680b7f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ ### Unreleased +### 4.1.0 (2025-04-24) +- [#920](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/920) Adds DRB as a dependency in preparation to Ruby 3.4.0 [@lucascppessoa](https://github.com/lucascppessoa) +- [#927](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/927) Allow :cancel_url nil in create Checkout Session [@shu-illy](https://github.com/shu-illy) +- [#929](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/929) Actions uses only supported ruby versions [@alexmamonchik](https://github.com/alexmamonchik) +- [#925](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/925) Fix github actions and add missing product attributes [@bettysteger](https://github.com/bettysteger) +- [#928](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/928) Add tax ID endpoints and data [@lfittl](https://github.com/lfittl) +- [#835](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/835) Account: replace deprecated verification hash by requirements [@fabianoarruda](https://github.com/fabianoarruda) +- [#903](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/903) Add require option to require additional file [@stevenharman](https://github.com/stevenharman) +- [#904](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/904) Add has_more to Subscription.items data [@stevenharman](https://github.com/stevenharman) +- [#843](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/843) Some updates around subscriptions [@donnguyen](https://github.com/donnguyen) +- [#917](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/917) Removed charges on PI and added latest_charge [@espen](https://github.com/espen) +- [#916](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/916) Better tests for payout [@espen](https://github.com/espen) +- [#862](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/862) Add simple support for us bank accounts [@smtlaissezfaire](https://github.com/smtlaissezfaire) +- [#907](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/907) Fix calls to use raise instead of throw [@smtlaissezfaire](https://github.com/smtlaissezfaire) +- [#908](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/908) Add update_payout [@espen](https://github.com/espen) + ### 4.0.0 (2024-08-07) - [#905](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/905) Allow Stripe SDK v11 by [@stevenharman ](https://github.com/stevenharman) - [#830](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/830) Implement search API by [@adamstegman](https://github.com/adamstegman) diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index 7c3410b93..5d2816ff4 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "4.0.0" + VERSION = "4.1.0" end From 3dba91d3061c4a7f5a0339c9792760fbb7066f9c Mon Sep 17 00:00:00 2001 From: Gabriel Taylor Russ Date: Sat, 26 Apr 2025 00:05:15 +1000 Subject: [PATCH 135/158] Support Stripe version 13 (#931) * Add support for Stripe version 13 * Support all v13 stripe versions * Add compatibility with new version of Stripe * Trigger Build * Add support for Stripe version 13 - Add Compat module for checking Stripe gem version - Add Appraisals gem for testing - Update Client and Instance code to handle both version 12 and 13 of Stripe - Update specs * Add object key to discount * Refactor compatibility module Make method name more intuitive * Add client_instance method * Update GitHub action to run specs against multiple stripe versions (cherry picked from commit 41cf1637ac158bf69e4ef4cee1399a3a6e7bbb60) * Update gemfile lock Run bundle exec appraisal update (cherry picked from commit 60632829ce0705b301588a8e457af367af97690a) * Add dependency update instructions to README (cherry picked from commit a83642da1c33a194aa5495ab7132e2e105cd095d) --- .github/workflows/rspec_tests.yml | 28 +++++---- Appraisals | 7 +++ Gemfile | 2 + README.md | 4 ++ gemfiles/.bundle/config | 2 + gemfiles/stripe_12.gemfile | 13 ++++ gemfiles/stripe_12.gemfile.lock | 60 +++++++++++++++++++ gemfiles/stripe_13.gemfile | 13 ++++ gemfiles/stripe_13.gemfile.lock | 60 +++++++++++++++++++ lib/stripe_mock.rb | 2 +- lib/stripe_mock/api/client.rb | 16 +++-- lib/stripe_mock/api/instance.rb | 8 ++- lib/stripe_mock/client.rb | 5 +- lib/stripe_mock/compat.rb | 29 +++++++++ lib/stripe_mock/instance.rb | 47 ++++++++++++++- .../helpers/coupon_helpers.rb | 1 + lib/stripe_mock/util.rb | 6 ++ spec/instance_spec.rb | 9 ++- spec/list_spec.rb | 2 +- spec/server_spec.rb | 15 ++++- .../account_examples.rb | 2 +- .../customer_examples.rb | 2 +- .../setup_intent_examples.rb | 6 +- .../subscription_examples.rb | 2 +- .../transfer_examples.rb | 13 +++- spec/spec_helper.rb | 1 + spec/stripe_mock_spec.rb | 48 +++++++++++---- stripe-ruby-mock.gemspec | 2 +- 28 files changed, 351 insertions(+), 54 deletions(-) create mode 100644 Appraisals create mode 100644 gemfiles/.bundle/config create mode 100644 gemfiles/stripe_12.gemfile create mode 100644 gemfiles/stripe_12.gemfile.lock create mode 100644 gemfiles/stripe_13.gemfile create mode 100644 gemfiles/stripe_13.gemfile.lock create mode 100644 lib/stripe_mock/compat.rb diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index 11db4f646..cb7d6a86d 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -9,9 +9,9 @@ name: Tests on: push: - branches: [ "master" ] + branches: ["master"] pull_request: - branches: [ "master" ] + branches: ["master"] permissions: contents: read @@ -20,16 +20,18 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - ruby: ['3.1', '3.2', '3.3'] + ruby: ["3.1", "3.2", "3.3"] + gemfile: ["stripe_12", "stripe_13"] + env: + BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile steps: - - uses: actions/checkout@v4 - - name: Install Ruby and gems - # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, - # change this to (see https://github.com/ruby/setup-ruby#versioning): - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - name: Run tests - run: bundle exec rspec + - uses: actions/checkout@v4 + - name: Set up Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: bundle exec rspec diff --git a/Appraisals b/Appraisals new file mode 100644 index 000000000..690909cd7 --- /dev/null +++ b/Appraisals @@ -0,0 +1,7 @@ +appraise "stripe-13" do + gem "stripe", "13.4.0" +end + +appraise "stripe-12" do + gem "stripe", "12.6.0" +end diff --git a/Gemfile b/Gemfile index e04240a41..81ed427ba 100644 --- a/Gemfile +++ b/Gemfile @@ -6,3 +6,5 @@ group :test do end gemspec + +gem 'appraisal' diff --git a/README.md b/README.md index 706f0cf88..7511bd82f 100644 --- a/README.md +++ b/README.md @@ -408,6 +408,10 @@ This will help ensure that the problem remains fixed in future updates. Note: You may need to `ulimit -n 4048` before running the test suite to get all tests to pass. +### Dependency updates + +When modifications are made to dependencies in addition to the changes to the Gemfile, Gemfile.lock and stripe-ruby-mock.gemspec you must also run `bundle exec appraisal update` to update the gemfiles specific to Stripe version 12 and Stripe version 13. + ## Copyright Copyright (c) 2013 Gilbert diff --git a/gemfiles/.bundle/config b/gemfiles/.bundle/config new file mode 100644 index 000000000..c127f8025 --- /dev/null +++ b/gemfiles/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_RETRY: "1" diff --git a/gemfiles/stripe_12.gemfile b/gemfiles/stripe_12.gemfile new file mode 100644 index 000000000..ad662a076 --- /dev/null +++ b/gemfiles/stripe_12.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "12.6.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_12.gemfile.lock b/gemfiles/stripe_12.gemfile.lock new file mode 100644 index 000000000..88b5215e0 --- /dev/null +++ b/gemfiles/stripe_12.gemfile.lock @@ -0,0 +1,60 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (4.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.1) + dotenv (2.8.1) + drb (2.2.1) + eventmachine (1.2.7) + multi_json (1.15.0) + rack (2.2.13) + rake (13.2.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + stripe (12.6.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.3.2) + +PLATFORMS + ruby + x86_64-darwin-22 + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 12.6.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/gemfiles/stripe_13.gemfile b/gemfiles/stripe_13.gemfile new file mode 100644 index 000000000..4ae017c7b --- /dev/null +++ b/gemfiles/stripe_13.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "13.4.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_13.gemfile.lock b/gemfiles/stripe_13.gemfile.lock new file mode 100644 index 000000000..8c3e4d4f1 --- /dev/null +++ b/gemfiles/stripe_13.gemfile.lock @@ -0,0 +1,60 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (4.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.1) + dotenv (2.8.1) + drb (2.2.1) + eventmachine (1.2.7) + multi_json (1.15.0) + rack (2.2.13) + rake (13.2.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + stripe (13.4.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.3.2) + +PLATFORMS + ruby + x86_64-darwin-22 + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 13.4.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index f8794d4d3..f3f6c35dd 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -8,7 +8,7 @@ require 'stripe_mock/version' require 'stripe_mock/util' require 'stripe_mock/error_queue' - +require 'stripe_mock/compat' require 'stripe_mock/data' require 'stripe_mock/data/list' diff --git a/lib/stripe_mock/api/client.rb b/lib/stripe_mock/api/client.rb index 9227425eb..bca75c8a4 100644 --- a/lib/stripe_mock/api/client.rb +++ b/lib/stripe_mock/api/client.rb @@ -8,7 +8,9 @@ def self.start_client(port=4999) return false if @state == 'live' return @client unless @client.nil? - Stripe::StripeClient.send(:define_method, :execute_request) { |*args, **keyword_args| StripeMock.redirect_to_mock_server(*args, **keyword_args) } + Compat.client.send(:define_method, Compat.method) do |*args, **keyword_args| + StripeMock.redirect_to_mock_server(*args, **keyword_args) + end @client = StripeMock::Client.new(port) @state = 'remote' @client @@ -27,7 +29,14 @@ def self.stop_client(opts={}) private - def self.redirect_to_mock_server(method, url, api_key: nil, api_base: nil, usage: [], params: {}, headers: {}) + def self.redirect_to_mock_server(*args, **kwargs) + if args.length == 2 && kwargs.key?(:api_key) # Legacy signature + method, url = args + elsif args.length == 6 # New signature + method, url, _base_address, _params, _opts, _usage = args + else + raise ArgumentError, "Invalid arguments for mock_request" + end handler = Instance.handler_for_method_url("#{method} #{url}") if mock_error = client.error_queue.error_for_handler_name(handler[:name]) @@ -35,7 +44,6 @@ def self.redirect_to_mock_server(method, url, api_key: nil, api_base: nil, usage raise mock_error end - Stripe::Util.symbolize_names client.mock_request(method, url, api_key: api_key, params: params, headers: headers) + Stripe::Util.symbolize_names client.mock_request(*args, **kwargs) end - end diff --git a/lib/stripe_mock/api/instance.rb b/lib/stripe_mock/api/instance.rb index 65ed2c7f4..2c0c464f3 100644 --- a/lib/stripe_mock/api/instance.rb +++ b/lib/stripe_mock/api/instance.rb @@ -2,12 +2,14 @@ module StripeMock @state = 'ready' @instance = nil - @original_execute_request_method = Stripe::StripeClient.instance_method(:execute_request) + @original_execute_request_method = Compat.client.instance_method(Compat.method) def self.start return false if @state == 'live' @instance = instance = Instance.new - Stripe::StripeClient.send(:define_method, :execute_request) { |*args, **keyword_args| instance.mock_request(*args, **keyword_args) } + Compat.client.send(:define_method, Compat.method) { |*args, **keyword_args| + instance.mock_request(*args, **keyword_args) + } @state = 'local' end @@ -29,7 +31,7 @@ def self.mock(&block) end def self.restore_stripe_execute_request_method - Stripe::StripeClient.send(:define_method, :execute_request, @original_execute_request_method) + Compat.client.send(:define_method, Compat.method, @original_execute_request_method) end def self.instance; @instance; end diff --git a/lib/stripe_mock/client.rb b/lib/stripe_mock/client.rb index 615fcdd04..7146096f8 100644 --- a/lib/stripe_mock/client.rb +++ b/lib/stripe_mock/client.rb @@ -13,9 +13,9 @@ def initialize(port) @state = 'ready' end - def mock_request(method, url, api_key: nil, params: {}, headers: {}) + def mock_request(*args, **kwargs) timeout_wrap do - @pipe.mock_request(method, url, api_key: api_key, params: params, headers: headers).tap {|result| + @pipe.mock_request(*args, **kwargs).tap {|result| response, api_key = result if response.is_a?(Hash) && response[:error_raised] == 'invalid_request' args, keyword_args = response[:error_params].first(2), response[:error_params].last @@ -124,5 +124,4 @@ def timeout_wrap(tries=1) end end end - end diff --git a/lib/stripe_mock/compat.rb b/lib/stripe_mock/compat.rb new file mode 100644 index 000000000..d03ce57a7 --- /dev/null +++ b/lib/stripe_mock/compat.rb @@ -0,0 +1,29 @@ +module StripeMock + module Compat + def self.stripe_gte_13? + Gem::Version.new(Stripe::VERSION) >= Gem::Version.new('13.0.0') + end + + def self.method + return :execute_request unless stripe_gte_13? + + :execute_request_internal + end + + def self.client + return Stripe::StripeClient unless stripe_gte_13? + + Stripe::APIRequestor + end + + def self.client_instance + @client ||= client.new + end + + def self.active_client + return client.active_client unless stripe_gte_13? + + client.active_requestor + end + end +end diff --git a/lib/stripe_mock/instance.rb b/lib/stripe_mock/instance.rb index 76c03e56b..e4ff006aa 100644 --- a/lib/stripe_mock/instance.rb +++ b/lib/stripe_mock/instance.rb @@ -111,7 +111,23 @@ def initialize @base_strategy = TestStrategies::Base.new end - def mock_request(method, url, api_key: nil, api_base: nil, usage: [], params: {}, headers: {}) + def mock_request(*args, **kwargs) + if args.length == 2 && kwargs.key?(:api_key) # Legacy signature + method, url = args + api_key = kwargs[:api_key] + params = kwargs[:params] || {} + headers = kwargs[:headers] || {} + elsif args.length == 6 # New signature + method, url, base_address, params, opts, usage = args + client = Compat.client_instance + config = client.send(:config) + opts = Stripe::RequestOptions.merge_config_and_opts(config, opts) + headers = client.send(:request_headers, method, Stripe::Util.get_api_mode(url), opts) + headers = headers.transform_keys { |key| Util.snake_case(key).to_sym } + else + raise ArgumentError, "Invalid arguments for mock_request" + end + return {} if method == :xtest api_key ||= (Stripe.api_key || DUMMY_API_KEY) @@ -134,12 +150,21 @@ def mock_request(method, url, api_key: nil, api_base: nil, usage: [], params: {} else res = self.send(handler[:name], handler[:route], method_url, params, headers) puts " [res] #{res}" if @debug == true - [to_faraday_hash(res), api_key] + + if Compat.stripe_gte_13? + [to_net_http(res, headers), opts] + else + [to_faraday_hash(res), api_key] + end end else puts "[StripeMock] Warning : Unrecognized endpoint + method : [#{method} #{url}]" puts "[StripeMock] params: #{params}" unless params.empty? - [{}, api_key] + if Compat.stripe_gte_13? + [{}, opts] + else + [{}, api_key] + end end end @@ -224,6 +249,22 @@ def to_faraday_hash(hash) response.new(hash) end + def to_net_http(hash, headers) + headers = headers.slice(*%w[request-id stripe-account-id stripe-version stripe-version-stabl idempotency-key content-type]) + fake_net_http_response(code: "200", message: "OK", body: hash.to_json, headers: headers) + end + + def fake_net_http_response(code: "200", message: "OK", body: "{}", headers: {}) + response_class = Net::HTTPResponse::CODE_TO_OBJ[code] + response = response_class.new("1.1", code, message) + headers.each { |k, v| response[k] = v } + response.instance_variable_set(:@body, body) + response.instance_variable_set(:@read, true) + + response + end + + def calculate_fees(params) application_fee = params[:application_fee] || 0 params[:fee] = processing_fee(params[:amount]) + application_fee diff --git a/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb b/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb index e8118057c..85682ca41 100644 --- a/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb @@ -3,6 +3,7 @@ module RequestHandlers module Helpers def add_coupon_to_object(object, coupon) discount_attrs = {}.tap do |attrs| + attrs[:object] = "discount" attrs[object[:object]] = object[:id] attrs[:coupon] = coupon attrs[:start] = Time.now.to_i diff --git a/lib/stripe_mock/util.rb b/lib/stripe_mock/util.rb index e7762184c..9427e92a5 100644 --- a/lib/stripe_mock/util.rb +++ b/lib/stripe_mock/util.rb @@ -40,5 +40,11 @@ def self.card_merge(old_param, new_param) old_param.merge(new_param) end + def self.snake_case(str) + str.gsub(/([a-z])([A-Z])/, '\1_\2') # Convert camelCase to snake_case + .gsub(/[-]/, '_') # Convert kebab-case to snake_case + .downcase # Ensure all characters are lowercase + end + end end diff --git a/spec/instance_spec.rb b/spec/instance_spec.rb index d7bed5b0e..27651455f 100644 --- a/spec/instance_spec.rb +++ b/spec/instance_spec.rb @@ -20,8 +20,13 @@ def test_data_source(type) "type" => "service" ) res, api_key = StripeMock.instance.mock_request('post', '/v1/products', api_key: 'api_key', params: symbol_params) - expect(res.data[:name]).to eq('Symbol Product') - expect(res.data[:type]).to eq('service') + if StripeMock::Compat.stripe_gte_13? + expect(JSON.parse(res.body)["name"]).to eq('Symbol Product') + expect(JSON.parse(res.body)["type"]).to eq('service') + else + expect(res.data[:name]).to eq('Symbol Product') + expect(res.data[:type]).to eq('service') + end end it "exits gracefully on an unrecognized handler url" do diff --git a/spec/list_spec.rb b/spec/list_spec.rb index af83fdcba..0064abd6e 100644 --- a/spec/list_spec.rb +++ b/spec/list_spec.rb @@ -149,7 +149,7 @@ 255.times { data << Stripe::Charge.create(amount: 1, currency: 'usd', source: stripe_helper.generate_card_token) } list = StripeMock::Data::List.new(data, starting_after: "test_ch_unknown") - expect { list.to_h }.to raise_error + expect { list.to_h }.to raise_error(RuntimeError, "No such object id: test_ch_unknown") end end diff --git a/spec/server_spec.rb b/spec/server_spec.rb index f0ad7d631..8e83320c4 100644 --- a/spec/server_spec.rb +++ b/spec/server_spec.rb @@ -54,11 +54,20 @@ def test_data_source(type); StripeMock.client.get_server_data(type); end it "returns a response with symbolized hash keys" do - stripe_helper.create_plan(id: 'x', product: product.id) - response, api_key = StripeMock.redirect_to_mock_server('get', '/v1/plans/x', api_key: 'xxx') - response.data.keys.each {|k| expect(k).to be_a(Symbol) } + unless StripeMock::Compat.stripe_gte_13? + stripe_helper.create_plan(id: 'x', product: product.id) + response, api_key = StripeMock.redirect_to_mock_server('get', '/v1/plans/x', api_key: 'xxx') + response.data.keys.each {|k| expect(k).to be_a(Symbol) } + end end + it "returns a Net::HttpResponse" do + if StripeMock::Compat.stripe_gte_13? + stripe_helper.create_plan(id: 'x', product: product.id) + response, api_key = StripeMock.redirect_to_mock_server('get', '/v1/plans/x', 'https://api.stripe.com', {}, {}, 'normal') + expect(response).to be_a(Net::HTTPResponse) + end + end it "can toggle debug" do StripeMock.toggle_debug(true) diff --git a/spec/shared_stripe_examples/account_examples.rb b/spec/shared_stripe_examples/account_examples.rb index 596602d21..fd109d844 100644 --- a/spec/shared_stripe_examples/account_examples.rb +++ b/spec/shared_stripe_examples/account_examples.rb @@ -55,7 +55,7 @@ account.tos_acceptance.date = nil expect { account.save - }.to raise_error + }.to raise_error(Stripe::InvalidRequestError, /Invalid integer/) end context 'with tos acceptance date' do diff --git a/spec/shared_stripe_examples/customer_examples.rb b/spec/shared_stripe_examples/customer_examples.rb index d564ad22f..9a8126d5c 100644 --- a/spec/shared_stripe_examples/customer_examples.rb +++ b/spec/shared_stripe_examples/customer_examples.rb @@ -24,7 +24,7 @@ def gen_card_tk expect(customer.default_source).to_not be_nil expect(customer.default_source).to eq customer.sources.data.first.id - expect { customer.source }.to raise_error + expect { customer.source }.to raise_error(NoMethodError) end it "creates a stripe customer with a default payment method" do diff --git a/spec/shared_stripe_examples/setup_intent_examples.rb b/spec/shared_stripe_examples/setup_intent_examples.rb index 10cbdc608..81c8f3f42 100644 --- a/spec/shared_stripe_examples/setup_intent_examples.rb +++ b/spec/shared_stripe_examples/setup_intent_examples.rb @@ -80,6 +80,10 @@ updated = Stripe::SetupIntent.retrieve(original.id) - expect(updated.metadata[:foo]).to eq(:bar) + if StripeMock::Compat.stripe_gte_13? + expect(updated.metadata[:foo]).to eq("bar") + else + expect(updated.metadata[:foo]).to eq(:bar) + end end end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index f72d8a400..216982b4e 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -203,7 +203,7 @@ expect { Stripe::Subscription.create(plan: plan.id, customer: customer.id, promotion_code: promotion_code.id) - }.not_to raise_error(Stripe::InvalidRequestError) + }.not_to raise_error end it "does not permit both coupon and promotion code" do diff --git a/spec/shared_stripe_examples/transfer_examples.rb b/spec/shared_stripe_examples/transfer_examples.rb index 444d33d51..eddbe9e63 100644 --- a/spec/shared_stripe_examples/transfer_examples.rb +++ b/spec/shared_stripe_examples/transfer_examples.rb @@ -98,10 +98,17 @@ end it "cancels a stripe transfer" do - original = Stripe::Transfer.create(amount: "100", currency: "usd") - res, api_key = Stripe::StripeClient.active_client.execute_request(:post, "/v1/transfers/#{original.id}/cancel", api_key: 'api_key') + if StripeMock::Compat.stripe_gte_13? + original = Stripe::Transfer.create(amount: "100", currency: "usd") + res, api_key = Stripe::APIRequestor.active_requestor.execute_request(:post, "/v1/transfers/#{original.id}/cancel", :api) - expect(res.data[:status]).to eq("canceled") + expect(res.status).to eq("canceled") + else + original = Stripe::Transfer.create(amount: "100", currency: "usd") + res, api_key = Stripe::StripeClient.active_client.execute_request(:post, "/v1/transfers/#{original.id}/cancel", api_key: 'api_key') + + expect(res.data[:status]).to eq("canceled") + end end it "cannot retrieve a transfer that doesn't exist" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9c5a17b9f..55d11847a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,7 @@ require 'stripe' require 'stripe_mock' require 'stripe_mock/server' +require 'stripe_mock/compat' require 'dotenv' Dotenv.load('.env') diff --git a/spec/stripe_mock_spec.rb b/spec/stripe_mock_spec.rb index 0cc26258c..965f1fbec 100644 --- a/spec/stripe_mock_spec.rb +++ b/spec/stripe_mock_spec.rb @@ -4,28 +4,50 @@ it "overrides stripe's request method" do StripeMock.start - Stripe::StripeClient.active_client.execute_request(:xtest, '/', api_key: 'abcde') # no error + if StripeMock::Compat.stripe_gte_13? + StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', :api, {}, {}, []) # no error + else + StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', api_key: 'abcde') # no error + end StripeMock.stop end it "overrides stripe's execute_request method in other threads" do StripeMock.start - Thread.new { Stripe::StripeClient.active_client.execute_request(:xtest, '/', api_key: 'abcde') }.join # no error + if StripeMock::Compat.stripe_gte_13? + Thread.new { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', :api, {}, {}, []) }.join # no error + else + Thread.new { StripeMock::Compat.active_client.send(StripeMock::Compat.method,:xtest, '/', api_key: 'abcde') }.join # no error + end StripeMock.stop end it "reverts overriding stripe's request method" do - StripeMock.start - Stripe::StripeClient.active_client.execute_request(:xtest, '/', api_key: 'abcde') # no error - StripeMock.stop - expect { Stripe::StripeClient.active_client.execute_request(:x, '/', api_key: 'abcde') }.to raise_error Stripe::APIError + if StripeMock::Compat.stripe_gte_13? + StripeMock.start + StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', :api, {}, {}, []) # no error + StripeMock.stop + expect { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :x, '/', :api, {}, {}, []) }.to raise_error Stripe::APIError + else + StripeMock.start + StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', api_key: 'abcde') # no error + StripeMock.stop + expect { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :x, '/', api_key: 'abcde') }.to raise_error Stripe::APIError + end end it "reverts overriding stripe's execute_request method in other threads" do - StripeMock.start - Thread.new { Stripe::StripeClient.active_client.execute_request(:xtest, '/', api_key: 'abcde') }.join # no error - StripeMock.stop - expect { Thread.new { Stripe::StripeClient.active_client.execute_request(:x, '/', api_key: 'abcde') }.join }.to raise_error Stripe::APIError + if StripeMock::Compat.stripe_gte_13? + StripeMock.start + Thread.new { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', :api, {}, {}, []) }.join # no error + StripeMock.stop + expect { Thread.new { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :x, '/', :api, {}, {}, []) }.join }.to raise_error Stripe::APIError + else + StripeMock.start + Thread.new { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :xtest, '/', api_key: 'abcde') }.join # no error + StripeMock.stop + expect { Thread.new { StripeMock::Compat.active_client.send(StripeMock::Compat.method, :x, '/', api_key: 'abcde') }.join }.to raise_error Stripe::APIError + end end it "does not persist data between mock sessions" do @@ -79,11 +101,11 @@ it "cannot be toggled when already started" do StripeMock.start - expect { StripeMock.toggle_live(true) }.to raise_error + expect { StripeMock.toggle_live(true) }.to raise_error(RuntimeError, "You cannot toggle StripeMock live when it has already started.") StripeMock.stop StripeMock.instance_variable_set(:@state, 'remote') - expect { StripeMock.toggle_live(true) }.to raise_error + expect { StripeMock.toggle_live(true) }.to raise_error(RuntimeError, "You cannot toggle StripeMock live when it has already started.") end end @@ -104,7 +126,7 @@ end it "throws an error on an unknown strategy" do - expect { StripeMock.create_test_helper(:lol) }.to raise_error + expect { StripeMock.create_test_helper(:lol) }.to raise_error(RuntimeError, "Invalid test helper strategy: :lol") end it "can configure the default strategy" do diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index d4a4f2417..b212ec912 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'stripe', '> 5', '< 13' + gem.add_dependency 'stripe', '> 5', '< 14' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' gem.add_dependency 'drb', '>= 2.0.4', '< 3' From 61246b5ec010c3d1902bc213db42008fb59f5465 Mon Sep 17 00:00:00 2001 From: Alexander Mamonchik Date: Fri, 25 Apr 2025 16:44:25 +0200 Subject: [PATCH 136/158] Forced run bundle exec in Github Actions (#935) * Disabled cack for first run after merge #931 * force execute bundle install --- .github/workflows/rspec_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index cb7d6a86d..816fda8a8 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -32,6 +32,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Install dependencies + run: bundle install - name: Run tests run: bundle exec rspec From fb7a7434ac700bc370c9c642708acb1350e7c708 Mon Sep 17 00:00:00 2001 From: Allan Wazacz Date: Sat, 26 Apr 2025 14:35:34 +0100 Subject: [PATCH 137/158] Add capabilities to mock account data (#919) --- lib/stripe_mock/data.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index e96a34a89..8b6f07154 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -11,6 +11,14 @@ def self.mock_account(params = {}) display_name: "Stripe.com", timezone: "US/Pacific", details_submitted: false, + capabilities: { + bank_transfer_payments: "active", + card_payments: "active", + ideal_payments: "active", + sepa_bank_transfer_payments: "active", + sepa_debit_payments:"active", + transfers:"active" + }, charges_enabled: false, payouts_enabled: false, currencies_supported: [ From ef2d45322c3c728f86297cce99d95e29b4965587 Mon Sep 17 00:00:00 2001 From: Alex MAmonchik Date: Sat, 26 Apr 2025 22:48:14 +0200 Subject: [PATCH 138/158] Release 5.0.0 --- CHANGELOG.md | 4 ++++ README.md | 8 +++++--- lib/stripe_mock/version.rb | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5680b7f5d..c53a78fbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ### Unreleased +### 5.0.0 +- [#919](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/919) Add capabilities to mock account data [@cazwazacz](https://github.com/cazwazacz) +- [#931](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/931) Support Stripe version 13 + 12 [@gabrieltaylor](https://github.com/gabrieltaylor) + ### 4.1.0 (2025-04-24) - [#920](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/920) Adds DRB as a dependency in preparation to Ruby 3.4.0 [@lucascppessoa](https://github.com/lucascppessoa) - [#927](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/927) Allow :cancel_url nil in create Checkout Session [@shu-illy](https://github.com/shu-illy) diff --git a/README.md b/README.md index 7511bd82f..c6d53c695 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ In your gemfile: We have [changelog](https://github.com/stripe-ruby-mock/stripe-ruby-mock/blob/master/CHANGELOG.md). It's first attempt. Feel free to update it and suggest to a new format of it. +version `5.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/931) - we support stripe > 5 and <= 13 for now and try to follow the newest API version. + version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/stripe-ruby-mock/pull/658) - we support stripe > 5 and < 6 for now and try to follow the newest API version. But if you still use older versions please [read](https://github.com/stripe-ruby-mock/stripe-ruby-mock#specifications). ## Features @@ -29,12 +31,12 @@ version `3.0.0` has [breaking changes](https://github.com/stripe-ruby-mock/strip ### Requirements -* ruby >= 2.7.0 -* stripe > 5 & < 11 +* ruby >= 3.0.0 +* stripe > 5 & <= 13 ### Specifications -**STRIPE API TARGET VERSION:** 2019-08-20 (master) - we try, but some features are not implemented yet. +**STRIPE API TARGET VERSION:** 2024-06-20 (master) - we try, but some features are not implemented yet. Older API version branches: diff --git a/lib/stripe_mock/version.rb b/lib/stripe_mock/version.rb index 5d2816ff4..78a52efd9 100644 --- a/lib/stripe_mock/version.rb +++ b/lib/stripe_mock/version.rb @@ -1,4 +1,4 @@ module StripeMock # stripe-ruby-mock version - VERSION = "4.1.0" + VERSION = "5.0.0" end From de626e813d5e140aaa674312265928220d151530 Mon Sep 17 00:00:00 2001 From: Jens Balvig Date: Wed, 7 May 2025 16:51:47 +0900 Subject: [PATCH 139/158] Add support for setting pause_collection (#936) --- .../request_handlers/subscriptions.rb | 4 +++ .../subscription_examples.rb | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 80bf97e3b..aae0f01af 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -287,6 +287,10 @@ def update_subscription(route, method_url, params, headers) end end + if params[:pause_collection] + subscription[:pause_collection] = { resumes_at: nil }.merge(params[:pause_collection]) + end + if params[:trial_period_days] subscription[:status] = 'trialing' end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 216982b4e..522df820d 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1170,7 +1170,35 @@ expect(sub.billing_cycle_anchor).to be_a(Integer) end + it "accepts pause_collection with an explicit resumes_at set" do + customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) + subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + resumes_at = Time.now.utc.to_i + 3600 + + subscription.pause_collection = { + behavior: 'mark_uncollectible', + resumes_at: resumes_at + } + + subscription.save + + expect(subscription.pause_collection.behavior).to eq('mark_uncollectible') + expect(subscription.pause_collection.resumes_at).to eq(resumes_at) + end + it "accepts pause_collection without an explicit resumes_at set" do + customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) + subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + + subscription.pause_collection = { + behavior: 'mark_uncollectible' + } + + subscription.save + + expect(subscription.pause_collection.behavior).to eq('mark_uncollectible') + expect(subscription.pause_collection.resumes_at).to be_nil + end end context "cancelling a subscription" do From 37e76a219b258822641aaeaf9f8924e7529f1a00 Mon Sep 17 00:00:00 2001 From: Jens Balvig Date: Thu, 29 May 2025 18:14:57 +0900 Subject: [PATCH 140/158] Add support for setting cancellation_details (#937) ## What Makes it possible to set cancellation_details when marking a subscription for cancellation https://docs.stripe.com/api/subscriptions/update#update_subscription-cancellation_details --- lib/stripe_mock/data.rb | 5 +++ .../helpers/subscription_helpers.rb | 11 ++++++ .../subscription_examples.rb | 39 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 8b6f07154..0b123042c 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -420,6 +420,11 @@ def self.mock_subscription(params={}) cancel_at_period_end: false, canceled_at: nil, collection_method: 'charge_automatically', + cancellation_details: { + comment: nil, + feedback: nil, + reason: nil + }, ended_at: nil, start_date: 1308595038, object: 'subscription', diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 8b8e4864e..520592718 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -46,6 +46,17 @@ def custom_subscription_params(plans, cus, options = {}) params.merge!(cancel_at_period_end: false, canceled_at: nil) end + options.fetch(:cancellation_details, {}).each do |key, value| + next if value.nil? + + if params[:cancel_at_period_end] + params[:cancellation_details] ||= {} + params[:cancellation_details][key] = value + else + raise Stripe::InvalidRequestError.new("`cancellation_details` can only be set on subscriptions that are set to cancel.", "cancellation_details", http_status: 400) + end + end + # TODO: Implement coupon logic if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now" diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 522df820d..213aa26f0 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -1220,6 +1220,41 @@ expect(customer.subscriptions.count).to eq(0) expect(customer.subscriptions.data.length).to eq(0) end + + it "supports adding a comment to cancellation_details" do + customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) + subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + + result = Stripe::Subscription.update( + subscription.id, + cancel_at_period_end: true, + cancellation_details: { comment: 'Cancelled by user' } + ) + + expect(result.cancellation_details.comment).to eq('Cancelled by user') + end + + it "supports adding feedback to cancellation_details" do + customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) + subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + + result = Stripe::Subscription.update( + subscription.id, + cancel_at_period_end: true, + cancellation_details: { feedback: 'customer_service' } + ) + + expect(result.cancellation_details.feedback).to eq('customer_service') + end + + it "raises an error if adding a comment to cancellation_details when not cancelling" do + customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) + subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) + + expect do + Stripe::Subscription.update(subscription.id, cancellation_details: { comment: 'Cancelled by user' }) + end.to raise_error Stripe::InvalidRequestError, /can only be set on subscriptions that are set to cancel./ + end end it "supports 'cancelling' by updating cancel_at_period_end" do @@ -1328,6 +1363,10 @@ it "has a start_date attribute" do expect(subscription).to respond_to(:start_date) end + + it "has cancellation_details" do + expect(subscription).to respond_to(:cancellation_details) + end end context "retrieve multiple subscriptions" do From c495f587fde78f59b630e534152dd0fd9f12183f Mon Sep 17 00:00:00 2001 From: Jens Balvig Date: Fri, 27 Jun 2025 20:41:27 +0900 Subject: [PATCH 141/158] Add missing schedule field to subscription object (#939) --- lib/stripe_mock/data.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/stripe_mock/data.rb b/lib/stripe_mock/data.rb index 0b123042c..551a3fb46 100644 --- a/lib/stripe_mock/data.rb +++ b/lib/stripe_mock/data.rb @@ -426,6 +426,7 @@ def self.mock_subscription(params={}) reason: nil }, ended_at: nil, + schedule: nil, start_date: 1308595038, object: 'subscription', trial_start: 1308595038, From 25e194e1306d180662208a8890f184d8681f51b4 Mon Sep 17 00:00:00 2001 From: Kenny <114894332+strawhatduckk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:04:59 +0800 Subject: [PATCH 142/158] Add Invoice.create_preview endpoint with tests (#947) --- lib/stripe_mock/request_handlers/invoices.rb | 94 ++++++++++++ .../invoice_examples.rb | 135 ++++++++++++++++++ 2 files changed, 229 insertions(+) diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index 069dbb865..e1c72b894 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -5,6 +5,7 @@ module Invoices def Invoices.included(klass) klass.add_handler 'post /v1/invoices', :new_invoice klass.add_handler 'get /v1/invoices/upcoming', :upcoming_invoice + klass.add_handler 'post /v1/invoices/create_preview', :create_preview_invoice klass.add_handler 'get /v1/invoices/(.*)/lines', :get_invoice_line_items klass.add_handler 'get /v1/invoices/((?!search).*)', :get_invoice klass.add_handler 'get /v1/invoices/search', :search_invoices @@ -170,6 +171,99 @@ def upcoming_invoice(route, method_url, params, headers = {}) next_payment_attempt: preview_subscription[:current_period_end] + 3600 ) end + def create_preview_invoice(route, method_url, params, headers = {}) + stripe_account = headers && headers[:stripe_account] || Stripe.api_key + route =~ method_url + + # Validate required parameters + raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, http_status: 400) if params[:customer].nil? + + customer = customers[stripe_account][params[:customer]] + assert_existence :customer, params[:customer], customer + + # If subscription is provided, use it as base for preview + subscription = nil + if params[:subscription] + subscription = customer[:subscriptions][:data].select{|s|s[:id] == params[:subscription]}.first + raise Stripe::InvalidRequestError.new("No such subscription: #{params[:subscription]}", nil, http_status: 404) unless subscription + elsif customer[:subscriptions][:data].length > 0 + subscription = customer[:subscriptions][:data].min_by { |sub| sub[:current_period_end] } + end + + invoice_lines = [] + invoice_date = Time.now.to_i + preview_subscription = subscription + + # Handle subscription items if provided + if params[:subscription_items] && subscription + # Calculate proration if needed + if params[:subscription_proration_date] + subscription_proration_date = params[:subscription_proration_date] + + if !((subscription[:current_period_start]..subscription[:current_period_end]) === subscription_proration_date) + raise Stripe::InvalidRequestError.new('Cannot specify proration date outside of current subscription period', nil, http_status: 400) + end + + # Add proration line items + plan_amount = subscription[:plan][:amount] || subscription[:plan][:unit_amount] + unused_amount = ( + plan_amount.to_f * + subscription[:quantity] * + (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start]) + ).ceil + + invoice_lines << Data.mock_line_item( + id: new_id('ii'), + amount: -unused_amount, + description: 'Unused time', + plan: subscription[:plan], + period: { + start: subscription_proration_date.to_i, + end: subscription[:current_period_end] + }, + quantity: subscription[:quantity], + proration: true + ) + end + end + + # Add subscription line if subscription exists + if preview_subscription + subscription_line = get_mock_subscription_line_item(preview_subscription) + invoice_lines << subscription_line + end + + # Add invoice items if provided + if params[:invoice_items] + params[:invoice_items].each do |item| + invoice_lines << Data.mock_line_item( + id: new_id('ii'), + amount: item[:amount] || 0, + description: item[:description] || 'Invoice item', + quantity: item[:quantity] || 1 + ) + end + end + + # If no line items were added, add a default one + if invoice_lines.empty? + invoice_lines << Data.mock_line_item() + end + + # Build preview invoice + Data.mock_invoice(invoice_lines, + id: new_id('in'), + customer: customer[:id], + discount: customer[:discount], + created: invoice_date, + starting_balance: customer[:account_balance], + subscription: preview_subscription ? preview_subscription[:id] : nil, + period_start: preview_subscription ? preview_subscription[:current_period_start] : invoice_date, + period_end: preview_subscription ? preview_subscription[:current_period_end] : invoice_date, + next_payment_attempt: preview_subscription ? preview_subscription[:current_period_end] + 3600 : invoice_date + 3600 + ) + end + private def get_mock_subscription_line_item(subscription) diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index 532793346..afab6c43f 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -629,4 +629,139 @@ end end + + context "creating a preview invoice" do + let(:customer) { Stripe::Customer.create(source: stripe_helper.generate_card_token) } + let(:product) { stripe_helper.create_product(id: "prod_preview") } + let(:plan) { stripe_helper.create_plan(id: 'preview_plan', product: product.id, amount: 50_00, interval: 'month', currency: 'usd') } + + before(with_customer: true) { customer } + before(with_plan: true) { plan } + + describe 'parameter validation' do + it 'fails without required customer parameter' do + expect { Stripe::Invoice.create_preview() }.to raise_error do |e| + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.http_status).to eq(400) + expect(e.message).to eq('Missing required param: customer') + end + end + + it 'fails with invalid customer' do + expect { Stripe::Invoice.create_preview(customer: 'nonexistent') }.to raise_error do |e| + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.message).to eq('No such customer: nonexistent') + end + end + end + + describe 'basic preview creation' do + it 'creates a preview invoice for a customer without subscription', with_customer: true do + preview = Stripe::Invoice.create_preview(customer: customer.id) + + expect(preview).to be_a(Stripe::Invoice) + expect(preview.id).to match(/^test_in/) + expect(preview.customer).to eq(customer.id) + expect(preview.lines.data.length).to be > 0 + end + + it 'does not store the preview invoice in memory', with_customer: true do + preview = Stripe::Invoice.create_preview(customer: customer.id) + data = test_data_source(:invoices) + expect(data[preview.id]).to be_nil + end + end + + describe 'with subscription' do + let(:subscription) { Stripe::Subscription.create(plan: plan.id, customer: customer.id) } + + before(with_subscription: true) { subscription } + + it 'creates a preview with existing subscription', with_subscription: true do + preview = Stripe::Invoice.create_preview( + customer: customer.id, + subscription: subscription.id + ) + + expect(preview).to be_a(Stripe::Invoice) + expect(preview.customer).to eq(customer.id) + expect(preview.subscription).to eq(subscription.id) + expect(preview.lines.data.length).to be > 0 + end + + it 'fails with non-existent subscription', with_customer: true do + expect { + Stripe::Invoice.create_preview( + customer: customer.id, + subscription: 'sub_nonexistent' + ) + }.to raise_error do |e| + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.http_status).to eq(404) + expect(e.message).to eq('No such subscription: sub_nonexistent') + end + end + end + + describe 'with invoice items' do + it 'includes custom invoice items in the preview', with_customer: true do + preview = Stripe::Invoice.create_preview( + customer: customer.id, + invoice_items: [ + { amount: 1000, description: 'Custom item 1', quantity: 1 }, + { amount: 2000, description: 'Custom item 2', quantity: 2 } + ] + ) + + expect(preview).to be_a(Stripe::Invoice) + expect(preview.customer).to eq(customer.id) + expect(preview.lines.data.length).to be >= 2 + end + end + + describe 'with proration' do + let(:subscription) { Stripe::Subscription.create(plan: plan.id, customer: customer.id, quantity: 1) } + + before(with_subscription: true) { subscription } + + it 'creates preview with proration date within subscription period', with_subscription: true do + proration_date = Time.now + 5 * 24 * 3600 # 5 days later + + preview = Stripe::Invoice.create_preview( + customer: customer.id, + subscription: subscription.id, + subscription_proration_date: proration_date.to_i, + subscription_items: [ + { plan: plan.id, quantity: 1 } + ] + ) + + expect(preview).to be_a(Stripe::Invoice) + expect(preview.customer).to eq(customer.id) + expect(preview.subscription).to eq(subscription.id) + # Should include proration line items + proration_lines = preview.lines.data.select { |line| line.proration } + expect(proration_lines.length).to be > 0 + end + + it 'fails with proration date outside subscription period', with_subscription: true do + proration_date = subscription.current_period_end + 1000 + + expect { + Stripe::Invoice.create_preview( + customer: customer.id, + subscription: subscription.id, + subscription_proration_date: proration_date, + subscription_items: [ + { plan: plan.id, quantity: 1 } + ] + ) + }.to raise_error do |e| + expect(e).to be_a(Stripe::InvalidRequestError) + expect(e.http_status).to eq(400) + expect(e.message).to eq('Cannot specify proration date outside of current subscription period') + end + end + end + end end From 674a7deb3ea5324988a1eecbe144816738f97572 Mon Sep 17 00:00:00 2001 From: Ryo Nakamura Date: Wed, 5 Nov 2025 19:59:34 +0900 Subject: [PATCH 143/158] Add `lib/stripe-ruby-mock.rb` for `Bundler.require` (#949) --- README.md | 2 +- lib/stripe-ruby-mock.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 lib/stripe-ruby-mock.rb diff --git a/README.md b/README.md index c6d53c695..f0fabcbe8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This gem has unexpectedly grown in popularity and I've gotten pretty busy, so I' In your gemfile: - gem 'stripe-ruby-mock', :require => 'stripe_mock' + gem 'stripe-ruby-mock' ## !!! Important diff --git a/lib/stripe-ruby-mock.rb b/lib/stripe-ruby-mock.rb new file mode 100644 index 000000000..fd600b8ce --- /dev/null +++ b/lib/stripe-ruby-mock.rb @@ -0,0 +1 @@ +require 'stripe_mock' From 5e9ecf2ad55cc3e5fa5d28eca04324a16400461f Mon Sep 17 00:00:00 2001 From: Tessa Nijssen <46453708+tnijssen@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:00:07 +0100 Subject: [PATCH 144/158] Add case-insenstive code filter to PromotionCode list handler (#946) --- .../request_handlers/promotion_codes.rb | 8 +++++++- .../promotion_code_examples.rb | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/promotion_codes.rb b/lib/stripe_mock/request_handlers/promotion_codes.rb index a8ecf11bc..77b4b625e 100644 --- a/lib/stripe_mock/request_handlers/promotion_codes.rb +++ b/lib/stripe_mock/request_handlers/promotion_codes.rb @@ -36,7 +36,13 @@ def get_promotion_code(route, method_url, params, headers) end def list_promotion_code(route, method_url, params, headers) - Data.mock_list_object(promotion_codes.values, params) + promotion_code_data = promotion_codes.values + + if params.key?(:code) + promotion_code_data.select! { |promotion_code| params[:code].downcase == promotion_code[:code].downcase } + end + + Data.mock_list_object(promotion_code_data, params) end end end diff --git a/spec/shared_stripe_examples/promotion_code_examples.rb b/spec/shared_stripe_examples/promotion_code_examples.rb index a3ae3aa70..6216c384f 100644 --- a/spec/shared_stripe_examples/promotion_code_examples.rb +++ b/spec/shared_stripe_examples/promotion_code_examples.rb @@ -65,4 +65,20 @@ expect(all.count).to eq(2) expect(all.map(&:code)).to include("10PERCENT", "20PERCENT") end + + it 'lists promotion codes filtering by case-insensitive code' do + Stripe::PromotionCode.create({ coupon: coupon.id, code: '10PERCENT' }) + Stripe::PromotionCode.create({ coupon: coupon.id, code: '20PERCENT' }) + + all = Stripe::PromotionCode.list + expect(all.count).to eq 2 + + one = Stripe::PromotionCode.list({ code: '10PERCENT' }) + expect(one.count).to eq 1 + expect(one.map(&:code)).to include('10PERCENT') + + two = Stripe::PromotionCode.list({ code: '20percent' }) + expect(two.count).to eq 1 + expect(two.map(&:code)).to include('20PERCENT') + end end From f2c8b7eed46f63195fdc7c6e4ee628e8a2aab7f3 Mon Sep 17 00:00:00 2001 From: Fumiya Nakamura Date: Tue, 31 Mar 2026 02:25:19 +0900 Subject: [PATCH 145/158] Rename shared context to fix duplicate definition warning (#954) * stripe_validator_spec.rb was loaded twice: once by RSpec (matching *_spec.rb pattern via load) and once by spec_helper (via require in Dir glob), causing a shared example group redefinition warning. * Rename to stripe_validator.rb since it is a shared context, not a spec file. --- .../{stripe_validator_spec.rb => stripe_validator.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/support/shared_contexts/{stripe_validator_spec.rb => stripe_validator.rb} (100%) diff --git a/spec/support/shared_contexts/stripe_validator_spec.rb b/spec/support/shared_contexts/stripe_validator.rb similarity index 100% rename from spec/support/shared_contexts/stripe_validator_spec.rb rename to spec/support/shared_contexts/stripe_validator.rb From cb219301eca69596b6258e90dc67c3014d2d64a3 Mon Sep 17 00:00:00 2001 From: Fumiya Nakamura Date: Tue, 31 Mar 2026 02:25:42 +0900 Subject: [PATCH 146/158] Update lockfiles for stripe-ruby-mock 5.0.0 (#953) --- gemfiles/stripe_12.gemfile.lock | 2 +- gemfiles/stripe_13.gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gemfiles/stripe_12.gemfile.lock b/gemfiles/stripe_12.gemfile.lock index 88b5215e0..88eeed1b6 100644 --- a/gemfiles/stripe_12.gemfile.lock +++ b/gemfiles/stripe_12.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - stripe-ruby-mock (4.0.0) + stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) multi_json (~> 1.0) diff --git a/gemfiles/stripe_13.gemfile.lock b/gemfiles/stripe_13.gemfile.lock index 8c3e4d4f1..45ed3970c 100644 --- a/gemfiles/stripe_13.gemfile.lock +++ b/gemfiles/stripe_13.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - stripe-ruby-mock (4.0.0) + stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) multi_json (~> 1.0) From dd6577c8ad19dd965ae099aa98685aee7601a2af Mon Sep 17 00:00:00 2001 From: Fumiya Nakamura Date: Tue, 31 Mar 2026 02:55:55 +0900 Subject: [PATCH 147/158] Use cancel helper for subscription tests (#955) * Stripe v6 uses Subscription#delete, v7+ uses #cancel. * Add cancel_subscription and cancel_subscription_by_id helpers that delegate to the correct method. --- .../subscription_examples.rb | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 213aa26f0..b4fa3b44b 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -4,6 +4,23 @@ shared_examples 'Customer Subscriptions with plans' do let(:gen_card_tk) { stripe_helper.generate_card_token } + # Stripe v6 uses #delete, v7+ uses #cancel + def cancel_subscription(sub, **opts) + if sub.respond_to?(:cancel) + sub.cancel(**opts) + else + sub.delete(**opts) + end + end + + def cancel_subscription_by_id(id) + if Stripe::Subscription.respond_to?(:cancel) + Stripe::Subscription.cancel(id) + else + Stripe::Subscription.delete(id) + end + end + let(:product) { stripe_helper.create_product } let(:plan_attrs) { {id: 'silver', product: product.id, amount: 4999, currency: 'usd'} } let(:plan) { stripe_helper.create_plan(plan_attrs) } @@ -687,7 +704,7 @@ customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) sub = Stripe::Subscription.create({ items: { '0' => { plan: 'silver' } }, customer: customer.id }) - sub.cancel(at_period_end: true) + cancel_subscription(sub, at_period_end: true) expect(sub.cancel_at_period_end).to be_truthy expect(sub.save).to be_truthy @@ -752,7 +769,7 @@ customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) subscription = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) - subscription.cancel + cancel_subscription(subscription) expect { subscription.save }.to raise_error { |e| expect(e).to be_a(Stripe::InvalidRequestError) @@ -765,7 +782,7 @@ customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) sub = Stripe::Subscription.create({ items: [ { plan: plan.id } ], customer: customer.id }) - sub.cancel(at_period_end: true) + cancel_subscription(sub, at_period_end: true) expect(sub.cancel_at_period_end).to be_truthy expect(sub.save).to be_truthy @@ -1208,7 +1225,7 @@ customer = Stripe::Customer.create(source: gen_card_tk, plan: plan.id) sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) - result = sub.cancel + result = cancel_subscription(sub) expect(result.status).to eq('canceled') expect(result.cancel_at_period_end).to eq false @@ -1310,7 +1327,7 @@ customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk, plan: "trial") sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id) - result = sub.cancel(at_period_end: true) + result = cancel_subscription(sub, at_period_end: true) expect(result.status).to eq('trialing') @@ -1398,7 +1415,7 @@ it "does not include canceled subscriptions by default" do customer = Stripe::Customer.create(source: gen_card_tk) subscription = Stripe::Subscription.create({ plan: plan.id, customer: customer.id }) - subscription.cancel + cancel_subscription(subscription) list = Stripe::Subscription.list({customer: customer.id}) @@ -1507,7 +1524,7 @@ customer = Stripe::Customer.create(email: 'johnny@appleseed.com', source: gen_card_tk) one = Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}], metadata: {key: 'uno'}) two = Stripe::Subscription.create(customer: customer.id, items: [{plan: "Sample5"}], metadata: {key: 'dos'}) - Stripe::Subscription.cancel(two.id) + cancel_subscription_by_id(two.id) response = Stripe::Subscription.search({query: 'status:"active"'}, stripe_version: '2020-08-27') expect(response.data.map(&:id)).to match_array([one.id]) From 8d168cbb3cfcefb2041fb829b7d99dd0d71e854e Mon Sep 17 00:00:00 2001 From: Fumiya Nakamura Date: Wed, 1 Apr 2026 18:38:28 +0900 Subject: [PATCH 148/158] Skip Invoice.create_preview tests on Stripe < 11 (#957) * Invoice.create_preview was added in Stripe v11. * Tests now check for method availability and skip gracefully on older versions. --- spec/shared_stripe_examples/invoice_examples.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index afab6c43f..d9654c3bf 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -630,7 +630,7 @@ end - context "creating a preview invoice" do + context "creating a preview invoice", skip: !Stripe::Invoice.respond_to?(:create_preview) && "Stripe::Invoice.create_preview not available in stripe #{Stripe::VERSION}" do let(:customer) { Stripe::Customer.create(source: stripe_helper.generate_card_token) } let(:product) { stripe_helper.create_product(id: "prod_preview") } let(:plan) { stripe_helper.create_plan(id: 'preview_plan', product: product.id, amount: 50_00, interval: 'month', currency: 'usd') } From 125f0c93cddb823a2bd0bcb7c87fe237d7034119 Mon Sep 17 00:00:00 2001 From: Fumiya Nakamura Date: Wed, 1 Apr 2026 18:39:42 +0900 Subject: [PATCH 149/158] Fix Invoice.upcoming test to pass empty hash (#956) * Stripe v6 requires a positional argument for Invoice.upcoming; passing {} works across all versions. --- spec/shared_stripe_examples/invoice_examples.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/shared_stripe_examples/invoice_examples.rb b/spec/shared_stripe_examples/invoice_examples.rb index d9654c3bf..6fa0908f6 100644 --- a/spec/shared_stripe_examples/invoice_examples.rb +++ b/spec/shared_stripe_examples/invoice_examples.rb @@ -235,10 +235,10 @@ describe 'parameter validation' do it 'fails without parameters' do - expect { Stripe::Invoice.upcoming() }.to raise_error {|e| + expect { Stripe::Invoice.upcoming({}) }.to raise_error {|e| expect(e).to be_a(Stripe::InvalidRequestError) expect(e.http_status).to eq(400) - expect(e.message).to eq('Missing required param: customer if subscription is not provided') + expect(e.message).to eq('Missing required param: customer if subscription is not provided') } end From f4f8f2b8f216599e24db578568df5eb3c6d4d8a7 Mon Sep 17 00:00:00 2001 From: Fumiya Nakamura Date: Wed, 1 Apr 2026 23:18:05 +0900 Subject: [PATCH 150/158] Add appraisal gemfiles for Stripe 6-11 to CI test matrix (#958) * Add appraisal gemfiles for Stripe 6-11 * Add gemfiles for testing against Stripe SDK v6 through v11 to verify backward compatibility. * Add lockfiles for Stripe 6-11 gemfiles * Add Stripe 6-11 to CI test matrix * Full Ruby matrix (3.1, 3.2, 3.3) for Stripe 12-13. * Single Ruby 3.3 for Stripe 6-11 backward compat checks. * Use full matrix for Stripe 6-11 CI test combinations --- .github/workflows/rspec_tests.yml | 2 +- gemfiles/stripe_10.gemfile | 13 +++++++ gemfiles/stripe_10.gemfile.lock | 59 +++++++++++++++++++++++++++++++ gemfiles/stripe_11.gemfile | 13 +++++++ gemfiles/stripe_11.gemfile.lock | 59 +++++++++++++++++++++++++++++++ gemfiles/stripe_6.gemfile | 13 +++++++ gemfiles/stripe_6.gemfile.lock | 59 +++++++++++++++++++++++++++++++ gemfiles/stripe_7.gemfile | 13 +++++++ gemfiles/stripe_7.gemfile.lock | 59 +++++++++++++++++++++++++++++++ gemfiles/stripe_8.gemfile | 13 +++++++ gemfiles/stripe_8.gemfile.lock | 59 +++++++++++++++++++++++++++++++ gemfiles/stripe_9.gemfile | 13 +++++++ gemfiles/stripe_9.gemfile.lock | 59 +++++++++++++++++++++++++++++++ 13 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 gemfiles/stripe_10.gemfile create mode 100644 gemfiles/stripe_10.gemfile.lock create mode 100644 gemfiles/stripe_11.gemfile create mode 100644 gemfiles/stripe_11.gemfile.lock create mode 100644 gemfiles/stripe_6.gemfile create mode 100644 gemfiles/stripe_6.gemfile.lock create mode 100644 gemfiles/stripe_7.gemfile create mode 100644 gemfiles/stripe_7.gemfile.lock create mode 100644 gemfiles/stripe_8.gemfile create mode 100644 gemfiles/stripe_8.gemfile.lock create mode 100644 gemfiles/stripe_9.gemfile create mode 100644 gemfiles/stripe_9.gemfile.lock diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index 816fda8a8..4ccb9771f 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -23,7 +23,7 @@ jobs: fail-fast: false matrix: ruby: ["3.1", "3.2", "3.3"] - gemfile: ["stripe_12", "stripe_13"] + gemfile: ["stripe_6", "stripe_7", "stripe_8", "stripe_9", "stripe_10", "stripe_11", "stripe_12", "stripe_13"] env: BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile steps: diff --git a/gemfiles/stripe_10.gemfile b/gemfiles/stripe_10.gemfile new file mode 100644 index 000000000..6c8f85abd --- /dev/null +++ b/gemfiles/stripe_10.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "10.15.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_10.gemfile.lock b/gemfiles/stripe_10.gemfile.lock new file mode 100644 index 000000000..1d7243d65 --- /dev/null +++ b/gemfiles/stripe_10.gemfile.lock @@ -0,0 +1,59 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (5.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.2) + dotenv (3.2.0) + drb (2.2.3) + eventmachine (1.2.7) + multi_json (1.19.1) + rack (2.2.22) + rake (13.3.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + stripe (10.15.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 10.15.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/gemfiles/stripe_11.gemfile b/gemfiles/stripe_11.gemfile new file mode 100644 index 000000000..70126711d --- /dev/null +++ b/gemfiles/stripe_11.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "11.7.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_11.gemfile.lock b/gemfiles/stripe_11.gemfile.lock new file mode 100644 index 000000000..8b9cf63f3 --- /dev/null +++ b/gemfiles/stripe_11.gemfile.lock @@ -0,0 +1,59 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (5.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.2) + dotenv (3.2.0) + drb (2.2.3) + eventmachine (1.2.7) + multi_json (1.19.1) + rack (2.2.22) + rake (13.3.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + stripe (11.7.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 11.7.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/gemfiles/stripe_6.gemfile b/gemfiles/stripe_6.gemfile new file mode 100644 index 000000000..f4cc536b1 --- /dev/null +++ b/gemfiles/stripe_6.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "6.5.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_6.gemfile.lock b/gemfiles/stripe_6.gemfile.lock new file mode 100644 index 000000000..764417045 --- /dev/null +++ b/gemfiles/stripe_6.gemfile.lock @@ -0,0 +1,59 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (5.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.2) + dotenv (3.2.0) + drb (2.2.3) + eventmachine (1.2.7) + multi_json (1.19.1) + rack (2.2.22) + rake (13.3.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + stripe (6.5.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 6.5.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/gemfiles/stripe_7.gemfile b/gemfiles/stripe_7.gemfile new file mode 100644 index 000000000..18c2fc622 --- /dev/null +++ b/gemfiles/stripe_7.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "7.1.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_7.gemfile.lock b/gemfiles/stripe_7.gemfile.lock new file mode 100644 index 000000000..d3ae828b5 --- /dev/null +++ b/gemfiles/stripe_7.gemfile.lock @@ -0,0 +1,59 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (5.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.2) + dotenv (3.2.0) + drb (2.2.3) + eventmachine (1.2.7) + multi_json (1.19.1) + rack (2.2.22) + rake (13.3.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + stripe (7.1.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 7.1.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/gemfiles/stripe_8.gemfile b/gemfiles/stripe_8.gemfile new file mode 100644 index 000000000..2278a61b1 --- /dev/null +++ b/gemfiles/stripe_8.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "8.7.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_8.gemfile.lock b/gemfiles/stripe_8.gemfile.lock new file mode 100644 index 000000000..70e91752d --- /dev/null +++ b/gemfiles/stripe_8.gemfile.lock @@ -0,0 +1,59 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (5.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.2) + dotenv (3.2.0) + drb (2.2.3) + eventmachine (1.2.7) + multi_json (1.19.1) + rack (2.2.22) + rake (13.3.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + stripe (8.7.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 8.7.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 diff --git a/gemfiles/stripe_9.gemfile b/gemfiles/stripe_9.gemfile new file mode 100644 index 000000000..a478f7235 --- /dev/null +++ b/gemfiles/stripe_9.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal" +gem "stripe", "9.4.0" + +group :test do + gem "rake" + gem "dotenv" +end + +gemspec path: "../" diff --git a/gemfiles/stripe_9.gemfile.lock b/gemfiles/stripe_9.gemfile.lock new file mode 100644 index 000000000..1e2c2bcde --- /dev/null +++ b/gemfiles/stripe_9.gemfile.lock @@ -0,0 +1,59 @@ +PATH + remote: .. + specs: + stripe-ruby-mock (5.0.0) + dante (>= 0.2.0) + drb (>= 2.0.4, < 3) + multi_json (~> 1.0) + stripe (> 5, < 14) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + daemons (1.4.1) + dante (0.2.0) + diff-lcs (1.6.2) + dotenv (3.2.0) + drb (2.2.3) + eventmachine (1.2.7) + multi_json (1.19.1) + rack (2.2.22) + rake (13.3.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + stripe (9.4.0) + thin (1.8.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal + dotenv + rake + rspec (~> 3.13.0) + stripe (= 9.4.0) + stripe-ruby-mock! + thin (~> 1.8.1) + +BUNDLED WITH + 2.6.8 From 2d5d752c0c6a6d5d64bcee2495a3f3d874df5ede Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Mon, 15 Jun 2026 18:14:09 +0100 Subject: [PATCH 151/158] Add ruby 3.4 to the test matrix (#961) --- .github/workflows/rspec_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index 4ccb9771f..ee4c192ea 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ["3.1", "3.2", "3.3"] + ruby: ["3.1", "3.2", "3.3", "3.4"] gemfile: ["stripe_6", "stripe_7", "stripe_8", "stripe_9", "stripe_10", "stripe_11", "stripe_12", "stripe_13"] env: BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile From 852fd53316ef9db387ae0b4f3f8c84e712c7950d Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Wed, 17 Jun 2026 20:27:01 +0100 Subject: [PATCH 152/158] Remove unused require 'ostruct' (#962) The require was added in c34c4e8 (2013) but was apparently never used. Removing it silences the Ruby 3.4 bundled-gem deprecation warning ("ostruct was loaded from the standard library, but will no longer be part of the default gems") without needing to add ostruct as a dependency. --- lib/stripe_mock.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index f3f6c35dd..a78cb5f34 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -1,4 +1,3 @@ -require 'ostruct' require 'multi_json' require 'dante' require 'time' From 8c80a43b34d2a9339f62650d8a9b0ce209693643 Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Tue, 23 Jun 2026 16:45:47 +0100 Subject: [PATCH 153/158] Fix various timing flakes in CI (#966) * test: Deflake time related tests (cherry picked from commit 1c0e057baadc186b8c95af8f1a89f3768a8cde98) * Deflake subscription current_period_start filter spec "filters out subscriptions based on their current_period" created two subscriptions and then asserted that filtering by subscription1.current_period_start returned both of them. That only holds if both Subscription.create calls happen within the same wall-clock second; if the clock ticks between them then the test fails. --------- Co-authored-by: strawhatduckk --- .../shared_stripe_examples/subscription_examples.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index b4fa3b44b..c62b4d4ae 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -603,7 +603,7 @@ def cancel_subscription_by_id(id) ] ) - expect(subscription.current_period_end).to eq (Time.now + (7 * 60 * 60 * 24)).to_i + expect(subscription.current_period_end).to be_within(2).of((Time.now + (7 * 60 * 60 * 24)).to_i) end it 'sets current_period_end based on price month interval', live: true do @@ -616,7 +616,7 @@ def cancel_subscription_by_id(id) ] ) - expect(subscription.current_period_end).to eq (DateTime.now >> 1).to_time.to_i + expect(subscription.current_period_end).to be_within(2).of((DateTime.now >> 1).to_time.to_i) end it 'sets current_period_end based on price year interval', live: true do @@ -629,7 +629,7 @@ def cancel_subscription_by_id(id) ] ) - expect(subscription.current_period_end).to eq (DateTime.now >> 12).to_time.to_i + expect(subscription.current_period_end).to be_within(2).of((DateTime.now >> 12).to_time.to_i) end it 'add a new subscription to bill via an invoice' do @@ -1449,8 +1449,13 @@ def cancel_subscription_by_id(id) list = Stripe::Subscription.list({ current_period_end: { lte: subscription1.current_period_end }}) expect(list.data).to contain_exactly(subscription1) + # subscription1 and subscription2 are created moments apart, so in some test runs have + # slightly different start times. Query by each start time in case they are different. list = Stripe::Subscription.list({ current_period_start: subscription1.current_period_start }) - expect(list.data).to contain_exactly(subscription1, subscription2) + expect(list.data).to include(subscription1) + + list = Stripe::Subscription.list({ current_period_start: subscription2.current_period_start }) + expect(list.data).to include(subscription2) list = Stripe::Subscription.list({ current_period_end: subscription2.current_period_end }) expect(list.data).to contain_exactly(subscription2) From 25e146f9141072475f9841adb00cb4c72e036528 Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Tue, 23 Jun 2026 16:46:58 +0100 Subject: [PATCH 154/158] Avoid repeatedly starting client-side DRb servers (#963) The DRb server needs to be running once client-side, but it holds no state and so doesn't need to be restarted. The same service is process-wide and can be used to talk with multiple backend DRb servers. `DRb.start_service` replaces the `primary_server` each time it is called, orphaning the previous one as it does so. This leads to fd leaks if `StripeMock.start_client` and `StripeMock.stop_client` are called repeatedly, such as when running the test suite. An alternative approach would be to stop the service during `stop_client`, but this would interfere with any other code in the same process that's using the DRb. --- README.md | 2 -- lib/stripe_mock/client.rb | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index f0fabcbe8..f69e2d063 100644 --- a/README.md +++ b/README.md @@ -408,8 +408,6 @@ Patches are welcome and greatly appreciated! If you're contributing to fix a pro be sure to write tests that illustrate the problem being fixed. This will help ensure that the problem remains fixed in future updates. -Note: You may need to `ulimit -n 4048` before running the test suite to get all tests to pass. - ### Dependency updates When modifications are made to dependencies in addition to the changes to the Gemfile, Gemfile.lock and stripe-ruby-mock.gemspec you must also run `bundle exec appraisal update` to update the gemfiles specific to Stripe version 12 and Stripe version 13. diff --git a/lib/stripe_mock/client.rb b/lib/stripe_mock/client.rb index 7146096f8..04d2994b0 100644 --- a/lib/stripe_mock/client.rb +++ b/lib/stripe_mock/client.rb @@ -5,7 +5,7 @@ class Client def initialize(port) @port = port - DRb.start_service + DRb.start_service unless DRb.primary_server @pipe = DRbObject.new_with_uri "druby://localhost:#{port}" # Ensure client can connect to server From f781771928e14f016a0e245b6bed78218dbea097 Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Wed, 24 Jun 2026 10:59:43 +0100 Subject: [PATCH 155/158] Add ruby 4.0 to CI matrix (#965) * Declare logger as an explicit dependency logger is a dependency of stripe, but it was removed from the default gems in ruby 4 and only added explicitly to stripe in v19. * Add Ruby 4.0 to the CI test matrix --- .github/workflows/rspec_tests.yml | 2 +- gemfiles/stripe_10.gemfile.lock | 2 ++ gemfiles/stripe_11.gemfile.lock | 2 ++ gemfiles/stripe_12.gemfile.lock | 2 ++ gemfiles/stripe_13.gemfile.lock | 2 ++ gemfiles/stripe_6.gemfile.lock | 2 ++ gemfiles/stripe_7.gemfile.lock | 2 ++ gemfiles/stripe_8.gemfile.lock | 2 ++ gemfiles/stripe_9.gemfile.lock | 2 ++ stripe-ruby-mock.gemspec | 3 +++ 10 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec_tests.yml b/.github/workflows/rspec_tests.yml index ee4c192ea..36c3c2b83 100644 --- a/.github/workflows/rspec_tests.yml +++ b/.github/workflows/rspec_tests.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ["3.1", "3.2", "3.3", "3.4"] + ruby: ["3.1", "3.2", "3.3", "3.4", "4.0"] gemfile: ["stripe_6", "stripe_7", "stripe_8", "stripe_9", "stripe_10", "stripe_11", "stripe_12", "stripe_13"] env: BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile diff --git a/gemfiles/stripe_10.gemfile.lock b/gemfiles/stripe_10.gemfile.lock index 1d7243d65..f41d792be 100644 --- a/gemfiles/stripe_10.gemfile.lock +++ b/gemfiles/stripe_10.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (3.2.0) drb (2.2.3) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.19.1) rack (2.2.22) rake (13.3.1) diff --git a/gemfiles/stripe_11.gemfile.lock b/gemfiles/stripe_11.gemfile.lock index 8b9cf63f3..1ebec6a93 100644 --- a/gemfiles/stripe_11.gemfile.lock +++ b/gemfiles/stripe_11.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (3.2.0) drb (2.2.3) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.19.1) rack (2.2.22) rake (13.3.1) diff --git a/gemfiles/stripe_12.gemfile.lock b/gemfiles/stripe_12.gemfile.lock index 88eeed1b6..6d437a2d6 100644 --- a/gemfiles/stripe_12.gemfile.lock +++ b/gemfiles/stripe_12.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (2.8.1) drb (2.2.1) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.15.0) rack (2.2.13) rake (13.2.1) diff --git a/gemfiles/stripe_13.gemfile.lock b/gemfiles/stripe_13.gemfile.lock index 45ed3970c..5647d6db9 100644 --- a/gemfiles/stripe_13.gemfile.lock +++ b/gemfiles/stripe_13.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (2.8.1) drb (2.2.1) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.15.0) rack (2.2.13) rake (13.2.1) diff --git a/gemfiles/stripe_6.gemfile.lock b/gemfiles/stripe_6.gemfile.lock index 764417045..2abea793c 100644 --- a/gemfiles/stripe_6.gemfile.lock +++ b/gemfiles/stripe_6.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (3.2.0) drb (2.2.3) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.19.1) rack (2.2.22) rake (13.3.1) diff --git a/gemfiles/stripe_7.gemfile.lock b/gemfiles/stripe_7.gemfile.lock index d3ae828b5..cdf07b479 100644 --- a/gemfiles/stripe_7.gemfile.lock +++ b/gemfiles/stripe_7.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (3.2.0) drb (2.2.3) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.19.1) rack (2.2.22) rake (13.3.1) diff --git a/gemfiles/stripe_8.gemfile.lock b/gemfiles/stripe_8.gemfile.lock index 70e91752d..29807a537 100644 --- a/gemfiles/stripe_8.gemfile.lock +++ b/gemfiles/stripe_8.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (3.2.0) drb (2.2.3) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.19.1) rack (2.2.22) rake (13.3.1) diff --git a/gemfiles/stripe_9.gemfile.lock b/gemfiles/stripe_9.gemfile.lock index 1e2c2bcde..d5fb3b199 100644 --- a/gemfiles/stripe_9.gemfile.lock +++ b/gemfiles/stripe_9.gemfile.lock @@ -4,6 +4,7 @@ PATH stripe-ruby-mock (5.0.0) dante (>= 0.2.0) drb (>= 2.0.4, < 3) + logger multi_json (~> 1.0) stripe (> 5, < 14) @@ -20,6 +21,7 @@ GEM dotenv (3.2.0) drb (2.2.3) eventmachine (1.2.7) + logger (1.7.0) multi_json (1.19.1) rack (2.2.22) rake (13.3.1) diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index b212ec912..011e8ac37 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -23,6 +23,9 @@ Gem::Specification.new do |gem| gem.require_paths = ['lib'] gem.add_dependency 'stripe', '> 5', '< 14' + # logger is a dependency of stripe, but was removed from the + # default gems in ruby 4 and only added explicitly to stripe in v19. + gem.add_dependency 'logger' gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' gem.add_dependency 'drb', '>= 2.0.4', '< 3' From 3c7c0578ab7f1efbc4d6f1cf10750a1de0519388 Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Wed, 24 Jun 2026 11:00:38 +0100 Subject: [PATCH 156/158] Fix duplicate "status" key in verified bank account data (#964) Elsewhere in the code the symbol key `:status` is used, and this mismatch is flagged up as a warning when using ruby 4.0 --- .../request_handlers/helpers/bank_account_helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb b/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb index ddf7f2360..da7dbfcd2 100644 --- a/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb @@ -6,7 +6,7 @@ def verify_bank_account(object, bank_account_id, class_name='Customer') bank_accounts = object[:external_accounts] || object[:bank_accounts] || object[:sources] bank_account = bank_accounts[:data].find{|acc| acc[:id] == bank_account_id } return if bank_account.nil? - bank_account['status'] = 'verified' + bank_account[:status] = 'verified' bank_account end end From 535fa8e62deff41b02e9305c84e075dab6d01d78 Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Sun, 26 Jul 2026 22:09:13 +0100 Subject: [PATCH 157/158] Add missing appraisals configuration (#968) PR #958 added the Gemfiles for Stripe v6-11, but didn't add the configuration. This means that these Gemfiles won't be updated when running `bundle exec appraisal update` --- Appraisals | 24 ++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Appraisals b/Appraisals index 690909cd7..e26cb4cf5 100644 --- a/Appraisals +++ b/Appraisals @@ -5,3 +5,27 @@ end appraise "stripe-12" do gem "stripe", "12.6.0" end + +appraise "stripe-11" do + gem "stripe", "11.7.0" +end + +appraise "stripe-10" do + gem "stripe", "10.15.0" +end + +appraise "stripe-9" do + gem "stripe", "9.4.0" +end + +appraise "stripe-8" do + gem "stripe", "8.7.0" +end + +appraise "stripe-7" do + gem "stripe", "7.1.0" +end + +appraise "stripe-6" do + gem "stripe", "6.5.0" +end diff --git a/README.md b/README.md index f69e2d063..1b0628268 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ This will help ensure that the problem remains fixed in future updates. ### Dependency updates -When modifications are made to dependencies in addition to the changes to the Gemfile, Gemfile.lock and stripe-ruby-mock.gemspec you must also run `bundle exec appraisal update` to update the gemfiles specific to Stripe version 12 and Stripe version 13. +When modifications are made to dependencies, in addition to the changes to the Gemfile, Gemfile.lock and stripe-ruby-mock.gemspec you must also run `bundle exec appraisal update` to update the various gemfiles that are specific to the supported Stripe versions. ## Copyright From 2908ae2f5d15df9850ac8dcf37447a5f18311122 Mon Sep 17 00:00:00 2001 From: Andy Allan Date: Wed, 12 Aug 2026 22:27:15 +0100 Subject: [PATCH 158/158] Drop multi_json dependency (#969) Using this gem isn't really necessary, given that we're only loading json fixtures during test runs. Modern rubies have performant json parsers in the default 'json' gem. Removing it simplifies the dependencies, and resolves the recent multi_json deprecation warnings without forcing specific gem version constraints on downstream apps. --- gemfiles/stripe_10.gemfile.lock | 2 -- gemfiles/stripe_11.gemfile.lock | 2 -- gemfiles/stripe_12.gemfile.lock | 2 -- gemfiles/stripe_13.gemfile.lock | 2 -- gemfiles/stripe_6.gemfile.lock | 2 -- gemfiles/stripe_7.gemfile.lock | 2 -- gemfiles/stripe_8.gemfile.lock | 2 -- gemfiles/stripe_9.gemfile.lock | 2 -- lib/stripe_mock.rb | 2 +- lib/stripe_mock/api/webhooks.rb | 2 +- stripe-ruby-mock.gemspec | 1 - 11 files changed, 2 insertions(+), 19 deletions(-) diff --git a/gemfiles/stripe_10.gemfile.lock b/gemfiles/stripe_10.gemfile.lock index f41d792be..02f021394 100644 --- a/gemfiles/stripe_10.gemfile.lock +++ b/gemfiles/stripe_10.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.3) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.19.1) rack (2.2.22) rake (13.3.1) rspec (3.13.2) diff --git a/gemfiles/stripe_11.gemfile.lock b/gemfiles/stripe_11.gemfile.lock index 1ebec6a93..a765677a6 100644 --- a/gemfiles/stripe_11.gemfile.lock +++ b/gemfiles/stripe_11.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.3) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.19.1) rack (2.2.22) rake (13.3.1) rspec (3.13.2) diff --git a/gemfiles/stripe_12.gemfile.lock b/gemfiles/stripe_12.gemfile.lock index 6d437a2d6..9ee12e21f 100644 --- a/gemfiles/stripe_12.gemfile.lock +++ b/gemfiles/stripe_12.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.1) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.15.0) rack (2.2.13) rake (13.2.1) rspec (3.13.0) diff --git a/gemfiles/stripe_13.gemfile.lock b/gemfiles/stripe_13.gemfile.lock index 5647d6db9..7e34d578e 100644 --- a/gemfiles/stripe_13.gemfile.lock +++ b/gemfiles/stripe_13.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.1) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.15.0) rack (2.2.13) rake (13.2.1) rspec (3.13.0) diff --git a/gemfiles/stripe_6.gemfile.lock b/gemfiles/stripe_6.gemfile.lock index 2abea793c..055a275c8 100644 --- a/gemfiles/stripe_6.gemfile.lock +++ b/gemfiles/stripe_6.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.3) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.19.1) rack (2.2.22) rake (13.3.1) rspec (3.13.2) diff --git a/gemfiles/stripe_7.gemfile.lock b/gemfiles/stripe_7.gemfile.lock index cdf07b479..9fd150809 100644 --- a/gemfiles/stripe_7.gemfile.lock +++ b/gemfiles/stripe_7.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.3) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.19.1) rack (2.2.22) rake (13.3.1) rspec (3.13.2) diff --git a/gemfiles/stripe_8.gemfile.lock b/gemfiles/stripe_8.gemfile.lock index 29807a537..14cad4092 100644 --- a/gemfiles/stripe_8.gemfile.lock +++ b/gemfiles/stripe_8.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.3) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.19.1) rack (2.2.22) rake (13.3.1) rspec (3.13.2) diff --git a/gemfiles/stripe_9.gemfile.lock b/gemfiles/stripe_9.gemfile.lock index d5fb3b199..d7a1de105 100644 --- a/gemfiles/stripe_9.gemfile.lock +++ b/gemfiles/stripe_9.gemfile.lock @@ -5,7 +5,6 @@ PATH dante (>= 0.2.0) drb (>= 2.0.4, < 3) logger - multi_json (~> 1.0) stripe (> 5, < 14) GEM @@ -22,7 +21,6 @@ GEM drb (2.2.3) eventmachine (1.2.7) logger (1.7.0) - multi_json (1.19.1) rack (2.2.22) rake (13.3.1) rspec (3.13.2) diff --git a/lib/stripe_mock.rb b/lib/stripe_mock.rb index a78cb5f34..acc335285 100644 --- a/lib/stripe_mock.rb +++ b/lib/stripe_mock.rb @@ -1,4 +1,4 @@ -require 'multi_json' +require 'json' require 'dante' require 'time' diff --git a/lib/stripe_mock/api/webhooks.rb b/lib/stripe_mock/api/webhooks.rb index 031076a3a..9eff4a981 100644 --- a/lib/stripe_mock/api/webhooks.rb +++ b/lib/stripe_mock/api/webhooks.rb @@ -11,7 +11,7 @@ def self.mock_webhook_payload(type, params = {}) fixture_file = File.join(@webhook_fixture_fallback_path, "#{type}.json") end - json = MultiJson.load File.read(fixture_file) + json = JSON.parse File.read(fixture_file) json = Stripe::Util.symbolize_names(json) params = Stripe::Util.symbolize_names(params) diff --git a/stripe-ruby-mock.gemspec b/stripe-ruby-mock.gemspec index 011e8ac37..20118eff7 100644 --- a/stripe-ruby-mock.gemspec +++ b/stripe-ruby-mock.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |gem| # logger is a dependency of stripe, but was removed from the # default gems in ruby 4 and only added explicitly to stripe in v19. gem.add_dependency 'logger' - gem.add_dependency 'multi_json', '~> 1.0' gem.add_dependency 'dante', '>= 0.2.0' gem.add_dependency 'drb', '>= 2.0.4', '< 3'