diff --git a/internal/logic/public/iap/apple/attachTransactionByIdLogic.go b/internal/logic/public/iap/apple/attachTransactionByIdLogic.go index c995c8c..16b4192 100644 --- a/internal/logic/public/iap/apple/attachTransactionByIdLogic.go +++ b/internal/logic/public/iap/apple/attachTransactionByIdLogic.go @@ -2,6 +2,7 @@ package apple import ( "context" + "encoding/json" "strings" "github.com/perfect-panel/server/internal/model/payment" @@ -66,6 +67,11 @@ func (l *AttachTransactionByIdLogic) AttachById(req *types.AttachAppleTransactio PrivateKey: cfg.PrivateKey, 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 { apiCfg.Sandbox = *req.Sandbox } @@ -92,6 +98,18 @@ SIRDAVLcWemp0fMlnfDE4EHmqcD58arEJWsr3aWEhc4BHocOUIGjko0cVWGchrFa l.Errorw("attach by id 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) if err != nil { l.Errorw("fetch transaction info error", logger.Field("error", err.Error())) diff --git a/internal/logic/public/iap/apple/attachTransactionByIdLogic_test.go b/internal/logic/public/iap/apple/attachTransactionByIdLogic_test.go deleted file mode 100644 index 98d5cf3..0000000 --- a/internal/logic/public/iap/apple/attachTransactionByIdLogic_test.go +++ /dev/null @@ -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) - } -} diff --git a/pkg/iap/apple/serverapi.go b/pkg/iap/apple/serverapi.go index 2fa8672..c34badc 100644 --- a/pkg/iap/apple/serverapi.go +++ b/pkg/iap/apple/serverapi.go @@ -19,6 +19,7 @@ type ServerAPIConfig struct { IssuerID string PrivateKey string Sandbox bool + BundleID string } func buildAPIToken(cfg ServerAPIConfig) (string, error) { @@ -34,6 +35,9 @@ func buildAPIToken(cfg ServerAPIConfig) (string, error) { "exp": now + 1800, "aud": "appstoreconnect-v1", } + if cfg.BundleID != "" { + payload["bid"] = cfg.BundleID + } hb, _ := json.Marshal(header) pb, _ := json.Marshal(payload) enc := func(b []byte) string {