✨ Multi-currency
This commit is contained in:
@@ -50,7 +50,7 @@ func (v *Server) MakeTransaction(ctx context.Context, request *proto.MakeTransac
|
||||
}
|
||||
}
|
||||
|
||||
transaction, err := services.MakeTransaction(request.GetAmount(), request.GetRemark(), payerWallet, payeeWallet)
|
||||
transaction, err := services.MakeTransaction(request.GetAmount(), request.GetRemark(), request.GetCurrency(), payerWallet, payeeWallet)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func (v *Server) MakeTransactionWithAccount(ctx context.Context, request *proto.
|
||||
}
|
||||
}
|
||||
|
||||
transaction, err := services.MakeTransaction(request.GetAmount(), request.GetRemark(), payerWallet, payeeWallet)
|
||||
transaction, err := services.MakeTransaction(request.GetAmount(), request.GetRemark(), request.GetCurrency(), payerWallet, payeeWallet)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
@@ -24,4 +24,5 @@ type Order struct {
|
||||
Transaction *Transaction `json:"transaction"`
|
||||
TransactionID *uint `json:"transaction_id"`
|
||||
ClientID *uint `json:"client_id"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
@@ -10,12 +10,13 @@ import (
|
||||
type Transaction struct {
|
||||
cruda.BaseModel
|
||||
|
||||
Remark string `json:"remark"` // The usage of this transaction
|
||||
Amount decimal.Decimal `json:"amount" type:"decimal(30,2);"`
|
||||
Payer *Wallet `json:"payer" foreignKey:"PayerID"` // Who give the money
|
||||
Payee *Wallet `json:"payee" foreignKey:"PayeeID"` // Who get the money
|
||||
PayerID *uint `json:"payer_id"` // Leave this field as nil means pay from the system
|
||||
PayeeID *uint `json:"payee_id"` // Leave this field as nil means pay to the system
|
||||
Remark string `json:"remark"` // The usage of this transaction
|
||||
Amount decimal.Decimal `json:"amount" type:"decimal(30,2);"`
|
||||
Currency string `json:"currency" gorm:"default:'normal'"`
|
||||
Payer *Wallet `json:"payer" foreignKey:"PayerID"` // Who give the money
|
||||
Payee *Wallet `json:"payee" foreignKey:"PayeeID"` // Who get the money
|
||||
PayerID *uint `json:"payer_id"` // Leave this field as nil means pay from the system
|
||||
PayeeID *uint `json:"payee_id"` // Leave this field as nil means pay to the system
|
||||
}
|
||||
|
||||
func (v *Transaction) ToTransactionInfo() *proto.TransactionInfo {
|
||||
|
@@ -9,9 +9,10 @@ import (
|
||||
type Wallet struct {
|
||||
cruda.BaseModel
|
||||
|
||||
Balance decimal.Decimal `json:"balance" sql:"type:decimal(30,2);"`
|
||||
Password string `json:"password"`
|
||||
AccountID uint `json:"account_id"`
|
||||
Balance decimal.Decimal `json:"balance" sql:"type:decimal(30,2);"`
|
||||
GoldenBalance decimal.Decimal `json:"golden_balance" sql:"type:decimal(30,2);"`
|
||||
Password string `json:"password"`
|
||||
AccountID uint `json:"account_id"`
|
||||
}
|
||||
|
||||
func (v *Wallet) ToWalletInfo() *proto.WalletInfo {
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"git.solsynth.dev/hypernet/wallet/pkg/internal/server/exts"
|
||||
"git.solsynth.dev/hypernet/wallet/pkg/internal/services"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/samber/lo"
|
||||
"github.com/shopspring/decimal"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@@ -34,12 +35,17 @@ func createOrder(c *fiber.Ctx) error {
|
||||
Amount float64 `json:"amount" validate:"required"`
|
||||
PayeeID *uint `json:"payee_id"`
|
||||
PayerID *uint `json:"payer_id"`
|
||||
Currency string `json:"currency" validate:"required"`
|
||||
}
|
||||
|
||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !lo.Contains([]string{"normal", "golden"}, data.Currency) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "invalid currency")
|
||||
}
|
||||
|
||||
// Validating client
|
||||
client, err := authkit.GetThirdClientByAlias(gap.Nx, data.ClientID, &data.ClientSecret)
|
||||
if err != nil {
|
||||
@@ -50,6 +56,7 @@ func createOrder(c *fiber.Ctx) error {
|
||||
Status: models.OrderStatusPending,
|
||||
Remark: data.Remark,
|
||||
Amount: decimal.NewFromFloat(data.Amount),
|
||||
Currency: data.Currency,
|
||||
ClientID: &client.ID,
|
||||
}
|
||||
|
||||
@@ -126,7 +133,7 @@ func payOrder(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
if tran, err := services.MakeTransaction(order.Amount.InexactFloat64(), order.Remark, payer, payee); err != nil {
|
||||
if tran, err := services.MakeTransaction(order.Amount.InexactFloat64(), order.Remark, order.Currency, payer, payee); err != nil {
|
||||
return fiber.NewError(fiber.StatusPaymentRequired, err.Error())
|
||||
} else {
|
||||
if err := database.C.Model(&order).Updates(&models.Order{
|
||||
@@ -137,6 +144,7 @@ func payOrder(c *fiber.Ctx) error {
|
||||
_, _ = services.MakeTransaction(
|
||||
order.Amount.InexactFloat64(),
|
||||
fmt.Sprintf("%s - #%d Refund", order.Remark, order.ID),
|
||||
order.Currency,
|
||||
payee,
|
||||
payer,
|
||||
)
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"git.solsynth.dev/hypernet/wallet/pkg/internal/server/exts"
|
||||
"git.solsynth.dev/hypernet/wallet/pkg/internal/services"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func listTransaction(c *fiber.Ctx) error {
|
||||
@@ -77,12 +78,17 @@ func makeTransaction(c *fiber.Ctx) error {
|
||||
Amount float64 `json:"amount" validate:"required"`
|
||||
PayeeID *uint `json:"payee_id"`
|
||||
PayerID *uint `json:"payer_id"`
|
||||
Currency string `json:"currency" validate:"required"`
|
||||
}
|
||||
|
||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !lo.Contains([]string{"normal", "golden"}, data.Currency) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "invalid currency")
|
||||
}
|
||||
|
||||
// Validating client
|
||||
client, err := authkit.GetThirdClientByAlias(gap.Nx, data.ClientID, &data.ClientSecret)
|
||||
if err != nil {
|
||||
@@ -110,7 +116,7 @@ func makeTransaction(c *fiber.Ctx) error {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "payee and payer cannot be both blank")
|
||||
}
|
||||
|
||||
tran, err := services.MakeTransaction(data.Amount, data.Remark, payer, payee)
|
||||
tran, err := services.MakeTransaction(data.Amount, data.Remark, data.Currency, payer, payee)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@@ -12,13 +12,14 @@ import (
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func MakeTransaction(amount float64, remark string, payer, payee *models.Wallet) (models.Transaction, error) {
|
||||
func MakeTransaction(amount float64, remark, currency string, payer, payee *models.Wallet) (models.Transaction, error) {
|
||||
// Round amount to keep 2 decimal places
|
||||
amount = math.Round(amount*100) / 100
|
||||
|
||||
transaction := models.Transaction{
|
||||
Amount: decimal.NewFromFloat(amount),
|
||||
Remark: remark,
|
||||
Amount: decimal.NewFromFloat(amount),
|
||||
Remark: remark,
|
||||
Currency: currency,
|
||||
}
|
||||
if payer != nil {
|
||||
if payer.Balance.LessThan(transaction.Amount) {
|
||||
@@ -38,7 +39,12 @@ func MakeTransaction(amount float64, remark string, payer, payee *models.Wallet)
|
||||
}
|
||||
|
||||
if payer != nil {
|
||||
payer.Balance = payer.Balance.Sub(transaction.Amount)
|
||||
switch currency {
|
||||
case "golden":
|
||||
payer.GoldenBalance = payer.GoldenBalance.Sub(transaction.Amount)
|
||||
default:
|
||||
payer.Balance = payer.Balance.Sub(transaction.Amount)
|
||||
}
|
||||
if err := tx.Model(payer).
|
||||
Updates(&models.Wallet{Balance: payer.Balance}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
@@ -46,7 +52,12 @@ func MakeTransaction(amount float64, remark string, payer, payee *models.Wallet)
|
||||
}
|
||||
}
|
||||
if payee != nil {
|
||||
payee.Balance = payee.Balance.Add(transaction.Amount)
|
||||
switch currency {
|
||||
case "golden":
|
||||
payee.GoldenBalance = payee.GoldenBalance.Add(transaction.Amount)
|
||||
default:
|
||||
payee.Balance = payee.Balance.Add(transaction.Amount)
|
||||
}
|
||||
if err := tx.Model(payee).
|
||||
Updates(&models.Wallet{Balance: payee.Balance}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
@@ -63,10 +74,18 @@ func MakeTransaction(amount float64, remark string, payer, payee *models.Wallet)
|
||||
Subtitle: transaction.Remark,
|
||||
Body: fmt.Sprintf("%.2f SRC removed from your wallet. Your new balance is %.2f", amount, payer.Balance.InexactFloat64()),
|
||||
Metadata: map[string]any{
|
||||
"id": transaction.ID,
|
||||
"amount": amount,
|
||||
"balance": payer.Balance.InexactFloat64(),
|
||||
"remark": transaction.Remark,
|
||||
"id": transaction.ID,
|
||||
"amount": amount,
|
||||
"balance": (func() float64 {
|
||||
switch currency {
|
||||
case "golden":
|
||||
return payer.GoldenBalance.InexactFloat64()
|
||||
default:
|
||||
return payer.Balance.InexactFloat64()
|
||||
}
|
||||
})(),
|
||||
"remark": transaction.Remark,
|
||||
"currency": currency,
|
||||
},
|
||||
Priority: 0,
|
||||
})
|
||||
@@ -78,10 +97,18 @@ func MakeTransaction(amount float64, remark string, payer, payee *models.Wallet)
|
||||
Subtitle: transaction.Remark,
|
||||
Body: fmt.Sprintf("%.2f SRC added to your wallet. Your new balance is %.2f", amount, payee.Balance.InexactFloat64()),
|
||||
Metadata: map[string]any{
|
||||
"id": transaction.ID,
|
||||
"amount": amount,
|
||||
"balance": payee.Balance.InexactFloat64(),
|
||||
"remark": transaction.Remark,
|
||||
"id": transaction.ID,
|
||||
"amount": amount,
|
||||
"balance": (func() float64 {
|
||||
switch currency {
|
||||
case "golden":
|
||||
return payee.GoldenBalance.InexactFloat64()
|
||||
default:
|
||||
return payee.Balance.InexactFloat64()
|
||||
}
|
||||
})(),
|
||||
"remark": transaction.Remark,
|
||||
"currency": currency,
|
||||
},
|
||||
Priority: 0,
|
||||
})
|
||||
|
Reference in New Issue
Block a user