mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 18:54:03 +08:00
feat: support WeChat Pay via JSAPI (#2488)
* feat: support wechat jsapi payment * feat: add log * feat: update sign * feat: process wechat pay result * feat: process wechat pay result * feat: save wechat openid for different app * feat: save wechat openid for different app * feat: add SetUserOAuthProperties for signup * feat: fix openid for wechat * feat: get user extra property in buyproduct * feat: remove log * feat: remove log * feat: gofumpt code * feat: change lr->crlf * feat: change crlf->lf * feat: improve code
This commit is contained in:
parent
d090e9c860
commit
0ac2b69f5a
@ -547,7 +547,12 @@ func (c *ApiController) Login() {
|
|||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
||||||
}
|
}
|
||||||
|
// sync info from 3rd-party if possible
|
||||||
|
_, err := object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
record := object.NewRecord(c.Ctx)
|
||||||
|
@ -163,6 +163,8 @@ func (c *ApiController) BuyProduct() {
|
|||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
host := c.Ctx.Request.Host
|
host := c.Ctx.Request.Host
|
||||||
providerName := c.Input().Get("providerName")
|
providerName := c.Input().Get("providerName")
|
||||||
|
paymentEnv := c.Input().Get("paymentEnv")
|
||||||
|
|
||||||
// buy `pricingName/planName` for `paidUserName`
|
// buy `pricingName/planName` for `paidUserName`
|
||||||
pricingName := c.Input().Get("pricingName")
|
pricingName := c.Input().Get("pricingName")
|
||||||
planName := c.Input().Get("planName")
|
planName := c.Input().Get("planName")
|
||||||
@ -187,11 +189,11 @@ func (c *ApiController) BuyProduct() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payment, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
|
payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(payment)
|
c.ResponseOk(payment, attachInfo)
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -32,6 +32,7 @@ require (
|
|||||||
github.com/go-webauthn/webauthn v0.6.0
|
github.com/go-webauthn/webauthn v0.6.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
github.com/lestrrat-go/jwx v1.2.21
|
github.com/lestrrat-go/jwx v1.2.21
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
|
1
go.sum
1
go.sum
@ -1246,6 +1246,7 @@ github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
|
|||||||
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
||||||
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
|
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
|
||||||
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
||||||
|
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
@ -31,6 +31,7 @@ type UserInfo struct {
|
|||||||
Phone string
|
Phone string
|
||||||
CountryCode string
|
CountryCode string
|
||||||
AvatarUrl string
|
AvatarUrl string
|
||||||
|
Extra map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderInfo struct {
|
type ProviderInfo struct {
|
||||||
|
@ -186,15 +186,24 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
id = wechatUserInfo.Openid
|
id = wechatUserInfo.Openid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extra := make(map[string]string)
|
||||||
|
extra["wechat_unionid"] = wechatUserInfo.Openid
|
||||||
|
// For WeChat, different appId corresponds to different openId
|
||||||
|
extra[BuildWechatOpenIdKey(idp.Config.ClientID)] = wechatUserInfo.Openid
|
||||||
userInfo := UserInfo{
|
userInfo := UserInfo{
|
||||||
Id: id,
|
Id: id,
|
||||||
Username: wechatUserInfo.Nickname,
|
Username: wechatUserInfo.Nickname,
|
||||||
DisplayName: wechatUserInfo.Nickname,
|
DisplayName: wechatUserInfo.Nickname,
|
||||||
AvatarUrl: wechatUserInfo.Headimgurl,
|
AvatarUrl: wechatUserInfo.Headimgurl,
|
||||||
|
Extra: extra,
|
||||||
}
|
}
|
||||||
return &userInfo, nil
|
return &userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildWechatOpenIdKey(appId string) string {
|
||||||
|
return fmt.Sprintf("wechat_openid_%s", appId)
|
||||||
|
}
|
||||||
|
|
||||||
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
||||||
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
||||||
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
||||||
|
@ -17,6 +17,8 @@ package object
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/idp"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/pp"
|
"github.com/casdoor/casdoor/pp"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -158,30 +160,28 @@ func (product *Product) getProvider(providerName string) (*Provider, error) {
|
|||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (*Payment, error) {
|
func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string) (payment *Payment, attachInfo map[string]interface{}, err error) {
|
||||||
product, err := GetProduct(id)
|
product, err := GetProduct(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if product == nil {
|
if product == nil {
|
||||||
return nil, fmt.Errorf("the product: %s does not exist", id)
|
return nil, nil, fmt.Errorf("the product: %s does not exist", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := product.getProvider(providerName)
|
provider, err := product.getProvider(providerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pProvider, err := GetPaymentProvider(provider)
|
pProvider, err := GetPaymentProvider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := product.Owner
|
owner := product.Owner
|
||||||
productName := product.Name
|
|
||||||
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
||||||
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
||||||
productDisplayName := product.DisplayName
|
|
||||||
|
|
||||||
originFrontend, originBackend := getOriginFromHost(host)
|
originFrontend, originBackend := getOriginFromHost(host)
|
||||||
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
||||||
@ -191,26 +191,46 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
if pricingName != "" && planName != "" {
|
if pricingName != "" && planName != "" {
|
||||||
plan, err := GetPlan(util.GetId(owner, planName))
|
plan, err := GetPlan(util.GetId(owner, planName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if plan == nil {
|
if plan == nil {
|
||||||
return nil, fmt.Errorf("the plan: %s does not exist", planName)
|
return nil, nil, fmt.Errorf("the plan: %s does not exist", planName)
|
||||||
}
|
}
|
||||||
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||||
_, err = AddSubscription(sub)
|
_, err = AddSubscription(sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create an OrderId and get the payUrl
|
// Create an order
|
||||||
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
payReq := &pp.PayReq{
|
||||||
|
ProviderName: providerName,
|
||||||
|
ProductName: product.Name,
|
||||||
|
PayerName: payerName,
|
||||||
|
PayerId: user.Id,
|
||||||
|
PaymentName: paymentName,
|
||||||
|
ProductDisplayName: product.DisplayName,
|
||||||
|
Price: product.Price,
|
||||||
|
Currency: product.Currency,
|
||||||
|
ReturnUrl: returnUrl,
|
||||||
|
NotifyUrl: notifyUrl,
|
||||||
|
PaymentEnv: paymentEnv,
|
||||||
|
}
|
||||||
|
// custom process for WeChat & WeChat Pay
|
||||||
|
if provider.Type == "WeChat Pay" {
|
||||||
|
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payResp, err := pProvider.Pay(payReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Create a Payment linked with Product and Order
|
// Create a Payment linked with Product and Order
|
||||||
payment := &Payment{
|
payment = &Payment{
|
||||||
Owner: product.Owner,
|
Owner: product.Owner,
|
||||||
Name: paymentName,
|
Name: paymentName,
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
@ -219,8 +239,8 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
Provider: provider.Name,
|
Provider: provider.Name,
|
||||||
Type: provider.Type,
|
Type: provider.Type,
|
||||||
|
|
||||||
ProductName: productName,
|
ProductName: product.Name,
|
||||||
ProductDisplayName: productDisplayName,
|
ProductDisplayName: product.DisplayName,
|
||||||
Detail: product.Detail,
|
Detail: product.Detail,
|
||||||
Tag: product.Tag,
|
Tag: product.Tag,
|
||||||
Currency: product.Currency,
|
Currency: product.Currency,
|
||||||
@ -228,10 +248,10 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
ReturnUrl: product.ReturnUrl,
|
ReturnUrl: product.ReturnUrl,
|
||||||
|
|
||||||
User: user.Name,
|
User: user.Name,
|
||||||
PayUrl: payUrl,
|
PayUrl: payResp.PayUrl,
|
||||||
SuccessUrl: returnUrl,
|
SuccessUrl: returnUrl,
|
||||||
State: pp.PaymentStateCreated,
|
State: pp.PaymentStateCreated,
|
||||||
OutOrderId: orderId,
|
OutOrderId: payResp.OrderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.Type == "Dummy" {
|
if provider.Type == "Dummy" {
|
||||||
@ -240,13 +260,13 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
|
|
||||||
affected, err := AddPayment(payment)
|
affected, err := AddPayment(payment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !affected {
|
if !affected {
|
||||||
return nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||||
}
|
}
|
||||||
return payment, err
|
return payment, payResp.AttachInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendProductWithProviders(product *Product) error {
|
func ExtendProductWithProviders(product *Product) error {
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/idp"
|
"github.com/casdoor/casdoor/idp"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@ -142,6 +144,25 @@ func setUserProperty(user *User, field string, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserProperty(user *User, field string) string {
|
||||||
|
if user.Properties == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return user.Properties[field]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserExtraProperty(user *User, providerType, key string) (string, error) {
|
||||||
|
extraJson := getUserProperty(user, fmt.Sprintf("oauth_%s_extra", providerType))
|
||||||
|
if extraJson == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
extra := make(map[string]string)
|
||||||
|
if err := jsoniter.Unmarshal([]byte(extraJson), &extra); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extra[key], nil
|
||||||
|
}
|
||||||
|
|
||||||
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo) (bool, error) {
|
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo) (bool, error) {
|
||||||
if userInfo.Id != "" {
|
if userInfo.Id != "" {
|
||||||
propertyName := fmt.Sprintf("oauth_%s_id", providerType)
|
propertyName := fmt.Sprintf("oauth_%s_id", providerType)
|
||||||
@ -185,6 +206,27 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userInfo.Extra != nil {
|
||||||
|
// Save extra info as json string
|
||||||
|
propertyName := fmt.Sprintf("oauth_%s_extra", providerType)
|
||||||
|
oldExtraJson := getUserProperty(user, propertyName)
|
||||||
|
extra := make(map[string]string)
|
||||||
|
if oldExtraJson != "" {
|
||||||
|
if err := jsoniter.Unmarshal([]byte(oldExtraJson), &extra); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range userInfo.Extra {
|
||||||
|
extra[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
newExtraJson, err := jsoniter.Marshal(extra)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
setUserProperty(user, propertyName, string(newExtraJson))
|
||||||
|
}
|
||||||
|
|
||||||
return UpdateUserForAllFields(user.GetId(), user)
|
return UpdateUserForAllFields(user.GetId(), user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
pp/alipay.go
20
pp/alipay.go
@ -49,20 +49,24 @@ func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *AlipayPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
// pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
pp.Client.SetReturnUrl(returnUrl)
|
pp.Client.SetReturnUrl(r.ReturnUrl)
|
||||||
pp.Client.SetNotifyUrl(notifyUrl)
|
pp.Client.SetNotifyUrl(r.NotifyUrl)
|
||||||
bm.Set("subject", joinAttachString([]string{productName, productDisplayName, providerName}))
|
bm.Set("subject", joinAttachString([]string{r.ProductName, r.ProductDisplayName, r.ProviderName}))
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", r.PaymentName)
|
||||||
bm.Set("total_amount", priceFloat64ToString(price))
|
bm.Set("total_amount", priceFloat64ToString(r.Price))
|
||||||
|
|
||||||
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return payUrl, paymentName, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: payUrl,
|
||||||
|
OrderId: r.PaymentName,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@ -21,8 +21,10 @@ func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *DummyPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *DummyPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
return returnUrl, "", nil
|
return &PayResp{
|
||||||
|
PayUrl: r.ReturnUrl,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *DummyPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *DummyPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
36
pp/gc.go
36
pp/gc.go
@ -153,22 +153,22 @@ func (pp *GcPaymentProvider) doPost(postBytes []byte) ([]byte, error) {
|
|||||||
return respBytes, nil
|
return respBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *GcPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
payReqInfo := GcPayReqInfo{
|
payReqInfo := GcPayReqInfo{
|
||||||
OrderDate: util.GenerateSimpleTimeId(),
|
OrderDate: util.GenerateSimpleTimeId(),
|
||||||
OrderNo: paymentName,
|
OrderNo: r.PaymentName,
|
||||||
Amount: getPriceString(price),
|
Amount: getPriceString(r.Price),
|
||||||
Xmpch: pp.Xmpch,
|
Xmpch: pp.Xmpch,
|
||||||
Body: productDisplayName,
|
Body: r.ProductDisplayName,
|
||||||
ReturnUrl: returnUrl,
|
ReturnUrl: r.ReturnUrl,
|
||||||
NotifyUrl: notifyUrl,
|
NotifyUrl: r.NotifyUrl,
|
||||||
Remark1: payerName,
|
Remark1: r.PayerName,
|
||||||
Remark2: productName,
|
Remark2: r.ProductName,
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(payReqInfo)
|
b, err := json.Marshal(payReqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
body := GcRequestBody{
|
body := GcRequestBody{
|
||||||
@ -184,36 +184,38 @@ func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerN
|
|||||||
|
|
||||||
bodyBytes, err := json.Marshal(body)
|
bodyBytes, err := json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
respBytes, err := pp.doPost(bodyBytes)
|
respBytes, err := pp.doPost(bodyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var respBody GcResponseBody
|
var respBody GcResponseBody
|
||||||
err = json.Unmarshal(respBytes, &respBody)
|
err = json.Unmarshal(respBytes, &respBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if respBody.ReturnCode != "SUCCESS" {
|
if respBody.ReturnCode != "SUCCESS" {
|
||||||
return "", "", fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
return nil, fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
payRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
payRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var payRespInfo GcPayRespInfo
|
var payRespInfo GcPayRespInfo
|
||||||
err = json.Unmarshal(payRespInfoBytes, &payRespInfo)
|
err = json.Unmarshal(payRespInfoBytes, &payRespInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
payResp := &PayResp{
|
||||||
return payRespInfo.PayUrl, "", nil
|
PayUrl: payRespInfo.PayUrl,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *GcPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
22
pp/paypal.go
22
pp/paypal.go
@ -49,16 +49,16 @@ func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentPro
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *PaypalPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md
|
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md
|
||||||
units := make([]*paypal.PurchaseUnit, 0, 1)
|
units := make([]*paypal.PurchaseUnit, 0, 1)
|
||||||
unit := &paypal.PurchaseUnit{
|
unit := &paypal.PurchaseUnit{
|
||||||
ReferenceId: util.GetRandomString(16),
|
ReferenceId: util.GetRandomString(16),
|
||||||
Amount: &paypal.Amount{
|
Amount: &paypal.Amount{
|
||||||
CurrencyCode: currency, // e.g."USD"
|
CurrencyCode: r.Currency, // e.g."USD"
|
||||||
Value: priceFloat64ToString(price), // e.g."100.00"
|
Value: priceFloat64ToString(r.Price), // e.g."100.00"
|
||||||
},
|
},
|
||||||
Description: joinAttachString([]string{productDisplayName, productName, providerName}),
|
Description: joinAttachString([]string{r.ProductDisplayName, r.ProductName, r.ProviderName}),
|
||||||
}
|
}
|
||||||
units = append(units, unit)
|
units = append(units, unit)
|
||||||
|
|
||||||
@ -68,23 +68,27 @@ func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
||||||
b.Set("brand_name", "Casdoor")
|
b.Set("brand_name", "Casdoor")
|
||||||
b.Set("locale", "en-PT")
|
b.Set("locale", "en-PT")
|
||||||
b.Set("return_url", returnUrl)
|
b.Set("return_url", r.ReturnUrl)
|
||||||
b.Set("cancel_url", returnUrl)
|
b.Set("cancel_url", r.ReturnUrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if ppRsp.Code != paypal.Success {
|
if ppRsp.Code != paypal.Success {
|
||||||
return "", "", errors.New(ppRsp.Error)
|
return nil, errors.New(ppRsp.Error)
|
||||||
}
|
}
|
||||||
// {"id":"9BR68863NE220374S","status":"CREATED",
|
// {"id":"9BR68863NE220374S","status":"CREATED",
|
||||||
// "links":[{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"self","method":"GET"},
|
// "links":[{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"self","method":"GET"},
|
||||||
// {"href":"https://www.sandbox.paypal.com/checkoutnow?token=9BR68863NE220374S","rel":"approve","method":"GET"},
|
// {"href":"https://www.sandbox.paypal.com/checkoutnow?token=9BR68863NE220374S","rel":"approve","method":"GET"},
|
||||||
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"update","method":"PATCH"},
|
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"update","method":"PATCH"},
|
||||||
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S/capture","rel":"capture","method":"POST"}]}
|
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S/capture","rel":"capture","method":"POST"}]}
|
||||||
return ppRsp.Response.Links[1].Href, ppRsp.Response.Id, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: ppRsp.Response.Links[1].Href,
|
||||||
|
OrderId: ppRsp.Response.Id,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *PaypalPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@ -24,6 +24,32 @@ const (
|
|||||||
PaymentStateError PaymentState = "Error"
|
PaymentStateError PaymentState = "Error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PaymentEnvWechatBrowser = "WechatBrowser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PayReq struct {
|
||||||
|
ProviderName string
|
||||||
|
ProductName string
|
||||||
|
PayerName string
|
||||||
|
PayerId string
|
||||||
|
PaymentName string
|
||||||
|
ProductDisplayName string
|
||||||
|
Price float64
|
||||||
|
Currency string
|
||||||
|
|
||||||
|
ReturnUrl string
|
||||||
|
NotifyUrl string
|
||||||
|
|
||||||
|
PaymentEnv string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayResp struct {
|
||||||
|
PayUrl string
|
||||||
|
OrderId string
|
||||||
|
AttachInfo map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type NotifyResult struct {
|
type NotifyResult struct {
|
||||||
PaymentName string
|
PaymentName string
|
||||||
PaymentStatus PaymentState
|
PaymentStatus PaymentState
|
||||||
@ -39,7 +65,7 @@ type NotifyResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PaymentProvider interface {
|
type PaymentProvider interface {
|
||||||
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error)
|
Pay(req *PayReq) (*PayResp, error)
|
||||||
Notify(body []byte, orderId string) (*NotifyResult, error)
|
Notify(body []byte, orderId string) (*NotifyResult, error)
|
||||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||||
GetResponseError(err error) string
|
GetResponseError(err error) string
|
||||||
|
32
pp/stripe.go
32
pp/stripe.go
@ -46,30 +46,30 @@ func NewStripePaymentProvider(PublishableKey, SecretKey string) (*StripePaymentP
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *StripePaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (payUrl string, orderId string, err error) {
|
func (pp *StripePaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// Create a temp product
|
// Create a temp product
|
||||||
description := joinAttachString([]string{productName, productDisplayName, providerName})
|
description := joinAttachString([]string{r.ProductName, r.ProductDisplayName, r.ProviderName})
|
||||||
productParams := &stripe.ProductParams{
|
productParams := &stripe.ProductParams{
|
||||||
Name: stripe.String(productDisplayName),
|
Name: stripe.String(r.ProductDisplayName),
|
||||||
Description: stripe.String(description),
|
Description: stripe.String(description),
|
||||||
DefaultPriceData: &stripe.ProductDefaultPriceDataParams{
|
DefaultPriceData: &stripe.ProductDefaultPriceDataParams{
|
||||||
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
|
UnitAmount: stripe.Int64(priceFloat64ToInt64(r.Price)),
|
||||||
Currency: stripe.String(currency),
|
Currency: stripe.String(r.Currency),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
sProduct, err := stripeProduct.New(productParams)
|
sProduct, err := stripeProduct.New(productParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create a price for an existing product
|
// Create a price for an existing product
|
||||||
priceParams := &stripe.PriceParams{
|
priceParams := &stripe.PriceParams{
|
||||||
Currency: stripe.String(currency),
|
Currency: stripe.String(r.Currency),
|
||||||
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
|
UnitAmount: stripe.Int64(priceFloat64ToInt64(r.Price)),
|
||||||
Product: stripe.String(sProduct.ID),
|
Product: stripe.String(sProduct.ID),
|
||||||
}
|
}
|
||||||
sPrice, err := stripePrice.New(priceParams)
|
sPrice, err := stripePrice.New(priceParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create a Checkout Session
|
// Create a Checkout Session
|
||||||
checkoutParams := &stripe.CheckoutSessionParams{
|
checkoutParams := &stripe.CheckoutSessionParams{
|
||||||
@ -80,17 +80,21 @@ func (pp *StripePaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
|
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
|
||||||
SuccessURL: stripe.String(returnUrl),
|
SuccessURL: stripe.String(r.ReturnUrl),
|
||||||
CancelURL: stripe.String(returnUrl),
|
CancelURL: stripe.String(r.ReturnUrl),
|
||||||
ClientReferenceID: stripe.String(paymentName),
|
ClientReferenceID: stripe.String(r.PaymentName),
|
||||||
ExpiresAt: stripe.Int64(time.Now().Add(30 * time.Minute).Unix()),
|
ExpiresAt: stripe.Int64(time.Now().Add(30 * time.Minute).Unix()),
|
||||||
}
|
}
|
||||||
checkoutParams.AddMetadata("product_description", description)
|
checkoutParams.AddMetadata("product_description", description)
|
||||||
sCheckout, err := stripeCheckout.New(checkoutParams)
|
sCheckout, err := stripeCheckout.New(checkoutParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return sCheckout.URL, sCheckout.ID, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: sCheckout.URL,
|
||||||
|
OrderId: sCheckout.ID,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *StripePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *StripePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@ -63,27 +63,66 @@ func NewWechatPaymentProvider(mchId string, apiV3Key string, appId string, seria
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *WechatPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
bm.Set("attach", joinAttachString([]string{productDisplayName, productName, providerName}))
|
desc := joinAttachString([]string{r.ProductDisplayName, r.ProductName, r.ProviderName})
|
||||||
|
bm.Set("attach", desc)
|
||||||
bm.Set("appid", pp.AppId)
|
bm.Set("appid", pp.AppId)
|
||||||
bm.Set("description", productDisplayName)
|
bm.Set("description", r.ProductDisplayName)
|
||||||
bm.Set("notify_url", notifyUrl)
|
bm.Set("notify_url", r.NotifyUrl)
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", r.PaymentName)
|
||||||
bm.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
bm.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
||||||
bm.Set("total", priceFloat64ToInt64(price))
|
bm.Set("total", priceFloat64ToInt64(r.Price))
|
||||||
bm.Set("currency", currency)
|
bm.Set("currency", r.Currency)
|
||||||
})
|
})
|
||||||
|
// In Wechat browser, we use JSAPI
|
||||||
|
if r.PaymentEnv == PaymentEnvWechatBrowser {
|
||||||
|
if r.PayerId == "" {
|
||||||
|
return nil, errors.New("failed to get the payer's openid, please retry login")
|
||||||
|
}
|
||||||
|
bm.SetBodyMap("payer", func(bm gopay.BodyMap) {
|
||||||
|
bm.Set("openid", r.PayerId) // If the account is signup via Wechat, the PayerId is the Wechat OpenId e.g.oxW9O1ZDvgreSHuBSQDiQ2F055PI
|
||||||
|
})
|
||||||
|
jsapiRsp, err := pp.Client.V3TransactionJsapi(context.Background(), bm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if jsapiRsp.Code != wechat.Success {
|
||||||
|
return nil, errors.New(jsapiRsp.Error)
|
||||||
|
}
|
||||||
|
// use RSA256 to sign the pay request
|
||||||
|
params, err := pp.Client.PaySignOfJSAPI(pp.AppId, jsapiRsp.Response.PrepayId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payResp := &PayResp{
|
||||||
|
PayUrl: "",
|
||||||
|
OrderId: r.PaymentName, // Wechat can use paymentName as the OutTradeNo to query order status
|
||||||
|
AttachInfo: map[string]interface{}{
|
||||||
|
"appId": params.AppId,
|
||||||
|
"timeStamp": params.TimeStamp,
|
||||||
|
"nonceStr": params.NonceStr,
|
||||||
|
"package": params.Package,
|
||||||
|
"signType": "RSA",
|
||||||
|
"paySign": params.PaySign,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
|
} else {
|
||||||
|
// In other case, we use NativeAPI
|
||||||
nativeRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
nativeRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if nativeRsp.Code != wechat.Success {
|
if nativeRsp.Code != wechat.Success {
|
||||||
return "", "", errors.New(nativeRsp.Error)
|
return nil, errors.New(nativeRsp.Error)
|
||||||
|
}
|
||||||
|
payResp := &PayResp{
|
||||||
|
PayUrl: nativeRsp.Response.CodeUrl,
|
||||||
|
OrderId: r.PaymentName, // Wechat can use paymentName as the OutTradeNo to query order status
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nativeRsp.Response.CodeUrl, paymentName, nil // Wechat can use paymentName as the OutTradeNo to query order status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *WechatPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@ -101,7 +101,7 @@ class PaymentResultPage extends React.Component {
|
|||||||
payment: payment,
|
payment: payment,
|
||||||
});
|
});
|
||||||
if (payment.state === "Created") {
|
if (payment.state === "Created") {
|
||||||
if (["PayPal", "Stripe", "Alipay"].includes(payment.type)) {
|
if (["PayPal", "Stripe", "Alipay", "WeChat Pay"].includes(payment.type)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
timeout: setTimeout(async() => {
|
timeout: setTimeout(async() => {
|
||||||
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
||||||
|
@ -31,6 +31,7 @@ class ProductBuyPage extends React.Component {
|
|||||||
pricingName: props?.pricingName ?? props?.match?.params?.pricingName ?? null,
|
pricingName: props?.pricingName ?? props?.match?.params?.pricingName ?? null,
|
||||||
planName: params.get("plan"),
|
planName: params.get("plan"),
|
||||||
userName: params.get("user"),
|
userName: params.get("user"),
|
||||||
|
paymentEnv: "",
|
||||||
product: null,
|
product: null,
|
||||||
pricing: props?.pricing ?? null,
|
pricing: props?.pricing ?? null,
|
||||||
plan: null,
|
plan: null,
|
||||||
@ -38,8 +39,21 @@ class ProductBuyPage extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPaymentEnv() {
|
||||||
|
let env = "";
|
||||||
|
const ua = navigator.userAgent.toLocaleLowerCase();
|
||||||
|
// Only support Wechat Pay in Wechat Browser for mobile devices
|
||||||
|
if (ua.indexOf("micromessenger") !== -1 && ua.indexOf("mobile") !== -1) {
|
||||||
|
env = "WechatBrowser";
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
paymentEnv: env,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
this.getProduct();
|
this.getProduct();
|
||||||
|
this.getPaymentEnv();
|
||||||
}
|
}
|
||||||
|
|
||||||
setStateAsync(state) {
|
setStateAsync(state) {
|
||||||
@ -127,23 +141,74 @@ class ProductBuyPage extends React.Component {
|
|||||||
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
|
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call Weechat Pay via jsapi
|
||||||
|
onBridgeReady(attachInfo) {
|
||||||
|
const {WeixinJSBridge} = window;
|
||||||
|
// Setting.showMessage("success", "attachInfo is " + JSON.stringify(attachInfo));
|
||||||
|
this.setState({
|
||||||
|
isPlacingOrder: false,
|
||||||
|
});
|
||||||
|
WeixinJSBridge.invoke(
|
||||||
|
"getBrandWCPayRequest", {
|
||||||
|
"appId": attachInfo.appId,
|
||||||
|
"timeStamp": attachInfo.timeStamp,
|
||||||
|
"nonceStr": attachInfo.nonceStr,
|
||||||
|
"package": attachInfo.package,
|
||||||
|
"signType": attachInfo.signType,
|
||||||
|
"paySign": attachInfo.paySign,
|
||||||
|
},
|
||||||
|
function(res) {
|
||||||
|
if (res.err_msg === "get_brand_wcpay_request:ok") {
|
||||||
|
Setting.goToLink(attachInfo.payment.successUrl);
|
||||||
|
return ;
|
||||||
|
} else {
|
||||||
|
if (res.err_msg === "get_brand_wcpay_request:cancel") {
|
||||||
|
Setting.showMessage("error", i18next.t("product:Payment cancelled"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", i18next.t("product:Payment failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Wechat browser, call this function to pay via jsapi
|
||||||
|
callWechatPay(attachInfo) {
|
||||||
|
const {WeixinJSBridge} = window;
|
||||||
|
if (typeof WeixinJSBridge === "undefined") {
|
||||||
|
if (document.addEventListener) {
|
||||||
|
document.addEventListener("WeixinJSBridgeReady", () => this.onBridgeReady(attachInfo), false);
|
||||||
|
} else if (document.attachEvent) {
|
||||||
|
document.attachEvent("WeixinJSBridgeReady", () => this.onBridgeReady(attachInfo));
|
||||||
|
document.attachEvent("onWeixinJSBridgeReady", () => this.onBridgeReady(attachInfo));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.onBridgeReady(attachInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buyProduct(product, provider) {
|
buyProduct(product, provider) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isPlacingOrder: true,
|
isPlacingOrder: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
ProductBackend.buyProduct(product.owner, product.name, provider.name, this.state.pricingName ?? "", this.state.planName ?? "", this.state.userName ?? "")
|
ProductBackend.buyProduct(product.owner, product.name, provider.name, this.state.pricingName ?? "", this.state.planName ?? "", this.state.userName ?? "", this.state.paymentEnv)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const payment = res.data;
|
const payment = res.data;
|
||||||
|
const attachInfo = res.data2;
|
||||||
let payUrl = payment.payUrl;
|
let payUrl = payment.payUrl;
|
||||||
if (provider.type === "WeChat Pay") {
|
if (provider.type === "WeChat Pay") {
|
||||||
|
if (this.state.paymentEnv === "WechatBrowser") {
|
||||||
|
attachInfo.payment = payment;
|
||||||
|
this.callWechatPay(attachInfo);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
payUrl = `/qrcode/${payment.owner}/${payment.name}?providerName=${provider.name}&payUrl=${encodeURI(payment.payUrl)}&successUrl=${encodeURI(payment.successUrl)}`;
|
payUrl = `/qrcode/${payment.owner}/${payment.name}?providerName=${provider.name}&payUrl=${encodeURI(payment.payUrl)}&successUrl=${encodeURI(payment.successUrl)}`;
|
||||||
}
|
}
|
||||||
Setting.goToLink(payUrl);
|
Setting.goToLink(payUrl);
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isPlacingOrder: false,
|
isPlacingOrder: false,
|
||||||
});
|
});
|
||||||
@ -218,7 +283,7 @@ class ProductBuyPage extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="login-content">
|
<div className="login-content">
|
||||||
<Spin spinning={this.state.isPlacingOrder} size="large" tip={i18next.t("product:Placing order...")} style={{paddingTop: "10%"}} >
|
<Spin spinning={this.state.isPlacingOrder} size="large" tip={i18next.t("product:Placing order...")} style={{paddingTop: "10%"}} >
|
||||||
<Descriptions title={<span style={{fontSize: 28}}>{i18next.t("product:Buy Product")}</span>} bordered>
|
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 20} : {fontSize: 28}}>{i18next.t("product:Buy Product")}</span>} bordered>
|
||||||
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
|
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
|
||||||
<span style={{fontSize: 25}}>
|
<span style={{fontSize: 25}}>
|
||||||
{Setting.getLanguageText(product?.displayName)}
|
{Setting.getLanguageText(product?.displayName)}
|
||||||
|
@ -70,8 +70,8 @@ export function deleteProduct(product) {
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buyProduct(owner, name, providerName, pricingName = "", planName = "", userName = "") {
|
export function buyProduct(owner, name, providerName, pricingName = "", planName = "", userName = "", paymentEnv = "") {
|
||||||
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerName}&pricingName=${pricingName}&planName=${planName}&userName=${userName}`, {
|
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerName}&pricingName=${pricingName}&planName=${planName}&userName=${userName}&paymentEnv=${paymentEnv}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user