# GSM IAP

## 1. Verify IAP

* Dùng khi user hoàn thành IAP.
* <mark style="color:red;">Nếu muốn GSM gửi dữ liệu Adjust, thì cần truyền lên Adjust.GetAdId() mỗi khi log IAP</mark>
* Phía server sẽ lưu doanh thu và thống kê với những dữ liệu IAP hợp lệ.

```csharp
void Check(InappData inappData, Action onSuccess, Action onFail)
```

**Các tham số**

* **`inappData`**: Các thông tin về gói mua, gồm id, purchase token, tên gói, mốc tiến trình mua gói, adid, loại user IAP
* **`onSuccess`**: Hàm được thực hiện khi gói mua hợp lệ.
* **`onFail`**: Hàm được thực hiện khi gói mua không hợp lệ

Code mẫu:

```csharp
string targetLevel = "3"; // Là level cao nhất mà user chưa vượt qua được
string productId = "com.cscmobi.cana.islands.tier2";
string purchaseToken = "bonlghknnoigpaegpeamnppf.AO-J1OzxViPPvXq9ZnZWF6w0DsHBCkRKkMibnrC2aOZwUC4lKvtURI4CPC_myA0C_Rpz4dRhbaNEMS2Pw2spYKdwNjWQWeiZKwNPuNtNyknKwg0KrQrRJKk";
string productName = "piggy_bank"; // Là Tên gói nạp IAP, Có thể bỏ trống
string group = ""; //Tên nhóm, phục vụ cho A/B test, hoặc các level có state khác nhau..  Ví dụ tệp userA, userB, state0, state1...
string subGroup= ""; // Tên nhóm, như group nhưng ở quy mô nhỏ hơn
string userType = "2"; // Là loại user. Có thể bỏ trống
string adid = ""; // Là Adjust Id  (Sample: Adjust.getAdid()). Có thể bỏ trống
GSM.Validator.Inapp.InappData data = new GSM.Validator.Inapp.InappData(targetLevel, productId, purchaseToken, productName, group, userType, adid, subGroup);
GSM.Validator.Inapp.Check(data, () => Debug.Log("Valid purchase"), () =>
{
    Debug.LogError("Invalid purchase");
});
```

<mark style="color:red;">**Lưu ý:**</mark>

* Phía game không được phụ thuộc vào kết quả trả về để trả phần thưởng cho user. Vì nếu server lỗi, thì toàn bộ user sẽ không nhận được thưởng gây ra phản hồi tiêu cực của user.

Cách lấy các thông tin được sử dụng trong API ValidateIAP

Code mẫu:

```csharp
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;

    [SerializedField] IAPButton iapButton;

    private void Start()
    {
        iapButton.onPurchaseComplete.AddListener(OnCompletePurchase);
    }

    public void OnCompletePurchase(Product product)//handle effect
    {
        var wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(product.receipt);
#if UNITY_IOS
            var purchaseToken = (string)wrapper["Payload"];
#elif UNITY_ANDROID
        var purchaseToken = (string)wrapper["TransactionID"];
#endif
        int targetLevel = <your targetLevel>; // Là level cao nhất mà user chưa vượt qua được
        string productId = product.definition.id;
        string productName = <productName>; // Là Tên gói nạp IAP do game định nghĩa, Có thể bỏ trống
        string group = ""; //Tên nhóm, phục vụ cho A/B test, hoặc các level có state khác nhau..  Ví dụ tệp userA, userB, state0, state1...
        string subGroup= ""; // Tên nhóm, như group nhưng ở quy mô nhỏ hơn
        string userType = <typeUser>; // Là loại user. Có thể bỏ trống
        string adid = Adjust.getAdid(); // Là Adjust Id  (Sample: Adjust.getAdid()). Có thể bỏ trống
        GSM.Validator.Inapp.InappData data = new GSM.Validator.Inapp.InappData(targetLevel, productId, purchaseToken, productName, group, userType, adid, subGroup);
        GSM.Validator.Inapp.Check(data, () => Debug.Log("Valid purchase"), () =>
        {
            Debug.LogError("Invalid purchase");
        });
    }
```

