Skip to content

Commit

Permalink
Add Notify() to payment provider.
Browse files Browse the repository at this point in the history
  • Loading branch information
hsluoyz committed Mar 13, 2022
1 parent 5de417e commit 4dca3bd
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 105 deletions.
15 changes: 8 additions & 7 deletions controllers/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/astaxie/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
"github.com/go-pay/gopay/alipay"
)

// GetPayments
Expand Down Expand Up @@ -142,14 +141,16 @@ func (c *ApiController) DeletePayment() {
// @Success 200 {object} controllers.Response The Response object
// @router /notify-payment [post]
func (c *ApiController) NotifyPayment() {
bm, err := alipay.ParseNotifyToBodyMap(c.Ctx.Request)
if err != nil {
panic(err)
}
owner := c.Ctx.Input.Param(":owner")
providerName := c.Ctx.Input.Param(":provider")
productName := c.Ctx.Input.Param(":product")
paymentName := c.Ctx.Input.Param(":payment")

body := c.Ctx.Input.RequestBody

ok := object.NotifyPayment(bm)
ok := object.NotifyPayment(c.Ctx.Request, body, owner, providerName, productName, paymentName)
if ok {
_, err = c.Ctx.ResponseWriter.Write([]byte("success"))
_, err := c.Ctx.ResponseWriter.Write([]byte("success"))
if err != nil {
panic(err)
}
Expand Down
8 changes: 4 additions & 4 deletions controllers/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ func (c *ApiController) DeleteProduct() {
// @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"
// @Param id query string true "The id of the product"
// @Param providerName query string true "The name 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")
providerName := c.Input().Get("providerName")
host := c.Ctx.Request.Host

userId := c.GetSessionUsername()
Expand All @@ -140,7 +140,7 @@ func (c *ApiController) BuyProduct() {
return
}

payUrl, err := object.BuyProduct(id, providerId, user, host)
payUrl, err := object.BuyProduct(id, providerName, user, host)
if err != nil {
c.ResponseError(err.Error())
return
Expand Down
67 changes: 27 additions & 40 deletions object/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ package object

import (
"fmt"
"net/http"

"github.com/casdoor/casdoor/util"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay"
"xorm.io/core"
)

Expand All @@ -29,12 +28,12 @@ type Payment struct {
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`

Provider string `xorm:"varchar(100)" json:"provider"`
Type string `xorm:"varchar(100)" json:"type"`
Organization string `xorm:"varchar(100)" json:"organization"`
User string `xorm:"varchar(100)" json:"user"`
ProductId string `xorm:"varchar(100)" json:"productId"`
ProductName string `xorm:"varchar(100)" json:"productName"`
Provider string `xorm:"varchar(100)" json:"provider"`
Type string `xorm:"varchar(100)" json:"type"`
Organization string `xorm:"varchar(100)" json:"organization"`
User string `xorm:"varchar(100)" json:"user"`
ProductName string `xorm:"varchar(100)" json:"productName"`
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`

Detail string `xorm:"varchar(100)" json:"detail"`
Tag string `xorm:"varchar(100)" json:"tag"`
Expand Down Expand Up @@ -143,57 +142,45 @@ func DeletePayment(payment *Payment) bool {
return affected != 0
}

func notifyPayment(bm gopay.BodyMap) (*Payment, error) {
owner := "admin"
productName := bm.Get("subject")
paymentId := bm.Get("out_trade_no")
priceString := bm.Get("total_amount")
price := util.ParseFloat(priceString)
productId := bm.Get("productId")
providerId := bm.Get("providerId")

product := getProduct(owner, productId)
if product == nil {
return nil, fmt.Errorf("the product: %s does not exist", productId)
}

if productName != product.DisplayName {
return nil, fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", productName, product.DisplayName)
}

if price != product.Price {
return nil, fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", price, product.Price)
func notifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string) (*Payment, error) {
payment := getPayment(owner, paymentName)
if payment == nil {
return nil, fmt.Errorf("the payment: %s does not exist", paymentName)
}

payment := getPayment(owner, paymentId)
if payment == nil {
return nil, fmt.Errorf("the payment: %s does not exist", paymentId)
product := getProduct(owner, productName)
if product == nil {
return nil, fmt.Errorf("the product: %s does not exist", productName)
}

provider, err := product.getProvider(providerId)
provider, err := product.getProvider(providerName)
if err != nil {
return payment, err
}

cert := getCert(owner, provider.Cert)
if cert == nil {
return payment, fmt.Errorf("the cert: %s does not exist", provider.Cert)
pProvider, cert, err := provider.getPaymentProvider()
if err != nil {
return payment, err
}

ok, err := alipay.VerifySignWithCert(cert.AuthorityPublicKey, bm)
productDisplayName, paymentName, price, productName, providerName, err := pProvider.Notify(request, body, cert.AuthorityPublicKey)
if err != nil {
return payment, err
}

if !ok {
return payment, fmt.Errorf("VerifySignWithCert() failed: %v", ok)
if productDisplayName != "" && productDisplayName != product.DisplayName {
return nil, fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", productDisplayName, product.DisplayName)
}

if price != product.Price {
return nil, fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", price, product.Price)
}

return payment, nil
}

func NotifyPayment(bm gopay.BodyMap) bool {
payment, err := notifyPayment(bm)
func NotifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string) bool {
payment, err := notifyPayment(request, body, owner, providerName, productName, paymentName)

if payment != nil {
if err != nil {
Expand Down
66 changes: 29 additions & 37 deletions object/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package object
import (
"fmt"

"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
Expand Down Expand Up @@ -153,61 +152,54 @@ func (product *Product) getProvider(providerId string) (*Provider, error) {
return provider, nil
}

func BuyProduct(id string, providerId string, user *User, host string) (string, error) {
func BuyProduct(id string, providerName string, user *User, host string) (string, error) {
product := GetProduct(id)
if product == nil {
return "", fmt.Errorf("the product: %s does not exist", id)
}

provider, err := product.getProvider(providerId)
provider, err := product.getProvider(providerName)
if err != nil {
return "", err
}

cert := &Cert{}
if provider.Cert != "" {
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, provider.ClientSecret, provider.Host, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
if pProvider == nil {
return "", fmt.Errorf("the payment provider type: %s is not supported", provider.Type)
pProvider, _, err := provider.getPaymentProvider()
if err != nil {
return "", err
}

paymentId := util.GenerateTimeId()
productName := product.DisplayName
productId := product.Name
owner := product.Owner
productName := product.Name
paymentName := util.GenerateTimeId()
productDisplayName := product.DisplayName

originFrontend, originBackend := getOriginFromHost(host)
returnUrl := fmt.Sprintf("%s/payments/%s/result", originFrontend, paymentId)
notifyUrl := fmt.Sprintf("%s/api/notify-payment", originBackend)
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(productName, productId, providerId, paymentId, product.Price, returnUrl, notifyUrl)
payUrl, err := pProvider.Pay(providerName, productName, paymentName, productDisplayName, product.Price, returnUrl, notifyUrl)
if err != nil {
return "", err
}

payment := Payment{
Owner: product.Owner,
Name: paymentId,
CreatedTime: util.GetCurrentTime(),
DisplayName: paymentId,
Provider: provider.Name,
Type: provider.Type,
Organization: user.Owner,
User: user.Name,
ProductId: productId,
ProductName: productName,
Detail: product.Detail,
Tag: product.Tag,
Currency: product.Currency,
Price: product.Price,
PayUrl: payUrl,
ReturnUrl: product.ReturnUrl,
State: "Created",
Owner: product.Owner,
Name: paymentName,
CreatedTime: util.GetCurrentTime(),
DisplayName: paymentName,
Provider: provider.Name,
Type: provider.Type,
Organization: user.Owner,
User: user.Name,
ProductName: productName,
ProductDisplayName: productDisplayName,
Detail: product.Detail,
Tag: product.Tag,
Currency: product.Currency,
Price: product.Price,
PayUrl: payUrl,
ReturnUrl: product.ReturnUrl,
State: "Created",
}
affected := AddPayment(&payment)
if !affected {
Expand Down
18 changes: 18 additions & 0 deletions object/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package object
import (
"fmt"

"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
Expand Down Expand Up @@ -182,6 +183,23 @@ func DeleteProvider(provider *Provider) bool {
return affected != 0
}

func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
cert := &Cert{}
if p.Cert != "" {
cert = getCert(p.Owner, p.Cert)
if cert == nil {
return nil, nil, fmt.Errorf("the cert: %s does not exist", p.Cert)
}
}

pProvider := pp.GetPaymentProvider(p.Type, p.ClientId, p.ClientSecret, p.Host, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
if pProvider == nil {
return nil, cert, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
}

return pProvider, cert, nil
}

func (p *Provider) GetId() string {
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
}
38 changes: 33 additions & 5 deletions pp/alipay.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ package pp

import (
"context"
"fmt"
"net/http"

"github.com/casdoor/casdoor/util"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay"
)
Expand All @@ -42,23 +45,48 @@ func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey s
return pp
}

func (pp *AlipayPaymentProvider) Pay(productName string, productId string, providerId string, paymentId string, price float64, returnUrl string, notifyUrl string) (string, error) {
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
//pp.Client.DebugSwitch = gopay.DebugOn

bm := gopay.BodyMap{}

bm.Set("providerName", providerName)
bm.Set("productName", productName)

bm.Set("return_url", returnUrl)
bm.Set("notify_url", notifyUrl)

bm.Set("subject", productName)
bm.Set("out_trade_no", paymentId)
bm.Set("subject", productDisplayName)
bm.Set("out_trade_no", paymentName)
bm.Set("total_amount", getPriceString(price))
bm.Set("productId", productId)
bm.Set("providerId", productId)

payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
if err != nil {
return "", err
}
return payUrl, nil
}

func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error) {
bm, err := alipay.ParseNotifyToBodyMap(request)
if err != nil {
return "", "", 0, "", "", err
}

providerName := bm.Get("providerName")
productName := bm.Get("productName")

productDisplayName := bm.Get("subject")
paymentName := bm.Get("out_trade_no")
price := util.ParseFloat(bm.Get("total_amount"))

ok, err := alipay.VerifySignWithCert(authorityPublicKey, bm)
if err != nil {
return "", "", 0, "", "", err
}
if !ok {
return "", "", 0, "", "", fmt.Errorf("VerifySignWithCert() failed: %v", ok)
}

return productDisplayName, paymentName, price, productName, providerName, nil
}

0 comments on commit 4dca3bd

Please sign in to comment.