영수증 검증쪽 null 값

문의를 남기실 경우 다음 항목을 작성해 주세요.
정보가 부족한 경우 확인 및 답변이 지연될 수 있습니다.

  • 뒤끝 SDK 버전 : 5.5
  • 프로젝트명 : 광산사냥꾼
  • 스테이터스 코드 : -
  • 에러 코드 : -
  • 에러 메시지 :
    BuyProductID: FAIL. Exception during purchase. System.NullReferenceException: Object reference not set to an instance of an object
    영수증 검증쪽에서 null 값이 뜨는데 확인되는 코드 부분은
    validation = Backend.Receipt.IsValidateGooglePurchase(args.purchasedProduct.receipt, “receiptDescriptionGoogle”); 이 부분에서 뜹니다.
    디버그로 확인 결과 Backend.Receipt 가 없는걸로 나오는 데 스토어 OnInitialized 도 패스했고 정상적으로 아이템 요소도 등록, 구매가능한 아이템도 뜨고 있습니다.(args.purchasedProduct.availableToPurchase)
    어떤 경우에 Null 값이 나오며 처리 방법은 무엇인가요?

안녕하세요 개발자님.

뒤끝 영수증 검증 함수에 사용되는 args.purchasedProduct.receipt이 결제에 실패하였거나 에러가 발생하여 null이 발생하는 것으로 확인이 됩니다.

만약 유니티 IAP를 사용하신다면, 아래 함수를 통해 결제중 실패가 발생하지 않았나, 그리고 구매 시 발생하는 함수에서 Debug.Log를 통해 args.purchasedProduct.receipt의 값이 정상적으로 출력되는지 확인해주시기 바랍니다.

    public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        Debug.LogError("IAP OnPurchaseFailed : " + product + " - " + failureReason);
    }
   //유니티 제공 함수
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
   {
        Debug.Log("영수증 검증 : " + args.purchasedProduct.receipt);
        Enqueue(Backend.Receipt.IsValidateGooglePurchase, args.purchasedProduct.receipt, "receiptDescription", false, callback =>
        {
            if (callback.IsSuccess())
            {
                Debug.Log("Receipt.IsValidateGooglePurchase : " + callback);
            }
            else
            {
                Debug.LogError("Receipt.IsValidateGooglePurchase : " + callback);
            }

            Debug.Log("영수증 검증(Google) 테스트 종료!!!!!");
        });
   }

안녕하세요.

함수로 확인 결과 영수증은 제대로 확인되는 것으로 나옵니다. 다만 추가로 확인된 에러가
BuyProductID: FAIL. Exception during purchase. System.ArgumentException: Value does not fall within the expected range.
부분인데 어떤 경우 생성되나요?

인자로 들어가는 데이터 형태가 올바르지 않은 에러로 추측됩니다.

좀 더 정확한 확인을 위해 영수증 검증 관련 스크립트와 에러 로그 전체 공유가 가능하시다면 공유해주심 감사하겠습니다.

코드입니다.
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
Debug.Log("아이템 구매 가능 : "+args.purchasedProduct.availableToPurchase); // 정상

    Debug.Log("영수증 검증 : " + args.purchasedProduct.receipt); // 정상
    Enqueue(Backend.Receipt.IsValidateGooglePurchase, args.purchasedProduct.receipt, "receiptDescription", false, callback => // 여기가 976 라인
    {
        if (callback.IsSuccess())
        {
            Debug.Log("Receipt.IsValidateGooglePurchase : " + callback);
        }
        else
        {
            Debug.LogError("Receipt.IsValidateGooglePurchase : " + callback);
        }

        Debug.Log("영수증 검증(Google) 테스트 종료!!!!!");
    });


    // 뒤끝 영수증 검증 처리    
    BackendReturnObject validation = null;

#if UNITY_ANDROID || UNITY_EDITOR
validation = Backend.Receipt.IsValidateGooglePurchase(args.purchasedProduct.receipt, “receiptDescriptionGoogle”);
#elif UNITY_IOS
validation = Backend.Receipt.IsValidateApplePurchase(args.purchasedProduct.receipt, “receiptDescriptionApple”);
#endif
string msg = “”;
Debug.Log(“영수증 검증 중”);

    // 영수증 검증에 성공한 경우
    if (validation.IsSuccess())
    {
        // 구매 성공한 제품에 대한 id 체크하여 그에맞는 보상 
        // A consumable product has been purchased by this user.
        if (String.Equals(args.purchasedProduct.definition.id, p_ID, StringComparison.Ordinal))
        {
            msg = string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id);            
            IAPManager.Instance.Purchase_Successed(true, p_ID);
            Debug.Log(msg);              
        }
    }
    // 영수증 검증에 실패한 경우 
    else
    {
        // Or ... an unknown product has been purchased by this user. Fill in additional products here....
        msg = string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id);
        IAPManager.Instance.Purchase_Successed(false, p_ID);
        Debug.Log(msg);
        Debug.Log(validation);
    }

    // Return a flag indicating whether this product has completely been received, or if the application needs 
    // to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still 
    // saving purchased products to the cloud, and when that save is delayed.
    return PurchaseProcessingResult.Complete;
}

