> For the complete documentation index, see [llms.txt](https://lionstudios.gitbook.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lionstudios.gitbook.io/readme/features/purchasing/purchase-validation/validating-purchases.md).

# Validating Purchases

Our Purchase Validator package is designed to work with both Unity Purchasing v4 and v5.

***

### Validate

Use PurchaseValidator class to validate the purchases.

```csharp
ValidationResponse response = await PurchaseValidator.Validate(
    payload, 
    gameplayInfo, 
    onSuccess,
    onRestored, 
    onFailure, 
    additionalData);
```

**Payload (Required)**

Pass the payload parameter generated by our PurchasePayloadHelper class. This is a simple helper class provided to extract and prepare all required validation data from Unity’s purchase response.

```csharp
//For Unity IAP-5
var payload = PurchasePayloadHelper.Parse(product, order);
```

```csharp
//For Unity IAP-4
var payload = PurchasePayloadHelper.Parse(product);
```

**GameplayInfo (Required)**

Used for analytics. Include the items and currency the player receives through the purchase.

<sub>Example:</sub>

```csharp
var gameplayInfo = new GameplayInfo(
    new List<Item>()
    {
        new Item("NoAds", 1),
        new Item("SpecialWeapon", 1),
    },
    new List<VirtualCurrency>()
    {
        new VirtualCurrency("coins", "normal", 10000),
        new VirtualCurrency("gems", "special", 10)
    },
    "shop"
);
```

**OnSuccess (Optional)**

Called when the purchase is successfully validated.

**OnRestored (Optional)**

Called when the same purchase is already validated before.

**OnFailure (Optional)**

Called when validation fails. The validation status is returned so you can handle the failure accordingly.

**AdditionalData (Optional)**

Optional analytics data to include with the validation request.

***

### Validation Response

The validation response is intentionally simple, containing the result of the validation request along with any relevant metadata. This is the object type returned by the `PurchaseValidator.Validate()` method.

```csharp
public class ValidationResponse
{
    public ValidationStatus status;
    public string message;
    public long code;
    public bool sandbox;
    public string transactionId;
}
```

#### &#x20;**Status**

Represents the outcome of the validation request.

The potential results are

* **Passed -** The purchase passed validation successfully.
* **Skipped -** No validation was performed for the purchase.
* **Failed -** The purchase failed validation.
* **Error -** An error occurred during validation.
* **Restored** - The purchase was already validated in a previous transaction and is treated as a success; no analytics events are sent.
* **Revoked** - The purchase was refunded/revoked already.

#### Message

A message returned from the server. This may include additional details about validation failures, warnings, or other processing information.

#### Code

An internal response or error code returned by the server.

#### Sandbox

Indicates whether the purchase was processed in a sandbox environment. This is returned when that information is available from the server.

#### **Transaction Id**

The validated transaction identifier returned by the server.

***

### **Advanced**

**Callbacks**

<details>

<summary>OnPurchaseRevalidated:</summary>

A static event that gets fired when a purchase from a previous session is successfully\
validated on retry. It can be used to grant remaining rewards that were missed in last session.

{% code overflow="wrap" lineNumbers="true" %}

```csharp
PurchaseValidator.OnRevalidationResult += result =>
{
   Debug.Log($"Product id: {result.ProductId}");
   Debug.Log($"Transaction id: {result.TransactionId}");
   Debug.Log($"Status {result.Status.ToString()}");
   
   //Can grant reward here
   GrantReward(result.ProductId);
};
```

{% endcode %}

{% hint style="info" %}
**Important:** Only use the `OnRevalidationResult` callback if you are waiting for validation to complete and granting the reward in the `OnSuccess` and/or `OnRestored` callbacks provided by the main `PurchaseValidator.Validate` method.

Otherwise, subscribing to `OnRevalidationResult` may result in the reward being granted twice.
{% endhint %}

</details>

**Unity Code Example**

<details>

<summary><strong>Unity IAP-5 example:</strong></summary>

{% code overflow="wrap" lineNumbers="true" %}

```csharp
//Subscribed to m_StoreController.OnPurchasePending event
void OnPurchasePending(PendingOrder order)
{
    //A function provided by Unity IAP-5 sample code
    var product = GetFirstProductInOrder(order);
    if (product == null)
    {
        Debug.Log("Could not find product in order.");
    }

    var parsedValue = PurchasePayloadHelper.Parse(product, order);
            
    var gameplayInfo = new GameplayInfo(
    new List<Item>(){
        new Item("NoAds", 1),
    },
    new List<VirtualCurrency>(){
        new VirtualCurrency("coins", "normal", 10000),
    },"shop");

    var additionalData = new Dictionary<string, object>() {
    {
        "test-key", "test-value"
    }};
            
    _ = PurchaseValidator.Validate(parsedValue, gameplayInfo,
        () => { Debug.log("Validation success"); }, 
        () => { Debug.log("Same purchase already validated before"); },
        (status) => { Debug.Log($"Purchase failed = {status.ToString()}");},
        additionalData);
        
    //Best practice is to grant reward even if validation is not passed, but upto devs
    GrantReward(product);
        
    m_StoreController.ConfirmPurchase(prodcut);
}
    
private void GrantReward(Product product)
{
    //Grant rewards here
    if (product.definition.id == NoAds_Coins_ProductId)
    {
        AddNoAdsAndCoins();
    }
}
```

{% endcode %}

</details>

<details>

<summary><strong>Unity IAP-4 example:</strong></summary>

<pre class="language-csharp" data-overflow="wrap" data-line-numbers><code class="lang-csharp"><strong>public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
</strong>{
    var product = args.purchasedProduct;
    var parsedValue = PurchasePayloadHelper.Parse(product);
    var gameplayInfo = new GameplayInfo(
        new List&#x3C;Item>()
        {
            new Item("NoAds", 1),
        },
        new List&#x3C;VirtualCurrency>()
        {
            new VirtualCurrency("coins", "normal", 10000),
        },
        "shop");

    var additionalData = new Dictionary&#x3C;string, object>() {
    {
        "test-key", "test-value"
    }};
            
    _ = PurchaseValidator.Validate(parsedValue, gameplayInfo,
        () => { Debug.log("Validation success"); }, 
        () => { Debug.log("Same purchase already validated before"); },
        (status) => { Debug.Log($"Purchase failed = {status.ToString()}");},
        additionalData);
        
    //Best practice is to grant reward even if validation is not passed, but upto devs
    GrantReward(product);
        
    return PurchaseProcessingResult.Complete;
}
    
private void GrantReward(Product product)
{
    //Grant rewards here
    if (product.definition.id == NoAds_Coins_ProductId)
    {
        AddNoAdsAndCoins();
    }
}
</code></pre>

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://lionstudios.gitbook.io/readme/features/purchasing/purchase-validation/validating-purchases.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
