diff --git a/app/controllers/discourse_subscriptions/hooks_controller.rb b/app/controllers/discourse_subscriptions/hooks_controller.rb index c7210fa..0e6eafe 100644 --- a/app/controllers/discourse_subscriptions/hooks_controller.rb +++ b/app/controllers/discourse_subscriptions/hooks_controller.rb @@ -57,13 +57,9 @@ module DiscourseSubscriptions discourse_customer = Customer.create(user_id: user.id, customer_id: customer_id) subscription = checkout_session[:subscription] - payment_intent_id = checkout_session[:payment_intent] if subscription.present? Subscription.create(customer_id: discourse_customer.id, external_id: subscription) - elsif payment_intent_id - # Attach the payment intent to the customer for one-off purchases - ::Stripe::PaymentIntent.update(payment_intent_id, { customer: customer_id }) end line_items = diff --git a/app/controllers/discourse_subscriptions/user/payments_controller.rb b/app/controllers/discourse_subscriptions/user/payments_controller.rb index 70973fa..4325759 100644 --- a/app/controllers/discourse_subscriptions/user/payments_controller.rb +++ b/app/controllers/discourse_subscriptions/user/payments_controller.rb @@ -44,6 +44,14 @@ module DiscourseSubscriptions end end + if SiteSetting.discourse_subscriptions_pricing_table_enabled && current_user.email + related_guest_payments = fetch_guest_payments(current_user.email) + if SiteSetting.discourse_subscriptions_enable_verbose_logging + Rails.logger.warn("Related guest payments: #{related_guest_payments}") + end + data = data | related_guest_payments + end + data = data.sort_by { |pmt| pmt[:created] }.reverse render_json_dump data @@ -71,6 +79,41 @@ module DiscourseSubscriptions invoice_lines[:plan][:product] invoice_product_id end + + def fetch_guest_payments(email) + guest_payments = [] + starting_after = nil + + begin + loop do + # Fetch charges in batches of 100, using pagination with starting_after + charges = + ::Stripe::Charge.list( + limit: 100, + starting_after: starting_after, + expand: ["data.payment_intent"], + ) + + charges[:data].each do |charge| + # Check if the charge is associated with the given email and has no customer ID + if charge[:billing_details][:email] == email && charge[:customer].nil? + guest_payments << charge + end + end + + # Check if there are more charges to fetch + break if charges[:data].empty? + break if charges[:data].count < 100 + + # Set starting_after to the last charge's ID for the next batch + starting_after = charges[:data].last[:id] + end + rescue ::Stripe::StripeError => e + Rails.logger.error("Stripe API error: #{e.message}") + end + + guest_payments + end end end end diff --git a/spec/requests/hooks_controller_spec.rb b/spec/requests/hooks_controller_spec.rb index 6afc958..3abf830 100644 --- a/spec/requests/hooks_controller_spec.rb +++ b/spec/requests/hooks_controller_spec.rb @@ -220,11 +220,6 @@ RSpec.describe DiscourseSubscriptions::HooksController do ::Stripe::Webhook.stubs(:construct_event).returns(event) ::Stripe::Customer.stubs(:create).returns(id: "cus_1234") - - ::Stripe::PaymentIntent - .expects(:update) - .with("pi_3PsohkGHcn", { customer: "cus_1234" }) - .returns({ id: "pi_3PsohkGHcn" }) end it "is returns 200" do diff --git a/spec/requests/user/payments_controller_spec.rb b/spec/requests/user/payments_controller_spec.rb index 7fe87d4..4697808 100644 --- a/spec/requests/user/payments_controller_spec.rb +++ b/spec/requests/user/payments_controller_spec.rb @@ -75,5 +75,65 @@ RSpec.describe DiscourseSubscriptions::User::PaymentsController do expect(parsed_body.count).to eq(1) end + + it "gets pricing table one-off purchases that show up as related guest payments" do + SiteSetting.discourse_subscriptions_pricing_table_enabled = true + ::Stripe::Invoice.expects(:list).with(customer: "c_345678").returns(data: []) + + ::Stripe::PaymentIntent + .expects(:list) + .with(customer: "c_345678") + .returns(data: [{ id: "pi_900010", invoice: nil, created: Time.now }]) + + ::Stripe::Charge + .expects(:list) + .with(limit: 100, starting_after: nil, expand: ["data.payment_intent"]) + .returns( + data: [ + { + id: "ch_1HtGz2GHcn71qeAp4YjA2oB4", + amount: 2000, + currency: "usd", + billing_details: { + email: user.email, + }, + customer: nil, # guest payment + payment_intent: "pi_1HtGz1GHcn71qeApT9N2Cjln", + created: Time.now.to_i, + }, + { + id: "ch_2HtGz2GHcn71qeAp4YjA2oB4", + amount: 2000, + currency: "usd", + billing_details: { + email: "zxcv@example.com", + }, + customer: nil, # different guest + payment_intent: "pi_2HtGz1GHcn71qeApT9N2Cjln", + created: Time.now.to_i, + }, + { + id: "ch_1HtGz3GHcn71qeAp5YjA2oC5", + amount: 3000, + currency: "usd", + billing_details: { + email: "fdsa@example.com", + }, + customer: "cus_1234", # This is not a guest payment + payment_intent: "pi_3HtGz2GHcn71qeApT9N2Cjln", + created: Time.now.to_i, + }, + ], + ) + + get "/s/user/payments.json" + + parsed_body = response.parsed_body + + # Validate that only guest payments with the specified email are returned + expect(parsed_body.count).to eq(2) + expect(parsed_body.first["id"]).to eq("ch_1HtGz2GHcn71qeAp4YjA2oB4") + expect(parsed_body.first["customer"]).to be_nil + end end end