iOS영수증 검수 문의2

고객님의 문의에 답변하는 직원은 고객 여러분의 가족 중 한 사람일 수 있습니다.
고객의 언어폭력(비하, 조롱, 욕설, 협박, 성희롱 등)으로부터 직원을 보호하기 위해
관련 법에 따라 수사기관에 필요한 조치를 요구할 수 있으며, 형법에 의해 처벌 대상이 될 수 있습니다.

커뮤니티 이용 정책에 위배되는 게시물을 작성할 경우, 별도 안내 없이 게시물 삭제 또는 커뮤니티 이용이 제한될 수 있습니다.

문의 응대 : 평일 오전 10시 ~ 오후 6시
문의를 남기실 경우 다음 항목을 작성해 주세요.
정보가 부족하거나 응대시간 외 문의하는 경우 확인 및 답변이 지연될 수 있습니다.

  • 베이스/채팅/데이터베이스/월드 SDK 버전 :
  • 프로젝트명 :
  • 스테이터스 코드 :
  • 에러 코드 :
  • 에러 메시지 :

뒤끝 개발팀을 통한 별도의 기술지원이 작업이 필요한 요청은 help@backnd.com 으로 문의해주시기 바랍니다.

#else
    //뒤끝 영수처리O
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        var product = args.purchasedProduct;
        string productId = product.definition.id;

        Debug.Log($"[IAP] 구매 진행: {productId}");
        StopPurchaseTimeout();

        if (!product.hasReceipt || string.IsNullOrEmpty(product.receipt))
        {
            Debug.LogWarning($"[IAP] receipt 없음: {productId}");
            UIManager.Instance.HideLoading();
            return PurchaseProcessingResult.Complete;
        }

        // 여기서 뒤끝 영수증 검증 시작 (비동기)
        ValidateReceiptAndGrant(product);

        //  검증이 끝난 뒤 ConfirmPendingPurchase를 호출해야 하므로 Pending
        return PurchaseProcessingResult.Pending;
    }

    private void ValidateReceiptAndGrant(Product product)
    {
        string productId = product.definition.id;
        string receiptJson = product.receipt;

        decimal iapPrice = product.metadata.localizedPrice;
        string iapCurrency = product.metadata.isoCurrencyCode;


#if UNITY_ANDROID
        SendQueue.Enqueue(
            Backend.Receipt.IsValidateGooglePurchase,
            receiptJson,
            "IAP Verify",
            false,
            iapPrice,
            iapCurrency,
            callback => OnReceiptValidated(callback, product)
        );

#elif UNITY_IOS
        SendQueue.Enqueue(
           Backend.Receipt.IsValidateApplePurchase,
           receiptJson,
           "IAP Verify",
           iapPrice,
           iapCurrency,
           callback => OnReceiptValidated(callback, product)
       );

#else
    // 에디터 등: 필요하면 바로 성공 처리
    OnReceiptValidated(null, product, forceSuccess:true);
#endif
    }
#endif

private void OnReceiptValidated(BackendReturnObject callback, Product product, bool forceSuccess = false)
    {
        string productId = product.definition.id;
        string transactionId = product.transactionID ?? "";

        bool ok = forceSuccess || (callback != null && callback.IsSuccess());
        Debug.Log(callback);
        if (!ok)
        {
            Debug.LogWarning($"[IAP] 영수증 검증 실패: {productId} / {callback?.GetMessage()}");
            UIManager.Instance.HideLoading();
            UIManager.Instance.AlertBoxInstant("결제 검증에 실패했습니다.", true);
            return; // 실패면 보상 지급 X, ConfirmPendingPurchase도 X
        }

        // 여기부터는 '검증 성공' 케이스만
        decimal iapPrice = product.metadata.localizedPrice;
        string iapCurrency = product.metadata.isoCurrencyCode;

        Debug.Log($"[IAP] 영수증 검증 성공: {productId}");

        Param logParam = new Param
        {
            { "productId", productId },
            { "price", iapPrice.ToString() },
            { "currency", iapCurrency },
            { "transactionId", transactionId },
            { "utc", DateTime.UtcNow.ToString("o") }
        };

        Backend.GameLog.InsertLogV2("IAP_Purchase", logParam, (logCb) =>
        {
            if (!logCb.IsSuccess())
                Debug.LogWarning("[IAP] 구매로그 저장 실패: " + logCb.GetMessage());
        });
       
        // ---- 기존 보상 지급 로직을 "그대로" 여기로 이동 ----
        if (productId == ProductADRemoval || productId == productADRemova_iOS)
        {
            if (!myChar.IsAdRemoved)
            {
                myChar.IsAdRemoved = true;
                RewardManager.Instance.PurchaseReward(productId);
            }
            else
            {
                Debug.Log($"[{productId}] 이미 광고 제거 처리됨. 중복 지급 안 함.");
            }
        }
        else if (productId == ProductAutoOverclockPackage || productId == ProductAutoOverclockPackage_iOS )
        {
            if (!myChar.AutoOverClock)
            {
                myChar.AutoOverClock = true;
                RewardManager.Instance.PurchaseReward(productId);
            }
            else
            {
                Debug.Log($"[{productId}] 이미 오버쿨럭 적용됨. 중복 지급 안 함.");
            }
        }
        else
        {
            RewardManager.Instance.PurchaseReward(productId);
        }

        // Pending 구매 확정 (중요)
        storeController.ConfirmPendingPurchase(product);

        UIManager.Instance.HideLoading();

#if UNITY_ANDROID
        firebaseManager.ShopProductPurchase(productId, (double)iapPrice, iapCurrency, transactionId);
        if (productId == "character_22000" || productId == "character_17000")
        {
            var PurchaseName = CostumeManager.Instance.CostumeDataSO.CostumeList[myChar.SelectPurchaseCostumeNum].costumeType.ToString();
            firebaseManager.CostumePurchase(PurchaseName);
        }
#elif UNITY_IOS
        firebaseManager.ShopProductPurchase(productId, (double)iapPrice, iapCurrency, transactionId);
         if(productId == "character_22000" || productId == "character_17000")
        {
            var PurchaseName = CostumeManager.Instance.CostumeDataSO.CostumeList[myChar.SelectPurchaseCostumeNum].costumeType.ToString();
            firebaseManager.CostumePurchase(PurchaseName);
        }
#endif
    }

3월까지는 위코드로 영수증검증을 잘 받고있었는데 최근에 iOS에서 영수증검증을 실행할때 400에러가 뜨면서 영수증검증에 실패하는데 이유를 모르겠습니다.

#elif UNITY_IOS
        SendQueue.Enqueue(
           Backend.Receipt.IsValidateApplePurchase,
           receiptJson,
           "IAP Verify",
           iapPrice,
           iapCurrency,
           callback => OnReceiptValidated(callback, product)
       );

이부분이 수정되야할까요?

안녕하세요 개발자님,
콘솔 내 Apple In-app Purchase Key ID 정보가 잘못 입력된 것으로 확인됩니다.
정보를 다시 확인하신 후 등록해주시기 바랍니다.

찾은 것 같습니다 감사합니다.

좋아요 1