mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-30 16:20:32 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c4089eacb7 | ||
![]() |
4acba2d493 | ||
![]() |
fc0ca4cceb | ||
![]() |
912d9d0c01 | ||
![]() |
8e48bddf5f | ||
![]() |
c05fb77224 | ||
![]() |
9af9ead939 | ||
![]() |
f5590c42f7 | ||
![]() |
5597f99e3c | ||
![]() |
ea005aaf4d | ||
![]() |
e5c1f560c5 | ||
![]() |
20fc7d1b58 | ||
![]() |
cf3b46130b | ||
![]() |
cab51fae9c | ||
![]() |
b867872da4 | ||
![]() |
305867f49a | ||
![]() |
3f90c18a19 | ||
![]() |
9e5a64c021 | ||
![]() |
4263af6f2c | ||
![]() |
3e92d761b9 | ||
![]() |
0e41568f62 | ||
![]() |
fb7e2729c6 | ||
![]() |
28b9154d7e | ||
![]() |
b0b3eb0805 | ||
![]() |
73bd9dd517 |
@@ -80,16 +80,17 @@ p, *, *, GET, /api/get-app-login, *, *
|
||||
p, *, *, POST, /api/logout, *, *
|
||||
p, *, *, GET, /api/get-account, *, *
|
||||
p, *, *, GET, /api/userinfo, *, *
|
||||
p, *, *, POST, /api/login/oauth/access_token, *, *
|
||||
p, *, *, POST, /api/login/oauth/refresh_token, *, *
|
||||
p, *, *, GET, /api/login/oauth/logout, *, *
|
||||
p, *, *, *, /api/login/oauth, *, *
|
||||
p, *, *, GET, /api/get-application, *, *
|
||||
p, *, *, GET, /api/get-applications, *, *
|
||||
p, *, *, GET, /api/get-user, *, *
|
||||
p, *, *, GET, /api/get-user-application, *, *
|
||||
p, *, *, GET, /api/get-resources, *, *
|
||||
p, *, *, GET, /api/get-product, *, *
|
||||
p, *, *, POST, /api/buy-product, *, *
|
||||
p, *, *, GET, /api/get-payment, *, *
|
||||
p, *, *, POST, /api/update-payment, *, *
|
||||
p, *, *, POST, /api/invoice-payment, *, *
|
||||
p, *, *, GET, /api/get-providers, *, *
|
||||
p, *, *, POST, /api/unlink, *, *
|
||||
p, *, *, POST, /api/set-password, *, *
|
||||
|
@@ -116,7 +116,7 @@ func (c *ApiController) Signup() {
|
||||
return
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Email") && form.Email != "" {
|
||||
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" {
|
||||
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode)
|
||||
if len(checkResult) != 0 {
|
||||
c.ResponseError(fmt.Sprintf("Email: %s", checkResult))
|
||||
@@ -210,7 +210,7 @@ func (c *ApiController) Signup() {
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
record.User = user.Name
|
||||
util.SafeGoroutine(func() {object.AddRecord(record)})
|
||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
||||
|
||||
userId := fmt.Sprintf("%s/%s", user.Owner, user.Name)
|
||||
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
||||
@@ -285,6 +285,7 @@ func (c *ApiController) GetUserinfo() {
|
||||
resp, err := object.GetUserInfo(userId, scope, aud, host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
|
@@ -215,7 +215,7 @@ func (c *ApiController) SyncLdapUsers() {
|
||||
|
||||
object.UpdateLdapSyncTime(ldapId)
|
||||
|
||||
exist, failed := object.SyncLdapUsers(owner, users)
|
||||
exist, failed := object.SyncLdapUsers(owner, users, ldapId)
|
||||
c.Data["json"] = &Response{Status: "ok", Data: &LdapSyncResp{
|
||||
Exist: *exist,
|
||||
Failed: *failed,
|
||||
|
@@ -158,3 +158,20 @@ func (c *ApiController) NotifyPayment() {
|
||||
panic(fmt.Errorf("NotifyPayment() failed: %v", ok))
|
||||
}
|
||||
}
|
||||
|
||||
// @Title InvoicePayment
|
||||
// @Tag Payment API
|
||||
// @Description invoice payment
|
||||
// @Param id query string true "The id of the payment"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /invoice-payment [post]
|
||||
func (c *ApiController) InvoicePayment() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
payment := object.GetPayment(id)
|
||||
invoiceUrl, err := object.InvoicePayment(payment)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
}
|
||||
c.ResponseOk(invoiceUrl)
|
||||
}
|
||||
|
@@ -230,7 +230,7 @@ func (c *ApiController) RefreshToken() {
|
||||
clientSecret = tokenRequest.ClientSecret
|
||||
grantType = tokenRequest.GrantType
|
||||
scope = tokenRequest.Scope
|
||||
|
||||
refreshToken = tokenRequest.RefreshToken
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,21 +275,20 @@ func (c *ApiController) IntrospectToken() {
|
||||
tokenValue := c.Input().Get("token")
|
||||
clientId, clientSecret, ok := c.Ctx.Request.BasicAuth()
|
||||
if !ok {
|
||||
util.LogWarning(c.Ctx, "Basic Authorization parses failed")
|
||||
c.Data["json"] = Response{Status: "error", Msg: "Unauthorized operation"}
|
||||
c.ServeJSON()
|
||||
return
|
||||
clientId = c.Input().Get("client_id")
|
||||
clientSecret = c.Input().Get("client_secret")
|
||||
if clientId == "" || clientSecret == "" {
|
||||
c.ResponseError("empty clientId or clientSecret")
|
||||
return
|
||||
}
|
||||
}
|
||||
application := object.GetApplicationByClientId(clientId)
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
util.LogWarning(c.Ctx, "Basic Authorization failed")
|
||||
c.Data["json"] = Response{Status: "error", Msg: "Unauthorized operation"}
|
||||
c.ServeJSON()
|
||||
c.ResponseError("invalid application or wrong clientSecret")
|
||||
return
|
||||
}
|
||||
token := object.GetTokenByTokenAndApplication(tokenValue, application.Name)
|
||||
if token == nil {
|
||||
util.LogWarning(c.Ctx, "application: %s can not find token", application.Name)
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
@@ -299,7 +298,6 @@ func (c *ApiController) IntrospectToken() {
|
||||
// and token revoked case. but we not implement
|
||||
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
|
||||
// refs: https://tools.ietf.org/html/rfc7009
|
||||
util.LogWarning(c.Ctx, "token invalid")
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
|
@@ -25,4 +25,5 @@ type TokenRequest struct {
|
||||
Password string `json:"password"`
|
||||
Tag string `json:"tag"`
|
||||
Avatar string `json:"avatar"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ services:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
entrypoint: /bin/sh -c './server --createDatabase=true'
|
||||
ports:
|
||||
- "8000:8000"
|
||||
depends_on:
|
||||
|
2
go.mod
2
go.mod
@@ -44,5 +44,5 @@ require (
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
xorm.io/core v0.7.2
|
||||
xorm.io/xorm v1.0.3
|
||||
xorm.io/xorm v1.0.4
|
||||
)
|
||||
|
5
go.sum
5
go.sum
@@ -258,8 +258,6 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/ma314smith/signedxml v0.0.0-20210628192057-abc5b481ae1c h1:UPJygtyk491bJJ/DnRJFuzcq9Dl9NSeFrJ7VdiRzMxc=
|
||||
github.com/ma314smith/signedxml v0.0.0-20210628192057-abc5b481ae1c/go.mod h1:KEgVcb43+f5KFUH/x6Vd3NROG0AIL2CuKMrIqYsmx6E=
|
||||
github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0=
|
||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911 h1:erppMjjp69Rertg1zlgRbLJH1u+eCmRPxKjMZ5I8/Ro=
|
||||
@@ -696,5 +694,6 @@ xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||
xorm.io/xorm v1.0.3 h1:3dALAohvINu2mfEix5a5x5ZmSVGSljinoSGgvGbaZp0=
|
||||
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||
xorm.io/xorm v1.0.4 h1:UBXA4I3NhiyjXfPqxXUkS2t5hMta9SSPATeMMaZg9oA=
|
||||
xorm.io/xorm v1.0.4/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||
|
200
idp/okta.go
Normal file
200
idp/okta.go
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2022 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 idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type OktaIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
Host string
|
||||
}
|
||||
|
||||
func NewOktaIdProvider(clientId string, clientSecret string, redirectUrl string, hostUrl string) *OktaIdProvider {
|
||||
idp := &OktaIdProvider{}
|
||||
|
||||
config := idp.getConfig(hostUrl, clientId, clientSecret, redirectUrl)
|
||||
config.ClientID = clientId
|
||||
config.ClientSecret = clientSecret
|
||||
config.RedirectURL = redirectUrl
|
||||
idp.Config = config
|
||||
idp.Host = hostUrl
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *OktaIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
func (idp *OktaIdProvider) getConfig(hostUrl string, clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
var endpoint = oauth2.Endpoint{
|
||||
TokenURL: fmt.Sprintf("%s/v1/token", hostUrl),
|
||||
AuthURL: fmt.Sprintf("%s/v1/authorize", hostUrl),
|
||||
}
|
||||
|
||||
var config = &oauth2.Config{
|
||||
// openid is required for authentication requests
|
||||
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#reserved-scopes
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
Endpoint: endpoint,
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#token
|
||||
/*
|
||||
{
|
||||
"access_token" : "eyJhbGciOiJSUzI1NiJ9.eyJ2ZXIiOjEsImlzcyI6Imh0dHA6Ly9yYWluLm9rdGExLmNvbToxODAyIiwiaWF0IjoxNDQ5Nj
|
||||
I0MDI2LCJleHAiOjE0NDk2Mjc2MjYsImp0aSI6IlVmU0lURzZCVVNfdHA3N21BTjJxIiwic2NvcGVzIjpbIm9wZW5pZCIsI
|
||||
mVtYWlsIl0sImNsaWVudF9pZCI6InVBYXVub2ZXa2FESnh1a0NGZUJ4IiwidXNlcl9pZCI6IjAwdWlkNEJ4WHc2STZUVjRt
|
||||
MGczIn0.HaBu5oQxdVCIvea88HPgr2O5evqZlCT4UXH4UKhJnZ5px-ArNRqwhxXWhHJisslswjPpMkx1IgrudQIjzGYbtLF
|
||||
jrrg2ueiU5-YfmKuJuD6O2yPWGTsV7X6i7ABT6P-t8PRz_RNbk-U1GXWIEkNnEWbPqYDAm_Ofh7iW0Y8WDA5ez1jbtMvd-o
|
||||
XMvJLctRiACrTMLJQ2e5HkbUFxgXQ_rFPNHJbNSUBDLqdi2rg_ND64DLRlXRY7hupNsvWGo0gF4WEUk8IZeaLjKw8UoIs-E
|
||||
TEwJlAMcvkhoVVOsN5dPAaEKvbyvPC1hUGXb4uuThlwdD3ECJrtwgKqLqcWonNtiw",
|
||||
"token_type" : "Bearer",
|
||||
"expires_in" : 3600,
|
||||
"scope" : "openid email",
|
||||
"refresh_token" : "a9VpZDRCeFh3Nkk2VdY",
|
||||
"id_token" : "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwMHVpZDRCeFh3Nkk2VFY0bTBnMyIsImVtYWlsIjoid2VibWFzdGVyQGNsb3VkaXR1ZG
|
||||
UubmV0IiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInZlciI6MSwiaXNzIjoiaHR0cDovL3JhaW4ub2t0YTEuY29tOjE4MDIiLCJsb
|
||||
2dpbiI6ImFkbWluaXN0cmF0b3IxQGNsb3VkaXR1ZGUubmV0IiwiYXVkIjoidUFhdW5vZldrYURKeHVrQ0ZlQngiLCJpYXQiOjE0
|
||||
NDk2MjQwMjYsImV4cCI6MTQ0OTYyNzYyNiwiYW1yIjpbInB3ZCJdLCJqdGkiOiI0ZUFXSk9DTUIzU1g4WGV3RGZWUiIsImF1dGh
|
||||
fdGltZSI6MTQ0OTYyNDAyNiwiYXRfaGFzaCI6ImNwcUtmZFFBNWVIODkxRmY1b0pyX1EifQ.Btw6bUbZhRa89DsBb8KmL9rfhku
|
||||
--_mbNC2pgC8yu8obJnwO12nFBepui9KzbpJhGM91PqJwi_AylE6rp-ehamfnUAO4JL14PkemF45Pn3u_6KKwxJnxcWxLvMuuis
|
||||
nvIs7NScKpOAab6ayZU0VL8W6XAijQmnYTtMWQfSuaaR8rYOaWHrffh3OypvDdrQuYacbkT0csxdrayXfBG3UF5-ZAlhfch1fhF
|
||||
T3yZFdWwzkSDc0BGygfiFyNhCezfyT454wbciSZgrA9ROeHkfPCaX7KCFO8GgQEkGRoQntFBNjluFhNLJIUkEFovEDlfuB4tv_M
|
||||
8BM75celdy3jkpOurg"
|
||||
}
|
||||
*/
|
||||
|
||||
type OktaToken struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
Scope string `json:"scope"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
IdToken string `json:"id_token"`
|
||||
}
|
||||
|
||||
// GetToken use code to get access_token
|
||||
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#token
|
||||
func (idp *OktaIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
payload := url.Values{}
|
||||
payload.Set("code", code)
|
||||
payload.Set("grant_type", "authorization_code")
|
||||
payload.Set("client_id", idp.Config.ClientID)
|
||||
payload.Set("client_secret", idp.Config.ClientSecret)
|
||||
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
||||
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pToken := &OktaToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to unmarshal token response: %s", err.Error())
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: pToken.AccessToken,
|
||||
TokenType: "Bearer",
|
||||
RefreshToken: pToken.RefreshToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#userinfo
|
||||
/*
|
||||
{
|
||||
"sub": "00uid4BxXw6I6TV4m0g3",
|
||||
"name" :"John Doe",
|
||||
"nickname":"Jimmy",
|
||||
"given_name":"John",
|
||||
"middle_name":"James",
|
||||
"family_name":"Doe",
|
||||
"profile":"https://example.com/john.doe",
|
||||
"zoneinfo":"America/Los_Angeles",
|
||||
"locale":"en-US",
|
||||
"updated_at":1311280970,
|
||||
"email":"john.doe@example.com",
|
||||
"email_verified":true,
|
||||
"address" : { "street_address":"123 Hollywood Blvd.", "locality":"Los Angeles", "region":"CA", "postal_code":"90210", "country":"US" },
|
||||
"phone_number":"+1 (425) 555-1212"
|
||||
}
|
||||
*/
|
||||
|
||||
type OktaUserInfo struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Picture string `json:"picture"`
|
||||
Sub string `json:"sub"`
|
||||
}
|
||||
|
||||
// GetUserInfo use token to get user profile
|
||||
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#userinfo
|
||||
func (idp *OktaIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/userinfo", idp.Host), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
|
||||
req.Header.Add("Accept", "application/json")
|
||||
resp, err := idp.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var oktaUserInfo OktaUserInfo
|
||||
err = json.Unmarshal(body, &oktaUserInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: oktaUserInfo.Sub,
|
||||
Username: oktaUserInfo.PreferredUsername,
|
||||
DisplayName: oktaUserInfo.Name,
|
||||
Email: oktaUserInfo.Email,
|
||||
AvatarUrl: oktaUserInfo.Picture,
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
@@ -84,6 +84,8 @@ func GetIdProvider(typ string, subType string, clientId string, clientSecret str
|
||||
}
|
||||
} else if typ == "Casdoor" {
|
||||
return NewCasdoorIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
|
||||
} else if typ == "Okta" {
|
||||
return NewOktaIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
|
||||
} else if isGothSupport(typ) {
|
||||
return NewGothIdProvider(typ, clientId, clientSecret, redirectUrl)
|
||||
}
|
||||
|
2
main.go
2
main.go
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
createDatabase := flag.Bool("createDatabase", false, "true if you need casdoor to create database")
|
||||
createDatabase := flag.Bool("createDatabase", false, "true if you need Casdoor to create database")
|
||||
flag.Parse()
|
||||
|
||||
object.InitAdapter(*createDatabase)
|
||||
|
@@ -109,7 +109,7 @@ func initBuiltInApplication() {
|
||||
{Name: "Display name", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
{Name: "Password", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
{Name: "Confirm password", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
{Name: "Email", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
{Name: "Email", Visible: true, Required: true, Prompted: false, Rule: "Normal"},
|
||||
{Name: "Phone", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
{Name: "Agreement", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
},
|
||||
|
103
object/ldap.go
103
object/ldap.go
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/thanhpk/randstr"
|
||||
@@ -42,6 +43,7 @@ type Ldap struct {
|
||||
|
||||
type ldapConn struct {
|
||||
Conn *goldap.Conn
|
||||
IsAD bool
|
||||
}
|
||||
|
||||
//type ldapGroup struct {
|
||||
@@ -78,6 +80,13 @@ type LdapRespUser struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type ldapServerType struct {
|
||||
Vendorname string
|
||||
Vendorversion string
|
||||
IsGlobalCatalogReady string
|
||||
ForestFunctionality string
|
||||
}
|
||||
|
||||
func LdapUsersToLdapRespUsers(users []ldapUser) []LdapRespUser {
|
||||
returnAnyNotEmpty := func(strs ...string) string {
|
||||
for _, str := range strs {
|
||||
@@ -104,6 +113,45 @@ func LdapUsersToLdapRespUsers(users []ldapUser) []LdapRespUser {
|
||||
return res
|
||||
}
|
||||
|
||||
func isMicrosoftAD(Conn *goldap.Conn) (bool, error) {
|
||||
SearchFilter := "(objectclass=*)"
|
||||
SearchAttributes := []string{"vendorname", "vendorversion", "isGlobalCatalogReady", "forestFunctionality"}
|
||||
|
||||
searchReq := goldap.NewSearchRequest("",
|
||||
goldap.ScopeBaseObject, goldap.NeverDerefAliases, 0, 0, false,
|
||||
SearchFilter, SearchAttributes, nil)
|
||||
searchResult, err := Conn.Search(searchReq)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(searchResult.Entries) == 0 {
|
||||
return false, errors.New("no result")
|
||||
}
|
||||
isMicrosoft := false
|
||||
var ldapServerType ldapServerType
|
||||
for _, entry := range searchResult.Entries {
|
||||
for _, attribute := range entry.Attributes {
|
||||
switch attribute.Name {
|
||||
case "vendorname":
|
||||
ldapServerType.Vendorname = attribute.Values[0]
|
||||
case "vendorversion":
|
||||
ldapServerType.Vendorversion = attribute.Values[0]
|
||||
case "isGlobalCatalogReady":
|
||||
ldapServerType.IsGlobalCatalogReady = attribute.Values[0]
|
||||
case "forestFunctionality":
|
||||
ldapServerType.ForestFunctionality = attribute.Values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
if ldapServerType.Vendorname == "" &&
|
||||
ldapServerType.Vendorversion == "" &&
|
||||
ldapServerType.IsGlobalCatalogReady == "TRUE" &&
|
||||
ldapServerType.ForestFunctionality != "" {
|
||||
isMicrosoft = true
|
||||
}
|
||||
return isMicrosoft, err
|
||||
}
|
||||
|
||||
func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) {
|
||||
conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
|
||||
if err != nil {
|
||||
@@ -115,7 +163,11 @@ func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*
|
||||
return nil, fmt.Errorf("fail to login Ldap server with [%s]", adminUser)
|
||||
}
|
||||
|
||||
return &ldapConn{Conn: conn}, nil
|
||||
isAD, err := isMicrosoftAD(conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to get Ldap server type [%s]", adminUser)
|
||||
}
|
||||
return &ldapConn{Conn: conn, IsAD: isAD}, nil
|
||||
}
|
||||
|
||||
//FIXME: The Base DN does not necessarily contain the Group
|
||||
@@ -158,10 +210,19 @@ func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) {
|
||||
SearchFilter := "(objectClass=posixAccount)"
|
||||
SearchAttributes := []string{"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email",
|
||||
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"}
|
||||
|
||||
searchReq := goldap.NewSearchRequest(baseDn,
|
||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
SearchFilter, SearchAttributes, nil)
|
||||
SearchFilterMsAD := "(objectClass=user)"
|
||||
SearchAttributesMsAD := []string{"uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email",
|
||||
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"}
|
||||
var searchReq *goldap.SearchRequest
|
||||
if l.IsAD {
|
||||
searchReq = goldap.NewSearchRequest(baseDn,
|
||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
SearchFilterMsAD, SearchAttributesMsAD, nil)
|
||||
} else {
|
||||
searchReq = goldap.NewSearchRequest(baseDn,
|
||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
SearchFilter, SearchAttributes, nil)
|
||||
}
|
||||
searchResult, err := l.Conn.SearchWithPaging(searchReq, 100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -180,12 +241,14 @@ func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) {
|
||||
case "uidNumber":
|
||||
ldapUserItem.UidNumber = attribute.Values[0]
|
||||
case "uid":
|
||||
case "sAMAccountName":
|
||||
ldapUserItem.Uid = attribute.Values[0]
|
||||
case "cn":
|
||||
ldapUserItem.Cn = attribute.Values[0]
|
||||
case "gidNumber":
|
||||
ldapUserItem.GidNumber = attribute.Values[0]
|
||||
case "entryUUID":
|
||||
case "objectGUID":
|
||||
ldapUserItem.Uuid = attribute.Values[0]
|
||||
case "mail":
|
||||
ldapUserItem.Mail = attribute.Values[0]
|
||||
@@ -300,7 +363,7 @@ func DeleteLdap(ldap *Ldap) bool {
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func SyncLdapUsers(owner string, users []LdapRespUser) (*[]LdapRespUser, *[]LdapRespUser) {
|
||||
func SyncLdapUsers(owner string, users []LdapRespUser, ldapId string) (*[]LdapRespUser, *[]LdapRespUser) {
|
||||
var existUsers []LdapRespUser
|
||||
var failedUsers []LdapRespUser
|
||||
var uuids []string
|
||||
@@ -311,6 +374,25 @@ func SyncLdapUsers(owner string, users []LdapRespUser) (*[]LdapRespUser, *[]Ldap
|
||||
|
||||
existUuids := CheckLdapUuidExist(owner, uuids)
|
||||
|
||||
organization := getOrganization("admin", owner)
|
||||
ldap := GetLdap(ldapId)
|
||||
|
||||
var dc []string
|
||||
for _, basedn := range strings.Split(ldap.BaseDn, ",") {
|
||||
if strings.Contains(basedn, "dc=") {
|
||||
dc = append(dc, basedn[3:])
|
||||
}
|
||||
}
|
||||
affiliation := strings.Join(dc, ".")
|
||||
|
||||
var ou []string
|
||||
for _, admin := range strings.Split(ldap.Admin, ",") {
|
||||
if strings.Contains(admin, "ou=") {
|
||||
ou = append(ou, admin[3:])
|
||||
}
|
||||
}
|
||||
tag := strings.Join(ou, ".")
|
||||
|
||||
for _, user := range users {
|
||||
found := false
|
||||
if len(existUuids) > 0 {
|
||||
@@ -325,15 +407,14 @@ func SyncLdapUsers(owner string, users []LdapRespUser) (*[]LdapRespUser, *[]Ldap
|
||||
Owner: owner,
|
||||
Name: buildLdapUserName(user.Uid, user.UidNumber),
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Password: "123",
|
||||
DisplayName: user.Cn,
|
||||
Avatar: "https://casbin.org/img/casbin.svg",
|
||||
Avatar: organization.DefaultAvatar,
|
||||
Email: user.Email,
|
||||
Phone: user.Phone,
|
||||
Address: []string{user.Address},
|
||||
Affiliation: "Example Inc.",
|
||||
Tag: "staff",
|
||||
Score: 2000,
|
||||
Affiliation: affiliation,
|
||||
Tag: tag,
|
||||
Score: beego.AppConfig.DefaultInt("initScore", 2000),
|
||||
Ldap: user.Uuid,
|
||||
}) {
|
||||
failedUsers = append(failedUsers, user)
|
||||
|
@@ -86,7 +86,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
|
||||
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||
continue
|
||||
}
|
||||
existed, failed := SyncLdapUsers(ldap.Owner, LdapUsersToLdapRespUsers(users))
|
||||
existed, failed := SyncLdapUsers(ldap.Owner, LdapUsersToLdapRespUsers(users), ldap.Id)
|
||||
if len(*failed) != 0 {
|
||||
logs.Warning(fmt.Sprintf("ldap autosync,%d new users,but %d user failed during :", len(users)-len(*existed)-len(*failed), len(*failed)), *failed)
|
||||
} else {
|
||||
|
@@ -105,6 +105,8 @@ func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
|
||||
jwk.Key = x509Cert.PublicKey
|
||||
jwk.Certificates = []*x509.Certificate{x509Cert}
|
||||
jwk.KeyID = cert.Name
|
||||
jwk.Algorithm = cert.CryptoAlgorithm
|
||||
jwk.Use = "sig"
|
||||
jwks.Keys = append(jwks.Keys, jwk)
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,16 @@ type Payment struct {
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
Message string `xorm:"varchar(1000)" json:"message"`
|
||||
|
||||
PersonName string `xorm:"varchar(100)" json:"personName"`
|
||||
PersonIdCard string `xorm:"varchar(100)" json:"personIdCard"`
|
||||
PersonEmail string `xorm:"varchar(100)" json:"personEmail"`
|
||||
PersonPhone string `xorm:"varchar(100)" json:"personPhone"`
|
||||
InvoiceType string `xorm:"varchar(100)" json:"invoiceType"`
|
||||
InvoiceTitle string `xorm:"varchar(100)" json:"invoiceTitle"`
|
||||
InvoiceTaxId string `xorm:"varchar(100)" json:"invoiceTaxId"`
|
||||
InvoiceRemark string `xorm:"varchar(100)" json:"invoiceRemark"`
|
||||
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
|
||||
}
|
||||
|
||||
func GetPaymentCount(owner, field, value string) int {
|
||||
@@ -197,6 +207,44 @@ func NotifyPayment(request *http.Request, body []byte, owner string, providerNam
|
||||
return ok
|
||||
}
|
||||
|
||||
func invoicePayment(payment *Payment) (string, error) {
|
||||
provider := getProvider(payment.Owner, payment.Provider)
|
||||
if provider == nil {
|
||||
return "", fmt.Errorf("the payment provider: %s does not exist", payment.Provider)
|
||||
}
|
||||
|
||||
pProvider, _, err := provider.getPaymentProvider()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
invoiceUrl, err := pProvider.GetInvoice(payment.Name, payment.PersonName, payment.PersonIdCard, payment.PersonEmail, payment.PersonPhone, payment.InvoiceType, payment.InvoiceTitle, payment.InvoiceTaxId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return invoiceUrl, nil
|
||||
}
|
||||
|
||||
func InvoicePayment(payment *Payment) (string, error) {
|
||||
if payment.State != "Paid" {
|
||||
return "", fmt.Errorf("the payment state is supposed to be: \"%s\", got: \"%s\"", "Paid", payment.State)
|
||||
}
|
||||
|
||||
invoiceUrl, err := invoicePayment(payment)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
payment.InvoiceUrl = invoiceUrl
|
||||
affected := UpdatePayment(payment.GetId(), payment)
|
||||
if !affected {
|
||||
return "", fmt.Errorf("failed to update the payment: %s", payment.Name)
|
||||
}
|
||||
|
||||
return invoiceUrl, nil
|
||||
}
|
||||
|
||||
func (payment *Payment) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", payment.Owner, payment.Name)
|
||||
}
|
||||
|
@@ -170,6 +170,7 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
|
||||
|
||||
owner := product.Owner
|
||||
productName := product.Name
|
||||
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
||||
paymentName := util.GenerateTimeId()
|
||||
productDisplayName := product.DisplayName
|
||||
|
||||
@@ -177,7 +178,7 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
|
||||
returnUrl := fmt.Sprintf("%s/payments/%s/result", originFrontend, paymentName)
|
||||
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s/%s/%s", originBackend, owner, providerName, productName, paymentName)
|
||||
|
||||
payUrl, err := pProvider.Pay(providerName, productName, paymentName, productDisplayName, product.Price, returnUrl, notifyUrl)
|
||||
payUrl, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, returnUrl, notifyUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@@ -94,6 +94,7 @@ type User struct {
|
||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||
Okta string `xorm:"okta varchar(100)" json:"okta"`
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
|
||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||
|
@@ -97,3 +97,14 @@ func TestGetMaskedUsers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserByField(t *testing.T) {
|
||||
InitConfig()
|
||||
|
||||
user := GetUserByField("built-in", "DingTalk", "test")
|
||||
if user != nil {
|
||||
t.Logf("%+v", user)
|
||||
} else {
|
||||
t.Log("no user found")
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ func GetUserByField(organizationName string, field string, value string) *User {
|
||||
}
|
||||
|
||||
user := User{Owner: organizationName}
|
||||
existed, err := adapter.Engine.Where(fmt.Sprintf("%s=?", field), value).Get(&user)
|
||||
existed, err := adapter.Engine.Where(fmt.Sprintf("%s=?", strings.ToLower(field)), value).Get(&user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey s
|
||||
return pp
|
||||
}
|
||||
|
||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
||||
//pp.Client.DebugSwitch = gopay.DebugOn
|
||||
|
||||
bm := gopay.BodyMap{}
|
||||
@@ -90,3 +90,7 @@ func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, auth
|
||||
|
||||
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||
}
|
||||
|
||||
func (pp *AlipayPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
112
pp/gc.go
112
pp/gc.go
@@ -38,11 +38,14 @@ type GcPayReqInfo struct {
|
||||
OrderDate string `json:"orderdate"`
|
||||
OrderNo string `json:"orderno"`
|
||||
Amount string `json:"amount"`
|
||||
PayerId string `json:"payerid"`
|
||||
PayerName string `json:"payername"`
|
||||
Xmpch string `json:"xmpch"`
|
||||
Body string `json:"body"`
|
||||
ReturnUrl string `json:"return_url"`
|
||||
NotifyUrl string `json:"notify_url"`
|
||||
PayerId string `json:"payerid"`
|
||||
PayerName string `json:"payername"`
|
||||
Remark1 string `json:"remark1"`
|
||||
Remark2 string `json:"remark2"`
|
||||
}
|
||||
|
||||
type GcPayRespInfo struct {
|
||||
@@ -87,6 +90,27 @@ type GcResponseBody struct {
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
type GcInvoiceReqInfo struct {
|
||||
BusNo string `json:"busno"`
|
||||
PayerName string `json:"payername"`
|
||||
IdNum string `json:"idnum"`
|
||||
PayerType string `json:"payertype"`
|
||||
InvoiceTitle string `json:"invoicetitle"`
|
||||
Tin string `json:"tin"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type GcInvoiceRespInfo struct {
|
||||
BusNo string `json:"busno"`
|
||||
State string `json:"state"`
|
||||
EbillCode string `json:"ebillcode"`
|
||||
EbillNo string `json:"ebillno"`
|
||||
CheckCode string `json:"checkcode"`
|
||||
Url string `json:"url"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func NewGcPaymentProvider(clientId string, clientSecret string, host string) *GcPaymentProvider {
|
||||
pp := &GcPaymentProvider{}
|
||||
|
||||
@@ -130,16 +154,17 @@ func (pp *GcPaymentProvider) doPost(postBytes []byte) ([]byte, error) {
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
func (pp *GcPaymentProvider) Pay(providerName string, productName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
||||
func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
||||
payReqInfo := GcPayReqInfo{
|
||||
OrderDate: util.GenerateSimpleTimeId(),
|
||||
OrderNo: util.GenerateTimeId(),
|
||||
OrderNo: paymentName,
|
||||
Amount: getPriceString(price),
|
||||
PayerId: "",
|
||||
PayerName: "",
|
||||
Xmpch: pp.Xmpch,
|
||||
Body: productDisplayName,
|
||||
ReturnUrl: returnUrl,
|
||||
NotifyUrl: notifyUrl,
|
||||
Remark1: payerName,
|
||||
Remark2: productName,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(payReqInfo)
|
||||
@@ -230,3 +255,78 @@ func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorit
|
||||
|
||||
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||
}
|
||||
|
||||
func (pp *GcPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
payerType := "0"
|
||||
if invoiceType == "Organization" {
|
||||
payerType = "1"
|
||||
}
|
||||
|
||||
invoiceReqInfo := GcInvoiceReqInfo{
|
||||
BusNo: paymentName,
|
||||
PayerName: personName,
|
||||
IdNum: personIdCard,
|
||||
PayerType: payerType,
|
||||
InvoiceTitle: invoiceTitle,
|
||||
Tin: invoiceTaxId,
|
||||
Phone: personPhone,
|
||||
Email: personEmail,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(invoiceReqInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body := GcRequestBody{
|
||||
Op: "InvoiceEBillByOrder",
|
||||
Xmpch: pp.Xmpch,
|
||||
Version: "1.4",
|
||||
Data: base64.StdEncoding.EncodeToString(b),
|
||||
RequestTime: util.GenerateSimpleTimeId(),
|
||||
}
|
||||
|
||||
params := fmt.Sprintf("data=%s&op=%s&requesttime=%s&version=%s&xmpch=%s%s", body.Data, body.Op, body.RequestTime, body.Version, body.Xmpch, pp.SecretKey)
|
||||
body.Sign = strings.ToUpper(util.GetMd5Hash(params))
|
||||
|
||||
bodyBytes, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
respBytes, err := pp.doPost(bodyBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var respBody GcResponseBody
|
||||
err = json.Unmarshal(respBytes, &respBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if respBody.ReturnCode != "SUCCESS" {
|
||||
return "", fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
||||
}
|
||||
|
||||
invoiceRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var invoiceRespInfo GcInvoiceRespInfo
|
||||
err = json.Unmarshal(invoiceRespInfoBytes, &invoiceRespInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if invoiceRespInfo.State == "0" {
|
||||
return "", fmt.Errorf("申请成功,开票中")
|
||||
}
|
||||
|
||||
if invoiceRespInfo.Url == "" {
|
||||
return "", fmt.Errorf("invoice URL is empty")
|
||||
}
|
||||
|
||||
return invoiceRespInfo.Url, nil
|
||||
}
|
||||
|
@@ -17,8 +17,9 @@ package pp
|
||||
import "net/http"
|
||||
|
||||
type PaymentProvider interface {
|
||||
Pay(providerName string, productName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error)
|
||||
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error)
|
||||
Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error)
|
||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||
}
|
||||
|
||||
func GetPaymentProvider(typ string, appId string, clientSecret string, host string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider {
|
||||
|
@@ -104,6 +104,11 @@ func getUrlPath(urlPath string) string {
|
||||
if strings.HasPrefix(urlPath, "/cas") && (strings.HasSuffix(urlPath, "/serviceValidate") || strings.HasSuffix(urlPath, "/proxy") || strings.HasSuffix(urlPath, "/proxyValidate") || strings.HasSuffix(urlPath, "/validate") || strings.HasSuffix(urlPath, "/p3/serviceValidate") || strings.HasSuffix(urlPath, "/p3/proxyValidate") || strings.HasSuffix(urlPath, "/samlValidate")) {
|
||||
return "/cas"
|
||||
}
|
||||
|
||||
if strings.HasPrefix(urlPath, "/api/login/oauth") {
|
||||
return "/api/login/oauth"
|
||||
}
|
||||
|
||||
return urlPath
|
||||
}
|
||||
|
||||
|
@@ -167,6 +167,7 @@ func initAPI() {
|
||||
beego.Router("/api/add-payment", &controllers.ApiController{}, "POST:AddPayment")
|
||||
beego.Router("/api/delete-payment", &controllers.ApiController{}, "POST:DeletePayment")
|
||||
beego.Router("/api/notify-payment/?:owner/?:provider/?:product/?:payment", &controllers.ApiController{}, "POST:NotifyPayment")
|
||||
beego.Router("/api/invoice-payment", &controllers.ApiController{}, "POST:InvoicePayment")
|
||||
|
||||
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
|
||||
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
|
||||
|
@@ -43,7 +43,7 @@ class ApplicationListPage extends BaseListPage {
|
||||
{name: "Display name", visible: true, required: true, rule: "None"},
|
||||
{name: "Password", visible: true, required: true, rule: "None"},
|
||||
{name: "Confirm password", visible: true, required: true, rule: "None"},
|
||||
{name: "Email", visible: true, required: true, rule: "None"},
|
||||
{name: "Email", visible: true, required: true, rule: "Normal"},
|
||||
{name: "Phone", visible: true, required: true, rule: "None"},
|
||||
{name: "Agreement", visible: true, required: true, rule: "None"},
|
||||
],
|
||||
|
@@ -27,7 +27,6 @@ export const CropperDiv = (props) => {
|
||||
const [confirmLoading, setConfirmLoading] = React.useState(false);
|
||||
const {title} = props;
|
||||
const {user} = props;
|
||||
const {account} = props;
|
||||
const {buttonText} = props;
|
||||
let uploadButton;
|
||||
|
||||
|
@@ -13,11 +13,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row} from 'antd';
|
||||
import {Button, Card, Col, Descriptions, Input, Modal, Row, Select} from 'antd';
|
||||
import {InfoCircleTwoTone} from "@ant-design/icons";
|
||||
import * as PaymentBackend from "./backend/PaymentBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
class PaymentEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -26,6 +29,8 @@ class PaymentEditPage extends React.Component {
|
||||
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||
paymentName: props.match.params.paymentName,
|
||||
payment: null,
|
||||
isModalVisible: false,
|
||||
isInvoiceLoading: false,
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
};
|
||||
}
|
||||
@@ -40,6 +45,8 @@ class PaymentEditPage extends React.Component {
|
||||
this.setState({
|
||||
payment: payment,
|
||||
});
|
||||
|
||||
Setting.scrollToDiv("invoice-area");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,6 +67,82 @@ class PaymentEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
issueInvoice() {
|
||||
this.setState({
|
||||
isModalVisible: false,
|
||||
isInvoiceLoading: true,
|
||||
});
|
||||
|
||||
PaymentBackend.invoicePayment(this.state.payment.owner, this.state.paymentName)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
isInvoiceLoading: false,
|
||||
});
|
||||
if (res.msg === "") {
|
||||
Setting.showMessage("success", `Successfully invoiced`);
|
||||
Setting.openLinkSafe(res.data);
|
||||
this.getPayment();
|
||||
} else {
|
||||
Setting.showMessage(res.msg.includes("成功") ? "info" : "error", res.msg);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
isInvoiceLoading: false,
|
||||
});
|
||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
downloadInvoice() {
|
||||
Setting.openLinkSafe(this.state.payment.invoiceUrl);
|
||||
}
|
||||
|
||||
renderModal() {
|
||||
const ths = this;
|
||||
const handleIssueInvoice = () => {
|
||||
ths.issueInvoice();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
this.setState({
|
||||
isModalVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal title={
|
||||
<div>
|
||||
<InfoCircleTwoTone twoToneColor="rgb(45,120,213)" />
|
||||
{" " + i18next.t("payment:Confirm your invoice information")}
|
||||
</div>
|
||||
}
|
||||
visible={this.state.isModalVisible}
|
||||
onOk={handleIssueInvoice}
|
||||
onCancel={handleCancel}
|
||||
okText={i18next.t("payment:Issue Invoice")}
|
||||
cancelText={i18next.t("general:Cancel")}>
|
||||
<p>
|
||||
{
|
||||
i18next.t("payment:Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.")
|
||||
}
|
||||
<br/>
|
||||
<br/>
|
||||
<Descriptions size={"small"} bordered>
|
||||
<Descriptions.Item label={i18next.t("payment:Person name")} span={3}>{this.state.payment?.personName}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Person ID card")} span={3}>{this.state.payment?.personIdCard}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Person Email")} span={3}>{this.state.payment?.personEmail}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Person phone")} span={3}>{this.state.payment?.personPhone}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Invoice type")} span={3}>{this.state.payment?.invoiceType === "Individual" ? i18next.t("payment:Individual") : i18next.t("payment:Organization")}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Invoice title")} span={3}>{this.state.payment?.invoiceTitle}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Invoice tax ID")} span={3}>{this.state.payment?.invoiceTaxId}</Descriptions.Item>
|
||||
<Descriptions.Item label={i18next.t("payment:Invoice remark")} span={3}>{this.state.payment?.invoiceRemark}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</p>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
renderPayment() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
@@ -75,7 +158,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.organization} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.organization} onChange={e => {
|
||||
// this.updatePaymentField('organization', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -85,7 +168,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.name} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.name} onChange={e => {
|
||||
// this.updatePaymentField('name', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -95,7 +178,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.displayName} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.displayName} onChange={e => {
|
||||
this.updatePaymentField('displayName', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -105,7 +188,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Provider"), i18next.t("general:Provider - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.provider} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.provider} onChange={e => {
|
||||
// this.updatePaymentField('provider', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -115,7 +198,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("payment:Type"), i18next.t("payment:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.type} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.type} onChange={e => {
|
||||
// this.updatePaymentField('type', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -125,7 +208,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("payment:Product"), i18next.t("payment:Product - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.productName} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.productName} onChange={e => {
|
||||
// this.updatePaymentField('productName', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -135,7 +218,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("payment:Price"), i18next.t("payment:Price - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.price} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.price} onChange={e => {
|
||||
// this.updatePaymentField('amount', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -145,7 +228,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("payment:Currency"), i18next.t("payment:Currency - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.currency} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.currency} onChange={e => {
|
||||
// this.updatePaymentField('currency', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -155,7 +238,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("payment:State"), i18next.t("payment:State - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.state} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.state} onChange={e => {
|
||||
// this.updatePaymentField('state', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@@ -165,18 +248,200 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("payment:Message"), i18next.t("payment:Message - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.payment.message} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.message} onChange={e => {
|
||||
// this.updatePaymentField('message', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Person name"), i18next.t("payment:Person name - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== ""} value={this.state.payment.personName} onChange={e => {
|
||||
this.updatePaymentField('personName', e.target.value);
|
||||
if (this.state.payment.invoiceType === "Individual") {
|
||||
this.updatePaymentField('invoiceTitle', e.target.value);
|
||||
this.updatePaymentField('invoiceTaxId', "");
|
||||
}
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Person ID card"), i18next.t("payment:Person ID card - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== ""} value={this.state.payment.personIdCard} onChange={e => {
|
||||
this.updatePaymentField('personIdCard', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Person Email"), i18next.t("payment:Person Email - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== ""} value={this.state.payment.personEmail} onChange={e => {
|
||||
this.updatePaymentField('personEmail', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Person phone"), i18next.t("payment:Person phone - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== ""} value={this.state.payment.personPhone} onChange={e => {
|
||||
this.updatePaymentField('personPhone', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Invoice type"), i18next.t("payment:Invoice type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select disabled={this.state.payment.invoiceUrl !== ""} virtual={false} style={{width: '100%'}} value={this.state.payment.invoiceType} onChange={(value => {
|
||||
this.updatePaymentField('invoiceType', value);
|
||||
if (value === "Individual") {
|
||||
this.updatePaymentField('invoiceTitle', this.state.payment.personName);
|
||||
this.updatePaymentField('invoiceTaxId', "");
|
||||
}
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: 'Individual', name: i18next.t("payment:Individual")},
|
||||
{id: 'Organization', name: i18next.t("payment:Organization")},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Invoice title"), i18next.t("payment:Invoice title - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== "" || this.state.payment.invoiceType === "Individual"} value={this.state.payment.invoiceTitle} onChange={e => {
|
||||
this.updatePaymentField('invoiceTitle', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Invoice tax ID"), i18next.t("payment:Invoice tax ID - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== "" || this.state.payment.invoiceType === "Individual"} value={this.state.payment.invoiceTaxId} onChange={e => {
|
||||
this.updatePaymentField('invoiceTaxId', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Invoice remark"), i18next.t("payment:Invoice remark - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={this.state.payment.invoiceUrl !== ""} value={this.state.payment.invoiceRemark} onChange={e => {
|
||||
this.updatePaymentField('invoiceRemark', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Invoice URL"), i18next.t("payment:Invoice URL - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.payment.invoiceUrl} onChange={e => {
|
||||
this.updatePaymentField('invoiceUrl', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row id={"invoice-area"} style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("payment:Invoice actions"), i18next.t("payment:Invoice actions - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{
|
||||
this.state.payment.invoiceUrl === "" ? (
|
||||
<Button type={"primary"} loading={this.state.isInvoiceLoading} onClick={() => {
|
||||
const errorText = this.checkError();
|
||||
if (errorText !== "") {
|
||||
Setting.showMessage("error", errorText);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isModalVisible: true,
|
||||
});
|
||||
}}>{i18next.t("payment:Issue Invoice")}</Button>
|
||||
) : (
|
||||
<Button type={"primary"} onClick={() => this.downloadInvoice(false)}>{i18next.t("payment:Download Invoice")}</Button>
|
||||
)
|
||||
}
|
||||
<Button style={{marginLeft: "20px"}} onClick={() => Setting.goToLink(this.state.payment.returnUrl)}>{i18next.t("payment:Return to Website")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
checkError() {
|
||||
if (this.state.payment.state !== "Paid") {
|
||||
return i18next.t("payment:Please pay the order first!");
|
||||
}
|
||||
|
||||
if (!Setting.isValidPersonName(this.state.payment.personName)) {
|
||||
return i18next.t("signup:Please input your real name!");
|
||||
}
|
||||
|
||||
if (!Setting.isValidIdCard(this.state.payment.personIdCard)) {
|
||||
return i18next.t("signup:Please input the correct ID card number!");
|
||||
}
|
||||
|
||||
if (!Setting.isValidEmail(this.state.payment.personEmail)) {
|
||||
return i18next.t("signup:The input is not valid Email!");
|
||||
}
|
||||
|
||||
if (!Setting.isValidPhone(this.state.payment.personPhone)) {
|
||||
return i18next.t("signup:The input is not valid Phone!");
|
||||
}
|
||||
|
||||
if (!Setting.isValidPhone(this.state.payment.personPhone)) {
|
||||
return i18next.t("signup:The input is not valid Phone!");
|
||||
}
|
||||
|
||||
if (this.state.payment.invoiceType === "Individual") {
|
||||
if (this.state.payment.invoiceTitle !== this.state.payment.personName) {
|
||||
return i18next.t("signup:The input is not invoice title!");
|
||||
}
|
||||
|
||||
if (this.state.payment.invoiceTaxId !== "") {
|
||||
return i18next.t("signup:The input is not invoice Tax ID!");
|
||||
}
|
||||
} else {
|
||||
if (!Setting.isValidInvoiceTitle(this.state.payment.invoiceTitle)) {
|
||||
return i18next.t("signup:The input is not invoice title!");
|
||||
}
|
||||
|
||||
if (!Setting.isValidTaxId(this.state.payment.invoiceTaxId)) {
|
||||
return i18next.t("signup:The input is not invoice Tax ID!");
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
submitPaymentEdit(willExist) {
|
||||
const errorText = this.checkError();
|
||||
if (errorText !== "") {
|
||||
Setting.showMessage("error", errorText);
|
||||
return;
|
||||
}
|
||||
|
||||
let payment = Setting.deepCopy(this.state.payment);
|
||||
PaymentBackend.updatePayment(this.state.organizationName, this.state.paymentName, payment)
|
||||
PaymentBackend.updatePayment(this.state.payment.owner, this.state.paymentName, payment)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
Setting.showMessage("success", `Successfully saved`);
|
||||
@@ -215,6 +480,9 @@ class PaymentEditPage extends React.Component {
|
||||
{
|
||||
this.state.payment !== null ? this.renderPayment() : null
|
||||
}
|
||||
{
|
||||
this.renderModal()
|
||||
}
|
||||
<div style={{marginTop: '20px', marginLeft: '40px'}}>
|
||||
<Button size="large" onClick={() => this.submitPaymentEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: '20px'}} type="primary" size="large" onClick={() => this.submitPaymentEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
|
@@ -17,7 +17,6 @@ import {Button, Descriptions, Spin} from "antd";
|
||||
import i18next from "i18next";
|
||||
import * as ProductBackend from "./backend/ProductBackend";
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
import * as Provider from "./auth/Provider";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class ProductBuyPage extends React.Component {
|
||||
|
@@ -382,7 +382,7 @@ class ProviderEditPage extends React.Component {
|
||||
)
|
||||
}
|
||||
{
|
||||
this.state.provider.type !== "Adfs" && this.state.provider.type !== "Casdoor" ? null : (
|
||||
this.state.provider.type !== "Adfs" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
|
@@ -188,6 +188,17 @@ export function getSignupItem(application, itemName) {
|
||||
return signupItems[0];
|
||||
}
|
||||
|
||||
export function isValidPersonName(personName) {
|
||||
// https://blog.css8.cn/post/14210975.html
|
||||
const personNameRegex = /^[\u4e00-\u9fa5]{2,6}$/;
|
||||
return personNameRegex.test(personName);
|
||||
}
|
||||
|
||||
export function isValidIdCard(idCard) {
|
||||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9X]$/;
|
||||
return idCardRegex.test(idCard);
|
||||
}
|
||||
|
||||
export function isValidEmail(email) {
|
||||
// https://github.com/yiminghe/async-validator/blob/057b0b047f88fac65457bae691d6cb7c6fe48ce1/src/rule/type.ts#L9
|
||||
const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
@@ -195,11 +206,36 @@ export function isValidEmail(email) {
|
||||
}
|
||||
|
||||
export function isValidPhone(phone) {
|
||||
if (phone === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://learnku.com/articles/31543, `^s*$` filter empty email individually.
|
||||
const phoneRegex = /^\s*$|^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/;
|
||||
return phoneRegex.test(phone);
|
||||
}
|
||||
|
||||
export function isValidInvoiceTitle(invoiceTitle) {
|
||||
if (invoiceTitle === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://blog.css8.cn/post/14210975.html
|
||||
const invoiceTitleRegex = /^[\(\)\(\)\u4e00-\u9fa5]{0,50}$/;
|
||||
return invoiceTitleRegex.test(invoiceTitle);
|
||||
}
|
||||
|
||||
export function isValidTaxId(taxId) {
|
||||
// https://www.codetd.com/article/8592083
|
||||
const regArr = [/^[\da-z]{10,15}$/i, /^\d{6}[\da-z]{10,12}$/i, /^[a-z]\d{6}[\da-z]{9,11}$/i, /^[a-z]{2}\d{6}[\da-z]{8,10}$/i, /^\d{14}[\dx][\da-z]{4,5}$/i, /^\d{17}[\dx][\da-z]{1,2}$/i, /^[a-z]\d{14}[\dx][\da-z]{3,4}$/i, /^[a-z]\d{17}[\dx][\da-z]{0,1}$/i, /^[\d]{6}[\da-z]{13,14}$/i];
|
||||
for (let i = 0; i < regArr.length; i++) {
|
||||
if (regArr[i].test(taxId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isAffiliationPrompted(application) {
|
||||
const signupItem = getSignupItem(application, "Affiliation");
|
||||
if (signupItem === null) {
|
||||
@@ -272,11 +308,25 @@ export function openLink(link) {
|
||||
w.location.href = link;
|
||||
}
|
||||
|
||||
export function openLinkSafe(link) {
|
||||
// Javascript window.open issue in safari
|
||||
// https://stackoverflow.com/questions/45569893/javascript-window-open-issue-in-safari
|
||||
let a = document.createElement('a');
|
||||
a.href = link;
|
||||
a.setAttribute('target', '_blank');
|
||||
a.click();
|
||||
}
|
||||
|
||||
export function goToLink(link) {
|
||||
window.location.href = link;
|
||||
}
|
||||
|
||||
export function goToLinkSoft(ths, link) {
|
||||
if (link.startsWith("http")) {
|
||||
openLink(link);
|
||||
return;
|
||||
}
|
||||
|
||||
ths.props.history.push(link);
|
||||
}
|
||||
|
||||
@@ -287,6 +337,8 @@ export function showMessage(type, text) {
|
||||
message.success(text);
|
||||
} else if (type === "error") {
|
||||
message.error(text);
|
||||
} else if (type === "info") {
|
||||
message.info(text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,6 +550,7 @@ export function getProviderTypeOptions(category) {
|
||||
{id: 'AzureAD', name: 'AzureAD'},
|
||||
{id: 'Slack', name: 'Slack'},
|
||||
{id: 'Steam', name: 'Steam'},
|
||||
{id: 'Okta', name: 'Okta'},
|
||||
{id: 'Custom', name: 'Custom'},
|
||||
]
|
||||
);
|
||||
@@ -745,6 +798,15 @@ export function getFromLink() {
|
||||
return from;
|
||||
}
|
||||
|
||||
export function scrollToDiv(divId) {
|
||||
if (divId) {
|
||||
let ele = document.getElementById(divId);
|
||||
if (ele) {
|
||||
ele.scrollIntoView({behavior: "smooth"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getSyncerTableColumns(syncer) {
|
||||
switch (syncer.type) {
|
||||
case "Keycloak":
|
||||
|
@@ -159,7 +159,7 @@ class SignupTable extends React.Component {
|
||||
title: i18next.t("provider:rule"),
|
||||
dataIndex: 'rule',
|
||||
key: 'rule',
|
||||
width: '120px',
|
||||
width: '155px',
|
||||
render: (text, record, index) => {
|
||||
let options = [];
|
||||
if (record.name === "ID") {
|
||||
@@ -167,12 +167,17 @@ class SignupTable extends React.Component {
|
||||
{id: 'Random', name: 'Random'},
|
||||
{id: 'Incremental', name: 'Incremental'},
|
||||
];
|
||||
} if (record.name === "Display name") {
|
||||
} else if (record.name === "Display name") {
|
||||
options = [
|
||||
{id: 'None', name: 'None'},
|
||||
{id: 'Real name', name: 'Real name'},
|
||||
{id: 'First, last', name: 'First, last'},
|
||||
];
|
||||
} else if (record.name === "Email") {
|
||||
options = [
|
||||
{id: 'Normal', name: 'Normal'},
|
||||
{id: 'No Verification', name: 'No verification'},
|
||||
];
|
||||
}
|
||||
|
||||
if (options.length === 0) {
|
||||
|
@@ -19,7 +19,6 @@ import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as TokenBackend from "./backend/TokenBackend";
|
||||
import i18next from "i18next";
|
||||
import * as ResourceBackend from "./backend/ResourceBackend";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
|
||||
class TokenListPage extends BaseListPage {
|
||||
|
@@ -21,7 +21,6 @@ import * as Setting from "./Setting";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import * as path from "path";
|
||||
|
||||
class UserListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
|
@@ -43,6 +43,7 @@ import AppleLoginButton from "./AppleLoginButton"
|
||||
import AzureADLoginButton from "./AzureADLoginButton";
|
||||
import SlackLoginButton from "./SlackLoginButton";
|
||||
import SteamLoginButton from "./SteamLoginButton";
|
||||
import OktaLoginButton from "./OktaLoginButton";
|
||||
import CustomGithubCorner from "../CustomGithubCorner";
|
||||
import {CountDownInput} from "../common/CountDownInput";
|
||||
|
||||
@@ -149,7 +150,7 @@ class LoginPage extends React.Component {
|
||||
AuthBackend.loginCas(values, casParams).then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
let msg = "Logged in successfully. "
|
||||
if (casParams.service == "") {
|
||||
if (casParams.service === "") {
|
||||
//If service was not specified, CAS MUST display a message notifying the client that it has successfully initiated a single sign-on session.
|
||||
msg += "Now you can visit apps protected by casdoor."
|
||||
}
|
||||
@@ -278,6 +279,8 @@ class LoginPage extends React.Component {
|
||||
return <SlackLoginButton text={text} align={"center"} />
|
||||
} else if (type === "Steam") {
|
||||
return <SteamLoginButton text={text} align={"center"} />
|
||||
} else if (type === "Okta") {
|
||||
return <OktaLoginButton text={text} align={"center"} />
|
||||
}
|
||||
|
||||
return text;
|
||||
@@ -593,6 +596,9 @@ class LoginPage extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/*{*/}
|
||||
{/* JSON.stringify(silentSignin)*/}
|
||||
{/*}*/}
|
||||
<div style={{fontSize: 16, textAlign: "left"}}>
|
||||
{i18next.t("login:Continue with")} :
|
||||
</div>
|
||||
|
32
web/src/auth/OktaLoginButton.js
Normal file
32
web/src/auth/OktaLoginButton.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
import {createButton} from "react-social-login-buttons";
|
||||
import {StaticBaseUrl} from "../Setting";
|
||||
|
||||
function Icon({ width = 24, height = 24, color }) {
|
||||
return <img src={`${StaticBaseUrl}/buttons/okta.svg`} alt="Sign in with Okta" style={{width: 24, height: 24}} />;
|
||||
}
|
||||
|
||||
const config = {
|
||||
text: "Sign in with Okta",
|
||||
icon: Icon,
|
||||
iconFormat: name => `fa fa-${name}`,
|
||||
style: {background: "#ffffff", color: "#000000"},
|
||||
activeStyle: {background: "#ededee"},
|
||||
};
|
||||
|
||||
const OktaLoginButton = createButton(config);
|
||||
|
||||
export default OktaLoginButton;
|
@@ -107,6 +107,10 @@ const authInfo = {
|
||||
Steam: {
|
||||
endpoint: "https://steamcommunity.com/openid/login",
|
||||
},
|
||||
Okta: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "http://example.com",
|
||||
},
|
||||
Custom: {
|
||||
endpoint: "https://example.com/",
|
||||
},
|
||||
@@ -230,6 +234,8 @@ export function getAuthUrl(application, provider, method) {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Steam") {
|
||||
return `${endpoint}?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.realm=${window.location.origin}&openid.return_to=${redirectUri}?state=${state}`;
|
||||
} else if (provider.type === "Okta") {
|
||||
return `${provider.domain}/v1/authorize?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Custom") {
|
||||
return `${provider.customAuthUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${provider.customScope}&response_type=code&state=${state}`;
|
||||
}
|
||||
|
@@ -23,6 +23,14 @@ class SelfLoginButton extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
getAccountShowName() {
|
||||
let {name, displayName} = this.props.account;
|
||||
if (displayName !== '') {
|
||||
name += ' (' + displayName + ')';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
render() {
|
||||
const config = {
|
||||
icon: this.generateIcon(),
|
||||
@@ -32,7 +40,7 @@ class SelfLoginButton extends React.Component {
|
||||
};
|
||||
|
||||
const SelfLoginButton = createButton(config);
|
||||
return <SelfLoginButton text={`${this.props.account.name} (${this.props.account.displayName})`} onClick={() => this.props.onClick()} align={"center"} />
|
||||
return <SelfLoginButton text={this.getAccountShowName()} onClick={() => this.props.onClick()} align={"center"}/>
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -325,20 +325,23 @@ class SignupPage extends React.Component {
|
||||
>
|
||||
<Input onChange={e => this.setState({email: e.target.value})} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="emailCode"
|
||||
key="emailCode"
|
||||
label={i18next.t("code:Email code")}
|
||||
rules={[{
|
||||
required: required,
|
||||
message: i18next.t("code:Please input your verification code!"),
|
||||
}]}
|
||||
>
|
||||
<CountDownInput
|
||||
disabled={!this.state.validEmail}
|
||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationOrgName(application)]}
|
||||
/>
|
||||
</Form.Item>
|
||||
{
|
||||
signupItem.rule !== "No verification" &&
|
||||
<Form.Item
|
||||
name="emailCode"
|
||||
key="emailCode"
|
||||
label={i18next.t("code:Email code")}
|
||||
rules={[{
|
||||
required: required,
|
||||
message: i18next.t("code:Please input your verification code!"),
|
||||
}]}
|
||||
>
|
||||
<CountDownInput
|
||||
disabled={!this.state.validEmail}
|
||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationOrgName(application)]}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
} else if (signupItem.name === "Phone") {
|
||||
|
@@ -54,3 +54,10 @@ export function deletePayment(payment) {
|
||||
body: JSON.stringify(newPayment),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function invoicePayment(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/invoice-payment?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include"
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Card, Col, Row} from "antd";
|
||||
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
||||
import * as Setting from "../Setting";
|
||||
import SingleCard from "./SingleCard";
|
||||
import i18next from "i18next";
|
||||
@@ -23,9 +24,23 @@ class HomePage extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
applications: null,
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getApplicationsByOrganization(this.props.account.owner);
|
||||
}
|
||||
|
||||
getApplicationsByOrganization(organizationName) {
|
||||
ApplicationBackend.getApplicationsByOrganization("admin", organizationName)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
applications: (res.msg === undefined) ? res : [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getItems() {
|
||||
let items = [];
|
||||
if (Setting.isAdminUser(this.props.account)) {
|
||||
@@ -35,25 +50,32 @@ class HomePage extends React.Component {
|
||||
{link: "/providers", name: i18next.t("general:Providers"), organizer: i18next.t("general:OAuth providers")},
|
||||
{link: "/applications", name: i18next.t("general:Applications"), organizer: i18next.t("general:Applications that require authentication")},
|
||||
];
|
||||
} else {
|
||||
items = [
|
||||
{link: "/account", name: i18next.t("account:My Account"), organizer: i18next.t("account:Settings for your account")},
|
||||
];
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i ++) {
|
||||
let filename = items[i].link;
|
||||
if (filename === "/account") {
|
||||
filename = "/users";
|
||||
for (let i = 0; i < items.length; i ++) {
|
||||
let filename = items[i].link;
|
||||
if (filename === "/account") {
|
||||
filename = "/users";
|
||||
}
|
||||
items[i].logo = `https://cdn.casbin.com/static/img${filename}.png`;
|
||||
items[i].createdTime = "";
|
||||
}
|
||||
items[i].logo = `https://cdn.casbin.com/static/img${filename}.png`;
|
||||
items[i].createdTime = "";
|
||||
} else {
|
||||
this.state.applications.forEach(application => {
|
||||
console.log(application)
|
||||
items.push({
|
||||
link: application.homepageUrl, name: application.displayName, organizer: application.description, logo: application.logo, createdTime: "",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
renderCards() {
|
||||
if (this.state.applications === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = this.getItems();
|
||||
|
||||
if (Setting.isMobile()) {
|
||||
|
@@ -51,10 +51,10 @@ class SingleCard extends React.Component {
|
||||
<Card
|
||||
hoverable
|
||||
cover={
|
||||
<img alt="logo" src={logo} width={"100%"} height={"100%"} />
|
||||
<img alt="logo" src={logo} style={{width: "100%", height: "210px", objectFit: "scale-down"}} />
|
||||
}
|
||||
onClick={() => Setting.goToLinkSoft(this, link)}
|
||||
style={isSingle ? {width: "320px"} : null}
|
||||
style={isSingle ? {width: "320px"} : {width: "100%"}}
|
||||
>
|
||||
<Meta title={title} description={desc} />
|
||||
<br/>
|
||||
|
@@ -18,8 +18,6 @@ import * as Setting from "../Setting";
|
||||
import i18next from "i18next";
|
||||
import * as UserBackend from "../backend/UserBackend";
|
||||
import {SafetyOutlined} from "@ant-design/icons";
|
||||
import * as Util from "../auth/Util";
|
||||
import {isValidEmail, isValidPhone} from "../Setting";
|
||||
|
||||
const { Search } = Input;
|
||||
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "Anmelden",
|
||||
"Logout": "Abmelden",
|
||||
"My Account": "Mein Konto",
|
||||
"Settings for your account": "Einstellungen für Ihr Konto",
|
||||
"Sign Up": "Registrieren"
|
||||
},
|
||||
"application": {
|
||||
@@ -171,9 +170,11 @@
|
||||
"Signup application": "Signup application",
|
||||
"Signup application - Tooltip": "Signup application - Tooltip",
|
||||
"Sorry, the page you visited does not exist.": "Die von Ihnen besuchte Seite existiert leider nicht.",
|
||||
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
|
||||
"State": "State",
|
||||
"State - Tooltip": "State - Tooltip",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
"Syncers": "Syncers",
|
||||
"Timestamp": "Zeitstempel",
|
||||
"Tokens": "Token",
|
||||
@@ -223,7 +224,7 @@
|
||||
"Continue with": "Weiter mit",
|
||||
"Email or phone": "E-Mail oder Telefon",
|
||||
"Forgot password?": "Passwort vergessen?",
|
||||
"Invalid Email or phone": "Ungültige E-Mail oder Telefon",
|
||||
"Logging out...": "Logging out...",
|
||||
"No account?": "Kein Konto?",
|
||||
"Or sign in with another account": "Oder melden Sie sich mit einem anderen Konto an",
|
||||
"Password": "Passwort",
|
||||
@@ -246,6 +247,8 @@
|
||||
"Default avatar": "Standard Avatar",
|
||||
"Edit Organization": "Organisation bearbeiten",
|
||||
"Favicon": "Févicon",
|
||||
"Is profile public": "Is profile public",
|
||||
"Is profile public - Tooltip": "Is profile public - Tooltip",
|
||||
"New Organization": "New Organization",
|
||||
"Soft deletion": "Weiche Löschung",
|
||||
"Soft deletion - Tooltip": "Weiche Löschung - Tooltip",
|
||||
@@ -255,11 +258,40 @@
|
||||
"Website URL - Tooltip": "Unique string-style identifier"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "Confirm your invoice information",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Download Invoice": "Download Invoice",
|
||||
"Edit Payment": "Edit Payment",
|
||||
"Individual": "Individual",
|
||||
"Invoice URL": "Invoice URL",
|
||||
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
|
||||
"Invoice actions": "Invoice actions",
|
||||
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
|
||||
"Invoice remark": "Invoice remark",
|
||||
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
|
||||
"Invoice tax ID": "Invoice tax ID",
|
||||
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
|
||||
"Invoice title": "Invoice title",
|
||||
"Invoice title - Tooltip": "Invoice title - Tooltip",
|
||||
"Invoice type": "Invoice type",
|
||||
"Invoice type - Tooltip": "Invoice type - Tooltip",
|
||||
"Issue Invoice": "Issue Invoice",
|
||||
"Message": "Message",
|
||||
"Message - Tooltip": "Message - Tooltip",
|
||||
"New Payment": "New Payment",
|
||||
"Organization": "Organization",
|
||||
"Person Email": "Person Email",
|
||||
"Person Email - Tooltip": "Person Email - Tooltip",
|
||||
"Person ID card": "Person ID card",
|
||||
"Person ID card - Tooltip": "Person ID card - Tooltip",
|
||||
"Person name": "Person name",
|
||||
"Person name - Tooltip": "Person name - Tooltip",
|
||||
"Person phone": "Person phone",
|
||||
"Person phone - Tooltip": "Person phone - Tooltip",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
|
||||
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
|
||||
"Please pay the order first!": "Please pay the order first!",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Processing...": "Processing...",
|
||||
@@ -330,11 +362,19 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - Tooltip",
|
||||
"Bucket": "Eimer",
|
||||
"Bucket - Tooltip": "Storage bucket name",
|
||||
"Can not parse Metadata": "Metadaten können nicht analysiert werden",
|
||||
"Category": "Kategorie",
|
||||
"Category - Tooltip": "Unique string-style identifier",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "Kunden-ID",
|
||||
"Client ID - Tooltip": "Unique string-style identifier",
|
||||
"Client ID 2": "Client ID 2",
|
||||
@@ -382,6 +422,8 @@
|
||||
"SP ACS URL": "SP-ACS-URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
|
||||
"SP Entity ID": "SP Entity ID",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Geheimer Zugangsschlüssel",
|
||||
"SecretAccessKey - Tooltip": "SecretAccessKey - Tooltip",
|
||||
"Sign Name": "Schild Name",
|
||||
@@ -400,8 +442,12 @@
|
||||
"Template Code - Tooltip": "Unique string-style identifier",
|
||||
"Terms of Use": "Nutzungsbedingungen",
|
||||
"Terms of Use - Tooltip": "Nutzungsbedingungen - Tooltip",
|
||||
"Token URL": "Token URL",
|
||||
"Token URL - Tooltip": "Token URL - Tooltip",
|
||||
"Type": "Typ",
|
||||
"Type - Tooltip": "Unique string-style identifier",
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"alertType": "alarmtyp",
|
||||
"canSignIn": "canSignIn",
|
||||
"canSignUp": "canSignUp",
|
||||
@@ -455,6 +501,8 @@
|
||||
"Please input your real name!": "Bitte geben Sie Ihren persönlichen Namen ein!",
|
||||
"Please select your country/region!": "Bitte wählen Sie Ihr Land/Ihre Region!",
|
||||
"Terms of Use": "Nutzungsbedingungen",
|
||||
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
|
||||
"The input is not invoice title!": "The input is not invoice title!",
|
||||
"The input is not valid Email!": "Die Eingabe ist ungültig!",
|
||||
"The input is not valid Phone!": "Die Eingabe ist nicht gültig!",
|
||||
"Unknown Check Type": "Unbekannter Schecktyp",
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"My Account": "My Account",
|
||||
"Settings for your account": "Settings for your account",
|
||||
"Sign Up": "Sign Up"
|
||||
},
|
||||
"application": {
|
||||
@@ -171,9 +170,11 @@
|
||||
"Signup application": "Signup application",
|
||||
"Signup application - Tooltip": "Signup application - Tooltip",
|
||||
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
|
||||
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
|
||||
"State": "State",
|
||||
"State - Tooltip": "State - Tooltip",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
"Syncers": "Syncers",
|
||||
"Timestamp": "Timestamp",
|
||||
"Tokens": "Tokens",
|
||||
@@ -223,7 +224,7 @@
|
||||
"Continue with": "Continue with",
|
||||
"Email or phone": "Email or phone",
|
||||
"Forgot password?": "Forgot password?",
|
||||
"Invalid Email or phone": "Invalid Email or phone",
|
||||
"Logging out...": "Logging out...",
|
||||
"No account?": "No account?",
|
||||
"Or sign in with another account": "Or sign in with another account",
|
||||
"Password": "Password",
|
||||
@@ -246,6 +247,8 @@
|
||||
"Default avatar": "Default avatar",
|
||||
"Edit Organization": "Edit Organization",
|
||||
"Favicon": "Favicon",
|
||||
"Is profile public": "Is profile public",
|
||||
"Is profile public - Tooltip": "Is profile public - Tooltip",
|
||||
"New Organization": "New Organization",
|
||||
"Soft deletion": "Soft deletion",
|
||||
"Soft deletion - Tooltip": "Soft deletion - Tooltip",
|
||||
@@ -255,11 +258,40 @@
|
||||
"Website URL - Tooltip": "Website URL - Tooltip"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "Confirm your invoice information",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Download Invoice": "Download Invoice",
|
||||
"Edit Payment": "Edit Payment",
|
||||
"Individual": "Individual",
|
||||
"Invoice URL": "Invoice URL",
|
||||
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
|
||||
"Invoice actions": "Invoice actions",
|
||||
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
|
||||
"Invoice remark": "Invoice remark",
|
||||
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
|
||||
"Invoice tax ID": "Invoice tax ID",
|
||||
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
|
||||
"Invoice title": "Invoice title",
|
||||
"Invoice title - Tooltip": "Invoice title - Tooltip",
|
||||
"Invoice type": "Invoice type",
|
||||
"Invoice type - Tooltip": "Invoice type - Tooltip",
|
||||
"Issue Invoice": "Issue Invoice",
|
||||
"Message": "Message",
|
||||
"Message - Tooltip": "Message - Tooltip",
|
||||
"New Payment": "New Payment",
|
||||
"Organization": "Organization",
|
||||
"Person Email": "Person Email",
|
||||
"Person Email - Tooltip": "Person Email - Tooltip",
|
||||
"Person ID card": "Person ID card",
|
||||
"Person ID card - Tooltip": "Person ID card - Tooltip",
|
||||
"Person name": "Person name",
|
||||
"Person name - Tooltip": "Person name - Tooltip",
|
||||
"Person phone": "Person phone",
|
||||
"Person phone - Tooltip": "Person phone - Tooltip",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
|
||||
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
|
||||
"Please pay the order first!": "Please pay the order first!",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Processing...": "Processing...",
|
||||
@@ -330,11 +362,19 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - Tooltip",
|
||||
"Bucket": "Bucket",
|
||||
"Bucket - Tooltip": "Bucket - Tooltip",
|
||||
"Can not parse Metadata": "Can not parse Metadata",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "Client ID",
|
||||
"Client ID - Tooltip": "Client ID - Tooltip",
|
||||
"Client ID 2": "Client ID 2",
|
||||
@@ -382,6 +422,8 @@
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
|
||||
"SP Entity ID": "SP Entity ID",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Secret access key",
|
||||
"SecretAccessKey - Tooltip": "SecretAccessKey - Tooltip",
|
||||
"Sign Name": "Sign Name",
|
||||
@@ -400,8 +442,12 @@
|
||||
"Template Code - Tooltip": "Template Code - Tooltip",
|
||||
"Terms of Use": "Terms of Use",
|
||||
"Terms of Use - Tooltip": "Terms of Use - Tooltip",
|
||||
"Token URL": "Token URL",
|
||||
"Token URL - Tooltip": "Token URL - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"alertType": "alertType",
|
||||
"canSignIn": "canSignIn",
|
||||
"canSignUp": "canSignUp",
|
||||
@@ -455,6 +501,8 @@
|
||||
"Please input your real name!": "Please input your real name!",
|
||||
"Please select your country/region!": "Please select your country/region!",
|
||||
"Terms of Use": "Terms of Use",
|
||||
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
|
||||
"The input is not invoice title!": "The input is not invoice title!",
|
||||
"The input is not valid Email!": "The input is not valid Email!",
|
||||
"The input is not valid Phone!": "The input is not valid Phone!",
|
||||
"Unknown Check Type": "Unknown Check Type",
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "Se connecter",
|
||||
"Logout": "Déconnexion",
|
||||
"My Account": "Mon Compte",
|
||||
"Settings for your account": "Paramètres de votre compte",
|
||||
"Sign Up": "S'inscrire"
|
||||
},
|
||||
"application": {
|
||||
@@ -171,9 +170,11 @@
|
||||
"Signup application": "Signup application",
|
||||
"Signup application - Tooltip": "Signup application - Tooltip",
|
||||
"Sorry, the page you visited does not exist.": "Désolé, la page que vous avez visitée n'existe pas.",
|
||||
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
|
||||
"State": "State",
|
||||
"State - Tooltip": "State - Tooltip",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
"Syncers": "Synchronisateurs",
|
||||
"Timestamp": "Horodatage",
|
||||
"Tokens": "Jetons",
|
||||
@@ -223,7 +224,7 @@
|
||||
"Continue with": "Continuer avec",
|
||||
"Email or phone": "Courriel ou téléphone",
|
||||
"Forgot password?": "Mot de passe oublié ?",
|
||||
"Invalid Email or phone": "E-mail ou téléphone invalide",
|
||||
"Logging out...": "Logging out...",
|
||||
"No account?": "Pas de compte ?",
|
||||
"Or sign in with another account": "Ou connectez-vous avec un autre compte",
|
||||
"Password": "Mot de passe",
|
||||
@@ -246,6 +247,8 @@
|
||||
"Default avatar": "Avatar par défaut",
|
||||
"Edit Organization": "Modifier l'organisation",
|
||||
"Favicon": "Favicon",
|
||||
"Is profile public": "Is profile public",
|
||||
"Is profile public - Tooltip": "Is profile public - Tooltip",
|
||||
"New Organization": "New Organization",
|
||||
"Soft deletion": "Suppression du logiciel",
|
||||
"Soft deletion - Tooltip": "Suppression de soft - infobulle",
|
||||
@@ -255,11 +258,40 @@
|
||||
"Website URL - Tooltip": "Unique string-style identifier"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "Confirm your invoice information",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Download Invoice": "Download Invoice",
|
||||
"Edit Payment": "Edit Payment",
|
||||
"Individual": "Individual",
|
||||
"Invoice URL": "Invoice URL",
|
||||
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
|
||||
"Invoice actions": "Invoice actions",
|
||||
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
|
||||
"Invoice remark": "Invoice remark",
|
||||
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
|
||||
"Invoice tax ID": "Invoice tax ID",
|
||||
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
|
||||
"Invoice title": "Invoice title",
|
||||
"Invoice title - Tooltip": "Invoice title - Tooltip",
|
||||
"Invoice type": "Invoice type",
|
||||
"Invoice type - Tooltip": "Invoice type - Tooltip",
|
||||
"Issue Invoice": "Issue Invoice",
|
||||
"Message": "Message",
|
||||
"Message - Tooltip": "Message - Tooltip",
|
||||
"New Payment": "New Payment",
|
||||
"Organization": "Organization",
|
||||
"Person Email": "Person Email",
|
||||
"Person Email - Tooltip": "Person Email - Tooltip",
|
||||
"Person ID card": "Person ID card",
|
||||
"Person ID card - Tooltip": "Person ID card - Tooltip",
|
||||
"Person name": "Person name",
|
||||
"Person name - Tooltip": "Person name - Tooltip",
|
||||
"Person phone": "Person phone",
|
||||
"Person phone - Tooltip": "Person phone - Tooltip",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
|
||||
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
|
||||
"Please pay the order first!": "Please pay the order first!",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Processing...": "Processing...",
|
||||
@@ -330,11 +362,19 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - Tooltip",
|
||||
"Bucket": "Seau",
|
||||
"Bucket - Tooltip": "Storage bucket name",
|
||||
"Can not parse Metadata": "Impossible d'analyser les métadonnées",
|
||||
"Category": "Catégorie",
|
||||
"Category - Tooltip": "Unique string-style identifier",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "ID du client",
|
||||
"Client ID - Tooltip": "Unique string-style identifier",
|
||||
"Client ID 2": "ID client 2",
|
||||
@@ -382,6 +422,8 @@
|
||||
"SP ACS URL": "URL du SP ACS",
|
||||
"SP ACS URL - Tooltip": "URL SP ACS - infobulle",
|
||||
"SP Entity ID": "ID de l'entité SP",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Clé d'accès secrète",
|
||||
"SecretAccessKey - Tooltip": "SecretAccessKey - Infobulle",
|
||||
"Sign Name": "Nom du panneau",
|
||||
@@ -400,8 +442,12 @@
|
||||
"Template Code - Tooltip": "Unique string-style identifier",
|
||||
"Terms of Use": "Conditions d'utilisation",
|
||||
"Terms of Use - Tooltip": "Conditions d'utilisation - Info-bulle",
|
||||
"Token URL": "Token URL",
|
||||
"Token URL - Tooltip": "Token URL - Tooltip",
|
||||
"Type": "Type de texte",
|
||||
"Type - Tooltip": "Unique string-style identifier",
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"alertType": "Type d'alerte",
|
||||
"canSignIn": "canSignIn",
|
||||
"canSignUp": "canSignUp",
|
||||
@@ -455,6 +501,8 @@
|
||||
"Please input your real name!": "Veuillez entrer votre nom personnel !",
|
||||
"Please select your country/region!": "Veuillez sélectionner votre pays/région!",
|
||||
"Terms of Use": "Conditions d'utilisation",
|
||||
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
|
||||
"The input is not invoice title!": "The input is not invoice title!",
|
||||
"The input is not valid Email!": "L'entrée n'est pas un email valide !",
|
||||
"The input is not valid Phone!": "L'entrée n'est pas un téléphone valide !",
|
||||
"Unknown Check Type": "Type de vérification inconnu",
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "ログイン",
|
||||
"Logout": "ログアウト",
|
||||
"My Account": "マイアカウント",
|
||||
"Settings for your account": "アカウントの設定",
|
||||
"Sign Up": "新規登録"
|
||||
},
|
||||
"application": {
|
||||
@@ -171,9 +170,11 @@
|
||||
"Signup application": "Signup application",
|
||||
"Signup application - Tooltip": "Signup application - Tooltip",
|
||||
"Sorry, the page you visited does not exist.": "申し訳ありませんが、訪問したページは存在しません。",
|
||||
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
|
||||
"State": "State",
|
||||
"State - Tooltip": "State - Tooltip",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
"Syncers": "Syncers",
|
||||
"Timestamp": "タイムスタンプ",
|
||||
"Tokens": "トークン",
|
||||
@@ -223,7 +224,7 @@
|
||||
"Continue with": "次で続ける",
|
||||
"Email or phone": "Eメールまたは電話番号",
|
||||
"Forgot password?": "パスワードを忘れましたか?",
|
||||
"Invalid Email or phone": "メールアドレスまたは電話番号が正しくありません",
|
||||
"Logging out...": "Logging out...",
|
||||
"No account?": "アカウントがありませんか?",
|
||||
"Or sign in with another account": "または別のアカウントでサインイン",
|
||||
"Password": "パスワード",
|
||||
@@ -246,6 +247,8 @@
|
||||
"Default avatar": "デフォルトのアバター",
|
||||
"Edit Organization": "組織を編集",
|
||||
"Favicon": "ファビコン",
|
||||
"Is profile public": "Is profile public",
|
||||
"Is profile public - Tooltip": "Is profile public - Tooltip",
|
||||
"New Organization": "New Organization",
|
||||
"Soft deletion": "ソフト削除",
|
||||
"Soft deletion - Tooltip": "ソフト削除 - ツールチップ",
|
||||
@@ -255,11 +258,40 @@
|
||||
"Website URL - Tooltip": "Unique string-style identifier"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "Confirm your invoice information",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Download Invoice": "Download Invoice",
|
||||
"Edit Payment": "Edit Payment",
|
||||
"Individual": "Individual",
|
||||
"Invoice URL": "Invoice URL",
|
||||
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
|
||||
"Invoice actions": "Invoice actions",
|
||||
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
|
||||
"Invoice remark": "Invoice remark",
|
||||
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
|
||||
"Invoice tax ID": "Invoice tax ID",
|
||||
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
|
||||
"Invoice title": "Invoice title",
|
||||
"Invoice title - Tooltip": "Invoice title - Tooltip",
|
||||
"Invoice type": "Invoice type",
|
||||
"Invoice type - Tooltip": "Invoice type - Tooltip",
|
||||
"Issue Invoice": "Issue Invoice",
|
||||
"Message": "Message",
|
||||
"Message - Tooltip": "Message - Tooltip",
|
||||
"New Payment": "New Payment",
|
||||
"Organization": "Organization",
|
||||
"Person Email": "Person Email",
|
||||
"Person Email - Tooltip": "Person Email - Tooltip",
|
||||
"Person ID card": "Person ID card",
|
||||
"Person ID card - Tooltip": "Person ID card - Tooltip",
|
||||
"Person name": "Person name",
|
||||
"Person name - Tooltip": "Person name - Tooltip",
|
||||
"Person phone": "Person phone",
|
||||
"Person phone - Tooltip": "Person phone - Tooltip",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
|
||||
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
|
||||
"Please pay the order first!": "Please pay the order first!",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Processing...": "Processing...",
|
||||
@@ -330,11 +362,19 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - Tooltip",
|
||||
"Bucket": "バケツ入りバケツ",
|
||||
"Bucket - Tooltip": "Storage bucket name",
|
||||
"Can not parse Metadata": "メタデータをパースできません",
|
||||
"Category": "カテゴリ",
|
||||
"Category - Tooltip": "Unique string-style identifier",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "クライアント ID",
|
||||
"Client ID - Tooltip": "Unique string-style identifier",
|
||||
"Client ID 2": "クライアント ID 2",
|
||||
@@ -382,6 +422,8 @@
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - ツールチップ",
|
||||
"SP Entity ID": "SP ID",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "シークレットアクセスキー",
|
||||
"SecretAccessKey - Tooltip": "シークレットアクセスキー - ツールチップ",
|
||||
"Sign Name": "署名名",
|
||||
@@ -400,8 +442,12 @@
|
||||
"Template Code - Tooltip": "Unique string-style identifier",
|
||||
"Terms of Use": "利用規約",
|
||||
"Terms of Use - Tooltip": "利用規約 - ツールチップ",
|
||||
"Token URL": "Token URL",
|
||||
"Token URL - Tooltip": "Token URL - Tooltip",
|
||||
"Type": "タイプ",
|
||||
"Type - Tooltip": "Unique string-style identifier",
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"alertType": "alertType",
|
||||
"canSignIn": "canSignIn",
|
||||
"canSignUp": "canSignUp",
|
||||
@@ -455,6 +501,8 @@
|
||||
"Please input your real name!": "個人名を入力してください!",
|
||||
"Please select your country/region!": "あなたの国/地域を選択してください!",
|
||||
"Terms of Use": "利用規約",
|
||||
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
|
||||
"The input is not invoice title!": "The input is not invoice title!",
|
||||
"The input is not valid Email!": "入力されたメールアドレスが無効です!",
|
||||
"The input is not valid Phone!": "入力された電話番号が正しくありません!",
|
||||
"Unknown Check Type": "不明なチェックタイプ",
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"My Account": "My Account",
|
||||
"Settings for your account": "Settings for your account",
|
||||
"Sign Up": "Sign Up"
|
||||
},
|
||||
"application": {
|
||||
@@ -171,9 +170,11 @@
|
||||
"Signup application": "Signup application",
|
||||
"Signup application - Tooltip": "Signup application - Tooltip",
|
||||
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
|
||||
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
|
||||
"State": "State",
|
||||
"State - Tooltip": "State - Tooltip",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
"Syncers": "Syncers",
|
||||
"Timestamp": "Timestamp",
|
||||
"Tokens": "Tokens",
|
||||
@@ -223,7 +224,7 @@
|
||||
"Continue with": "Continue with",
|
||||
"Email or phone": "Email or phone",
|
||||
"Forgot password?": "Forgot password?",
|
||||
"Invalid Email or phone": "Invalid Email or phone",
|
||||
"Logging out...": "Logging out...",
|
||||
"No account?": "No account?",
|
||||
"Or sign in with another account": "Or sign in with another account",
|
||||
"Password": "Password",
|
||||
@@ -246,6 +247,8 @@
|
||||
"Default avatar": "Default avatar",
|
||||
"Edit Organization": "Edit Organization",
|
||||
"Favicon": "Favicon",
|
||||
"Is profile public": "Is profile public",
|
||||
"Is profile public - Tooltip": "Is profile public - Tooltip",
|
||||
"New Organization": "New Organization",
|
||||
"Soft deletion": "Soft deletion",
|
||||
"Soft deletion - Tooltip": "Soft deletion - Tooltip",
|
||||
@@ -255,11 +258,40 @@
|
||||
"Website URL - Tooltip": "Unique string-style identifier"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "Confirm your invoice information",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Download Invoice": "Download Invoice",
|
||||
"Edit Payment": "Edit Payment",
|
||||
"Individual": "Individual",
|
||||
"Invoice URL": "Invoice URL",
|
||||
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
|
||||
"Invoice actions": "Invoice actions",
|
||||
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
|
||||
"Invoice remark": "Invoice remark",
|
||||
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
|
||||
"Invoice tax ID": "Invoice tax ID",
|
||||
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
|
||||
"Invoice title": "Invoice title",
|
||||
"Invoice title - Tooltip": "Invoice title - Tooltip",
|
||||
"Invoice type": "Invoice type",
|
||||
"Invoice type - Tooltip": "Invoice type - Tooltip",
|
||||
"Issue Invoice": "Issue Invoice",
|
||||
"Message": "Message",
|
||||
"Message - Tooltip": "Message - Tooltip",
|
||||
"New Payment": "New Payment",
|
||||
"Organization": "Organization",
|
||||
"Person Email": "Person Email",
|
||||
"Person Email - Tooltip": "Person Email - Tooltip",
|
||||
"Person ID card": "Person ID card",
|
||||
"Person ID card - Tooltip": "Person ID card - Tooltip",
|
||||
"Person name": "Person name",
|
||||
"Person name - Tooltip": "Person name - Tooltip",
|
||||
"Person phone": "Person phone",
|
||||
"Person phone - Tooltip": "Person phone - Tooltip",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
|
||||
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
|
||||
"Please pay the order first!": "Please pay the order first!",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Processing...": "Processing...",
|
||||
@@ -330,11 +362,19 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - Tooltip",
|
||||
"Bucket": "Bucket",
|
||||
"Bucket - Tooltip": "Storage bucket name",
|
||||
"Can not parse Metadata": "Can not parse Metadata",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Unique string-style identifier",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "Client ID",
|
||||
"Client ID - Tooltip": "Unique string-style identifier",
|
||||
"Client ID 2": "Client ID 2",
|
||||
@@ -382,6 +422,8 @@
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
|
||||
"SP Entity ID": "SP Entity ID",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Secret access key",
|
||||
"SecretAccessKey - Tooltip": "SecretAccessKey - Tooltip",
|
||||
"Sign Name": "Sign Name",
|
||||
@@ -400,8 +442,12 @@
|
||||
"Template Code - Tooltip": "Unique string-style identifier",
|
||||
"Terms of Use": "Terms of Use",
|
||||
"Terms of Use - Tooltip": "Terms of Use - Tooltip",
|
||||
"Token URL": "Token URL",
|
||||
"Token URL - Tooltip": "Token URL - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Unique string-style identifier",
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"alertType": "alertType",
|
||||
"canSignIn": "canSignIn",
|
||||
"canSignUp": "canSignUp",
|
||||
@@ -455,6 +501,8 @@
|
||||
"Please input your real name!": "Please input your real name!",
|
||||
"Please select your country/region!": "Please select your country/region!",
|
||||
"Terms of Use": "Terms of Use",
|
||||
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
|
||||
"The input is not invoice title!": "The input is not invoice title!",
|
||||
"The input is not valid Email!": "The input is not valid Email!",
|
||||
"The input is not valid Phone!": "The input is not valid Phone!",
|
||||
"Unknown Check Type": "Unknown Check Type",
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "Логин",
|
||||
"Logout": "Выйти",
|
||||
"My Account": "Мой аккаунт",
|
||||
"Settings for your account": "Настройки учетной записи",
|
||||
"Sign Up": "Регистрация"
|
||||
},
|
||||
"application": {
|
||||
@@ -171,9 +170,11 @@
|
||||
"Signup application": "Signup application",
|
||||
"Signup application - Tooltip": "Signup application - Tooltip",
|
||||
"Sorry, the page you visited does not exist.": "Извините, посещенная вами страница не существует.",
|
||||
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
|
||||
"State": "State",
|
||||
"State - Tooltip": "State - Tooltip",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
"Syncers": "Синхронизаторы",
|
||||
"Timestamp": "Отметка времени",
|
||||
"Tokens": "Жетоны",
|
||||
@@ -223,7 +224,7 @@
|
||||
"Continue with": "Продолжить с",
|
||||
"Email or phone": "Электронная почта или телефон",
|
||||
"Forgot password?": "Забыли пароль?",
|
||||
"Invalid Email or phone": "Неверный адрес электронной почты или телефон",
|
||||
"Logging out...": "Logging out...",
|
||||
"No account?": "Нет учетной записи?",
|
||||
"Or sign in with another account": "Или войти с помощью другой учетной записи",
|
||||
"Password": "Пароль",
|
||||
@@ -246,6 +247,8 @@
|
||||
"Default avatar": "Аватар по умолчанию",
|
||||
"Edit Organization": "Изменить организацию",
|
||||
"Favicon": "Иконка",
|
||||
"Is profile public": "Is profile public",
|
||||
"Is profile public - Tooltip": "Is profile public - Tooltip",
|
||||
"New Organization": "New Organization",
|
||||
"Soft deletion": "Мягкое удаление",
|
||||
"Soft deletion - Tooltip": "Мягкое удаление - Подсказка",
|
||||
@@ -255,11 +258,40 @@
|
||||
"Website URL - Tooltip": "Unique string-style identifier"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "Confirm your invoice information",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Download Invoice": "Download Invoice",
|
||||
"Edit Payment": "Edit Payment",
|
||||
"Individual": "Individual",
|
||||
"Invoice URL": "Invoice URL",
|
||||
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
|
||||
"Invoice actions": "Invoice actions",
|
||||
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
|
||||
"Invoice remark": "Invoice remark",
|
||||
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
|
||||
"Invoice tax ID": "Invoice tax ID",
|
||||
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
|
||||
"Invoice title": "Invoice title",
|
||||
"Invoice title - Tooltip": "Invoice title - Tooltip",
|
||||
"Invoice type": "Invoice type",
|
||||
"Invoice type - Tooltip": "Invoice type - Tooltip",
|
||||
"Issue Invoice": "Issue Invoice",
|
||||
"Message": "Message",
|
||||
"Message - Tooltip": "Message - Tooltip",
|
||||
"New Payment": "New Payment",
|
||||
"Organization": "Organization",
|
||||
"Person Email": "Person Email",
|
||||
"Person Email - Tooltip": "Person Email - Tooltip",
|
||||
"Person ID card": "Person ID card",
|
||||
"Person ID card - Tooltip": "Person ID card - Tooltip",
|
||||
"Person name": "Person name",
|
||||
"Person name - Tooltip": "Person name - Tooltip",
|
||||
"Person phone": "Person phone",
|
||||
"Person phone - Tooltip": "Person phone - Tooltip",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
|
||||
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
|
||||
"Please pay the order first!": "Please pay the order first!",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Processing...": "Processing...",
|
||||
@@ -330,11 +362,19 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - Tooltip",
|
||||
"Bucket": "Ведро",
|
||||
"Bucket - Tooltip": "Storage bucket name",
|
||||
"Can not parse Metadata": "Невозможно разобрать метаданные",
|
||||
"Category": "Категория",
|
||||
"Category - Tooltip": "Unique string-style identifier",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "ID клиента",
|
||||
"Client ID - Tooltip": "Unique string-style identifier",
|
||||
"Client ID 2": "ID клиента 2",
|
||||
@@ -382,6 +422,8 @@
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - Подсказка",
|
||||
"SP Entity ID": "Идентификатор сущности SP",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Секретный ключ доступа",
|
||||
"SecretAccessKey - Tooltip": "SecretAccessKey - Подсказка",
|
||||
"Sign Name": "Имя подписи",
|
||||
@@ -400,8 +442,12 @@
|
||||
"Template Code - Tooltip": "Unique string-style identifier",
|
||||
"Terms of Use": "Условия использования",
|
||||
"Terms of Use - Tooltip": "Условия использования - Tooltip",
|
||||
"Token URL": "Token URL",
|
||||
"Token URL - Tooltip": "Token URL - Tooltip",
|
||||
"Type": "Тип",
|
||||
"Type - Tooltip": "Unique string-style identifier",
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"alertType": "тип оповещения",
|
||||
"canSignIn": "canSignIn",
|
||||
"canSignUp": "canSignUp",
|
||||
@@ -455,6 +501,8 @@
|
||||
"Please input your real name!": "Пожалуйста, введите ваше личное имя!",
|
||||
"Please select your country/region!": "Пожалуйста, выберите вашу страну/регион!",
|
||||
"Terms of Use": "Условия использования",
|
||||
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
|
||||
"The input is not invoice title!": "The input is not invoice title!",
|
||||
"The input is not valid Email!": "Ввод не является допустимым Email!",
|
||||
"The input is not valid Phone!": "Введен неверный телефон!",
|
||||
"Unknown Check Type": "Неизвестный тип проверки",
|
||||
|
@@ -3,7 +3,6 @@
|
||||
"Login": "登录",
|
||||
"Logout": "登出",
|
||||
"My Account": "我的账户",
|
||||
"Settings for your account": "账户设置选项",
|
||||
"Sign Up": "注册"
|
||||
},
|
||||
"application": {
|
||||
@@ -175,6 +174,7 @@
|
||||
"State": "状态",
|
||||
"State - Tooltip": "状态",
|
||||
"Swagger": "API文档",
|
||||
"Sync": "同步",
|
||||
"Syncers": "同步器",
|
||||
"Timestamp": "时间戳",
|
||||
"Tokens": "令牌",
|
||||
@@ -224,7 +224,7 @@
|
||||
"Continue with": "使用以下账号继续",
|
||||
"Email or phone": "Email或手机号",
|
||||
"Forgot password?": "忘记密码?",
|
||||
"Invalid Email or phone": "无效的Email或手机号",
|
||||
"Logging out...": "正在退出登录...",
|
||||
"No account?": "没有账号?",
|
||||
"Or sign in with another account": "或者,登录其他账号",
|
||||
"Password": "密码",
|
||||
@@ -247,22 +247,51 @@
|
||||
"Default avatar": "默认头像",
|
||||
"Edit Organization": "编辑组织",
|
||||
"Favicon": "图标",
|
||||
"Is profile public": "公开用户主页",
|
||||
"Is profile public - Tooltip": "关闭后,只有全局管理员或同组织用户才能访问用户主页",
|
||||
"New Organization": "添加组织",
|
||||
"Soft deletion": "软删除",
|
||||
"Soft deletion - Tooltip": "启用后,删除用户信息时不会在数据库彻底清除,只会标记为已删除状态",
|
||||
"Tags": "标签集合",
|
||||
"Tags - Tooltip": "可供用户选择的标签的集合",
|
||||
"Website URL": "网页地址",
|
||||
"Website URL - Tooltip": "网页地址",
|
||||
"Is profile public": "公开用户信息",
|
||||
"Is profile public - Tooltip": "关闭后,只有全局管理员或同组织才能访问用户信息"
|
||||
"Website URL - Tooltip": "网页地址"
|
||||
},
|
||||
"payment": {
|
||||
"Confirm your invoice information": "确认您的发票信息",
|
||||
"Currency": "币种",
|
||||
"Currency - Tooltip": "如USD(美元),CNY(人民币)等",
|
||||
"Download Invoice": "下载发票",
|
||||
"Edit Payment": "编辑付款",
|
||||
"Individual": "个人",
|
||||
"Invoice URL": "发票URL",
|
||||
"Invoice URL - Tooltip": "发票的下载地址URL",
|
||||
"Invoice actions": "发票操作",
|
||||
"Invoice actions - Tooltip": "操作包含开具发票和下载发票两种",
|
||||
"Invoice remark": "发票备注",
|
||||
"Invoice remark - Tooltip": "备注不超过50个字",
|
||||
"Invoice tax ID": "纳税人识别号",
|
||||
"Invoice tax ID - Tooltip": "开票类型为单位时,必须输入单位纳税人识别号;开票类型为个人时,不需要填写",
|
||||
"Invoice title": "发票抬头",
|
||||
"Invoice title - Tooltip": "开票类型为单位时,发票抬头可输入单位名称;开票类型为个人时,系统自动填写为缴费人姓名",
|
||||
"Invoice type": "开票类型",
|
||||
"Invoice type - Tooltip": "开票类型可以为个人或者单位",
|
||||
"Issue Invoice": "开具发票",
|
||||
"Message": "消息",
|
||||
"Message - Tooltip": "付款处理结果消息",
|
||||
"New Payment": "添加付款",
|
||||
"Organization": "单位",
|
||||
"Person Email": "缴费人电子邮箱",
|
||||
"Person Email - Tooltip": "缴费人本人的电子邮箱",
|
||||
"Person ID card": "缴费人身份证号",
|
||||
"Person ID card - Tooltip": "缴费人本人的身份证号",
|
||||
"Person name": "缴费人姓名",
|
||||
"Person name - Tooltip": "缴费人本人的姓名",
|
||||
"Person phone": "缴费人手机号",
|
||||
"Person phone - Tooltip": "缴费人本人的手机号",
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "请仔细检查下列发票信息,发票一经开具,无法退换。",
|
||||
"Please click the below button to return to the original website": "请点击下方按钮返回原网站",
|
||||
"Please pay the order first!": "请先完成支付后再进行操作!",
|
||||
"Price": "价格",
|
||||
"Price - Tooltip": "商品价格",
|
||||
"Processing...": "正在处理...",
|
||||
@@ -333,6 +362,10 @@
|
||||
"Agent ID - Tooltip": "Agent ID - Tooltip",
|
||||
"App ID": "App ID",
|
||||
"App ID - Tooltip": "App ID - Tooltip",
|
||||
"App key": "App key",
|
||||
"App key - Tooltip": "App key - Tooltip",
|
||||
"App secret": "App secret",
|
||||
"AppSecret - Tooltip": "AppSecret - Tooltip",
|
||||
"Auth URL": "Auth URL",
|
||||
"Auth URL - Tooltip": "Auth URL - 工具提示",
|
||||
"Bucket": "存储桶",
|
||||
@@ -340,6 +373,8 @@
|
||||
"Can not parse Metadata": "无法解析元数据",
|
||||
"Category": "分类",
|
||||
"Category - Tooltip": "分类",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Channel No. - Tooltip",
|
||||
"Client ID": "客户端ID",
|
||||
"Client ID - Tooltip": "Client ID",
|
||||
"Client ID 2": "客户端 ID 2",
|
||||
@@ -382,13 +417,13 @@
|
||||
"Region endpoint for Internet": "地域节点 (外网)",
|
||||
"Region endpoint for Intranet": "地域节点 (内网)",
|
||||
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - 工具提示",
|
||||
"SMS account": "SMS account",
|
||||
"SMS account - Tooltip": "SMS account - Tooltip",
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - 工具提示",
|
||||
"SP Entity ID": "SP 实体 ID",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - 工具提示",
|
||||
"Secret access key": "秘密访问密钥",
|
||||
"SecretAccessKey - Tooltip": "访问密钥-工具提示",
|
||||
"Sign Name": "签名名称",
|
||||
@@ -466,6 +501,8 @@
|
||||
"Please input your real name!": "请输入您的姓名!",
|
||||
"Please select your country/region!": "请选择您的国家/地区",
|
||||
"Terms of Use": "《用户协议》",
|
||||
"The input is not invoice Tax ID!": "您输入的纳税人识别号有误!",
|
||||
"The input is not invoice title!": "您输入的发票抬头有误!",
|
||||
"The input is not valid Email!": "您输入的电子邮箱格式有误!",
|
||||
"The input is not valid Phone!": "您输入的手机号格式有误!",
|
||||
"Unknown Check Type": "未知的验证码类型",
|
||||
|
Reference in New Issue
Block a user