forked from casdoor/casdoor
feat: add Plan.IsExclusive field for single subscription enforcement (#5004)
This commit is contained in:
@@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/beego/beego/v2/core/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -150,6 +151,26 @@ func (c *ApiController) AddSubscription() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if plan restricts user to one subscription
|
||||
if subscription.Plan != "" {
|
||||
plan, err := object.GetPlan(util.GetId(subscription.Owner, subscription.Plan))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if plan != nil && plan.IsExclusive {
|
||||
hasSubscription, err := object.HasActiveSubscriptionForPlan(subscription.Owner, subscription.User, subscription.Plan)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if hasSubscription {
|
||||
c.ResponseError(fmt.Sprintf("User already has an active subscription for plan: %s", subscription.Plan))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddSubscription(&subscription))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -179,6 +179,17 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
|
||||
return nil, nil, fmt.Errorf("the plan: %s does not exist", productInfo.PlanName)
|
||||
}
|
||||
|
||||
// Check if plan restricts user to one subscription
|
||||
if plan.IsExclusive {
|
||||
hasSubscription, err := HasActiveSubscriptionForPlan(owner, user.Name, plan.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if hasSubscription {
|
||||
return nil, nil, fmt.Errorf("user already has an active subscription for plan: %s", plan.Name)
|
||||
}
|
||||
}
|
||||
|
||||
sub, err := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -35,6 +35,7 @@ type Plan struct {
|
||||
Product string `xorm:"varchar(100)" json:"product"`
|
||||
PaymentProviders []string `xorm:"varchar(100)" json:"paymentProviders"` // payment providers for related product
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
IsExclusive bool `json:"isExclusive"` // if true, a user can only have at most one subscription of this plan
|
||||
|
||||
Role string `xorm:"varchar(100)" json:"role"`
|
||||
Options []string `xorm:"-" json:"options"`
|
||||
|
||||
@@ -217,6 +217,26 @@ func GetSubscription(id string) (*Subscription, error) {
|
||||
return getSubscription(owner, name)
|
||||
}
|
||||
|
||||
func HasActiveSubscriptionForPlan(owner, userName, planName string) (bool, error) {
|
||||
subscriptions := []*Subscription{}
|
||||
err := ormer.Engine.Find(&subscriptions, &Subscription{Owner: owner, User: userName, Plan: planName})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, sub := range subscriptions {
|
||||
err = sub.UpdateState()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Check if subscription is active, upcoming, or pending (not expired, error, or suspended)
|
||||
if sub.State == SubStateActive || sub.State == SubStateUpcoming || sub.State == SubStatePending {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func UpdateSubscription(id string, subscription *Subscription) (bool, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
|
||||
@@ -260,6 +260,16 @@ class PlanEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||
{Setting.getLabel(i18next.t("plan:Is exclusive"), i18next.t("plan:Is exclusive - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={1} >
|
||||
<Switch checked={this.state.plan.isExclusive} disabled={isViewMode} onChange={checked => {
|
||||
this.updatePlanField("isExclusive", checked);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user