Title. Is this a bug or am I slowly going insane? How can the TransactionStatus ever be set to succeeded if the user never actually did the payment in PayPal?
My code:
/// <summary> /// Step 3: Start the purchase request in PlayFab. Triggered by the player press ing the buy button. /// </summary> public void StartPurchase() { ClientController.clientController.ToggleCanvasGroupSmooth(content, false); ClientController.clientController.ToggleCanvasGroupSmooth(contentIsPurchasing, true); ClientController.clientController.ToggleCanvasGroupSmooth(contentIsPurchased, false); var startPurchaseRequest = new StartPurchaseRequest() { Items = new List<ItemPurchaseRequest>() { new ItemPurchaseRequest() { ItemId = selectedChestItemId, Quantity = 1, Annotation = "Purchased via in-game store" } } }; PlayFabClientAPI.StartPurchase(startPurchaseRequest, OnStartPurchaseSuccess, StratUtilityMethods.LogPlayFabError); } /// <summary> /// Step 4: The PlayFab purchase request was successfully created. /// A PayForPurchaseRequest is created and on success this class goes to OnPayForPurchaseSuccess() /// </summary> /// <param name="startPurchaseResult"></param> void OnStartPurchaseSuccess(StartPurchaseResult startPurchaseResult) { Debug.Log($"{name}: OnStartPurchaseSuccess(): orderId: {startPurchaseResult.OrderId}"); var payForPurchaseRequest = new PayForPurchaseRequest() { OrderId = startPurchaseResult.OrderId, ProviderName = "PayPal", Currency = "RM" }; PlayFabClientAPI.PayForPurchase(payForPurchaseRequest, OnPayForPurchaseSuccess, StratUtilityMethods.LogPlayFabError); } /// <summary> /// Step 5: The PlayFab pay for purchase request was sucessfully created. Does NOT mean the payment/purchase was successful, just that the payment process started. /// The user's browser is opened with their PayPal URL so they can actually do the payment. /// </summary> /// <param name="payForPurchaseResult"></param> void OnPayForPurchaseSuccess(PayForPurchaseResult payForPurchaseResult) { Debug.Log($"{name}: OnPayForPurchaseSuccess(): orderId: {payForPurchaseResult.OrderId}"); string payPalConfirmUrl = payForPurchaseResult.PurchaseConfirmationPageURL; Application.OpenURL(payPalConfirmUrl); StartCoroutine(TryConfirmPurchasePayment(payForPurchaseResult.OrderId)); } /// <summary> /// Step 6: (repeats): This method tries to confirm the payment for the purchase. /// Sends out PlayFab ConfirmPurchaseRequests repeatedly, and when those requests successfully go out the OnConfirmPurchaseSuccess() method runs. /// OnConfirmPurchaseSuccess() does a PlayFab GetPurchaseRequest which returns a transaction state, and that state is used for validation (validating if the payment completed successfully). /// Pending purchases get cancelled after 5 minutes of no payment or if the player hits the cancel button. /// </summary> /// <param name="orderId"></param> /// <returns></returns> IEnumerator TryConfirmPurchasePayment(string orderId) { for (int i = 0; i < 300; i++) { if (isPurchaseSuccessfull) yield break; Debug.Log($"{name}: TryConfirmPurchasePayment();"); var confirmPurchaseRequest = new ConfirmPurchaseRequest() { OrderId = orderId }; PlayFabClientAPI.ConfirmPurchase(confirmPurchaseRequest, OnConfirmPurchaseSuccess, StratUtilityMethods.LogPlayFabError); yield return new WaitForSeconds(1f); } // If this code is reached, it means the purchase failed after waiting for 5 minutes. CancelPendingPurchase(); } /// <summary> /// Pending purchases get cancelled after 5 minutes of no payment or if the player hits the cancel button. /// </summary> public void CancelPendingPurchase() => OnShowPopUp(); /// <summary> /// Step 6.1: Runs when a PlayFab ConfirmPurchaseRequest() successfully gets sent out. /// Doesn't mean the purchase was actually successful (payment completed)! /// See step 6 for more info. /// </summary> /// <param name="confirmPurchaseResult"></param> void OnConfirmPurchaseSuccess(ConfirmPurchaseResult confirmPurchaseResult) { var getPurchaseRequest = new GetPurchaseRequest() { OrderId = confirmPurchaseResult.OrderId }; if (isPurchaseSuccessfull) return; PlayFabClientAPI.GetPurchase(getPurchaseRequest, OnGetPurchaseRequestSuccess, StratUtilityMethods.LogPlayFabError); } /// <summary> /// Step 6.2: The purchase payment completion gets validated here based on the transactionStatus returned by PlayFab. /// If the transactionStatus == 'Succeeded', PlayFabItems.playFabItems.GetPlayerInventory() gets triggered, which in turn triggers CompletePurchaseAndUnlockSGChest() /// CompletePurchaseAndUnlockSGChest() checks if their is and item with item class 'sgchest' in PlayFabItems.playFabItems.cachedPlayFabUserInventory and end the purchase process if so. /// See step 6 for more info. /// </summary> /// <param name="getPurchaseResult"></param> void OnGetPurchaseRequestSuccess(GetPurchaseResult getPurchaseResult) { Debug.Log($"{name}: OnGetPurchaseRequestSuccess(): order id: {getPurchaseResult.OrderId} transaction status: {getPurchaseResult.TransactionStatus}"); if (isPurchaseSuccessfull) return; if (getPurchaseResult.TransactionStatus == "Succeeded") PlayFabItems.playFabItems.GetPlayerInventory(); } /// <summary> /// Step 7: Player paid for their purchase and now the purchase is completed and the SG chest is unlocked. /// </summary> /// <param name="unUsedLastPurchasedItemId"></param> void CompletePurchaseAndUnlockSGChest(string unUsedLastPurchasedItemId) { foreach (var item in PlayFabItems.playFabItems.cachedPlayFabUserInventory) { if (item.ItemClass == "sgchest") { isPurchaseSuccessfull = true; PlayFabItems.playFabItems.UnlockContainer(item.ItemId); ClientController.clientController.ToggleCanvasGroupSmooth(content, false); ClientController.clientController.ToggleCanvasGroupSmooth(contentIsPurchasing, false); ClientController.clientController.ToggleCanvasGroupSmooth(contentIsPurchased, true); GameObject instantiatedChest = Instantiate ( Resources.Load<GameObject>("Client/FullScreenPopUps/BuySGPopUp/" + item.ItemId + "/Prefab"), contentIsPurchased.transform.Find("ChestContainer") ); instantiatedChest.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, -407f); } } }