mirror of
https://github.com/discourse/discourse-subscriptions.git
synced 2025-07-07 15:02:23 +00:00
FEATURE: Give option to repurchase products multiple times (#46)
Feature requested here: https://meta.discourse.org/t/subscriptions-allow-users-to-purchase-one-time-products-multiple-times/173732/ There may be cases where a site admin wants to allow the repurchasing of a product. This implements the functionality by adding a repurchaseable toggle in the admin screen when creating a product. This saves an attribute to the Stripe product metadata. When a user has already purchased an item with this toggle enabled, they will be able to purchase it again when browsing to `/s`.
This commit is contained in:
parent
3f8fca3246
commit
072b558d40
@ -93,7 +93,10 @@ module DiscourseSubscriptions
|
|||||||
name: params[:name],
|
name: params[:name],
|
||||||
active: params[:active],
|
active: params[:active],
|
||||||
statement_descriptor: params[:statement_descriptor],
|
statement_descriptor: params[:statement_descriptor],
|
||||||
metadata: { description: params.dig(:metadata, :description) }
|
metadata: {
|
||||||
|
description: params.dig(:metadata, :description),
|
||||||
|
repurchaseable: params.dig(:metadata, :repurchaseable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -136,7 +136,8 @@ module DiscourseSubscriptions
|
|||||||
id: product[:id],
|
id: product[:id],
|
||||||
name: product[:name],
|
name: product[:name],
|
||||||
description: PrettyText.cook(product[:metadata][:description]),
|
description: PrettyText.cook(product[:metadata][:description]),
|
||||||
subscribed: current_user_products.include?(product[:id])
|
subscribed: current_user_products.include?(product[:id]),
|
||||||
|
repurchaseable: product[:metadata][:repurchaseable]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classNames: ["product"],
|
||||||
|
});
|
@ -3,6 +3,7 @@ import Subscription from "discourse/plugins/discourse-subscriptions/discourse/mo
|
|||||||
import Transaction from "discourse/plugins/discourse-subscriptions/discourse/models/transaction";
|
import Transaction from "discourse/plugins/discourse-subscriptions/discourse/models/transaction";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { not } from "@ember/object/computed";
|
import { not } from "@ember/object/computed";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
selectedPlan: null,
|
selectedPlan: null,
|
||||||
@ -24,6 +25,15 @@ export default Controller.extend({
|
|||||||
bootbox.alert(I18n.t(`discourse_subscriptions.${path}`));
|
bootbox.alert(I18n.t(`discourse_subscriptions.${path}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("model.product.repurchaseable", "model.product.subscribed")
|
||||||
|
canPurchase(repurchaseable, subscribed) {
|
||||||
|
if (!repurchaseable && subscribed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
createSubscription(plan) {
|
createSubscription(plan) {
|
||||||
return this.stripe.createToken(this.get("cardElement")).then((result) => {
|
return this.stripe.createToken(this.get("cardElement")).then((result) => {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
|
@ -23,6 +23,13 @@
|
|||||||
{{i18n 'discourse_subscriptions.admin.products.product.statement_descriptor_help'}}
|
{{i18n 'discourse_subscriptions.admin.products.product.statement_descriptor_help'}}
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="repurchaseable">{{i18n 'discourse_subscriptions.admin.products.product.repurchaseable'}}</label>
|
||||||
|
{{input type="checkbox" name="repurchaseable" checked=model.product.metadata.repurchaseable}}
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n 'discourse_subscriptions.admin.products.product.repurchase_help'}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="active">{{i18n 'discourse_subscriptions.admin.products.product.active'}}</label>
|
<label for="active">{{i18n 'discourse_subscriptions.admin.products.product.active'}}</label>
|
||||||
{{input type="checkbox" name="active" checked=model.product.active}}
|
{{input type="checkbox" name="active" checked=model.product.active}}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<h2>{{product.name}}</h2>
|
||||||
|
|
||||||
|
<p class="product-description">
|
||||||
|
{{html-safe product.description}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{#if isLoggedIn}}
|
||||||
|
<div class="product-purchase">
|
||||||
|
{{#if product.repurchaseable}}
|
||||||
|
{{#if product.subscribed}}
|
||||||
|
<span class="purchased">✓ {{i18n 'discourse_subscriptions.subscribe.purchased'}}</span>
|
||||||
|
{{#link-to "user.billing.subscriptions" currentUser.username class="billing-link"}}
|
||||||
|
{{i18n 'discourse_subscriptions.subscribe.go_to_billing'}}
|
||||||
|
{{/link-to}}
|
||||||
|
{{/if}}
|
||||||
|
{{#link-to "s.show" product.id class="btn btn-primary"}}
|
||||||
|
{{i18n 'discourse_subscriptions.subscribe.title'}}
|
||||||
|
{{/link-to}}
|
||||||
|
{{else}}
|
||||||
|
{{#if product.subscribed}}
|
||||||
|
<span class="purchased">✓ {{i18n 'discourse_subscriptions.subscribe.purchased'}}</span>
|
||||||
|
{{#link-to "user.billing.subscriptions" currentUser.username class="billing-link"}}
|
||||||
|
{{i18n 'discourse_subscriptions.subscribe.go_to_billing'}}
|
||||||
|
{{/link-to}}
|
||||||
|
{{else}}
|
||||||
|
{{#link-to "s.show" product.id disabled=product.subscribed class="btn btn-primary"}}
|
||||||
|
{{i18n 'discourse_subscriptions.subscribe.title'}}
|
||||||
|
{{/link-to}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -2,28 +2,6 @@
|
|||||||
<p>{{i18n 'discourse_subscriptions.subscribe.no_products'}}</p>
|
<p>{{i18n 'discourse_subscriptions.subscribe.no_products'}}</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#each products as |product|}}
|
{{#each products as |product|}}
|
||||||
<div class="product">
|
{{product-item product=product isLoggedIn=isLoggedIn}}
|
||||||
<h2>{{product.name}}</h2>
|
|
||||||
|
|
||||||
<p class="product-description">
|
|
||||||
{{html-safe product.description}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#if isLoggedIn}}
|
|
||||||
<div class="product-purchase">
|
|
||||||
{{#if product.subscribed}}
|
|
||||||
<span class="purchased">✓ {{i18n 'discourse_subscriptions.subscribe.purchased'}}</span>
|
|
||||||
{{#link-to "user.billing.subscriptions" currentUser.username class="billing-link"}}
|
|
||||||
{{i18n 'discourse_subscriptions.subscribe.go_to_billing'}}
|
|
||||||
{{/link-to}}
|
|
||||||
{{else}}
|
|
||||||
{{#link-to "s.show" product.id disabled=product.subscribed class="btn btn-primary"}}
|
|
||||||
{{i18n 'discourse_subscriptions.subscribe.title'}}
|
|
||||||
{{/link-to}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-column">
|
<div class="section-column">
|
||||||
{{#unless model.product.subscribed}}
|
{{#if canPurchase}}
|
||||||
<h2>
|
<h2>
|
||||||
{{i18n 'discourse_subscriptions.subscribe.card.title'}}
|
{{i18n 'discourse_subscriptions.subscribe.card.title'}}
|
||||||
</h2>
|
</h2>
|
||||||
@ -45,6 +45,6 @@
|
|||||||
}}
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{/unless}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,6 +111,8 @@ en:
|
|||||||
plan_help: Create a pricing plan to subscribe customers to this product.
|
plan_help: Create a pricing plan to subscribe customers to this product.
|
||||||
description: Description
|
description: Description
|
||||||
description_help: This describes your subscription product.
|
description_help: This describes your subscription product.
|
||||||
|
repurchaseable: Repurchaseable?
|
||||||
|
repurchase_help: Allow product and associated plans to be purchased multiple times
|
||||||
active: Active
|
active: Active
|
||||||
active_help: Toggle this off until your product is ready for consumers.
|
active_help: Toggle this off until your product is ready for consumers.
|
||||||
created_at: Created
|
created_at: Created
|
||||||
|
@ -81,9 +81,15 @@ module DiscourseSubscriptions
|
|||||||
post "/s/admin/products.json", params: { statement_descriptor: '' }
|
post "/s/admin/products.json", params: { statement_descriptor: '' }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has a description' do
|
it 'has metadata' do
|
||||||
::Stripe::Product.expects(:create).with(has_entry(metadata: { description: 'Oi, I think he just said bless be all the bignoses!' }))
|
::Stripe::Product.expects(:create).with(has_entry(metadata: { description: 'Oi, I think he just said bless be all the bignoses!', repurchaseable: 'false' }))
|
||||||
post "/s/admin/products.json", params: { metadata: { description: 'Oi, I think he just said bless be all the bignoses!' } }
|
|
||||||
|
post "/s/admin/products.json", params: {
|
||||||
|
metadata: {
|
||||||
|
description: 'Oi, I think he just said bless be all the bignoses!',
|
||||||
|
repurchaseable: 'false'
|
||||||
|
}
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ module DiscourseSubscriptions
|
|||||||
id: "prodct_23456",
|
id: "prodct_23456",
|
||||||
name: "Very Special Product",
|
name: "Very Special Product",
|
||||||
metadata: {
|
metadata: {
|
||||||
description: "Many people listened to my phone call with the Ukrainian President while it was being made"
|
description: "Many people listened to my phone call with the Ukrainian President while it was being made",
|
||||||
|
repurchaseable: false
|
||||||
},
|
},
|
||||||
otherstuff: true,
|
otherstuff: true,
|
||||||
}
|
}
|
||||||
@ -48,7 +49,8 @@ module DiscourseSubscriptions
|
|||||||
"id" => "prodct_23456",
|
"id" => "prodct_23456",
|
||||||
"name" => "Very Special Product",
|
"name" => "Very Special Product",
|
||||||
"description" => PrettyText.cook("Many people listened to my phone call with the Ukrainian President while it was being made"),
|
"description" => PrettyText.cook("Many people listened to my phone call with the Ukrainian President while it was being made"),
|
||||||
"subscribed" => false
|
"subscribed" => false,
|
||||||
|
"repurchaseable" => false,
|
||||||
}])
|
}])
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -82,7 +84,8 @@ module DiscourseSubscriptions
|
|||||||
"id" => "prodct_23456",
|
"id" => "prodct_23456",
|
||||||
"name" => "Very Special Product",
|
"name" => "Very Special Product",
|
||||||
"description" => PrettyText.cook("Many people listened to my phone call with the Ukrainian President while it was being made"),
|
"description" => PrettyText.cook("Many people listened to my phone call with the Ukrainian President while it was being made"),
|
||||||
"subscribed" => false
|
"subscribed" => false,
|
||||||
|
"repurchaseable" => false
|
||||||
},
|
},
|
||||||
"plans" => [
|
"plans" => [
|
||||||
{ "currency" => "aud", "id" => "plan_id123", "recurring" => { "interval" => "year" }, "unit_amount" => 1220 },
|
{ "currency" => "aud", "id" => "plan_id123", "recurring" => { "interval" => "year" }, "unit_amount" => 1220 },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user