mirror of
https://github.com/discourse/discourse.git
synced 2026-08-14 13:58:53 +08:00
Previously, subscription detail and checkout paths could accept Stripe products or prices that were not published in Discourse. This change checks the local product list before showing products, creating purchases, or finalizing pending purchases, keeping checkout behavior consistent with the public catalog.
885 lines
30 KiB
Ruby
Vendored
885 lines
30 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe DiscourseSubscriptions::SubscribeController do
|
|
let(:user) { Fabricate(:user) }
|
|
let(:campaign_user) { Fabricate(:user) }
|
|
|
|
before do
|
|
SiteSetting.discourse_subscriptions_enabled = true
|
|
SiteSetting.discourse_subscriptions_secret_key = "secret-key"
|
|
end
|
|
|
|
context "when showing products" do
|
|
let(:product) do
|
|
{
|
|
id: "prodct_23456",
|
|
name: "Very Special Product",
|
|
metadata: {
|
|
description:
|
|
"Many people listened to my phone call with the Ukrainian President while it was being made",
|
|
repurchaseable: false,
|
|
},
|
|
otherstuff: true,
|
|
}
|
|
end
|
|
|
|
let(:prices) do
|
|
{
|
|
data: [
|
|
{
|
|
id: "plan_id123",
|
|
unit_amount: 1220,
|
|
currency: "aud",
|
|
recurring: {
|
|
interval: "year",
|
|
},
|
|
metadata: {
|
|
},
|
|
},
|
|
{
|
|
id: "plan_id234",
|
|
unit_amount: 1399,
|
|
currency: "usd",
|
|
recurring: {
|
|
interval: "year",
|
|
},
|
|
metadata: {
|
|
},
|
|
},
|
|
{
|
|
id: "plan_id678",
|
|
unit_amount: 1000,
|
|
currency: "aud",
|
|
recurring: {
|
|
interval: "week",
|
|
},
|
|
metadata: {
|
|
},
|
|
},
|
|
],
|
|
}
|
|
end
|
|
|
|
let(:product_ids) { ["prodct_23456"] }
|
|
|
|
before do
|
|
sign_in(user)
|
|
Fabricate(:product, external_id: "prodct_23456")
|
|
SiteSetting.discourse_subscriptions_public_key = "public-key"
|
|
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)
|
|
.with({ ids: product_ids, active: true }, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(data: [product])
|
|
|
|
get "/s.json"
|
|
|
|
expect(response.parsed_body).to eq(
|
|
[
|
|
{
|
|
"id" => "prodct_23456",
|
|
"name" => "Very Special Product",
|
|
"description" =>
|
|
PrettyText.cook(
|
|
"Many people listened to my phone call with the Ukrainian President while it was being made",
|
|
),
|
|
"subscribed" => false,
|
|
"repurchaseable" => false,
|
|
},
|
|
],
|
|
)
|
|
end
|
|
|
|
it "is subscribed" do
|
|
Fabricate(:subscription, external_id: "sub_12345", customer_id: customer.id, status: nil)
|
|
|
|
::Stripe::Product
|
|
.expects(:list)
|
|
.with({ ids: product_ids, active: true }, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(data: [product])
|
|
|
|
get "/s.json"
|
|
data = response.parsed_body
|
|
expect(data.first["subscribed"]).to eq true
|
|
end
|
|
|
|
it "is not subscribed" do
|
|
DiscourseSubscriptions::Customer.delete_all
|
|
::Stripe::Product
|
|
.expects(:list)
|
|
.with({ ids: product_ids, active: true }, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(data: [product])
|
|
|
|
get "/s.json"
|
|
data = response.parsed_body
|
|
expect(data.first["subscribed"]).to eq false
|
|
end
|
|
end
|
|
|
|
describe "#get_contributors" do
|
|
before do
|
|
Fabricate(:product, external_id: "prod_campaign")
|
|
Fabricate(:customer, product_id: "prodct_23456", user_id: user.id, customer_id: "x")
|
|
Fabricate(
|
|
:customer,
|
|
product_id: "prod_campaign",
|
|
user_id: campaign_user.id,
|
|
customer_id: "y",
|
|
)
|
|
end
|
|
context "when not showing contributors" do
|
|
it "returns nothing if not set to show contributors" do
|
|
SiteSetting.discourse_subscriptions_campaign_show_contributors = false
|
|
get "/s/contributors.json"
|
|
|
|
data = response.parsed_body
|
|
expect(data).to be_empty
|
|
end
|
|
end
|
|
|
|
context "when showing contributors" do
|
|
before { SiteSetting.discourse_subscriptions_campaign_show_contributors = true }
|
|
|
|
it "filters users by campaign product if set" do
|
|
SiteSetting.discourse_subscriptions_campaign_product = "prod_campaign"
|
|
|
|
get "/s/contributors.json"
|
|
|
|
data = response.parsed_body
|
|
expect(data.first["id"]).to eq campaign_user.id
|
|
expect(data.length).to eq 1
|
|
end
|
|
|
|
it "shows all purchases if campaign product not set" do
|
|
SiteSetting.discourse_subscriptions_campaign_product = ""
|
|
|
|
get "/s/contributors.json"
|
|
|
|
data = response.parsed_body
|
|
expect(data.length).to eq 2
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#show" do
|
|
it "retrieves the product" do
|
|
Fabricate(:product, external_id: "prod_walterwhite")
|
|
|
|
::Stripe::Product
|
|
.expects(:retrieve)
|
|
.with("prod_walterwhite", DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(product)
|
|
::Stripe::Price
|
|
.expects(:list)
|
|
.with(
|
|
{ active: true, product: "prod_walterwhite" },
|
|
DiscourseSubscriptions::Stripe.request_opts,
|
|
)
|
|
.returns(prices)
|
|
get "/s/prod_walterwhite.json"
|
|
|
|
expect(response.parsed_body).to eq(
|
|
{
|
|
"product" => {
|
|
"id" => "prodct_23456",
|
|
"name" => "Very Special Product",
|
|
"description" =>
|
|
PrettyText.cook(
|
|
"Many people listened to my phone call with the Ukrainian President while it was being made",
|
|
),
|
|
"subscribed" => false,
|
|
"repurchaseable" => false,
|
|
},
|
|
"plans" => [
|
|
{
|
|
"currency" => "aud",
|
|
"id" => "plan_id123",
|
|
"recurring" => {
|
|
"interval" => "year",
|
|
},
|
|
"unit_amount" => 1220,
|
|
},
|
|
{
|
|
"currency" => "usd",
|
|
"id" => "plan_id234",
|
|
"recurring" => {
|
|
"interval" => "year",
|
|
},
|
|
"unit_amount" => 1399,
|
|
},
|
|
{
|
|
"currency" => "aud",
|
|
"id" => "plan_id678",
|
|
"recurring" => {
|
|
"interval" => "week",
|
|
},
|
|
"unit_amount" => 1000,
|
|
},
|
|
],
|
|
},
|
|
)
|
|
end
|
|
|
|
it "returns 404 for a product outside the allowlist" do
|
|
::Stripe::Product.expects(:retrieve).never
|
|
::Stripe::Price.expects(:list).never
|
|
|
|
get "/s/prod_hidden.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when creating subscriptions" do
|
|
context "when unauthenticated" do
|
|
it "does not create a subscription" do
|
|
::Stripe::Customer.expects(:create).never
|
|
::Stripe::Price.expects(:retrieve).never
|
|
::Stripe::Subscription.expects(:create).never
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
end
|
|
end
|
|
|
|
context "when authenticated" do
|
|
fab!(:published_product) { Fabricate(:product, external_id: "product_12345") }
|
|
|
|
before { sign_in(user) }
|
|
|
|
describe "#create" do
|
|
before do
|
|
::Stripe::Customer
|
|
.stubs(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "cus_1234")
|
|
end
|
|
|
|
it "creates a subscription without automatic_tax param" do
|
|
SiteSetting.discourse_subscriptions_enable_automatic_tax = false
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
expected_subscription_params = {
|
|
customer: "cus_1234",
|
|
items: [{ price: "plan_1234" }],
|
|
metadata: {
|
|
user_id: user.id,
|
|
username: user.username_lower,
|
|
},
|
|
trial_period_days: 0,
|
|
promotion_code: nil,
|
|
}
|
|
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with do |params, opts|
|
|
params == expected_subscription_params && !params.key?(:automatic_tax) &&
|
|
opts == DiscourseSubscriptions::Stripe.request_opts
|
|
end
|
|
.returns(status: "active", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
|
|
it "creates a subscription with automatic tax" do
|
|
SiteSetting.discourse_subscriptions_enable_automatic_tax = true
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
expected_subscription_params = {
|
|
customer: "cus_1234",
|
|
items: [{ price: "plan_1234" }],
|
|
metadata: {
|
|
user_id: user.id,
|
|
username: user.username_lower,
|
|
},
|
|
trial_period_days: 0,
|
|
promotion_code: nil,
|
|
automatic_tax: {
|
|
enabled: true,
|
|
},
|
|
}
|
|
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(expected_subscription_params, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "active", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
|
|
it "rejects a plan outside the allowlist" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with("price_hidden", DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "prod_hidden",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
::Stripe::Customer.expects(:create).never
|
|
::Stripe::Subscription.expects(:create).never
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "price_hidden", source: "tok_1234" }
|
|
}.not_to change { DiscourseSubscriptions::Customer.count }
|
|
|
|
expect(response.status).to eq(404)
|
|
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)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "one_time",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
},
|
|
)
|
|
|
|
::Stripe::InvoiceItem.expects(:create).with(
|
|
anything,
|
|
DiscourseSubscriptions::Stripe.request_opts,
|
|
)
|
|
|
|
::Stripe::Invoice
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "open", id: "in_123")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:finalize_invoice)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.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 without automatic tax" do
|
|
SiteSetting.discourse_subscriptions_enable_automatic_tax = false
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "one_time",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
},
|
|
)
|
|
|
|
::Stripe::InvoiceItem.expects(:create).with(
|
|
anything,
|
|
DiscourseSubscriptions::Stripe.request_opts,
|
|
)
|
|
|
|
expected_one_time_params = { customer: "cus_1234" }
|
|
|
|
::Stripe::Invoice
|
|
.expects(:create)
|
|
.with do |params, opts|
|
|
params == expected_one_time_params && !params.key?(:automatic_tax) &&
|
|
opts == DiscourseSubscriptions::Stripe.request_opts
|
|
end
|
|
.returns(status: "open", id: "in_123")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:finalize_invoice)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", status: "open", payment_intent: "pi_123")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", status: "open", payment_intent: "pi_123")
|
|
|
|
::Stripe::PaymentIntent
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "successful")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:pay)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "paid", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
|
|
it "creates a one time payment subscription" do
|
|
SiteSetting.discourse_subscriptions_enable_automatic_tax = true
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "one_time",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
},
|
|
)
|
|
|
|
::Stripe::InvoiceItem.expects(:create).with(
|
|
anything,
|
|
DiscourseSubscriptions::Stripe.request_opts,
|
|
)
|
|
|
|
expected_one_time_params = { customer: "cus_1234", automatic_tax: { enabled: true } }
|
|
|
|
::Stripe::Invoice
|
|
.expects(:create)
|
|
.with(expected_one_time_params, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "open", id: "in_123")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:finalize_invoice)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", status: "open", payment_intent: "pi_123")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", status: "open", payment_intent: "pi_123")
|
|
|
|
::Stripe::PaymentIntent
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "successful")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:pay)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "paid", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
|
|
it "creates a customer model" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(type: "recurring", product: "product_12345", metadata: {})
|
|
.twice
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "active", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
|
|
::Stripe::Customer.expects(:retrieve).with(
|
|
"cus_1234",
|
|
DiscourseSubscriptions::Stripe.request_opts,
|
|
)
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_5678", source: "tok_5678" }
|
|
}.not_to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
|
|
context "with customer name & address" do
|
|
it "creates a customer & subscription when a customer address is provided" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(type: "recurring", product: "product_12345", metadata: {})
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "active", customer: "cus_1234")
|
|
expect {
|
|
post "/s/create.json",
|
|
params: {
|
|
plan: "plan_1234",
|
|
source: "tok_1234",
|
|
cardholder_name: "A. Customer",
|
|
cardholder_address: {
|
|
line1: "123 Main Street",
|
|
city: "Anywhere",
|
|
state: "VT",
|
|
country: "US",
|
|
postal_code: "12345",
|
|
},
|
|
}
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
end
|
|
|
|
context "with promo code" do
|
|
context "with invalid code" do
|
|
it "prevents use of invalid coupon codes" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
::Stripe::PromotionCode
|
|
.expects(:list)
|
|
.with({ code: "invalid" }, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(data: [])
|
|
|
|
post "/s/create.json",
|
|
params: {
|
|
plan: "plan_1234",
|
|
source: "tok_1234",
|
|
promo: "invalid",
|
|
}
|
|
|
|
data = response.parsed_body
|
|
expect(data["errors"]).not_to be_blank
|
|
end
|
|
end
|
|
|
|
context "with valid code" do
|
|
before do
|
|
::Stripe::PromotionCode
|
|
.expects(:list)
|
|
.with({ code: "123" }, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(data: [{ id: "promo123", coupon: { id: "c123" } }])
|
|
end
|
|
|
|
it "applies promo code to recurring subscription" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
expected_subscription_params = {
|
|
customer: "cus_1234",
|
|
items: [price: "plan_1234"],
|
|
metadata: {
|
|
user_id: user.id,
|
|
username: user.username_lower,
|
|
},
|
|
trial_period_days: 0,
|
|
promotion_code: "promo123",
|
|
}
|
|
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(expected_subscription_params, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "active", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json",
|
|
params: {
|
|
plan: "plan_1234",
|
|
source: "tok_1234",
|
|
promo: "123",
|
|
}
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
|
|
it "applies promo code to one time purchase" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "one_time",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
},
|
|
)
|
|
|
|
::Stripe::Invoice
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "open", id: "in_123")
|
|
|
|
::Stripe::InvoiceItem.expects(:create).with(
|
|
{
|
|
customer: "cus_1234",
|
|
price: "plan_1234",
|
|
discounts: [{ coupon: "c123" }],
|
|
invoice: "in_123",
|
|
},
|
|
DiscourseSubscriptions::Stripe.request_opts,
|
|
)
|
|
|
|
::Stripe::Invoice
|
|
.expects(:finalize_invoice)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", status: "open", payment_intent: "pi_123")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", status: "open", payment_intent: "pi_123")
|
|
|
|
::Stripe::PaymentIntent
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "successful")
|
|
|
|
::Stripe::Invoice
|
|
.expects(:pay)
|
|
.with(anything, anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "paid", customer: "cus_1234")
|
|
|
|
expect {
|
|
post "/s/create.json",
|
|
params: {
|
|
plan: "plan_1234",
|
|
source: "tok_1234",
|
|
promo: "123",
|
|
}
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#finalize strong customer authenticated transaction" do
|
|
context "with subscription" do
|
|
it "finalizes the subscription" do
|
|
server_session["pending_subscription"] = {
|
|
transaction_id: "sub_1234",
|
|
plan_id: "plan_1234",
|
|
}
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "plan_1234", product: "product_12345", metadata: {})
|
|
::Stripe::Subscription
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "sub_123", customer: "cus_1234", status: "active")
|
|
|
|
expect { post "/s/finalize.json" }.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
end
|
|
|
|
context "with one-time payment" do
|
|
it "finalizes the one-time payment" do
|
|
server_session["pending_subscription"] = {
|
|
transaction_id: "in_1234",
|
|
plan_id: "plan_1234",
|
|
}
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "plan_1234", product: "product_12345", metadata: {})
|
|
::Stripe::Invoice
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "in_123", customer: "cus_1234", status: "paid")
|
|
|
|
expect { post "/s/finalize.json" }.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
end
|
|
|
|
it "returns 403 with no pending server session" do
|
|
post "/s/finalize.json"
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "prevents replay by rejecting a second finalize" do
|
|
server_session["pending_subscription"] = {
|
|
transaction_id: "sub_123",
|
|
plan_id: "plan_1234",
|
|
}
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "plan_1234", product: "product_12345", metadata: {})
|
|
::Stripe::Subscription
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "sub_123", customer: "cus_1234", status: "active")
|
|
|
|
post "/s/finalize.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
post "/s/finalize.json"
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "rejects a pending plan outside the allowlist" do
|
|
server_session["pending_subscription"] = {
|
|
transaction_id: "sub_123",
|
|
plan_id: "price_hidden",
|
|
}
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with("price_hidden", DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "price_hidden", product: "prod_hidden", metadata: {})
|
|
::Stripe::Subscription.expects(:retrieve).never
|
|
|
|
expect { post "/s/finalize.json" }.not_to change {
|
|
DiscourseSubscriptions::Customer.count
|
|
}
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
end
|
|
|
|
describe "user groups" do
|
|
let(:group_name) { "group-123" }
|
|
let(:group) { Fabricate(:group, name: group_name) }
|
|
|
|
context "with unauthorized group" do
|
|
before do
|
|
::Stripe::Customer
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "cus_1234")
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "active")
|
|
end
|
|
|
|
it "does not add the user to the admins group" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "admins",
|
|
},
|
|
)
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
expect(user.admin).to eq false
|
|
end
|
|
|
|
it "does not add the user to other group" do
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "other",
|
|
},
|
|
)
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
expect(user.groups).to be_empty
|
|
end
|
|
end
|
|
|
|
context "when plan has group in metadata" do
|
|
before do
|
|
::Stripe::Customer
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(id: "cus_1234")
|
|
::Stripe::Price
|
|
.expects(:retrieve)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: group_name,
|
|
},
|
|
)
|
|
end
|
|
|
|
it "does not add the user to the group when subscription fails" do
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "failed")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.not_to change { group.users.count }
|
|
|
|
expect(user.groups).to be_empty
|
|
end
|
|
|
|
it "adds the user to the group when the subscription is active" do
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "active")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { group.users.count }
|
|
|
|
expect(user.groups).not_to be_empty
|
|
end
|
|
|
|
it "adds the user to the group when the subscription is trialing" do
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(anything, DiscourseSubscriptions::Stripe.request_opts)
|
|
.returns(status: "trialing")
|
|
|
|
expect {
|
|
post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" }
|
|
}.to change { group.users.count }
|
|
|
|
expect(user.groups).not_to be_empty
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|