FIX: Include one-time purchases in campaign total (#95)

* FIX: Include one-time purchases in campaign total

On the subscription banner that shows on the top of the forum it shows
the total amount raised toward a goal. But the amount shown was not
including one-time purchases. It was only showing subscriptions.

This change updates the sync with stripe logic so that it also includes
one-time purchases.

See: https://meta.discourse.org/t/-/209591

* rubocop: remove extra blank line
This commit is contained in:
Blake Erickson 2021-11-22 12:46:40 -07:00 committed by GitHub
parent 4f2af3b01a
commit c6c5ed2ed3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 2 deletions

View File

@ -21,6 +21,12 @@ module DiscourseSubscriptions
subscriptions = get_subscription_data subscriptions = get_subscription_data
subscriptions = filter_to_subscriptions_products(subscriptions, product_ids) subscriptions = filter_to_subscriptions_products(subscriptions, product_ids)
# Fetch product purchases
one_time_payments = get_one_time_payments(product_ids)
one_time_payments.each do |c|
amount += c[:price].to_f / 100.00
end
# get number of subscribers # get number of subscribers
SiteSetting.discourse_subscriptions_campaign_subscribers = subscriptions&.length.to_i SiteSetting.discourse_subscriptions_campaign_subscribers = subscriptions&.length.to_i
@ -144,6 +150,50 @@ module DiscourseSubscriptions
plan = ::Stripe::Price.create(price_object) plan = ::Stripe::Price.create(price_object)
end end
def get_one_time_payments(product_ids)
one_time_payments = []
current_set = {
has_more: true,
last_record: nil
}
if product_ids.present?
# lots of matching because the Stripe API doesn't make it easy to match products => payments except from invoices
until current_set[:has_more] == false
all_invoices = ::Stripe::Invoice.list(
limit: 100,
starting_after: current_set[:last_record]
)
current_set[:last_record] = all_invoices[:data].last[:id] if all_invoices[:data].present?
current_set[:has_more] = all_invoices[:has_more]
all_invoices[:data].each do |invoice|
customer_id = invoice[:customer]
line_item = invoice[:lines][:data][0] if invoice[:lines] && invoice[:lines][:data] # Discourse only makes single-line item charges
# check if non-subscription and that the plan is active
if line_item[:plan] == nil &&
line_item[:price] &&
line_item[:price][:recurring] == nil &&
line_item[:price][:active] == true
product_id = line_item[:price][:product]
if product_ids.include? product_id
line_data = {
customer_id: customer_id,
product_id: product_id,
price: line_item[:price][:unit_amount],
}
one_time_payments << line_data
end
end
end
end
end
one_time_payments
end
def get_subscription_data def get_subscription_data
subscriptions = [] subscriptions = []
current_set = { current_set = {

View File

@ -5,6 +5,7 @@ require 'rails_helper'
describe DiscourseSubscriptions::Campaign do describe DiscourseSubscriptions::Campaign do
describe 'campaign data is refreshed' do describe 'campaign data is refreshed' do
let (:user) { Fabricate(:user) } let (:user) { Fabricate(:user) }
let (:user2) { Fabricate(:user) }
let(:subscription) do let(:subscription) do
{ {
id: "sub_1234", id: "sub_1234",
@ -23,10 +24,30 @@ describe DiscourseSubscriptions::Campaign do
} }
} }
end end
let(:invoice) do
{
id: "in_1234",
lines: {
data: [
{
plan: nil,
price: {
product: "prodct_65432",
active: true,
unit_amount: 1000,
recurring: nil,
}
}
]
}
}
end
before do before do
Fabricate(:product, external_id: "prodct_23456") Fabricate(:product, external_id: "prodct_23456")
Fabricate(:customer, product_id: "prodct_23456", user_id: user.id, customer_id: 'x') Fabricate(:customer, product_id: "prodct_23456", user_id: user.id, customer_id: 'x')
Fabricate(:product, external_id: "prodct_65432")
Fabricate(:customer, product_id: "prodct_65432", user_id: user2.id, customer_id: 'y')
SiteSetting.discourse_subscriptions_public_key = "public-key" SiteSetting.discourse_subscriptions_public_key = "public-key"
SiteSetting.discourse_subscriptions_secret_key = "secret-key" SiteSetting.discourse_subscriptions_secret_key = "secret-key"
end end
@ -35,25 +56,28 @@ describe DiscourseSubscriptions::Campaign do
context "for all subscription purchases" do context "for all subscription purchases" do
it "refreshes the campaign data properly" do it "refreshes the campaign data properly" do
::Stripe::Subscription.expects(:list).returns(data: [subscription], has_more: false) ::Stripe::Subscription.expects(:list).returns(data: [subscription], has_more: false)
::Stripe::Invoice.expects(:list).returns(data: [invoice], has_more: false)
DiscourseSubscriptions::Campaign.new.refresh_data DiscourseSubscriptions::Campaign.new.refresh_data
expect(SiteSetting.discourse_subscriptions_campaign_subscribers).to eq 1 expect(SiteSetting.discourse_subscriptions_campaign_subscribers).to eq 1
expect(SiteSetting.discourse_subscriptions_campaign_amount_raised).to eq 10.00 expect(SiteSetting.discourse_subscriptions_campaign_amount_raised).to eq 20.00
end end
it "checks if the goal is completed or not" do it "checks if the goal is completed or not" do
SiteSetting.discourse_subscriptions_campaign_goal = 5 SiteSetting.discourse_subscriptions_campaign_goal = 5
::Stripe::Subscription.expects(:list).returns(data: [subscription], has_more: false) ::Stripe::Subscription.expects(:list).returns(data: [subscription], has_more: false)
::Stripe::Invoice.expects(:list).returns(data: [invoice], has_more: false)
DiscourseSubscriptions::Campaign.new.refresh_data DiscourseSubscriptions::Campaign.new.refresh_data
expect(Discourse.redis.get('subscriptions_goal_met_date')).to be_present expect(Discourse.redis.get('subscriptions_goal_met_date')).to be_present
end end
it "checks if goal is < 90% met after being met" do it "checks if goal is < 90% met after being met" do
SiteSetting.discourse_subscriptions_campaign_goal = 15 SiteSetting.discourse_subscriptions_campaign_goal = 25
Discourse.redis.set('subscriptions_goal_met_date', 10.days.ago) Discourse.redis.set('subscriptions_goal_met_date', 10.days.ago)
::Stripe::Subscription.expects(:list).returns(data: [subscription], has_more: false) ::Stripe::Subscription.expects(:list).returns(data: [subscription], has_more: false)
::Stripe::Invoice.expects(:list).returns(data: [invoice], has_more: false)
DiscourseSubscriptions::Campaign.new.refresh_data DiscourseSubscriptions::Campaign.new.refresh_data
expect(Discourse.redis.get('subscriptions_goal_met_date')).to be_blank expect(Discourse.redis.get('subscriptions_goal_met_date')).to be_blank
@ -89,6 +113,7 @@ describe DiscourseSubscriptions::Campaign do
it "refreshes campaign data with only the campaign product/subscriptions" do it "refreshes campaign data with only the campaign product/subscriptions" do
::Stripe::Subscription.expects(:list).returns(data: [subscription, campaign_subscription], has_more: false) ::Stripe::Subscription.expects(:list).returns(data: [subscription, campaign_subscription], has_more: false)
::Stripe::Invoice.expects(:list).returns(data: [invoice], has_more: false)
DiscourseSubscriptions::Campaign.new.refresh_data DiscourseSubscriptions::Campaign.new.refresh_data