FIX: One-time purchase pending invoice item (#210)

This change ensures we attach the invoice item to the invoice to avoid
any occurrences of an empty invoice being paid with pending invoice
items.
This commit is contained in:
Blake Erickson 2024-05-07 08:57:37 -06:00 committed by GitHub
parent e132913db4
commit d63c84eca5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 5 deletions

View File

@ -100,16 +100,22 @@ module DiscourseSubscriptions
else
coupon_id = promo_code[:coupon][:id] if promo_code && promo_code[:coupon] &&
promo_code[:coupon][:id]
invoice = ::Stripe::Invoice.create(customer: customer[:id])
invoice_item =
::Stripe::InvoiceItem.create(
customer: customer[:id],
price: params[:plan],
discounts: [{ coupon: coupon_id }],
invoice: invoice[:id],
)
invoice = ::Stripe::Invoice.create(customer: customer[:id])
transaction = ::Stripe::Invoice.finalize_invoice(invoice[:id])
payment_intent = retrieve_payment_intent(transaction[:id]) if transaction[:status] ==
"open"
if payment_intent.nil?
return(
render_json_error I18n.t("js.discourse_subscriptions.subscribe.transaction_error")
)
end
transaction = ::Stripe::Invoice.pay(invoice[:id]) if payment_intent[:status] ==
"successful"
end

View File

@ -45,8 +45,10 @@ module DiscourseSubscriptions
invoices_with_products =
all_invoices[:data].select do |invoice|
invoice_lines = invoice[:lines][:data][0] if invoice[:lines] && invoice[:lines][:data]
invoice_product_id = parse_invoice_lines(invoice_lines)
product_ids.include?(invoice_product_id)
if invoice_lines
invoice_product_id = parse_invoice_lines(invoice_lines)
product_ids.include?(invoice_product_id)
end
end
end

View File

@ -266,6 +266,35 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do
}.to change { DiscourseSubscriptions::Customer.count }
end
it "returns 422 on a one time payment subscription error" do
# It's possible that the invoice item doesn't get attached
# to the invoice. This means the invoice is paid, but for $0.00 with
# a pending invoice item.
::Stripe::Price.expects(:retrieve).returns(
type: "one_time",
product: "product_12345",
metadata: {
group_name: "awesome",
},
)
::Stripe::InvoiceItem.expects(:create)
::Stripe::Invoice.expects(:create).returns(status: "open", id: "in_123")
::Stripe::Invoice.expects(:finalize_invoice).returns(
id: "in_123",
status: "paid",
payment_intent: "pi_123",
)
expect {
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
}.not_to change { DiscourseSubscriptions::Customer.count }
expect(response.status).to eq 422
end
it "creates a one time payment subscription" do
::Stripe::Price.expects(:retrieve).returns(
type: "one_time",
@ -414,14 +443,15 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do
},
)
::Stripe::Invoice.expects(:create).returns(status: "open", id: "in_123")
::Stripe::InvoiceItem.expects(:create).with(
customer: "cus_1234",
price: "plan_1234",
discounts: [{ coupon: "c123" }],
invoice: "in_123",
)
::Stripe::Invoice.expects(:create).returns(status: "open", id: "in_123")
::Stripe::Invoice.expects(:finalize_invoice).returns(
id: "in_123",
status: "open",