## 2. Cập nhật tiến trình IAP

### 2.1 Tạo một Id giao dịch (orderId)

Code mẫu:

```csharp
System.Guid newGuid = System.Guid.NewGuid();
string orderId = newGuid.ToString(); //Id giao dịch
```

### 2.2 Bắt đầu một IAP

* Khi user nhấn vào nút mua 1 gói IAP nào đó, thì game gửi lên server trạng thái start 1 iap

Code mẫu:

```csharp
GSM.Analytics.GSMAnalytics.LogEvent("iap_progress", new GSM.Models.Parameter[]
{
    new GSM.Models.Parameter("orderId", "{orderId}"), //Id giao dịch
    new GSM.Models.Parameter("productName", "{tên gói nạp}"),
    new GSM.Models.Parameter("status", -1) // -1 là thể hiện trạng thái bắt đầu 1 IAP
});
```

### 2.3 Khi IAP thành công

* Sau khi người chơi hoàn thành việc mua IAP, thì game gửi lên trạng thái thành công

Code mẫu:

```csharp
GSM.Analytics.GSMAnalytics.LogEvent("iap_progress", new GSM.Models.Parameter[]
{
    new GSM.Models.Parameter("orderId", "{orderId}"),//Id giao dịch
    new GSM.Models.Parameter("storeOrderId", "{storeOrderId}"), // Là Id giao dịch của store trả về khi IAP thành công
    new GSM.Models.Parameter("productName", "{tên gói nạp}"),
    new GSM.Models.Parameter("status", 8) // 8 là thể hiện việc mua IAP thành công
});
```

### 2.4 Khi IAP bị fail

* Trong quá trình thực hiện 1 IAP bị fail vì 1 lý do gì đó, thì client cần gửi lên tiến trình đang fail ở đâu

Code mẫu:

```csharp
GSM.Analytics.GSMAnalytics.LogEvent("iap_progress", new GSM.Models.Parameter[]
{
    new GSM.Models.Parameter("orderId", "{orderId}"),
    new GSM.Models.Parameter("productName", "{tên gói nạp}"),
    new GSM.Models.Parameter("status", {0->7}) //Các trạng thái fail
});
```

**Các Status -1->8**

* -1: Start
* `0`: PurchasingUnavailable
* `1`: ExistingPurchasePending
* `2`: ProductUnavailable
* `3`: SignatureInvalid
* `4`: UserCancelled
* `5`: PaymentDeclined
* `6`: DuplicateTransaction
* `7`: Unknown
* `8`: Success

Code mẫu:

