diff --git a/controllers/product.go b/controllers/product.go index 55fb9cd0..d034e294 100644 --- a/controllers/product.go +++ b/controllers/product.go @@ -17,6 +17,7 @@ package controllers import ( "encoding/json" "fmt" + "strconv" "github.com/beego/beego/utils/pagination" "github.com/casdoor/casdoor/object" @@ -164,6 +165,16 @@ func (c *ApiController) BuyProduct() { host := c.Ctx.Request.Host providerName := c.Input().Get("providerName") paymentEnv := c.Input().Get("paymentEnv") + customPriceStr := c.Input().Get("customPrice") + if customPriceStr == "" { + customPriceStr = "0" + } + + customPrice, err := strconv.ParseFloat(customPriceStr, 64) + if err != nil { + c.ResponseError(err.Error()) + return + } // buy `pricingName/planName` for `paidUserName` pricingName := c.Input().Get("pricingName") @@ -189,7 +200,7 @@ func (c *ApiController) BuyProduct() { return } - payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv) + payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv, customPrice) if err != nil { c.ResponseError(err.Error()) return diff --git a/object/payment.go b/object/payment.go index 282776e6..4282e9be 100644 --- a/object/payment.go +++ b/object/payment.go @@ -39,6 +39,8 @@ type Payment struct { Currency string `xorm:"varchar(100)" json:"currency"` Price float64 `json:"price"` ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"` + IsRecharge bool `xorm:"bool" json:"isRecharge"` + // Payer Info User string `xorm:"varchar(100)" json:"user"` PersonName string `xorm:"varchar(100)" json:"personName"` @@ -193,11 +195,16 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp return payment, nil, err } - if notifyResult.Price != product.Price { + if notifyResult.Price != product.Price && !product.IsRecharge { err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", notifyResult.Price, product.Price) return payment, nil, err } + if payment.IsRecharge { + err = updateUserBalance(payment.Owner, payment.User, payment.Price) + return payment, notifyResult, err + } + return payment, notifyResult, nil } diff --git a/object/product.go b/object/product.go index d26af73b..15feef09 100644 --- a/object/product.go +++ b/object/product.go @@ -39,6 +39,7 @@ type Product struct { Price float64 `json:"price"` Quantity int `json:"quantity"` Sold int `json:"sold"` + IsRecharge bool `json:"isRecharge"` Providers []string `xorm:"varchar(255)" json:"providers"` ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"` @@ -160,7 +161,7 @@ func (product *Product) getProvider(providerName string) (*Provider, error) { return provider, nil } -func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string) (payment *Payment, attachInfo map[string]interface{}, err error) { +func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string, customPrice float64) (payment *Payment, attachInfo map[string]interface{}, err error) { product, err := GetProduct(id) if err != nil { return nil, nil, err @@ -169,6 +170,14 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host return nil, nil, fmt.Errorf("the product: %s does not exist", id) } + if product.IsRecharge { + if customPrice <= 0 { + return nil, nil, fmt.Errorf("the custom price should bigger than zero") + } else { + product.Price = customPrice + } + } + provider, err := product.getProvider(providerName) if err != nil { return nil, nil, err @@ -246,6 +255,7 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host Currency: product.Currency, Price: product.Price, ReturnUrl: product.ReturnUrl, + IsRecharge: product.IsRecharge, User: user.Name, PayUrl: payResp.PayUrl, @@ -256,6 +266,10 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host if provider.Type == "Dummy" { payment.State = pp.PaymentStatePaid + err = updateUserBalance(user.Owner, user.Name, payment.Price) + if err != nil { + return nil, nil, err + } } affected, err := AddPayment(payment) @@ -304,8 +318,9 @@ func CreateProductForPlan(plan *Plan) *Product { Price: plan.Price, Currency: plan.Currency, - Quantity: 999, - Sold: 0, + Quantity: 999, + Sold: 0, + IsRecharge: false, Providers: plan.PaymentProviders, State: "Published", diff --git a/object/user.go b/object/user.go index 91a4c77d..1d3a28b7 100644 --- a/object/user.go +++ b/object/user.go @@ -687,7 +687,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er } } if isAdmin { - columns = append(columns, "name", "id", "email", "phone", "country_code", "type") + columns = append(columns, "name", "id", "email", "phone", "country_code", "type", "balance") } columns = append(columns, "updated_time") @@ -1157,3 +1157,13 @@ func GenerateIdForNewUser(application *Application) (string, error) { res := strconv.Itoa(lastUserId + 1) return res, nil } + +func updateUserBalance(owner string, name string, balance float64) error { + user, err := getUser(owner, name) + if err != nil { + return err + } + user.Balance += balance + _, err = UpdateUser(user.GetId(), user, []string{"balance"}, true) + return err +} diff --git a/object/user_util.go b/object/user_util.go index 2d802546..4c1b6618 100644 --- a/object/user_util.go +++ b/object/user_util.go @@ -416,6 +416,11 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str itemsChanged = append(itemsChanged, item) } + if oldUser.Balance != newUser.Balance { + item := GetAccountItemByName("Balance", organization) + itemsChanged = append(itemsChanged, item) + } + if oldUser.Score != newUser.Score { item := GetAccountItemByName("Score", organization) itemsChanged = append(itemsChanged, item) diff --git a/web/src/PaymentResultPage.js b/web/src/PaymentResultPage.js index cfb69381..c445293a 100644 --- a/web/src/PaymentResultPage.js +++ b/web/src/PaymentResultPage.js @@ -17,6 +17,7 @@ import {Button, Result, Spin} from "antd"; import * as PaymentBackend from "./backend/PaymentBackend"; import * as PricingBackend from "./backend/PricingBackend"; import * as SubscriptionBackend from "./backend/SubscriptionBackend"; +import * as UserBackend from "./backend/UserBackend"; import * as Setting from "./Setting"; import i18next from "i18next"; @@ -34,6 +35,7 @@ class PaymentResultPage extends React.Component { pricing: props.pricing ?? null, subscription: props.subscription ?? null, timeout: null, + user: null, }; } @@ -41,6 +43,25 @@ class PaymentResultPage extends React.Component { this.getPayment(); } + getUser() { + UserBackend.getUser(this.props.account.owner, this.props.account.name) + .then((res) => { + if (res.data === null) { + this.props.history.push("/404"); + return; + } + + if (res.status === "error") { + Setting.showMessage("error", res.msg); + return; + } + + this.setState({ + user: res.data, + }); + }); + } + componentWillUnmount() { if (this.state.timeout !== null) { clearTimeout(this.state.timeout); @@ -114,6 +135,12 @@ class PaymentResultPage extends React.Component { }); } } + + if (payment.state === "Paid") { + if (this.props.account) { + this.getUser(); + } + } } catch (err) { Setting.showMessage("error", err.message); return; @@ -136,6 +163,27 @@ class PaymentResultPage extends React.Component { } if (payment.state === "Paid") { + if (payment.isRecharge) { + return ( +