에러 내용입니다.
BuyProductID: FAIL. Exception during purchase. System.ArgumentException: Value does not fall within the expected range.
at BackEnd_Server.ProcessPurchase (UnityEngine.Purchasing.PurchaseEventArgs args) [0x0003f] in D:\UnityProject\MineHunter\Assets\02.Scripts\Manager\BackEnd_Server.cs:976
at UnityEngine.Purchasing.StoreListenerProxy.ProcessPurchase (UnityEngine.Purchasing.PurchaseEventArgs e) [0x00013] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Purchasing\StoreListenerProxy.cs:34
at UnityEngine.Purchasing.PurchasingManager.ProcessPurchaseIfNew (UnityEngine.Purchasing.Product product) [0x00064] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Purchasing\PurchasingManager.cs:264
at UnityEngine.Purchasing.PurchasingManager.OnPurchaseSucceeded (System.String id, System.String receipt, System.String transactionId) [0x00036] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Purchasing\PurchasingManager.cs:111
at UnityEngine.Purchasing.JSONStore.OnPurchaseSucceeded (System.String id, System.String receipt, System.String transactionID) [0x0001a] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Stores\BaseStore\JSONStore.cs:379
at UnityEngine.Purchasing.FakeStore.<>n__0 (System.String id, System.String receipt, System.String transactionID) <0x1e40403d030 + 0x0006a> in <7c5ea98336f04550a9c33c16418ca23e>:0
at UnityEngine.Purchasing.FakeStore+<>c__DisplayClass15_0.b__0 (System.Boolean allow, UnityEngine.Purchasing.PurchaseFailureReason failureReason) [0x00007] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Stores\FakeStore\FakeStore.cs:147
at UnityEngine.Purchasing.FakeStore.FakePurchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x00068] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Stores\FakeStore\FakeStore.cs:166
at UnityEngine.Purchasing.FakeStore.Purchase (System.String productJSON, System.String developerPayload) [0x0008c] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Stores\FakeStore\FakeStore.cs:132
at UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x000f2] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Stores\BaseStore\JSONStore.cs:348
at UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product, System.String developerPayload) [0x00040] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Purchasing\PurchasingManager.cs:60
at UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product) [0x00001] in D:\UnityProject\MineHunter\Library\PackageCache\com.unity.purchasing@3.2.3\Runtime\Purchasing\PurchasingManager.cs:38
at BackEnd_Server.BuyProductID (System.String _productId) [0x0007e] in D:\UnityProject\MineHunter\Assets\02.Scripts\Manager\BackEnd_Server.cs:889
UnityEngine.Debug:Log (object)
BackEnd_Server:BuyProductID (string) (at Assets/02.Scripts/Manager/BackEnd_Server.cs:905)
IAPManager:OnBtnPurchaseClicked (string) (at Assets/02.Scripts/Data/IAPManager.cs:53)
NormalStoreItem:Buy_Confirmation () (at Assets/02.Scripts/NormalStoreItem.cs:137)
NormalStoreItem:BuyClick () (at Assets/02.Scripts/NormalStoreItem.cs:123)
UnityEngine.EventSystems.EventSystem:Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:377)

혹시 모바일 기기가 아닌 유니티 에디터등으로 테스트를 진행하시나요?

뒤끝 영수증 검증은 실제 구매, 모바일 기기에서의 테스트 결제를 통해서만 정상적으로 작동이 되며, 에디터 상에서의 테스트는 실제 인앱과 리턴되는 값이 달라 파싱등의 오류가 발생할 수 있습니다.

모바일 기기와 유니티 에디터 두 부분에서 테스트하는 데 에러 메시지가 동일하게 나옵니다.

기존에 사용하시던 코드에는 딱히 에러가 날만한 부분은 없어 보입니다.

정확한 진행을 위해 동기함수로 해당 사항 진행해주시면 감사하겠습니다.

  1. 모바일 환경인지
  2. 로그인 함수가 호출되었고 성공하였는지,
  3. 다른 뒤끝 함수는 잘 불러와지는지(Backend.BMember.GetUserInfo() 등 호출하여 200 성공이 발생하는지),
  4. 에러를 출력하는 Debug.Log("BuyProductID: FAIL. Exception during purchase. ") 해당 코드 공유

번거로우시겠지만 위와 같은 사항 확인해주시면 감사하겠습니다.

(아래 코드는 개발자 님의 기존 코드를 약간 변경한 코드입니다)

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
Debug.Log(args.purchasedProduct.receipt);
Debug.Log(“영수증 검증 중”);
    // 뒤끝 영수증 검증 처리    
    BackendReturnObject validation = null;
#if UNITY_ANDROID
validation = Backend.Receipt.IsValidateGooglePurchase(args.purchasedProduct.receipt, “receiptDescriptionGoogle”);
#elif UNITY_IOS
validation = Backend.Receipt.IsValidateApplePurchase(args.purchasedProduct.receipt, “receiptDescriptionApple”);
#endif
string msg = “”;

    // 영수증 검증에 성공한 경우
    if (validation.IsSuccess())
    {
        // 구매 성공한 제품에 대한 id 체크하여 그에맞는 보상 
        // A consumable product has been purchased by this user.
        if (String.Equals(args.purchasedProduct.definition.id, p_ID, StringComparison.Ordinal))
        {
            msg = string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id);            
            IAPManager.Instance.Purchase_Successed(true, p_ID);
            Debug.Log(msg);              
        }
    }
    // 영수증 검증에 실패한 경우 
    else
    {
        // Or ... an unknown product has been purchased by this user. Fill in additional products here....
        msg = string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id);
        IAPManager.Instance.Purchase_Successed(false, p_ID);
        Debug.Log(msg);
        Debug.Log(validation);
    }

    // Return a flag indicating whether this product has completely been received, or if the application needs 
    // to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still 
    // saving purchased products to the cloud, and when that save is delayed.
    return PurchaseProcessingResult.Complete;
}