```csharp
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
using UnityEngine.UI;

    [SerializedField] IAPButton iapButton;

    private void Start()
    {
        iapButton.GetComponent<Button>().onClick.AddListener(PurchaseProduct);
        iapButton.onPurchaseComplete.AddListener(OnCompletePurchase);
        iapButton.onPurchaseFailed.AddListener(OnFailPurchase);
    }

    public void OnCompletePurchase(Product product)//handle effect
    {
        var wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(product.receipt);
        string storeOrderId;
#if UNITY_IOS
        var purchaseToken = (string)wrapper["Payload"];
        try
        {
            var orderId = (string)wrapper["TransactionID"];
            storeOrderId = orderId;
        }
        catch (System.Exception e)
        {
            storeOrderId = "";
        }    
#elif UNITY_ANDROID
        var purchaseToken = (string)wrapper["TransactionID"];
        try
        {
            var gpPayLoad = (string)wrapper["Payload"];
            var gpJson = (Dictionary<string, object>)MiniJson.JsonDecode(gpPayLoad);
            var json = (string)gpJson["json"];
            var jsonDetails = (Dictionary<string, object>)MiniJson.JsonDecode(json);
            string orderId = (string)jsonDetails["orderId"];
            storeOrderId = orderId;
        }
        catch (System.Exception e)
        {
            storeOrderId = "";
            Debug.LogException(e);
        }
#endif
        // Other logic
        GSM.Analytics.GSMAnalytics.LogEvent("iap_progress", new GSM.Models.Parameter[]
        {
            new GSM.Models.Parameter("orderId", orderId),
            new GSM.Models.Parameter("storeOrderId ", storeOrderId ),
            new GSM.Models.Parameter("productName", product.metadata.localizedTitle),
            new GSM.Models.Parameter("status", 8) 
        });
    }

    public void OnFailPurchase(Product product, PurchaseFailureDescription reason)
    {
        // Other logic
        GSM.Analytics.GSMAnalytics.LogEvent("iap_progress", new GSM.Models.Parameter[]
        {
            new GSM.Models.Parameter("orderId", orderId),
            new GSM.Models.Parameter("productName", product.metadata.localizedTitle),
            new GSM.Models.Parameter("status", (int)reason.reason) //Các trạng thái fail
        });
    }

    public void PurchaseProduct()
    {
        if (buttonType == CodelessButtonType.Purchase)
        {
            // Other logic
            GSM.Analytics.GSMAnalytics.LogEvent("iap_progress", new GSM.Models.Parameter[]
            {
                new GSM.Models.Parameter("orderId",orderId),
                new GSM.Models.Parameter("productName", product.metadata.localizedTitle),
                new GSM.Models.Parameter("status", -1)
            });
        }
    }
```

## 3. Verify Subcription

* Dùng khi user hoàn thành Subcription.
* Phía server sẽ lưu doanh thu và thống kê với những dữ liệu Subcription hợp lệ.

```csharp
void Check(SubcriptionData data, Action onSuccess, Action onFail)
```

**Các tham số**

* **`data`**: Các thông tin về gói subcription, gồm id, purchase token, tên gói, mốc tiến trình mua gói, adid, loại user
* **`onSuccess`**: Hàm được thực hiện khi gói subcription hợp lệ.
* **`onFail`**: Hàm được thực hiện khi gói subcription không hợp lệ

Code mẫu:

```csharp
string targetLevel = "3"; // Là level cao nhất mà user chưa vượt qua được
string productId = "com.cscmobi.cana.islands.tier2";
string purchaseToken = "bonlghknnoigpaegpeamnppf.AO-J1OzxViPPvXq9ZnZWF6w0DsHBCkRKkMibnrC2aOZwUC4lKvtURI4CPC_myA0C_Rpz4dRhbaNEMS2Pw2spYKdwNjWQWeiZKwNPuNtNyknKwg0KrQrRJKk";
string productName = "piggy_bank"; // Là Tên gói nạp Subcription, Có thể bỏ trống
string group = ""; //Tên nhóm, phục vụ cho A/B test, hoặc các level có state khác nhau..  Ví dụ tệp userA, userB, state0, state1...
string subGroup= ""; // Tên nhóm, như group nhưng ở quy mô nhỏ hơn
string userType = "2"; // Là loại user. Có thể bỏ trống
string adid = ""; // Là Adjust Id  (Sample: Adjust.getAdid()). Có thể bỏ trống
GSM.Validator.Subcription.SubcriptionData data = new GSM.Validator.Subcription.SubcriptionData(targetLevel, productId, purchaseToken, productName, group, userType, adid);

GSM.Validator.Subcription.Check(data, () => Debug.Log("Valid subcription"), () =>
{
    Debug.LogError("Invalid subcription");
});
```

<mark style="color:red;">**Lưu ý:**</mark>

* <mark style="color:red;">Phía game không được phụ thuộc vào kết quả trả về để trả phần thưởng cho user. Vì nếu server lỗi, thì toàn bộ user sẽ không nhận được thưởng gây ra phản hồi tiêu cực của user.</mark>


---

# Agent Instructions: 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:

```
GET https://developer.cscmobicorp.com/gsm/gsm-sdk/iii.-gsm-api/gsm-iap.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
