569 lines
18 KiB
Ruby
569 lines
18 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "rails_helper"
|
|
|
|
RSpec.describe DiscourseSubscriptions::SubscribeController do
|
|
let(:user) { Fabricate(:user) }
|
|
let(:campaign_user) { Fabricate(:user) }
|
|
|
|
before { SiteSetting.discourse_subscriptions_enabled = true }
|
|
|
|
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"
|
|
SiteSetting.discourse_subscriptions_secret_key = "secret-key"
|
|
end
|
|
|
|
describe "#index" do
|
|
it "gets products" do
|
|
::Stripe::Product
|
|
.expects(:list)
|
|
.with({ ids: product_ids, active: true })
|
|
.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(:customer, product_id: product[:id], user_id: user.id, customer_id: "x")
|
|
::Stripe::Product
|
|
.expects(:list)
|
|
.with({ ids: product_ids, active: true })
|
|
.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 })
|
|
.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 = nil
|
|
|
|
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
|
|
::Stripe::Product.expects(:retrieve).with("prod_walterwhite").returns(product)
|
|
::Stripe::Price
|
|
.expects(:list)
|
|
.with(active: true, product: "prod_walterwhite")
|
|
.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
|
|
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
|
|
before { sign_in(user) }
|
|
|
|
describe "#create" do
|
|
before { ::Stripe::Customer.expects(:create).returns(id: "cus_1234") }
|
|
|
|
it "creates a subscription" do
|
|
::Stripe::Price.expects(:retrieve).returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(
|
|
customer: "cus_1234",
|
|
items: [price: "plan_1234"],
|
|
metadata: {
|
|
user_id: user.id,
|
|
username: user.username_lower,
|
|
},
|
|
trial_period_days: 0,
|
|
promotion_code: nil,
|
|
)
|
|
.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 one time payment subscription" do
|
|
::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: "open",
|
|
payment_intent: "pi_123",
|
|
)
|
|
|
|
::Stripe::Invoice.expects(:retrieve).returns(
|
|
id: "in_123",
|
|
status: "open",
|
|
payment_intent: "pi_123",
|
|
)
|
|
|
|
::Stripe::PaymentIntent.expects(:retrieve).returns(status: "successful")
|
|
|
|
::Stripe::Invoice.expects(:pay).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).returns(type: "recurring", metadata: {}).twice
|
|
::Stripe::Subscription.expects(:create).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")
|
|
|
|
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).returns(type: "recurring", metadata: {})
|
|
::Stripe::Subscription.expects(:create).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).returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
::Stripe::PromotionCode.expects(:list).with({ code: "invalid" }).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" })
|
|
.returns(data: [{ id: "promo123", coupon: { id: "c123" } }])
|
|
end
|
|
|
|
it "applies promo code to recurring subscription" do
|
|
::Stripe::Price.expects(:retrieve).returns(
|
|
type: "recurring",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
trial_period_days: 0,
|
|
},
|
|
)
|
|
|
|
::Stripe::Subscription
|
|
.expects(:create)
|
|
.with(
|
|
customer: "cus_1234",
|
|
items: [price: "plan_1234"],
|
|
metadata: {
|
|
user_id: user.id,
|
|
username: user.username_lower,
|
|
},
|
|
trial_period_days: 0,
|
|
promotion_code: "promo123",
|
|
)
|
|
.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).returns(
|
|
type: "one_time",
|
|
product: "product_12345",
|
|
metadata: {
|
|
group_name: "awesome",
|
|
},
|
|
)
|
|
|
|
::Stripe::InvoiceItem.expects(:create).with(
|
|
customer: "cus_1234",
|
|
price: "plan_1234",
|
|
discounts: [{ coupon: "c123" }],
|
|
)
|
|
|
|
::Stripe::Invoice.expects(:create).returns(status: "open", id: "in_123")
|
|
|
|
::Stripe::Invoice.expects(:finalize_invoice).returns(
|
|
id: "in_123",
|
|
status: "open",
|
|
payment_intent: "pi_123",
|
|
)
|
|
|
|
::Stripe::Invoice.expects(:retrieve).returns(
|
|
id: "in_123",
|
|
status: "open",
|
|
payment_intent: "pi_123",
|
|
)
|
|
|
|
::Stripe::PaymentIntent.expects(:retrieve).returns(status: "successful")
|
|
|
|
::Stripe::Invoice.expects(:pay).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
|
|
::Stripe::Price.expects(:retrieve).returns(
|
|
id: "plan_1234",
|
|
product: "prod_1234",
|
|
metadata: {
|
|
},
|
|
)
|
|
::Stripe::Subscription.expects(:retrieve).returns(
|
|
id: "sub_123",
|
|
customer: "cus_1234",
|
|
status: "active",
|
|
)
|
|
|
|
expect {
|
|
post "/s/finalize.json", params: { plan: "plan_1234", transaction: "sub_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
end
|
|
|
|
context "with one-time payment" do
|
|
it "finalizes the one-time payment" do
|
|
::Stripe::Price.expects(:retrieve).returns(
|
|
id: "plan_1234",
|
|
product: "prod_1234",
|
|
metadata: {
|
|
},
|
|
)
|
|
::Stripe::Invoice.expects(:retrieve).returns(
|
|
id: "in_123",
|
|
customer: "cus_1234",
|
|
status: "paid",
|
|
)
|
|
|
|
expect {
|
|
post "/s/finalize.json", params: { plan: "plan_1234", transaction: "in_1234" }
|
|
}.to change { DiscourseSubscriptions::Customer.count }
|
|
end
|
|
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).returns(id: "cus_1234")
|
|
::Stripe::Subscription.expects(:create).returns(status: "active")
|
|
end
|
|
|
|
it "does not add the user to the admins group" do
|
|
::Stripe::Price.expects(:retrieve).returns(
|
|
type: "recurring",
|
|
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).returns(
|
|
type: "recurring",
|
|
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).returns(id: "cus_1234")
|
|
::Stripe::Price.expects(:retrieve).returns(
|
|
type: "recurring",
|
|
metadata: {
|
|
group_name: group_name,
|
|
},
|
|
)
|
|
end
|
|
|
|
it "does not add the user to the group when subscription fails" do
|
|
::Stripe::Subscription.expects(:create).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).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).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
|