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?.detail}
+ {product?.tag}
+ {product?.name}
+
+
+
+
{`${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": "库存",