diff --git a/controllers/payment.go b/controllers/payment.go index 8280cf62..2a6ad5ba 100644 --- a/controllers/payment.go +++ b/controllers/payment.go @@ -16,10 +16,12 @@ package controllers import ( "encoding/json" + "fmt" "github.com/astaxie/beego/utils/pagination" "github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/util" + "github.com/go-pay/gopay/alipay" ) // GetPayments @@ -122,12 +124,18 @@ func (c *ApiController) DeletePayment() { // @Success 200 {object} controllers.Response The Response object // @router /notify-payment [post] func (c *ApiController) NotifyPayment() { - var payment object.Payment - err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment) + bm, err := alipay.ParseNotifyToBodyMap(c.Ctx.Request) if err != nil { panic(err) } - c.Data["json"] = wrapActionResponse(object.NotifyPayment("111", "222")) - c.ServeJSON() + ok := object.NotifyPayment(bm) + if ok { + _, err = c.Ctx.ResponseWriter.Write([]byte("success")) + if err != nil { + panic(err) + } + } else { + panic(fmt.Errorf("NotifyPayment() failed: %v", ok)) + } } diff --git a/object/payment.go b/object/payment.go index f6190456..a1a3edce 100644 --- a/object/payment.go +++ b/object/payment.go @@ -18,6 +18,8 @@ import ( "fmt" "github.com/casdoor/casdoor/util" + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/alipay" "xorm.io/core" ) @@ -27,13 +29,14 @@ type Payment struct { CreatedTime string `xorm:"varchar(100)" json:"createdTime"` DisplayName string `xorm:"varchar(100)" json:"displayName"` - Provider string `xorm:"varchar(100)" json:"provider"` - Type string `xorm:"varchar(100)" json:"type"` - Organization string `xorm:"varchar(100)" json:"organization"` - User string `xorm:"varchar(100)" json:"user"` - Good string `xorm:"varchar(100)" json:"good"` - Amount string `xorm:"varchar(100)" json:"amount"` - Currency string `xorm:"varchar(100)" json:"currency"` + Provider string `xorm:"varchar(100)" json:"provider"` + Type string `xorm:"varchar(100)" json:"type"` + Organization string `xorm:"varchar(100)" json:"organization"` + User string `xorm:"varchar(100)" json:"user"` + ProductId string `xorm:"varchar(100)" json:"productId"` + ProductName string `xorm:"varchar(100)" json:"productName"` + Price float64 `json:"price"` + Currency string `xorm:"varchar(100)" json:"currency"` State string `xorm:"varchar(100)" json:"state"` } @@ -124,16 +127,58 @@ func DeletePayment(payment *Payment) bool { return affected != 0 } -func NotifyPayment(id string, state string) bool { - owner, name := util.GetOwnerAndNameFromId(id) - payment := getPayment(owner, name) - if payment == nil { - return false +func NotifyPayment(bm gopay.BodyMap) bool { + owner := "admin" + productName := bm.Get("subject") + paymentId := bm.Get("out_trade_no") + priceString := bm.Get("total_amount") + price := util.ParseFloat(priceString) + productId := bm.Get("productId") + providerId := bm.Get("providerId") + + product := getProduct(owner, productId) + if product == nil { + panic(fmt.Errorf("the product: %s does not exist", productId)) } - payment.State = state + if productName != product.DisplayName { + panic(fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", productName, product.DisplayName)) + } - affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(payment) + if price != product.Price { + panic(fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", price, product.Price)) + } + + payment := getPayment(owner, paymentId) + if payment == nil { + panic(fmt.Errorf("the payment: %s does not exist", paymentId)) + } + + provider, err := product.getProvider(providerId) + if err != nil { + panic(err) + } + + cert := getCert(owner, provider.Cert) + if cert == nil { + panic(fmt.Errorf("the cert: %s does not exist", provider.Cert)) + } + + ok, err := alipay.VerifySignWithCert(cert.AuthorityPublicKey, bm) + if err != nil { + panic(err) + } + + if ok { + payment.State = "Paid" + } else { + if cert == nil { + panic(fmt.Errorf("VerifySignWithCert() failed: %v", ok)) + } + //payment.State = "Failed" + } + + affected, err := adapter.Engine.ID(core.PK{owner, paymentId}).AllCols().Update(payment) if err != nil { panic(err) } diff --git a/object/product.go b/object/product.go index f75d26d8..ab095e07 100644 --- a/object/product.go +++ b/object/product.go @@ -139,19 +139,28 @@ func (product *Product) isValidProvider(provider *Provider) bool { return false } +func (product *Product) getProvider(providerId string) (*Provider, error) { + provider := getProvider(product.Owner, providerId) + if provider == nil { + return nil, fmt.Errorf("the payment provider: %s does not exist", providerId) + } + + if !product.isValidProvider(provider) { + return nil, fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerId, product.Name) + } + + return provider, nil +} + func BuyProduct(id string, providerId string, host string) (string, error) { product := GetProduct(id) if product == nil { return "", fmt.Errorf("the product: %s does not exist", id) } - provider := getProvider(product.Owner, providerId) - if provider == nil { - return "", fmt.Errorf("the payment provider: %s does not exist", providerId) - } - - if !product.isValidProvider(provider) { - return "", fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerId, id) + provider, err := product.getProvider(providerId) + if err != nil { + return "", err } cert := getCert(product.Owner, provider.Cert) @@ -170,6 +179,6 @@ func BuyProduct(id string, providerId string, host string) (string, error) { returnUrl := fmt.Sprintf("%s/payments/%s", originFrontend, paymentId) notifyUrl := fmt.Sprintf("%s/api/notify-payment", originBackend) - payUrl, err := pProvider.Pay(product.DisplayName, paymentId, product.Price, returnUrl, notifyUrl) + payUrl, err := pProvider.Pay(product.DisplayName, product.Name, provider.Name, paymentId, product.Price, returnUrl, notifyUrl) return payUrl, err } diff --git a/object/product_test.go b/object/product_test.go index f5670809..98b2572e 100644 --- a/object/product_test.go +++ b/object/product_test.go @@ -32,7 +32,7 @@ func TestProvider(t *testing.T) { paymentId := util.GenerateTimeId() returnUrl := "" notifyUrl := "" - payUrl, err := pProvider.Pay(product.DisplayName, paymentId, product.Price, returnUrl, notifyUrl) + payUrl, err := pProvider.Pay(product.DisplayName, product.Name, provider.Name, paymentId, product.Price, returnUrl, notifyUrl) if err != nil { panic(err) } diff --git a/pp/alipay.go b/pp/alipay.go index ef7bdbcc..f043aeb3 100644 --- a/pp/alipay.go +++ b/pp/alipay.go @@ -44,7 +44,7 @@ func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey s return pp } -func (pp *AlipayPaymentProvider) Pay(productName string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error) { +func (pp *AlipayPaymentProvider) Pay(productName string, productId string, providerId string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error) { pp.Client.DebugSwitch = gopay.DebugOn priceString := strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.2f", price), "0"), ".") @@ -53,10 +53,12 @@ func (pp *AlipayPaymentProvider) Pay(productName string, paymentId string, price bm.Set("subject", productName) bm.Set("out_trade_no", paymentId) bm.Set("total_amount", priceString) - bm.Set("return_url", returnUrl) bm.Set("notify_url", notifyUrl) + bm.Set("productId", productId) + bm.Set("providerId", productId) + payUrl, err := pp.Client.TradePagePay(context.Background(), bm) if err != nil { return "", err diff --git a/pp/provider.go b/pp/provider.go index 66342369..12cec8fa 100644 --- a/pp/provider.go +++ b/pp/provider.go @@ -15,7 +15,7 @@ package pp type PaymentProvider interface { - Pay(productName string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error) + Pay(productName string, productId string, providerId string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error) } func GetPaymentProvider(typ string, appId string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider { diff --git a/util/string.go b/util/string.go index c2c71656..6c80e023 100644 --- a/util/string.go +++ b/util/string.go @@ -42,6 +42,15 @@ func ParseInt(s string) int { return i } +func ParseFloat(s string) float64 { + f, err := strconv.ParseFloat(s, 64) + if err != nil { + panic(err) + } + + return f +} + func ParseBool(s string) bool { i := ParseInt(s) return i != 0 diff --git a/web/src/PaymentEditPage.js b/web/src/PaymentEditPage.js index 29325092..219ddd03 100644 --- a/web/src/PaymentEditPage.js +++ b/web/src/PaymentEditPage.js @@ -112,7 +112,7 @@ class PaymentEditPage extends React.Component { - {Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} : + {Setting.getLabel(i18next.t("payment:Type"), i18next.t("payment:Type - Tooltip"))} : { @@ -122,20 +122,20 @@ class PaymentEditPage extends React.Component { - {Setting.getLabel(i18next.t("payment:Good"), i18next.t("payment:Good - Tooltip"))} : + {Setting.getLabel(i18next.t("payment:Product"), i18next.t("payment:Product - Tooltip"))} : - { - // this.updatePaymentField('good', e.target.value); + { + // this.updatePaymentField('productName', e.target.value); }} /> - {Setting.getLabel(i18next.t("payment:Amount"), i18next.t("payment:Amount - Tooltip"))} : + {Setting.getLabel(i18next.t("payment:Price"), i18next.t("payment:Price - Tooltip"))} : - { + { // this.updatePaymentField('amount', e.target.value); }} /> diff --git a/web/src/PaymentListPage.js b/web/src/PaymentListPage.js index ee45893c..7b27eb39 100644 --- a/web/src/PaymentListPage.js +++ b/web/src/PaymentListPage.js @@ -34,8 +34,9 @@ class PaymentListPage extends BaseListPage { type: "PayPal", organization: "built-in", user: "admin", - good: "A notebook computer", - amount: "300", + productId: "computer-1", + productName: "A notebook computer", + price: 300.00, currency: "USD", state: "Paid", } @@ -151,7 +152,7 @@ class PaymentListPage extends BaseListPage { } }, { - title: i18next.t("provider:Type"), + title: i18next.t("payment:Type"), dataIndex: 'type', key: 'type', width: '110px', @@ -165,20 +166,20 @@ class PaymentListPage extends BaseListPage { } }, { - title: i18next.t("payment:Good"), - dataIndex: 'good', - key: 'good', + title: i18next.t("payment:Product"), + dataIndex: 'productName', + key: 'productName', width: '160px', sorter: true, - ...this.getColumnSearchProps('good'), + ...this.getColumnSearchProps('productName'), }, { - title: i18next.t("payment:Amount"), - dataIndex: 'amount', - key: 'amount', + title: i18next.t("payment:Price"), + dataIndex: 'price', + key: 'price', width: '120px', sorter: true, - ...this.getColumnSearchProps('amount'), + ...this.getColumnSearchProps('price'), }, { title: i18next.t("payment:Currency"), diff --git a/web/src/ProductBuyPage.js b/web/src/ProductBuyPage.js index cf8cf385..bf54f049 100644 --- a/web/src/ProductBuyPage.js +++ b/web/src/ProductBuyPage.js @@ -13,7 +13,7 @@ // limitations under the License. import React from "react"; -import {Button, Descriptions} from "antd"; +import {Button, Descriptions, Spin} from "antd"; import i18next from "i18next"; import * as ProductBackend from "./backend/ProductBackend"; import * as ProviderBackend from "./backend/ProviderBackend"; @@ -28,6 +28,7 @@ class ProductBuyPage extends React.Component { productName: props.match?.params.productName, product: null, providers: [], + isPlacingOrder: false, }; } @@ -109,16 +110,21 @@ class ProductBuyPage extends React.Component { } buyProduct(product, provider) { + this.setState({ + isPlacingOrder: true, + }); + ProductBackend.buyProduct(this.state.product.owner, this.state.productName, provider.name) .then((res) => { if (res.msg === "") { const payUrl = res.data; Setting.goToLink(payUrl); - this.setState({ - productName: this.state.product.name, - }); } else { Setting.showMessage("error", res.msg); + + this.setState({ + isPlacingOrder: false, + }); } }) .catch(error => { @@ -182,31 +188,33 @@ class ProductBuyPage extends React.Component { return (
- - + + + {product?.displayName} - - {product?.detail} - {product?.tag} - {product?.name} - - {product?.image} - - + + {product?.detail} + {product?.tag} + {product?.name} + + {product?.image} + + {`${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`} - - {product?.quantity} - {product?.sold} - - { - this.renderPay(product) - } - - + + {product?.quantity} + {product?.sold} + + { + this.renderPay(product) + } + + +
) } diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index 469a4a92..336fdd4c 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "Unique string-style identifier" }, "payment": { - "Amount": "Amount", - "Amount - Tooltip": "Amount - Tooltip", "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", "Edit Payment": "Edit Payment", - "Good": "Good", - "Good - Tooltip": "Good - Tooltip", - "New Payment": "New Payment" + "New Payment": "New Payment", + "Price": "Price", + "Price - Tooltip": "Price - Tooltip", + "Product": "Product", + "Product - Tooltip": "Product - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" }, "permission": { "Actions": "Aktionen", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "Alipay", + "Buy": "Buy", "Buy Product": "Buy Product", "CNY": "CNY", "Currency": "Currency", @@ -288,6 +291,7 @@ "Payment providers": "Payment providers", "Payment providers - Tooltip": "Payment providers - Tooltip", "Paypal": "Paypal", + "Placing order...": "Placing order...", "Price": "Price", "Price - Tooltip": "Price - Tooltip", "Quantity": "Quantity", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 12d93dc5..112dc6c1 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "Website URL - Tooltip" }, "payment": { - "Amount": "Amount", - "Amount - Tooltip": "Amount - Tooltip", "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", "Edit Payment": "Edit Payment", - "Good": "Good", - "Good - Tooltip": "Good - Tooltip", - "New Payment": "New Payment" + "New Payment": "New Payment", + "Price": "Price", + "Price - Tooltip": "Price - Tooltip", + "Product": "Product", + "Product - Tooltip": "Product - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" }, "permission": { "Actions": "Actions", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "Alipay", + "Buy": "Buy", "Buy Product": "Buy Product", "CNY": "CNY", "Currency": "Currency", @@ -288,6 +291,7 @@ "Payment providers": "Payment providers", "Payment providers - Tooltip": "Payment providers - Tooltip", "Paypal": "Paypal", + "Placing order...": "Placing order...", "Price": "Price", "Price - Tooltip": "Price - Tooltip", "Quantity": "Quantity", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index 7d79ba85..57eea955 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "Unique string-style identifier" }, "payment": { - "Amount": "Amount", - "Amount - Tooltip": "Amount - Tooltip", "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", "Edit Payment": "Edit Payment", - "Good": "Good", - "Good - Tooltip": "Good - Tooltip", - "New Payment": "New Payment" + "New Payment": "New Payment", + "Price": "Price", + "Price - Tooltip": "Price - Tooltip", + "Product": "Product", + "Product - Tooltip": "Product - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" }, "permission": { "Actions": "Actions", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "Alipay", + "Buy": "Buy", "Buy Product": "Buy Product", "CNY": "CNY", "Currency": "Currency", @@ -288,6 +291,7 @@ "Payment providers": "Payment providers", "Payment providers - Tooltip": "Payment providers - Tooltip", "Paypal": "Paypal", + "Placing order...": "Placing order...", "Price": "Price", "Price - Tooltip": "Price - Tooltip", "Quantity": "Quantity", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index 76e6324b..664964db 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "Unique string-style identifier" }, "payment": { - "Amount": "Amount", - "Amount - Tooltip": "Amount - Tooltip", "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", "Edit Payment": "Edit Payment", - "Good": "Good", - "Good - Tooltip": "Good - Tooltip", - "New Payment": "New Payment" + "New Payment": "New Payment", + "Price": "Price", + "Price - Tooltip": "Price - Tooltip", + "Product": "Product", + "Product - Tooltip": "Product - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" }, "permission": { "Actions": "アクション", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "Alipay", + "Buy": "Buy", "Buy Product": "Buy Product", "CNY": "CNY", "Currency": "Currency", @@ -288,6 +291,7 @@ "Payment providers": "Payment providers", "Payment providers - Tooltip": "Payment providers - Tooltip", "Paypal": "Paypal", + "Placing order...": "Placing order...", "Price": "Price", "Price - Tooltip": "Price - Tooltip", "Quantity": "Quantity", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index 6e60fbc0..2797d26a 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "Unique string-style identifier" }, "payment": { - "Amount": "Amount", - "Amount - Tooltip": "Amount - Tooltip", "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", "Edit Payment": "Edit Payment", - "Good": "Good", - "Good - Tooltip": "Good - Tooltip", - "New Payment": "New Payment" + "New Payment": "New Payment", + "Price": "Price", + "Price - Tooltip": "Price - Tooltip", + "Product": "Product", + "Product - Tooltip": "Product - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" }, "permission": { "Actions": "Actions", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "Alipay", + "Buy": "Buy", "Buy Product": "Buy Product", "CNY": "CNY", "Currency": "Currency", @@ -288,6 +291,7 @@ "Payment providers": "Payment providers", "Payment providers - Tooltip": "Payment providers - Tooltip", "Paypal": "Paypal", + "Placing order...": "Placing order...", "Price": "Price", "Price - Tooltip": "Price - Tooltip", "Quantity": "Quantity", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index 455b4e5f..71879750 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "Unique string-style identifier" }, "payment": { - "Amount": "Amount", - "Amount - Tooltip": "Amount - Tooltip", "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", "Edit Payment": "Edit Payment", - "Good": "Good", - "Good - Tooltip": "Good - Tooltip", - "New Payment": "New Payment" + "New Payment": "New Payment", + "Price": "Price", + "Price - Tooltip": "Price - Tooltip", + "Product": "Product", + "Product - Tooltip": "Product - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" }, "permission": { "Actions": "Действия", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "Alipay", + "Buy": "Buy", "Buy Product": "Buy Product", "CNY": "CNY", "Currency": "Currency", @@ -288,6 +291,7 @@ "Payment providers": "Payment providers", "Payment providers - Tooltip": "Payment providers - Tooltip", "Paypal": "Paypal", + "Placing order...": "Placing order...", "Price": "Price", "Price - Tooltip": "Price - Tooltip", "Quantity": "Quantity", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index 4adeb13a..da0df316 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -252,14 +252,16 @@ "Website URL - Tooltip": "网页地址" }, "payment": { - "Amount": "金额", - "Amount - Tooltip": "付款的金额", "Currency": "币种", "Currency - Tooltip": "如USD(美元),CNY(人民币)等", "Edit Payment": "编辑付款", - "Good": "商品", - "Good - Tooltip": "购买的商品名称", - "New Payment": "添加付款" + "New Payment": "添加付款", + "Price": "价格", + "Price - Tooltip": "商品价格", + "Product": "商品", + "Product - Tooltip": "商品名称", + "Type": "支付方式", + "Type - Tooltip": "商品购买时的支付方式" }, "permission": { "Actions": "动作", @@ -274,6 +276,7 @@ }, "product": { "Alipay": "支付宝", + "Buy": "购买", "Buy Product": "购买商品", "CNY": "人民币", "Currency": "币种", @@ -288,6 +291,7 @@ "Payment providers": "支付提供商", "Payment providers - Tooltip": "支付提供商 - 工具提示", "Paypal": "Paypal", + "Placing order...": "正在下单...", "Price": "价格", "Price - Tooltip": "价格 - 工具提示", "Quantity": "库存",