Skip to content

Commit

Permalink
feat: link transaction with balance and payment (casdoor#3052)
Browse files Browse the repository at this point in the history
* feat: add and update transaction when recharging

* feat: add pay with balance

* feat: improve code format

* feat: update icon url for balance
  • Loading branch information
dacongda authored Jul 12, 2024
1 parent cef2ab2 commit 7f2869c
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 6 deletions.
15 changes: 14 additions & 1 deletion object/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp
}

if payment.IsRecharge {
err = updateUserBalance(payment.Owner, payment.User, payment.Price)
err = UpdateUserBalance(payment.Owner, payment.User, payment.Price)
return payment, notifyResult, err
}

Expand All @@ -222,6 +222,19 @@ func NotifyPayment(body []byte, owner string, paymentName string) (*Payment, err
if err != nil {
return nil, err
}

transaction, err := GetTransaction(payment.GetId())
if err != nil {
return nil, err
}

if transaction != nil {
transaction.State = payment.State
_, err = UpdateTransaction(transaction.GetId(), transaction)
if err != nil {
return nil, err
}
}
}

return payment, nil
Expand Down
51 changes: 50 additions & 1 deletion object/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
NotifyUrl: notifyUrl,
PaymentEnv: paymentEnv,
}

// custom process for WeChat & WeChat Pay
if provider.Type == "WeChat Pay" {
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
if err != nil {
return nil, nil, err
}
} else if provider.Type == "Balance" {
payReq.PayerId = user.GetId()
}

payResp, err := pProvider.Pay(payReq)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -264,12 +268,46 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
OutOrderId: payResp.OrderId,
}

transaction := &Transaction{
Owner: payment.Owner,
Name: payment.Name,
DisplayName: payment.DisplayName,
Provider: provider.Name,
Category: provider.Category,
Type: provider.Type,

ProductName: product.Name,
ProductDisplayName: product.DisplayName,
Detail: product.Detail,
Tag: product.Tag,
Currency: product.Currency,
Amount: payment.Price,
ReturnUrl: payment.ReturnUrl,

User: payment.User,
Application: owner,
Payment: payment.GetId(),

State: pp.PaymentStateCreated,
}

if provider.Type == "Dummy" {
payment.State = pp.PaymentStatePaid
err = updateUserBalance(user.Owner, user.Name, payment.Price)
err = UpdateUserBalance(user.Owner, user.Name, payment.Price)
if err != nil {
return nil, nil, err
}
} else if provider.Type == "Balance" {
if product.Price > user.Balance {
return nil, nil, fmt.Errorf("insufficient user balance")
}
transaction.Amount = -transaction.Amount
err = UpdateUserBalance(user.Owner, user.Name, -product.Price)
if err != nil {
return nil, nil, err
}
payment.State = pp.PaymentStatePaid
transaction.State = pp.PaymentStatePaid
}

affected, err := AddPayment(payment)
Expand All @@ -280,6 +318,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
if !affected {
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
}

if product.IsRecharge || provider.Type == "Balance" {
affected, err = AddTransaction(transaction)
if err != nil {
return nil, nil, err
}
if !affected {
return nil, nil, fmt.Errorf("failed to add transaction: %s", util.StructToJson(payment))
}
}

return payment, payResp.AttachInfo, nil
}

Expand Down
6 changes: 6 additions & 0 deletions object/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ func GetPaymentProvider(p *Provider) (pp.PaymentProvider, error) {
return nil, err
}
return pp, nil
} else if typ == "Balance" {
pp, err := pp.NewBalancePaymentProvider()
if err != nil {
return nil, err
}
return pp, nil
} else {
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
}
Expand Down
3 changes: 2 additions & 1 deletion object/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package object
import (
"fmt"

"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
)
Expand All @@ -43,7 +44,7 @@ type Transaction struct {
Application string `xorm:"varchar(100)" json:"application"`
Payment string `xorm:"varchar(100)" json:"payment"`

State string `xorm:"varchar(100)" json:"state"`
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
}

func GetTransactionCount(owner, field, value string) (int64, error) {
Expand Down
2 changes: 1 addition & 1 deletion object/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@ func GenerateIdForNewUser(application *Application) (string, error) {
return res, nil
}

func updateUserBalance(owner string, name string, balance float64) error {
func UpdateUserBalance(owner string, name string, balance float64) error {
user, err := getUser(owner, name)
if err != nil {
return err
Expand Down
50 changes: 50 additions & 0 deletions pp/balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pp

import (
"fmt"

"github.com/casdoor/casdoor/util"
)

type BalancePaymentProvider struct{}

func NewBalancePaymentProvider() (*BalancePaymentProvider, error) {
pp := &BalancePaymentProvider{}
return pp, nil
}

func (pp *BalancePaymentProvider) Pay(r *PayReq) (*PayResp, error) {
owner, _ := util.GetOwnerAndNameFromId(r.PayerId)
return &PayResp{
PayUrl: r.ReturnUrl,
OrderId: fmt.Sprintf("%s/%s", owner, r.PaymentName),
}, nil
}

func (pp *BalancePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
return &NotifyResult{
PaymentStatus: PaymentStatePaid,
}, nil
}

func (pp *BalancePaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
return "", nil
}

func (pp *BalancePaymentProvider) GetResponseError(err error) string {
return ""
}
2 changes: 1 addition & 1 deletion web/src/PaymentResultPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class PaymentResultPage extends React.Component {
payment: payment,
});
if (payment.state === "Created") {
if (["PayPal", "Stripe", "Alipay", "WeChat Pay"].includes(payment.type)) {
if (["PayPal", "Stripe", "Alipay", "WeChat Pay", "Balance"].includes(payment.type)) {
this.setState({
timeout: setTimeout(async() => {
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
Expand Down
2 changes: 1 addition & 1 deletion web/src/ProviderEditPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ class ProviderEditPage extends React.Component {
(this.state.provider.category === "Web3") ||
(this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ||
(this.state.provider.category === "SMS" && this.state.provider.type === "Custom HTTP SMS") ||
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP")) ? null : (
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP") || this.state.provider.type === "Balance") ? null : (
<React.Fragment>
{
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
Expand Down
5 changes: 5 additions & 0 deletions web/src/Setting.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/payment_paypal.png`,
url: "",
},
"Balance": {
logo: `${StaticBaseUrl}/img/payment_balance.svg`,
url: "",
},
"Alipay": {
logo: `${StaticBaseUrl}/img/payment_alipay.png`,
url: "https://www.alipay.com/",
Expand Down Expand Up @@ -1067,6 +1071,7 @@ export function getProviderTypeOptions(category) {
} else if (category === "Payment") {
return ([
{id: "Dummy", name: "Dummy"},
{id: "Balance", name: "Balance"},
{id: "Alipay", name: "Alipay"},
{id: "WeChat Pay", name: "WeChat Pay"},
{id: "PayPal", name: "PayPal"},
Expand Down

0 comments on commit 7f2869c

Please sign in to comment.