mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-02 11:20:18 +08:00
Add alipay provider.
This commit is contained in:
@ -132,3 +132,11 @@ func wrapActionResponse(affected bool) *Response {
|
|||||||
return &Response{Status: "ok", Msg: "", Data: "Unaffected"}
|
return &Response{Status: "ok", Msg: "", Data: "Unaffected"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapErrorResponse(err error) *Response {
|
||||||
|
if err == nil {
|
||||||
|
return &Response{Status: "ok", Msg: ""}
|
||||||
|
} else {
|
||||||
|
return &Response{Status: "error", Msg: err.Error()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -114,3 +114,20 @@ func (c *ApiController) DeletePayment() {
|
|||||||
c.Data["json"] = wrapActionResponse(object.DeletePayment(&payment))
|
c.Data["json"] = wrapActionResponse(object.DeletePayment(&payment))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Title NotifyPayment
|
||||||
|
// @Tag Payment API
|
||||||
|
// @Description notify payment
|
||||||
|
// @Param body body object.Payment true "The details of the payment"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /notify-payment [post]
|
||||||
|
func (c *ApiController) NotifyPayment() {
|
||||||
|
var payment object.Payment
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.NotifyPayment("111", "222"))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
@ -114,3 +114,24 @@ func (c *ApiController) DeleteProduct() {
|
|||||||
c.Data["json"] = wrapActionResponse(object.DeleteProduct(&product))
|
c.Data["json"] = wrapActionResponse(object.DeleteProduct(&product))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Title BuyProduct
|
||||||
|
// @Tag Product API
|
||||||
|
// @Description buy product
|
||||||
|
// @Param id query string true "The id of the product"
|
||||||
|
// @Param providerId query string true "The id of the provider"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /buy-product [post]
|
||||||
|
func (c *ApiController) BuyProduct() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
providerId := c.Input().Get("providerId")
|
||||||
|
host := c.Ctx.Request.Host
|
||||||
|
|
||||||
|
payUrl, err := object.BuyProduct(id, providerId, host)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(payUrl)
|
||||||
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -13,6 +13,7 @@ require (
|
|||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0
|
github.com/go-ldap/ldap/v3 v3.3.0
|
||||||
|
github.com/go-pay/gopay v1.5.72
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.1.0
|
github.com/golang-jwt/jwt/v4 v4.1.0
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
@ -29,7 +30,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/tealeg/xlsx v1.0.5
|
github.com/tealeg/xlsx v1.0.5
|
||||||
github.com/thanhpk/randstr v1.0.4
|
github.com/thanhpk/randstr v1.0.4
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
|
6
go.sum
6
go.sum
@ -124,6 +124,8 @@ github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ
|
|||||||
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-pay/gopay v1.5.72 h1:3zm64xMBhJBa8rXbm//q5UiGgOa4WO5XYEnU394N2Zw=
|
||||||
|
github.com/go-pay/gopay v1.5.72/go.mod h1:0qOGIJuFW7PKDOjmecwKyW0mgsVImgwB9yPJj0ilpn8=
|
||||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
@ -379,8 +381,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954 h1:BkypuErRT9A9I/iljuaG3/zdMjd/J6m8tKKJQtGfSdA=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -33,8 +33,10 @@ type Cert struct {
|
|||||||
BitSize int `json:"bitSize"`
|
BitSize int `json:"bitSize"`
|
||||||
ExpireInYears int `json:"expireInYears"`
|
ExpireInYears int `json:"expireInYears"`
|
||||||
|
|
||||||
PublicKey string `xorm:"mediumtext" json:"publicKey"`
|
PublicKey string `xorm:"mediumtext" json:"publicKey"`
|
||||||
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
||||||
|
AuthorityPublicKey string `xorm:"mediumtext" json:"authorityPublicKey"`
|
||||||
|
AuthorityRootPublicKey string `xorm:"mediumtext" json:"authorityRootPublicKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaskedCert(cert *Cert) *Cert {
|
func GetMaskedCert(cert *Cert) *Cert {
|
||||||
|
@ -124,6 +124,23 @@ func DeletePayment(payment *Payment) bool {
|
|||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NotifyPayment(id string, state string) bool {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
payment := getPayment(owner, name)
|
||||||
|
if payment == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
payment.State = state
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(payment)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
func (payment *Payment) GetId() string {
|
func (payment *Payment) GetId() string {
|
||||||
return fmt.Sprintf("%s/%s", payment.Owner, payment.Name)
|
return fmt.Sprintf("%s/%s", payment.Owner, payment.Name)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/pp"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
)
|
)
|
||||||
@ -31,7 +32,7 @@ type Product struct {
|
|||||||
Detail string `xorm:"varchar(100)" json:"detail"`
|
Detail string `xorm:"varchar(100)" json:"detail"`
|
||||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||||
Price int `json:"price"`
|
Price float64 `json:"price"`
|
||||||
Quantity int `json:"quantity"`
|
Quantity int `json:"quantity"`
|
||||||
Sold int `json:"sold"`
|
Sold int `json:"sold"`
|
||||||
Providers []string `xorm:"varchar(100)" json:"providers"`
|
Providers []string `xorm:"varchar(100)" json:"providers"`
|
||||||
@ -128,3 +129,47 @@ func DeleteProduct(product *Product) bool {
|
|||||||
func (product *Product) GetId() string {
|
func (product *Product) GetId() string {
|
||||||
return fmt.Sprintf("%s/%s", product.Owner, product.Name)
|
return fmt.Sprintf("%s/%s", product.Owner, product.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (product *Product) isValidProvider(provider *Provider) bool {
|
||||||
|
for _, providerName := range product.Providers {
|
||||||
|
if providerName == provider.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuyProduct(id string, providerId string, host string) (string, error) {
|
||||||
|
product := GetProduct(id)
|
||||||
|
if product == nil {
|
||||||
|
return "", fmt.Errorf("the product: %s does not exist", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := getProvider(product.Owner, providerId)
|
||||||
|
if provider == nil {
|
||||||
|
return "", fmt.Errorf("the payment provider: %s does not exist", providerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !product.isValidProvider(provider) {
|
||||||
|
return "", fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert := getCert(product.Owner, provider.Cert)
|
||||||
|
if cert == nil {
|
||||||
|
return "", fmt.Errorf("the cert: %s does not exist", provider.Cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
|
||||||
|
if pProvider == nil {
|
||||||
|
return "", fmt.Errorf("the payment provider type: %s is not supported", provider.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentId := util.GenerateTimeId()
|
||||||
|
|
||||||
|
originFrontend, originBackend := getOriginFromHost(host)
|
||||||
|
returnUrl := fmt.Sprintf("%s/payments/%s", originFrontend, paymentId)
|
||||||
|
notifyUrl := fmt.Sprintf("%s/api/notify-payment", originBackend)
|
||||||
|
|
||||||
|
payUrl, err := pProvider.Pay(product.DisplayName, paymentId, product.Price, returnUrl, notifyUrl)
|
||||||
|
return payUrl, err
|
||||||
|
}
|
||||||
|
41
object/product_test.go
Normal file
41
object/product_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/pp"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProvider(t *testing.T) {
|
||||||
|
InitConfig()
|
||||||
|
|
||||||
|
product := GetProduct("admin/product_123")
|
||||||
|
provider := getProvider(product.Owner, "provider_pay_alipay")
|
||||||
|
cert := getCert(product.Owner, "cert-pay-alipay")
|
||||||
|
pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
|
||||||
|
|
||||||
|
paymentId := util.GenerateTimeId()
|
||||||
|
returnUrl := ""
|
||||||
|
notifyUrl := ""
|
||||||
|
payUrl, err := pProvider.Pay(product.DisplayName, paymentId, product.Price, returnUrl, notifyUrl)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
println(payUrl)
|
||||||
|
}
|
@ -35,6 +35,7 @@ type Provider struct {
|
|||||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||||
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
||||||
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
|
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
|
||||||
|
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||||
|
|
||||||
Host string `xorm:"varchar(100)" json:"host"`
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
|
65
pp/alipay.go
Normal file
65
pp/alipay.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// 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 pp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-pay/gopay"
|
||||||
|
"github.com/go-pay/gopay/alipay"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlipayPaymentProvider struct {
|
||||||
|
Client *alipay.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) *AlipayPaymentProvider {
|
||||||
|
pp := &AlipayPaymentProvider{}
|
||||||
|
|
||||||
|
client, err := alipay.NewClient(appId, appPrivateKey, true)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SetCertSnByContent([]byte(appPublicKey), []byte(authorityRootPublicKey), []byte(authorityPublicKey))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pp.Client = client
|
||||||
|
return pp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *AlipayPaymentProvider) Pay(productName string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
||||||
|
pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
|
|
||||||
|
priceString := strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.2f", price), "0"), ".")
|
||||||
|
|
||||||
|
bm := gopay.BodyMap{}
|
||||||
|
bm.Set("subject", productName)
|
||||||
|
bm.Set("out_trade_no", paymentId)
|
||||||
|
bm.Set("total_amount", priceString)
|
||||||
|
|
||||||
|
bm.Set("return_url", returnUrl)
|
||||||
|
bm.Set("notify_url", notifyUrl)
|
||||||
|
|
||||||
|
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return payUrl, nil
|
||||||
|
}
|
26
pp/provider.go
Normal file
26
pp/provider.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 pp
|
||||||
|
|
||||||
|
type PaymentProvider interface {
|
||||||
|
Pay(productName string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaymentProvider(typ string, appId string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider {
|
||||||
|
if typ == "Alipay" {
|
||||||
|
return NewAlipayPaymentProvider(appId, appPublicKey, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -156,12 +156,14 @@ func initAPI() {
|
|||||||
beego.Router("/api/update-product", &controllers.ApiController{}, "POST:UpdateProduct")
|
beego.Router("/api/update-product", &controllers.ApiController{}, "POST:UpdateProduct")
|
||||||
beego.Router("/api/add-product", &controllers.ApiController{}, "POST:AddProduct")
|
beego.Router("/api/add-product", &controllers.ApiController{}, "POST:AddProduct")
|
||||||
beego.Router("/api/delete-product", &controllers.ApiController{}, "POST:DeleteProduct")
|
beego.Router("/api/delete-product", &controllers.ApiController{}, "POST:DeleteProduct")
|
||||||
|
beego.Router("/api/buy-product", &controllers.ApiController{}, "POST:BuyProduct")
|
||||||
|
|
||||||
beego.Router("/api/get-payments", &controllers.ApiController{}, "GET:GetPayments")
|
beego.Router("/api/get-payments", &controllers.ApiController{}, "GET:GetPayments")
|
||||||
beego.Router("/api/get-payment", &controllers.ApiController{}, "GET:GetPayment")
|
beego.Router("/api/get-payment", &controllers.ApiController{}, "GET:GetPayment")
|
||||||
beego.Router("/api/update-payment", &controllers.ApiController{}, "POST:UpdatePayment")
|
beego.Router("/api/update-payment", &controllers.ApiController{}, "POST:UpdatePayment")
|
||||||
beego.Router("/api/add-payment", &controllers.ApiController{}, "POST:AddPayment")
|
beego.Router("/api/add-payment", &controllers.ApiController{}, "POST:AddPayment")
|
||||||
beego.Router("/api/delete-payment", &controllers.ApiController{}, "POST:DeletePayment")
|
beego.Router("/api/delete-payment", &controllers.ApiController{}, "POST:DeletePayment")
|
||||||
|
beego.Router("/api/notify-payment", &controllers.ApiController{}, "POST:NotifyPayment")
|
||||||
|
|
||||||
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
|
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
|
||||||
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
|
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -88,6 +89,17 @@ func GenerateId() string {
|
|||||||
return uuid.NewString()
|
return uuid.NewString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateTimeId() string {
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
tm := time.Unix(timestamp, 0)
|
||||||
|
t := tm.Format("20060102_150405")
|
||||||
|
|
||||||
|
random := uuid.NewString()[0:7]
|
||||||
|
|
||||||
|
res := fmt.Sprintf("%s_%s", t, random)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func GetId(name string) string {
|
func GetId(name string) string {
|
||||||
return fmt.Sprintf("admin/%s", name)
|
return fmt.Sprintf("admin/%s", name)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import i18next from "i18next";
|
|||||||
import * as ProductBackend from "./backend/ProductBackend";
|
import * as ProductBackend from "./backend/ProductBackend";
|
||||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||||
import * as Provider from "./auth/Provider";
|
import * as Provider from "./auth/Provider";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
|
||||||
class ProductBuyPage extends React.Component {
|
class ProductBuyPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -107,6 +108,24 @@ class ProductBuyPage extends React.Component {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buyProduct(product, provider) {
|
||||||
|
ProductBackend.buyProduct(this.state.product.owner, this.state.productName, provider.name)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.msg === "") {
|
||||||
|
const payUrl = res.data;
|
||||||
|
Setting.goToLink(payUrl);
|
||||||
|
this.setState({
|
||||||
|
productName: this.state.product.name,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getPayButton(provider) {
|
getPayButton(provider) {
|
||||||
let text = provider.type;
|
let text = provider.type;
|
||||||
if (provider.type === "Alipay") {
|
if (provider.type === "Alipay") {
|
||||||
@ -131,11 +150,11 @@ class ProductBuyPage extends React.Component {
|
|||||||
renderProviderButton(provider, product) {
|
renderProviderButton(provider, product) {
|
||||||
return (
|
return (
|
||||||
<span key={provider.name} style={{width: "200px", marginRight: "20px", marginBottom: "10px"}}>
|
<span key={provider.name} style={{width: "200px", marginRight: "20px", marginBottom: "10px"}}>
|
||||||
<a style={{width: "200px"}} href={this.getPayUrl(product, provider)}>
|
<span style={{width: "200px", cursor: "pointer"}} onClick={() => this.buyProduct(product, provider)}>
|
||||||
{
|
{
|
||||||
this.getPayButton(provider)
|
this.getPayButton(provider)
|
||||||
}
|
}
|
||||||
</a>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -225,11 +225,12 @@ class ProductListPage extends BaseListPage {
|
|||||||
title: i18next.t("general:Action"),
|
title: i18next.t("general:Action"),
|
||||||
dataIndex: '',
|
dataIndex: '',
|
||||||
key: 'op',
|
key: 'op',
|
||||||
width: '170px',
|
width: '230px',
|
||||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} onClick={() => this.props.history.push(`/products/${record.name}/buy`)}>{i18next.t("product:Buy")}</Button>
|
||||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/products/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/products/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={`Sure to delete product: ${record.name} ?`}
|
title={`Sure to delete product: ${record.name} ?`}
|
||||||
|
@ -54,3 +54,10 @@ export function deleteProduct(product) {
|
|||||||
body: JSON.stringify(newProduct),
|
body: JSON.stringify(newProduct),
|
||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buyProduct(owner, name, providerId) {
|
||||||
|
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerId=${providerId}`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
}).then(res => res.json());
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user