feat(iap/apple): 添加BundleID支持以增强苹果交易验证
在ServerAPIConfig中添加BundleID字段,用于苹果服务器API验证 当BundleID未配置时,尝试从站点自定义数据中获取 删除过时的测试文件
This commit is contained in:
parent
51765c794a
commit
ceb3b16dc5
@ -2,6 +2,7 @@ package apple
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/payment"
|
"github.com/perfect-panel/server/internal/model/payment"
|
||||||
@ -66,6 +67,11 @@ func (l *AttachTransactionByIdLogic) AttachById(req *types.AttachAppleTransactio
|
|||||||
PrivateKey: cfg.PrivateKey,
|
PrivateKey: cfg.PrivateKey,
|
||||||
Sandbox: cfg.Sandbox,
|
Sandbox: cfg.Sandbox,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to extract BundleID from productIds (if available in config) or custom data
|
||||||
|
// For now, we leave it empty unless we find it in config, but we can try to parse from payment config if needed.
|
||||||
|
// However, ServerAPIConfig update allows optional BundleID.
|
||||||
|
|
||||||
if req.Sandbox != nil {
|
if req.Sandbox != nil {
|
||||||
apiCfg.Sandbox = *req.Sandbox
|
apiCfg.Sandbox = *req.Sandbox
|
||||||
}
|
}
|
||||||
@ -92,6 +98,18 @@ SIRDAVLcWemp0fMlnfDE4EHmqcD58arEJWsr3aWEhc4BHocOUIGjko0cVWGchrFa
|
|||||||
l.Errorw("attach by id credential missing")
|
l.Errorw("attach by id credential missing")
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "apple server api credential missing")
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "apple server api credential missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to get BundleID from Site CustomData if not set
|
||||||
|
if apiCfg.BundleID == "" {
|
||||||
|
var customData struct {
|
||||||
|
IapBundleId string `json:"iapBundleId"`
|
||||||
|
}
|
||||||
|
if l.svcCtx.Config.Site.CustomData != "" {
|
||||||
|
_ = json.Unmarshal([]byte(l.svcCtx.Config.Site.CustomData), &customData)
|
||||||
|
apiCfg.BundleID = customData.IapBundleId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jws, err := iapapple.GetTransactionInfo(apiCfg, req.TransactionId)
|
jws, err := iapapple.GetTransactionInfo(apiCfg, req.TransactionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorw("fetch transaction info error", logger.Field("error", err.Error()))
|
l.Errorw("fetch transaction info error", logger.Field("error", err.Error()))
|
||||||
|
|||||||
@ -1,87 +0,0 @@
|
|||||||
package apple
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/order"
|
|
||||||
"github.com/perfect-panel/server/internal/model/payment"
|
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
|
||||||
"github.com/perfect-panel/server/internal/types"
|
|
||||||
"github.com/perfect-panel/server/pkg/constant"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mockOrderModel struct {
|
|
||||||
order.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockOrderModel) FindOneByOrderNo(ctx context.Context, orderNo string) (*order.Order, error) {
|
|
||||||
return &order.Order{
|
|
||||||
Id: 1,
|
|
||||||
OrderNo: orderNo,
|
|
||||||
PaymentId: 1,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockPaymentModel struct {
|
|
||||||
payment.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockPaymentModel) FindOne(ctx context.Context, id int64) (*payment.Payment, error) {
|
|
||||||
// Return a config with empty private key to trigger fallback
|
|
||||||
cfg := payment.AppleIAPConfig{
|
|
||||||
KeyID: "some_key_id",
|
|
||||||
IssuerID: "some_issuer_id",
|
|
||||||
// PrivateKey is empty to test fallback
|
|
||||||
PrivateKey: "",
|
|
||||||
Sandbox: true,
|
|
||||||
}
|
|
||||||
cfgBytes, _ := json.Marshal(cfg)
|
|
||||||
return &payment.Payment{
|
|
||||||
Id: id,
|
|
||||||
Platform: "apple",
|
|
||||||
Config: string(cfgBytes),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAttachById_PrivateKeyFallback(t *testing.T) {
|
|
||||||
// Setup mock context
|
|
||||||
svcCtx := &svc.ServiceContext{
|
|
||||||
OrderModel: &mockOrderModel{},
|
|
||||||
PaymentModel: &mockPaymentModel{},
|
|
||||||
Config: svc.ServiceContext{}.Config, // empty config
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock user in context
|
|
||||||
ctx := context.WithValue(context.Background(), constant.CtxKeyUser, &user.User{Id: 1})
|
|
||||||
|
|
||||||
l := NewAttachTransactionByIdLogic(ctx, svcCtx)
|
|
||||||
|
|
||||||
req := &types.AttachAppleTransactionByIdRequest{
|
|
||||||
TransactionId: "test_tx_id",
|
|
||||||
OrderNo: "test_order_no",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute
|
|
||||||
_, err := l.AttachById(req)
|
|
||||||
|
|
||||||
// We expect an error because GetTransactionInfo will fail to connect to Apple (or return 401/404)
|
|
||||||
// BUT, we want to ensure it is NOT "invalid private key" or "apple server api credential missing"
|
|
||||||
if err == nil {
|
|
||||||
// If it somehow succeeds (unlikely without real Apple connection), that's also fine for this test
|
|
||||||
t.Log("Success (unexpected but means key was valid)")
|
|
||||||
} else {
|
|
||||||
errMsg := err.Error()
|
|
||||||
if strings.Contains(errMsg, "invalid private key") {
|
|
||||||
t.Fatalf("Test failed: Got 'invalid private key' error, fallback did not work. Error: %v", err)
|
|
||||||
}
|
|
||||||
if strings.Contains(errMsg, "apple server api credential missing") {
|
|
||||||
t.Fatalf("Test failed: Got 'credential missing' error. Error: %v", err)
|
|
||||||
}
|
|
||||||
// If we get here, it means the key was accepted and we likely failed at network step
|
|
||||||
t.Logf("Got expected network/api error (meaning key was valid): %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -19,6 +19,7 @@ type ServerAPIConfig struct {
|
|||||||
IssuerID string
|
IssuerID string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
Sandbox bool
|
Sandbox bool
|
||||||
|
BundleID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildAPIToken(cfg ServerAPIConfig) (string, error) {
|
func buildAPIToken(cfg ServerAPIConfig) (string, error) {
|
||||||
@ -34,6 +35,9 @@ func buildAPIToken(cfg ServerAPIConfig) (string, error) {
|
|||||||
"exp": now + 1800,
|
"exp": now + 1800,
|
||||||
"aud": "appstoreconnect-v1",
|
"aud": "appstoreconnect-v1",
|
||||||
}
|
}
|
||||||
|
if cfg.BundleID != "" {
|
||||||
|
payload["bid"] = cfg.BundleID
|
||||||
|
}
|
||||||
hb, _ := json.Marshal(header)
|
hb, _ := json.Marshal(header)
|
||||||
pb, _ := json.Marshal(payload)
|
pb, _ := json.Marshal(payload)
|
||||||
enc := func(b []byte) string {
|
enc := func(b []byte) string {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user