DEV: Stop deleting customers on cancel (#207)
Instead of deleting customers on cancel we will now update the subscription status to canceled. This way we can have some visibility on which users have canceled.
This commit is contained in:
parent
aaa4baec8a
commit
66e8857c20
|
@ -83,6 +83,7 @@ module DiscourseSubscriptions
|
|||
expand: ["data.plan.product"],
|
||||
limit: PAGE_LIMIT,
|
||||
starting_after: start,
|
||||
status: "all",
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -39,11 +39,7 @@ module DiscourseSubscriptions
|
|||
|
||||
user = ::User.find_by_username_or_email(email)
|
||||
|
||||
discourse_customer = Customer.find_by(user_id: user.id)
|
||||
|
||||
if discourse_customer.nil?
|
||||
discourse_customer = Customer.create(user_id: user.id, customer_id: customer_id)
|
||||
end
|
||||
|
||||
Subscription.create(
|
||||
customer_id: discourse_customer.id,
|
||||
|
@ -64,48 +60,60 @@ module DiscourseSubscriptions
|
|||
)
|
||||
when "customer.subscription.created"
|
||||
when "customer.subscription.updated"
|
||||
status = event[:data][:object][:status]
|
||||
subscription = event[:data][:object]
|
||||
status = subscription[:status]
|
||||
return head 200 if !%w[complete active].include?(status)
|
||||
|
||||
customer =
|
||||
Customer.find_by(
|
||||
customer_id: event[:data][:object][:customer],
|
||||
product_id: event[:data][:object][:plan][:product],
|
||||
)
|
||||
customer = find_active_customer(subscription[:customer], subscription[:plan][:product])
|
||||
|
||||
return render_json_error "customer not found" if !customer
|
||||
|
||||
update_status(customer.id, subscription[:id], status)
|
||||
|
||||
user = ::User.find_by(id: customer.user_id)
|
||||
return render_json_error "user not found" if !user
|
||||
|
||||
if group = plan_group(event[:data][:object][:plan])
|
||||
if group = plan_group(subscription[:plan])
|
||||
group.add(user)
|
||||
end
|
||||
when "customer.subscription.deleted"
|
||||
customer =
|
||||
Customer.find_by(
|
||||
customer_id: event[:data][:object][:customer],
|
||||
product_id: event[:data][:object][:plan][:product],
|
||||
)
|
||||
subscription = event[:data][:object]
|
||||
|
||||
customer = find_active_customer(subscription[:customer], subscription[:plan][:product])
|
||||
|
||||
return render_json_error "customer not found" if !customer
|
||||
|
||||
Subscription.find_by(
|
||||
customer_id: customer.id,
|
||||
external_id: event[:data][:object][:id],
|
||||
)&.destroy!
|
||||
update_status(customer.id, subscription[:id], subscription[:status])
|
||||
|
||||
user = ::User.find(customer.user_id)
|
||||
return render_json_error "user not found" if !user
|
||||
|
||||
if group = plan_group(event[:data][:object][:plan])
|
||||
if group = plan_group(subscription[:plan])
|
||||
group.remove(user)
|
||||
end
|
||||
|
||||
customer.destroy!
|
||||
end
|
||||
|
||||
head 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_status(customer_id, subscription_id, status)
|
||||
discourse_subscription =
|
||||
Subscription.find_by(customer_id: customer_id, external_id: subscription_id)
|
||||
discourse_subscription.update(status: status) if discourse_subscription
|
||||
end
|
||||
|
||||
def find_active_customer(customer_id, product_id)
|
||||
Customer
|
||||
.joins(:subscriptions)
|
||||
.where(customer_id: customer_id, product_id: product_id)
|
||||
.where(
|
||||
Subscription.arel_table[:status].eq(nil).or(
|
||||
Subscription.arel_table[:status].not_eq("canceled"),
|
||||
),
|
||||
)
|
||||
.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -150,7 +150,11 @@ module DiscourseSubscriptions
|
|||
)
|
||||
|
||||
if transaction[:object] == "subscription"
|
||||
Subscription.create(customer_id: customer.id, external_id: transaction[:id])
|
||||
Subscription.create(
|
||||
customer_id: customer.id,
|
||||
external_id: transaction[:id],
|
||||
status: transaction[:status],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,7 +173,17 @@ module DiscourseSubscriptions
|
|||
def current_user_products
|
||||
return [] if current_user.nil?
|
||||
|
||||
Customer.select(:product_id).where(user_id: current_user.id).map { |c| c.product_id }.compact
|
||||
Customer
|
||||
.joins(:subscriptions)
|
||||
.where(user_id: current_user.id)
|
||||
.where(
|
||||
Subscription.arel_table[:status].eq(nil).or(
|
||||
Subscription.arel_table[:status].not_eq("canceled"),
|
||||
),
|
||||
)
|
||||
.select(:product_id)
|
||||
.distinct
|
||||
.pluck(:product_id)
|
||||
end
|
||||
|
||||
def serialize_plans(plans)
|
||||
|
|
|
@ -15,6 +15,7 @@ module DiscourseSubscriptions
|
|||
begin
|
||||
customer = Customer.where(user_id: current_user.id)
|
||||
customer_ids = customer.map { |c| c.id } if customer
|
||||
stripe_customer_ids = customer.map { |c| c.customer_id } if customer
|
||||
subscription_ids =
|
||||
Subscription.where("customer_id in (?)", customer_ids).pluck(
|
||||
:external_id,
|
||||
|
@ -24,15 +25,14 @@ module DiscourseSubscriptions
|
|||
|
||||
if subscription_ids
|
||||
plans = ::Stripe::Price.list(expand: ["data.product"], limit: 100)
|
||||
all_subscriptions = []
|
||||
|
||||
customers =
|
||||
::Stripe::Customer.list(email: current_user.email, expand: ["data.subscriptions"])
|
||||
|
||||
subscriptions =
|
||||
customers[:data].map { |sub_customer| sub_customer[:subscriptions][:data] }.flatten(1)
|
||||
|
||||
subscriptions = subscriptions.select { |sub| subscription_ids.include?(sub[:id]) }
|
||||
|
||||
stripe_customer_ids.each do |stripe_customer_id|
|
||||
customer_subscriptions =
|
||||
::Stripe::Subscription.list(customer: stripe_customer_id, status: "all")
|
||||
all_subscriptions.concat(customer_subscriptions[:data])
|
||||
end
|
||||
subscriptions = all_subscriptions.select { |sub| subscription_ids.include?(sub[:id]) }
|
||||
subscriptions.map! do |subscription|
|
||||
plan = plans[:data].find { |p| p[:id] == subscription[:items][:data][0][:price][:id] }
|
||||
subscription.to_h.except!(:plan)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddStatusToSubscriptions < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :discourse_subscriptions_subscriptions, :status, :string
|
||||
end
|
||||
end
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "price",
|
||||
"active": true,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1709840311,
|
||||
"currency": "usd",
|
||||
"custom_unit_amount": null,
|
||||
"livemode": false,
|
||||
"lookup_key": null,
|
||||
"metadata": {
|
||||
"group_name": "subscribers",
|
||||
"trial_period_days": "0"
|
||||
},
|
||||
"nickname": "EA1",
|
||||
"product": {
|
||||
"id": "prod_PhB6IpGhEX14Hi",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [
|
||||
|
||||
],
|
||||
"created": 1709840195,
|
||||
"default_price": null,
|
||||
"description": null,
|
||||
"images": [
|
||||
|
||||
],
|
||||
"livemode": false,
|
||||
"marketing_features": [
|
||||
|
||||
],
|
||||
"metadata": {
|
||||
"description": "Sign up and get access to an exclusive group of enthusiasts just like you!"
|
||||
},
|
||||
"name": "Exclusive Access",
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": "TESTING",
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1709840195,
|
||||
"url": null
|
||||
},
|
||||
"recurring": {
|
||||
"aggregate_usage": null,
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"meter": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"tax_behavior": "unspecified",
|
||||
"tiers_mode": null,
|
||||
"transform_quantity": null,
|
||||
"type": "recurring",
|
||||
"unit_amount": 1000,
|
||||
"unit_amount_decimal": "1000"
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"url": "/v1/prices"
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
{
|
||||
"has_more": false,
|
||||
"data": [
|
||||
{
|
||||
"id": "sub_10z",
|
||||
"object": "subscription",
|
||||
"created": 1714594277,
|
||||
"current_period_end": 1717272677,
|
||||
"current_period_start": 1714594277,
|
||||
"customer": "cus_Q1n43We0YFjnlc",
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_Q1n45g1Ifcluuu",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1714594277,
|
||||
"discounts": [],
|
||||
"metadata": {},
|
||||
"plan": {
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"created": 1709840311,
|
||||
"metadata": {
|
||||
"group_name": "subscribers",
|
||||
"trial_period_days": "0"
|
||||
},
|
||||
"nickname": "EA1",
|
||||
"product": "prod_PhB6IpGhEX14Hi"
|
||||
},
|
||||
"price": {
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "price",
|
||||
"metadata": {
|
||||
"group_name": "subscribers",
|
||||
"trial_period_days": "0"
|
||||
},
|
||||
"nickname": "EA1",
|
||||
"product": "prod_PhB6IpGhEX14Hi"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_1PBjUnEYXaQnncShE7USquGd",
|
||||
"tax_rates": []
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_1PBjUnEYXaQnncShE7USquGd"
|
||||
},
|
||||
"latest_invoice": "in_1PBjUnEYXaQnncSh5c7HZ2jG",
|
||||
"metadata": { "user_id": "108", "username": "f79fc8fde" },
|
||||
"plan": {
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"metadata": { "group_name": "subscribers", "trial_period_days": "0" },
|
||||
"meter": null,
|
||||
"nickname": "EA1",
|
||||
"product": {
|
||||
"id": "prod_PhB6IpGhEX14Hi",
|
||||
"object": "product",
|
||||
"metadata": {
|
||||
"description": "Sign up and get access to an exclusive group of enthusiasts just like you!"
|
||||
},
|
||||
"name": "Exclusive Access"
|
||||
}
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start_date": 1714594277,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": "sub_32b",
|
||||
"object": "subscription",
|
||||
"created": 1714594277,
|
||||
"current_period_end": 1717272677,
|
||||
"current_period_start": 1714594277,
|
||||
"customer": "cus_Q1n43We0YFjnlc",
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_Q1n45g1Ifcluuu",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1714594277,
|
||||
"discounts": [],
|
||||
"metadata": {},
|
||||
"plan": {
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"created": 1709840311,
|
||||
"metadata": {
|
||||
"group_name": "subscribers",
|
||||
"trial_period_days": "0"
|
||||
},
|
||||
"nickname": "EA1",
|
||||
"product": "prod_PhB6IpGhEX14Hi"
|
||||
},
|
||||
"price": {
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "price",
|
||||
"metadata": {
|
||||
"group_name": "subscribers",
|
||||
"trial_period_days": "0"
|
||||
},
|
||||
"nickname": "EA1",
|
||||
"product": "prod_PhB6IpGhEX14Hi"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_1PBjUnEYXaQnncShE7USquGd",
|
||||
"tax_rates": []
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_1PBjUnEYXaQnncShE7USquGd"
|
||||
},
|
||||
"latest_invoice": "in_1PBjUnEYXaQnncSh5c7HZ2jG",
|
||||
"metadata": { "user_id": "108", "username": "f79fc8fde" },
|
||||
"plan": {
|
||||
"id": "price_1OrmlvEYXaQnncShNahrpKvA",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"metadata": { "group_name": "subscribers", "trial_period_days": "0" },
|
||||
"meter": null,
|
||||
"nickname": "EA1",
|
||||
"product": {
|
||||
"id": "prod_PhB6IpGhEX14Hi",
|
||||
"object": "product",
|
||||
"metadata": {
|
||||
"description": "Sign up and get access to an exclusive group of enthusiasts just like you!"
|
||||
},
|
||||
"name": "Exclusive Access"
|
||||
}
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start_date": 1714594277,
|
||||
"status": "canceled"
|
||||
}
|
||||
],
|
||||
"length": 2,
|
||||
"last_record": "sub_1P9aohEYXaQnncSh4wf1wzuL"
|
||||
}
|
|
@ -48,7 +48,7 @@ RSpec.describe DiscourseSubscriptions::Admin::SubscriptionsController do
|
|||
it "gets the subscriptions and products" do
|
||||
::Stripe::Subscription
|
||||
.expects(:list)
|
||||
.with(expand: ["data.plan.product"], limit: 10, starting_after: nil)
|
||||
.with(expand: ["data.plan.product"], limit: 10, starting_after: nil, status: "all")
|
||||
.returns(has_more: false, data: [{ id: "sub_12345" }, { id: "sub_nope" }])
|
||||
get "/s/admin/subscriptions.json"
|
||||
subscriptions = response.parsed_body["data"][0]["id"]
|
||||
|
@ -60,7 +60,7 @@ RSpec.describe DiscourseSubscriptions::Admin::SubscriptionsController do
|
|||
it "handles starting at a different point in the set" do
|
||||
::Stripe::Subscription
|
||||
.expects(:list)
|
||||
.with(expand: ["data.plan.product"], limit: 10, starting_after: "sub_nope")
|
||||
.with(expand: ["data.plan.product"], limit: 10, starting_after: "sub_nope", status: "all")
|
||||
.returns(has_more: false, data: [{ id: "sub_77777" }, { id: "sub_yepnoep" }])
|
||||
get "/s/admin/subscriptions.json", params: { last_record: "sub_nope" }
|
||||
subscriptions = response.parsed_body["data"][0]["id"]
|
||||
|
|
|
@ -27,6 +27,9 @@ RSpec.describe DiscourseSubscriptions::HooksController do
|
|||
let(:customer) do
|
||||
Fabricate(:customer, customer_id: "c_575768", product_id: "p_8654", user_id: user.id)
|
||||
end
|
||||
let!(:subscription) do
|
||||
Fabricate(:subscription, external_id: "sub_12345", customer_id: customer.id, status: nil)
|
||||
end
|
||||
let(:group) { Fabricate(:group, name: "subscribers-group") }
|
||||
|
||||
let(:event_data) do
|
||||
|
@ -43,6 +46,22 @@ RSpec.describe DiscourseSubscriptions::HooksController do
|
|||
}
|
||||
end
|
||||
|
||||
let(:customer_subscription_deleted_data) do
|
||||
{
|
||||
object: {
|
||||
id: subscription.external_id,
|
||||
customer: customer.customer_id,
|
||||
plan: {
|
||||
product: customer.product_id,
|
||||
metadata: {
|
||||
group_name: group.name,
|
||||
},
|
||||
},
|
||||
status: "canceled",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
let(:checkout_session_completed_data) do
|
||||
{
|
||||
object: {
|
||||
|
@ -217,7 +236,7 @@ RSpec.describe DiscourseSubscriptions::HooksController do
|
|||
|
||||
describe "customer.subscription.deleted" do
|
||||
before do
|
||||
event = { type: "customer.subscription.deleted", data: event_data }
|
||||
event = { type: "customer.subscription.deleted", data: customer_subscription_deleted_data }
|
||||
|
||||
::Stripe::Webhook.stubs(:construct_event).returns(event)
|
||||
|
||||
|
@ -225,7 +244,9 @@ RSpec.describe DiscourseSubscriptions::HooksController do
|
|||
end
|
||||
|
||||
it "deletes the customer" do
|
||||
expect { post "/s/hooks.json" }.to change { DiscourseSubscriptions::Customer.count }.by(-1)
|
||||
expect { post "/s/hooks.json" }.to change {
|
||||
DiscourseSubscriptions::Subscription.where(status: "canceled").count
|
||||
}.by(+1)
|
||||
|
||||
expect(response.status).to eq 200
|
||||
end
|
||||
|
|
|
@ -69,6 +69,10 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do
|
|||
end
|
||||
|
||||
describe "#index" do
|
||||
let(:customer) do
|
||||
Fabricate(:customer, product_id: product[:id], user_id: user.id, customer_id: "x")
|
||||
end
|
||||
|
||||
it "gets products" do
|
||||
::Stripe::Product
|
||||
.expects(:list)
|
||||
|
@ -94,7 +98,8 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do
|
|||
end
|
||||
|
||||
it "is subscribed" do
|
||||
Fabricate(:customer, product_id: product[:id], user_id: user.id, customer_id: "x")
|
||||
Fabricate(:subscription, external_id: "sub_12345", customer_id: customer.id, status: nil)
|
||||
|
||||
::Stripe::Product
|
||||
.expects(:list)
|
||||
.with({ ids: product_ids, active: true })
|
||||
|
|
|
@ -37,62 +37,48 @@ RSpec.describe DiscourseSubscriptions::User::SubscriptionsController do
|
|||
|
||||
before do
|
||||
sign_in(user)
|
||||
Fabricate(:subscription, customer_id: customer.id, external_id: "sub_1234")
|
||||
Fabricate(:subscription, customer_id: customer.id, external_id: "sub_10z")
|
||||
end
|
||||
|
||||
describe "index" do
|
||||
let(:plans) do
|
||||
{
|
||||
data: [
|
||||
{ id: "plan_1", product: { name: "ACME Subscriptions" } },
|
||||
{ id: "plan_2", product: { name: "ACME Other Subscriptions" } },
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
let(:customers) do
|
||||
{
|
||||
data: [
|
||||
{
|
||||
id: "cus_23456",
|
||||
subscriptions: {
|
||||
data: [
|
||||
{ id: "sub_1234", items: { data: [price: { id: "plan_1" }] } },
|
||||
{ id: "sub_4567", items: { data: [price: { id: "plan_2" }] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
plans_json =
|
||||
File.read(
|
||||
Rails.root.join(
|
||||
"plugins",
|
||||
"discourse-subscriptions",
|
||||
"spec",
|
||||
"fixtures",
|
||||
"json",
|
||||
"stripe-price-list.json",
|
||||
),
|
||||
)
|
||||
|
||||
it "gets subscriptions" do
|
||||
::Stripe::Price.expects(:list).with(expand: ["data.product"], limit: 100).returns(plans)
|
||||
::Stripe::Price.stubs(:list).returns(JSON.parse(plans_json, symbolize_names: true))
|
||||
|
||||
::Stripe::Customer
|
||||
.expects(:list)
|
||||
.with(email: user.email, expand: ["data.subscriptions"])
|
||||
.returns(customers)
|
||||
subscriptions_json =
|
||||
File.read(
|
||||
Rails.root.join(
|
||||
"plugins",
|
||||
"discourse-subscriptions",
|
||||
"spec",
|
||||
"fixtures",
|
||||
"json",
|
||||
"stripe-subscription-list.json",
|
||||
),
|
||||
)
|
||||
|
||||
::Stripe::Subscription.stubs(:list).returns(
|
||||
JSON.parse(subscriptions_json, symbolize_names: true),
|
||||
)
|
||||
|
||||
get "/s/user/subscriptions.json"
|
||||
|
||||
subscription = response.parsed_body.first
|
||||
subscription = JSON.parse(response.body, symbolize_names: true).first
|
||||
|
||||
expect(subscription).to eq(
|
||||
"id" => "sub_1234",
|
||||
"items" => {
|
||||
"data" => [{ "price" => { "id" => "plan_1" } }],
|
||||
},
|
||||
"plan" => {
|
||||
"id" => "plan_1",
|
||||
"product" => {
|
||||
"name" => "ACME Subscriptions",
|
||||
},
|
||||
},
|
||||
"product" => {
|
||||
"name" => "ACME Subscriptions",
|
||||
},
|
||||
)
|
||||
expect(subscription[:id]).to eq("sub_10z")
|
||||
expect(subscription[:items][:data][0][:plan][:id]).to eq("price_1OrmlvEYXaQnncShNahrpKvA")
|
||||
expect(subscription[:product][:name]).to eq("Exclusive Access")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -100,7 +86,7 @@ RSpec.describe DiscourseSubscriptions::User::SubscriptionsController do
|
|||
it "updates the payment method for subscription" do
|
||||
::Stripe::Subscription.expects(:update).once
|
||||
::Stripe::PaymentMethod.expects(:attach).once
|
||||
put "/s/user/subscriptions/sub_1234.json", params: { payment_method: "pm_abc123abc" }
|
||||
put "/s/user/subscriptions/sub_10z.json", params: { payment_method: "pm_abc123abc" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Pages
|
||||
class AdminSubscriptionSubscription < PageObjects::Pages::Base
|
||||
SUBSCRIPTIONS_TABLE_SELECTOR = "table.discourse-patrons-table"
|
||||
|
||||
def visit_subscriptions
|
||||
visit("/admin/plugins/discourse-subscriptions/subscriptions")
|
||||
self
|
||||
end
|
||||
|
||||
def has_subscription?(id)
|
||||
has_css?("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr", text: id)
|
||||
self
|
||||
end
|
||||
|
||||
def subscription_row(id)
|
||||
find("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr", text: id)
|
||||
end
|
||||
|
||||
def has_number_of_subscriptions?(count)
|
||||
has_css?("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr", count:)
|
||||
self
|
||||
end
|
||||
|
||||
def click_cancel_nth_row(row)
|
||||
find("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr:nth-child(#{row}) button.btn-danger").click()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Pages
|
||||
class UserBillingSubscription < PageObjects::Pages::Base
|
||||
SUBSCRIPTIONS_TABLE_SELECTOR = "table.discourse-subscriptions-user-table"
|
||||
|
||||
def visit_subscriptions
|
||||
visit("/my/billing/subscriptions")
|
||||
self
|
||||
end
|
||||
|
||||
def has_subscription?(id)
|
||||
has_css?("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr", text: id)
|
||||
self
|
||||
end
|
||||
|
||||
def subscription_row(id)
|
||||
find("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr", text: id)
|
||||
end
|
||||
|
||||
def has_number_of_subscriptions?(count)
|
||||
has_css?("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr", count:)
|
||||
self
|
||||
end
|
||||
|
||||
def click_cancel_nth_row(row)
|
||||
find("#{SUBSCRIPTIONS_TABLE_SELECTOR} tr:nth-child(#{row}) button.btn-danger").click()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -67,14 +67,4 @@ RSpec.describe "Pricing Table", type: :system, js: true do
|
|||
text: "There are currently no products available.",
|
||||
)
|
||||
end
|
||||
|
||||
# Commenting out for now, not sure how to stub network reqeusts made in the browser to stripe
|
||||
# it "Shows a pricing table when setup" do
|
||||
# SiteSetting.discourse_subscriptions_pricing_table = '{"insert-pricing-table-embed-code"}'
|
||||
|
||||
# visit("/")
|
||||
# find("li.nav-item_subscribe a").click
|
||||
|
||||
# expect(page).to have_selector('stripe-pricing-table')
|
||||
# end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe "Subscription products", type: :system do
|
||||
fab!(:admin)
|
||||
fab!(:user)
|
||||
fab!(:product) { Fabricate(:product, external_id: "prod_OiK") }
|
||||
fab!(:customer) do
|
||||
Fabricate(:customer, customer_id: "cus_Q1n", product_id: product.external_id, user_id: user.id)
|
||||
end
|
||||
fab!(:subscription) do
|
||||
Fabricate(:subscription, customer_id: customer.id, external_id: "sub_10z", status: "active")
|
||||
end
|
||||
fab!(:subscription) do
|
||||
Fabricate(:subscription, customer_id: customer.id, external_id: "sub_32b", status: "canceled")
|
||||
end
|
||||
let(:dialog) { PageObjects::Components::Dialog.new }
|
||||
let(:product_subscriptions_page) { PageObjects::Pages::AdminSubscriptionProduct.new }
|
||||
let(:admin_subscriptions_page) { PageObjects::Pages::AdminSubscriptionSubscription.new }
|
||||
let(:user_billing_subscriptions_page) { PageObjects::Pages::UserBillingSubscription.new }
|
||||
|
||||
before do
|
||||
SiteSetting.discourse_subscriptions_enabled = true
|
||||
|
||||
SiteSetting.discourse_subscriptions_secret_key = "sk_test_51xuu"
|
||||
SiteSetting.discourse_subscriptions_public_key = "pk_test_51xuu"
|
||||
|
||||
# # this needs to be stubbed or it will try to make a request to stripe
|
||||
one_product = {
|
||||
id: "prod_OiK",
|
||||
active: true,
|
||||
name: "Tomtom",
|
||||
metadata: {
|
||||
description: "Photos of tomtom",
|
||||
repurchaseable: true,
|
||||
},
|
||||
}
|
||||
|
||||
plans_json =
|
||||
File.read(
|
||||
Rails.root.join(
|
||||
"plugins",
|
||||
"discourse-subscriptions",
|
||||
"spec",
|
||||
"fixtures",
|
||||
"json",
|
||||
"stripe-price-list.json",
|
||||
),
|
||||
)
|
||||
|
||||
subscriptions_json =
|
||||
File.read(
|
||||
Rails.root.join(
|
||||
"plugins",
|
||||
"discourse-subscriptions",
|
||||
"spec",
|
||||
"fixtures",
|
||||
"json",
|
||||
"stripe-subscription-list.json",
|
||||
),
|
||||
)
|
||||
|
||||
::Stripe::Product.stubs(:list).returns({ data: [one_product] })
|
||||
::Stripe::Product.stubs(:delete).returns({ id: "prod_OiK" })
|
||||
::Stripe::Product.stubs(:retrieve).returns(one_product)
|
||||
::Stripe::Price.stubs(:list).returns(JSON.parse(plans_json, symbolize_names: true))
|
||||
::Stripe::Subscription.stubs(:list).returns(
|
||||
JSON.parse(subscriptions_json, symbolize_names: true),
|
||||
)
|
||||
end
|
||||
|
||||
it "shows active and canceled subscriptions for admins" do
|
||||
sign_in(admin)
|
||||
|
||||
active_subscription_row =
|
||||
admin_subscriptions_page.visit_subscriptions.subscription_row("sub_10z")
|
||||
expect(active_subscription_row).to have_text("active")
|
||||
canceled_subscription_row =
|
||||
admin_subscriptions_page.visit_subscriptions.subscription_row("sub_32b")
|
||||
expect(canceled_subscription_row).to have_text("canceled")
|
||||
end
|
||||
|
||||
it "shows active and canceled subscriptions for users" do
|
||||
sign_in(user)
|
||||
|
||||
active_subscription_row =
|
||||
user_billing_subscriptions_page.visit_subscriptions.subscription_row("sub_10z")
|
||||
expect(active_subscription_row).to have_text("active")
|
||||
canceled_subscription_row =
|
||||
user_billing_subscriptions_page.visit_subscriptions.subscription_row("sub_32b")
|
||||
expect(canceled_subscription_row).to have_text("canceled")
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue