From 940aa2bc2d94ddce0d22df7651d506eccc01e9f4 Mon Sep 17 00:00:00 2001 From: Yang Luo Date: Sat, 5 Feb 2022 01:18:13 +0800 Subject: [PATCH] Add payment pages. --- controllers/payment.go | 116 +++++++++++++ object/adapter.go | 7 +- object/payment.go | 129 +++++++++++++++ routers/router.go | 6 + web/src/App.js | 13 ++ web/src/PaymentEditPage.js | 210 ++++++++++++++++++++++++ web/src/PaymentListPage.js | 264 ++++++++++++++++++++++++++++++ web/src/ProviderEditPage.js | 1 + web/src/Setting.js | 6 + web/src/auth/Provider.js | 14 ++ web/src/backend/PaymentBackend.js | 56 +++++++ web/src/locales/de/data.json | 11 ++ web/src/locales/en/data.json | 11 ++ web/src/locales/fr/data.json | 11 ++ web/src/locales/ja/data.json | 11 ++ web/src/locales/ko/data.json | 11 ++ web/src/locales/ru/data.json | 11 ++ web/src/locales/zh/data.json | 11 ++ 18 files changed, 898 insertions(+), 1 deletion(-) create mode 100644 controllers/payment.go create mode 100644 object/payment.go create mode 100644 web/src/PaymentEditPage.js create mode 100644 web/src/PaymentListPage.js create mode 100644 web/src/backend/PaymentBackend.js diff --git a/controllers/payment.go b/controllers/payment.go new file mode 100644 index 00000000..b2bbcc36 --- /dev/null +++ b/controllers/payment.go @@ -0,0 +1,116 @@ +// Copyright 2022 The casbin 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 controllers + +import ( + "encoding/json" + + "github.com/astaxie/beego/utils/pagination" + "github.com/casdoor/casdoor/object" + "github.com/casdoor/casdoor/util" +) + +// GetPayments +// @Title GetPayments +// @Tag Payment API +// @Description get payments +// @Param owner query string true "The owner of payments" +// @Success 200 {array} object.Payment The Response object +// @router /get-payments [get] +func (c *ApiController) GetPayments() { + owner := c.Input().Get("owner") + limit := c.Input().Get("pageSize") + page := c.Input().Get("p") + field := c.Input().Get("field") + value := c.Input().Get("value") + sortField := c.Input().Get("sortField") + sortOrder := c.Input().Get("sortOrder") + if limit == "" || page == "" { + c.Data["json"] = object.GetPayments(owner) + c.ServeJSON() + } else { + limit := util.ParseInt(limit) + paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetPaymentCount(owner, field, value))) + payments := object.GetPaginationPayments(owner, paginator.Offset(), limit, field, value, sortField, sortOrder) + c.ResponseOk(payments, paginator.Nums()) + } +} + +// @Title GetPayment +// @Tag Payment API +// @Description get payment +// @Param id query string true "The id of the payment" +// @Success 200 {object} object.Payment The Response object +// @router /get-payment [get] +func (c *ApiController) GetPayment() { + id := c.Input().Get("id") + + c.Data["json"] = object.GetPayment(id) + c.ServeJSON() +} + +// @Title UpdatePayment +// @Tag Payment API +// @Description update payment +// @Param id query string true "The id of the payment" +// @Param body body object.Payment true "The details of the payment" +// @Success 200 {object} controllers.Response The Response object +// @router /update-payment [post] +func (c *ApiController) UpdatePayment() { + id := c.Input().Get("id") + + var payment object.Payment + err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment) + if err != nil { + panic(err) + } + + c.Data["json"] = wrapActionResponse(object.UpdatePayment(id, &payment)) + c.ServeJSON() +} + +// @Title AddPayment +// @Tag Payment API +// @Description add payment +// @Param body body object.Payment true "The details of the payment" +// @Success 200 {object} controllers.Response The Response object +// @router /add-payment [post] +func (c *ApiController) AddPayment() { + var payment object.Payment + err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment) + if err != nil { + panic(err) + } + + c.Data["json"] = wrapActionResponse(object.AddPayment(&payment)) + c.ServeJSON() +} + +// @Title DeletePayment +// @Tag Payment API +// @Description delete payment +// @Param body body object.Payment true "The details of the payment" +// @Success 200 {object} controllers.Response The Response object +// @router /delete-payment [post] +func (c *ApiController) DeletePayment() { + var payment object.Payment + err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment) + if err != nil { + panic(err) + } + + c.Data["json"] = wrapActionResponse(object.DeletePayment(&payment)) + c.ServeJSON() +} diff --git a/object/adapter.go b/object/adapter.go index 2fcf93a6..3efae0d2 100644 --- a/object/adapter.go +++ b/object/adapter.go @@ -183,6 +183,11 @@ func (a *Adapter) createTable() { panic(err) } + err = a.Engine.Sync2(new(Payment)) + if err != nil { + panic(err) + } + err = a.Engine.Sync2(new(Ldap)) if err != nil { panic(err) @@ -211,4 +216,4 @@ func GetSession(owner string, offset, limit int, field, value, sortField, sortOr session = session.Desc(util.SnakeString(sortField)) } return session -} \ No newline at end of file +} diff --git a/object/payment.go b/object/payment.go new file mode 100644 index 00000000..1b3912b0 --- /dev/null +++ b/object/payment.go @@ -0,0 +1,129 @@ +// Copyright 2022 The casbin 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 object + +import ( + "fmt" + + "github.com/casdoor/casdoor/util" + "xorm.io/core" +) + +type Payment struct { + Owner string `xorm:"varchar(100) notnull pk" json:"owner"` + Name string `xorm:"varchar(100) notnull pk" json:"name"` + 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"` + + State string `xorm:"varchar(100)" json:"state"` +} + +func GetPaymentCount(owner, field, value string) int { + session := GetSession(owner, -1, -1, field, value, "", "") + count, err := session.Count(&Payment{}) + if err != nil { + panic(err) + } + + return int(count) +} + +func GetPayments(owner string) []*Payment { + payments := []*Payment{} + err := adapter.Engine.Desc("created_time").Find(&payments, &Payment{Owner: owner}) + if err != nil { + panic(err) + } + + return payments +} + +func GetPaginationPayments(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Payment { + payments := []*Payment{} + session := GetSession(owner, offset, limit, field, value, sortField, sortOrder) + err := session.Find(&payments) + if err != nil { + panic(err) + } + + return payments +} + +func getPayment(owner string, name string) *Payment { + if owner == "" || name == "" { + return nil + } + + payment := Payment{Owner: owner, Name: name} + existed, err := adapter.Engine.Get(&payment) + if err != nil { + panic(err) + } + + if existed { + return &payment + } else { + return nil + } +} + +func GetPayment(id string) *Payment { + owner, name := util.GetOwnerAndNameFromId(id) + return getPayment(owner, name) +} + +func UpdatePayment(id string, payment *Payment) bool { + owner, name := util.GetOwnerAndNameFromId(id) + if getPayment(owner, name) == nil { + return false + } + + affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(payment) + if err != nil { + panic(err) + } + + return affected != 0 +} + +func AddPayment(payment *Payment) bool { + affected, err := adapter.Engine.Insert(payment) + if err != nil { + panic(err) + } + + return affected != 0 +} + +func DeletePayment(payment *Payment) bool { + affected, err := adapter.Engine.ID(core.PK{payment.Owner, payment.Name}).Delete(&Payment{}) + if err != nil { + panic(err) + } + + return affected != 0 +} + +func (payment *Payment) GetId() string { + return fmt.Sprintf("%s/%s", payment.Owner, payment.Name) +} diff --git a/routers/router.go b/routers/router.go index 348818bc..aa67e269 100644 --- a/routers/router.go +++ b/routers/router.go @@ -149,6 +149,12 @@ func initAPI() { beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert") beego.Router("/api/delete-cert", &controllers.ApiController{}, "POST:DeleteCert") + beego.Router("/api/get-payments", &controllers.ApiController{}, "GET:GetPayments") + beego.Router("/api/get-payment", &controllers.ApiController{}, "GET:GetPayment") + beego.Router("/api/update-payment", &controllers.ApiController{}, "POST:UpdatePayment") + beego.Router("/api/add-payment", &controllers.ApiController{}, "POST:AddPayment") + beego.Router("/api/delete-payment", &controllers.ApiController{}, "POST:DeletePayment") + beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail") beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms") diff --git a/web/src/App.js b/web/src/App.js index 7f9553a1..dbb12e7f 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -43,6 +43,8 @@ import SyncerListPage from "./SyncerListPage"; import SyncerEditPage from "./SyncerEditPage"; import CertListPage from "./CertListPage"; import CertEditPage from "./CertEditPage"; +import PaymentListPage from "./PaymentListPage"; +import PaymentEditPage from "./PaymentEditPage"; import AccountPage from "./account/AccountPage"; import HomePage from "./basic/HomePage"; import CustomGithubCorner from "./CustomGithubCorner"; @@ -126,6 +128,8 @@ class App extends Component { this.setState({ selectedMenuKey: '/syncers' }); } else if (uri.includes('/certs')) { this.setState({ selectedMenuKey: '/certs' }); + } else if (uri.includes('/payments')) { + this.setState({ selectedMenuKey: '/payments' }); } else if (uri.includes('/signup')) { this.setState({ selectedMenuKey: '/signup' }); } else if (uri.includes('/login')) { @@ -408,6 +412,13 @@ class App extends Component { ); + res.push( + + + {i18next.t("general:Payments")} + + + ); res.push( @@ -478,6 +489,8 @@ class App extends Component { this.renderLoginIfNotLoggedIn()}/> this.renderLoginIfNotLoggedIn()}/> this.renderLoginIfNotLoggedIn()}/> + this.renderLoginIfNotLoggedIn()}/> + this.renderLoginIfNotLoggedIn()}/> this.renderLoginIfNotLoggedIn()}/> }/> { + this.setState({ + payment: payment, + }); + }); + } + + parsePaymentField(key, value) { + if ([""].includes(key)) { + value = Setting.myParseInt(value); + } + return value; + } + + updatePaymentField(key, value) { + value = this.parsePaymentField(key, value); + + let payment = this.state.payment; + payment[key] = value; + this.setState({ + payment: payment, + }); + } + + renderPayment() { + return ( + + {i18next.t("payment:Edit Payment")}     + + + + } style={(Setting.isMobile())? {margin: '5px'}:{}} type="inner"> + + + {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : + + + { + // this.updatePaymentField('organization', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} : + + + { + // this.updatePaymentField('name', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} : + + + { + this.updatePaymentField('displayName', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} : + + + { + // this.updatePaymentField('name', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("general:Provider"), i18next.t("general:Provider - Tooltip"))} : + + + { + // this.updatePaymentField('provider', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} : + + + { + // this.updatePaymentField('type', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("payment:Good"), i18next.t("payment:Good - Tooltip"))} : + + + { + // this.updatePaymentField('good', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("payment:Amount"), i18next.t("payment:Amount - Tooltip"))} : + + + { + // this.updatePaymentField('amount', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("payment:Currency"), i18next.t("payment:Currency - Tooltip"))} : + + + { + // this.updatePaymentField('currency', e.target.value); + }} /> + + + + ) + } + + submitPaymentEdit(willExist) { + let payment = Setting.deepCopy(this.state.payment); + PaymentBackend.updatePayment(this.state.organizationName, this.state.paymentName, payment) + .then((res) => { + if (res.msg === "") { + Setting.showMessage("success", `Successfully saved`); + this.setState({ + paymentName: this.state.payment.name, + }); + + if (willExist) { + this.props.history.push(`/payments`); + } else { + this.props.history.push(`/payments/${this.state.payment.name}`); + } + } else { + Setting.showMessage("error", res.msg); + this.updatePaymentField('name', this.state.paymentName); + } + }) + .catch(error => { + Setting.showMessage("error", `Failed to connect to server: ${error}`); + }); + } + + render() { + return ( +
+ { + this.state.payment !== null ? this.renderPayment() : null + } +
+ + +
+
+ ); + } +} + +export default PaymentEditPage; diff --git a/web/src/PaymentListPage.js b/web/src/PaymentListPage.js new file mode 100644 index 00000000..80314ce4 --- /dev/null +++ b/web/src/PaymentListPage.js @@ -0,0 +1,264 @@ +// Copyright 2022 The casbin 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. + +import React from "react"; +import {Link} from "react-router-dom"; +import {Button, Popconfirm, Switch, Table} from 'antd'; +import moment from "moment"; +import * as Setting from "./Setting"; +import * as PaymentBackend from "./backend/PaymentBackend"; +import i18next from "i18next"; +import BaseListPage from "./BaseListPage"; +import * as Provider from "./auth/Provider"; + +class PaymentListPage extends BaseListPage { + newPayment() { + const randomName = Setting.getRandomName(); + return { + owner: "admin", + name: `payment_${randomName}`, + createdTime: moment().format(), + displayName: `New Payment - ${randomName}`, + provider: "provider_pay_paypal", + type: "PayPal", + organization: "built-in", + user: "admin", + good: "A notebook computer", + amount: "300", + currency: "USD", + state: "Paid", + } + } + + addPayment() { + const newPayment = this.newPayment(); + PaymentBackend.addPayment(newPayment) + .then((res) => { + Setting.showMessage("success", `Payment added successfully`); + this.props.history.push(`/payments/${newPayment.name}`); + } + ) + .catch(error => { + Setting.showMessage("error", `Payment failed to add: ${error}`); + }); + } + + deletePayment(i) { + PaymentBackend.deletePayment(this.state.data[i]) + .then((res) => { + Setting.showMessage("success", `Payment deleted successfully`); + this.setState({ + data: Setting.deleteRow(this.state.data, i), + pagination: {total: this.state.pagination.total - 1}, + }); + } + ) + .catch(error => { + Setting.showMessage("error", `Payment failed to delete: ${error}`); + }); + } + + renderTable(payments) { + const columns = [ + { + title: i18next.t("general:Organization"), + dataIndex: 'owner', + key: 'owner', + width: '120px', + sorter: true, + ...this.getColumnSearchProps('owner'), + render: (text, record, index) => { + return ( + + {text} + + ) + } + }, + { + title: i18next.t("general:User"), + dataIndex: 'user', + key: 'user', + width: '120px', + sorter: true, + ...this.getColumnSearchProps('user'), + render: (text, record, index) => { + return ( + + {text} + + ) + } + }, + { + title: i18next.t("general:Name"), + dataIndex: 'name', + key: 'name', + width: '150px', + fixed: 'left', + sorter: true, + ...this.getColumnSearchProps('name'), + render: (text, record, index) => { + return ( + + {text} + + ) + } + }, + { + title: i18next.t("general:Created time"), + dataIndex: 'createdTime', + key: 'createdTime', + width: '160px', + sorter: true, + render: (text, record, index) => { + return Setting.getFormattedDate(text); + } + }, + // { + // title: i18next.t("general:Display name"), + // dataIndex: 'displayName', + // key: 'displayName', + // width: '160px', + // sorter: true, + // ...this.getColumnSearchProps('displayName'), + // }, + { + title: i18next.t("general:Provider"), + dataIndex: 'provider', + key: 'provider', + width: '150px', + fixed: 'left', + sorter: true, + ...this.getColumnSearchProps('provider'), + render: (text, record, index) => { + return ( + + {text} + + ) + } + }, + { + title: i18next.t("provider:Type"), + dataIndex: 'type', + key: 'type', + width: '110px', + align: 'center', + filterMultiple: false, + filters: [ + {text: 'Payment', value: 'Payment', children: Setting.getProviderTypeOptions('Payment').map((o) => {return {text:o.id, value:o.name}})}, + ], + sorter: true, + render: (text, record, index) => { + return Provider.getProviderLogoWidget(record); + } + }, + { + title: i18next.t("payment:Good"), + dataIndex: 'good', + key: 'good', + width: '160px', + sorter: true, + ...this.getColumnSearchProps('good'), + }, + { + title: i18next.t("payment:Amount"), + dataIndex: 'amount', + key: 'amount', + width: '120px', + sorter: true, + ...this.getColumnSearchProps('amount'), + }, + { + title: i18next.t("payment:Currency"), + dataIndex: 'currency', + key: 'currency', + width: '120px', + sorter: true, + ...this.getColumnSearchProps('currency'), + }, + { + title: i18next.t("general:Action"), + dataIndex: '', + key: 'op', + width: '170px', + fixed: (Setting.isMobile()) ? "false" : "right", + render: (text, record, index) => { + return ( +
+ + this.deletePayment(index)} + > + + +
+ ) + } + }, + ]; + + const paginationProps = { + total: this.state.pagination.total, + showQuickJumper: true, + showSizeChanger: true, + showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total), + }; + + return ( +
+ ( +
+ {i18next.t("general:Payments")}     + +
+ )} + loading={this.state.loading} + onChange={this.handleTableChange} + /> + + ); + } + + fetch = (params = {}) => { + let field = params.searchedColumn, value = params.searchText; + let sortField = params.sortField, sortOrder = params.sortOrder; + if (params.type !== undefined && params.type !== null) { + field = "type"; + value = params.type; + } + this.setState({ loading: true }); + PaymentBackend.getPayments("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) + .then((res) => { + if (res.status === "ok") { + this.setState({ + loading: false, + data: res.data, + pagination: { + ...params.pagination, + total: res.data2, + }, + searchText: params.searchText, + searchedColumn: params.searchedColumn, + }); + } + }); + }; +} + +export default PaymentListPage; diff --git a/web/src/ProviderEditPage.js b/web/src/ProviderEditPage.js index 61d3ceed..6c07e697 100644 --- a/web/src/ProviderEditPage.js +++ b/web/src/ProviderEditPage.js @@ -184,6 +184,7 @@ class ProviderEditPage extends React.Component { {id: 'SMS', name: 'SMS'}, {id: 'Storage', name: 'Storage'}, {id: 'SAML', name: 'SAML'}, + {id: 'Payment', name: 'Payment'}, ].map((providerCategory, index) => ) } diff --git a/web/src/Setting.js b/web/src/Setting.js index aa287667..980b4b30 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -432,6 +432,12 @@ export function getProviderTypeOptions(category) { {id: 'Aliyun IDaaS', name: 'Aliyun IDaaS'}, {id: 'Keycloak', name: 'Keycloak'}, ]); + } else if (category === "Payment") { + return ([ + {id: 'Alipay', name: 'Alipay'}, + {id: 'WeChat Pay', name: 'WeChat Pay'}, + {id: 'PayPal', name: 'PayPal'}, + ]); } else { return []; } diff --git a/web/src/auth/Provider.js b/web/src/auth/Provider.js index 2c7f16d2..48ebc7ca 100644 --- a/web/src/auth/Provider.js +++ b/web/src/auth/Provider.js @@ -137,6 +137,20 @@ const otherProviderInfo = { url: "https://www.keycloak.org/" }, }, + Payment: { + "Alipay": { + logo: `${StaticBaseUrl}/img/payment_alipay.png`, + url: "https://www.alipay.com/" + }, + "WeChat Pay": { + logo: `${StaticBaseUrl}/img/payment_wechat_pay.png`, + url: "https://pay.weixin.qq.com/" + }, + "PayPal": { + logo: `${StaticBaseUrl}/img/payment_paypal.png`, + url: "https://www.paypal.com/" + }, + }, }; export function getProviderLogo(provider) { diff --git a/web/src/backend/PaymentBackend.js b/web/src/backend/PaymentBackend.js new file mode 100644 index 00000000..85bb76f6 --- /dev/null +++ b/web/src/backend/PaymentBackend.js @@ -0,0 +1,56 @@ +// Copyright 2022 The casbin 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. + +import * as Setting from "../Setting"; + +export function getPayments(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") { + return fetch(`${Setting.ServerUrl}/api/get-payments?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { + method: "GET", + credentials: "include" + }).then(res => res.json()); +} + +export function getPayment(owner, name) { + return fetch(`${Setting.ServerUrl}/api/get-payment?id=${owner}/${encodeURIComponent(name)}`, { + method: "GET", + credentials: "include" + }).then(res => res.json()); +} + +export function updatePayment(owner, name, payment) { + let newPayment = Setting.deepCopy(payment); + return fetch(`${Setting.ServerUrl}/api/update-payment?id=${owner}/${encodeURIComponent(name)}`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify(newPayment), + }).then(res => res.json()); +} + +export function addPayment(payment) { + let newPayment = Setting.deepCopy(payment); + return fetch(`${Setting.ServerUrl}/api/add-payment`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify(newPayment), + }).then(res => res.json()); +} + +export function deletePayment(payment) { + let newPayment = Setting.deepCopy(payment); + return fetch(`${Setting.ServerUrl}/api/delete-payment`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify(newPayment), + }).then(res => res.json()); +} diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index 07d2ae25..20fb2cea 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "Random parameters used for password encryption", "Password type": "Passworttyp", "Password type - Tooltip": "The form in which the password is stored in the database", + "Payments": "Payments", "Permissions": "Berechtigungen", "Personal name": "Persönlicher Name", "Phone": "Telefon", @@ -142,6 +143,7 @@ "Preview": "Vorschau", "Preview - Tooltip": "The form in which the password is stored in the database", "Provider": "Anbieter", + "Provider - Tooltip": "Provider - Tooltip", "Providers": "Anbieter", "Providers - Tooltip": "List of third-party applications that can be used to log in", "Records": "Datensätze", @@ -234,6 +236,15 @@ "Website URL": "Website-URL", "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" + }, "permission": { "Actions": "Aktionen", "Actions - Tooltip": "Aktionen - Tooltip", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 0bb974a2..0954ba08 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "Password salt - Tooltip", "Password type": "Password type", "Password type - Tooltip": "Password type - Tooltip", + "Payments": "Payments", "Permissions": "Permissions", "Personal name": "Personal name", "Phone": "Phone", @@ -142,6 +143,7 @@ "Preview": "Preview", "Preview - Tooltip": "Preview - Tooltip", "Provider": "Provider", + "Provider - Tooltip": "Provider - Tooltip", "Providers": "Providers", "Providers - Tooltip": "Providers - Tooltip", "Records": "Records", @@ -234,6 +236,15 @@ "Website URL": "Website URL", "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" + }, "permission": { "Actions": "Actions", "Actions - Tooltip": "Actions - Tooltip", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index 646e54cd..4493ef75 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "Random parameters used for password encryption", "Password type": "Type de mot de passe", "Password type - Tooltip": "The form in which the password is stored in the database", + "Payments": "Payments", "Permissions": "Permissions", "Personal name": "Nom personnel", "Phone": "Téléphone", @@ -142,6 +143,7 @@ "Preview": "Aperçu", "Preview - Tooltip": "The form in which the password is stored in the database", "Provider": "Fournisseur", + "Provider - Tooltip": "Provider - Tooltip", "Providers": "Fournisseurs", "Providers - Tooltip": "List of third-party applications that can be used to log in", "Records": "Enregistrements", @@ -234,6 +236,15 @@ "Website URL": "URL du site web", "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" + }, "permission": { "Actions": "Actions", "Actions - Tooltip": "Actions - Info-bulle", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index be36da5b..d3c44d67 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "Random parameters used for password encryption", "Password type": "パスワードの種類", "Password type - Tooltip": "The form in which the password is stored in the database", + "Payments": "Payments", "Permissions": "アクセス許可", "Personal name": "個人名", "Phone": "電話番号", @@ -142,6 +143,7 @@ "Preview": "プレビュー", "Preview - Tooltip": "The form in which the password is stored in the database", "Provider": "プロバイダー", + "Provider - Tooltip": "Provider - Tooltip", "Providers": "プロバイダー", "Providers - Tooltip": "List of third-party applications that can be used to log in", "Records": "レコード", @@ -234,6 +236,15 @@ "Website URL": "Website URL", "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" + }, "permission": { "Actions": "アクション", "Actions - Tooltip": "アクション → ツールチップ", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index a106821e..2dc7b696 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "Random parameters used for password encryption", "Password type": "Password type", "Password type - Tooltip": "The form in which the password is stored in the database", + "Payments": "Payments", "Permissions": "Permissions", "Personal name": "Personal name", "Phone": "Phone", @@ -142,6 +143,7 @@ "Preview": "Preview", "Preview - Tooltip": "The form in which the password is stored in the database", "Provider": "Provider", + "Provider - Tooltip": "Provider - Tooltip", "Providers": "Providers", "Providers - Tooltip": "List of third-party applications that can be used to log in", "Records": "Records", @@ -234,6 +236,15 @@ "Website URL": "Website URL", "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" + }, "permission": { "Actions": "Actions", "Actions - Tooltip": "Actions - Tooltip", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index 7b74ca2d..aa1517bb 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "Random parameters used for password encryption", "Password type": "Тип пароля", "Password type - Tooltip": "The form in which the password is stored in the database", + "Payments": "Payments", "Permissions": "Права доступа", "Personal name": "Личное имя", "Phone": "Телефон", @@ -142,6 +143,7 @@ "Preview": "Предпросмотр", "Preview - Tooltip": "The form in which the password is stored in the database", "Provider": "Поставщик", + "Provider - Tooltip": "Provider - Tooltip", "Providers": "Поставщики", "Providers - Tooltip": "List of third-party applications that can be used to log in", "Records": "Отчеты", @@ -234,6 +236,15 @@ "Website URL": "URL сайта", "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" + }, "permission": { "Actions": "Действия", "Actions - Tooltip": "Действия - Подсказка", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index dce0f11b..9af83849 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -133,6 +133,7 @@ "Password salt - Tooltip": "用于密码加密的随机参数", "Password type": "密码类型", "Password type - Tooltip": "密码在数据库中存储的形式", + "Payments": "付款", "Permissions": "权限", "Personal name": "姓名", "Phone": "手机号", @@ -142,6 +143,7 @@ "Preview": "预览", "Preview - Tooltip": "预览", "Provider": "提供商", + "Provider - Tooltip": "第三方登录需要配置的提供方", "Providers": "提供商", "Providers - Tooltip": "第三方登录需要配置的提供方", "Records": "日志", @@ -234,6 +236,15 @@ "Website URL": "网页地址", "Website URL - Tooltip": "网页地址" }, + "payment": { + "Amount": "金额", + "Amount - Tooltip": "付款的金额", + "Currency": "币种", + "Currency - Tooltip": "如USD(美元),CNY(人民币)等", + "Edit Payment": "编辑付款", + "Good": "商品", + "Good - Tooltip": "购买的商品名称" + }, "permission": { "Actions": "动作", "Actions - Tooltip": "授权的动作",