# 3. SDK Features

## Extend

Activity using SDK must be extend `FragmentActivity` by `Java` or `AppcompatActivity` by `Kotlin`&#x20;

{% tabs %}
{% tab title="Java" %}

```java
public class MainActivity extends FragmentActivity {
 ...
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppcompatActivity {
 ...
}
```

{% endtab %}
{% endtabs %}

## Login

* This function will initialize the default configuration for the SDK and call the login/register form automatically.
* Upon successful login/registration the SDK returns a model containing the user's data as `result` in callback `onSuccess()`. Handling of successful login/registration here
* When login / registration is not successful, it will be handled at the callback `onFail()`

{% tabs %}
{% tab title="Java" %}

```java
 FID.INSTANCE.login(new FIDCallBack<FIDUser>() {

            @Override
            public void onFail(@Nullable String error) {

            }

            @Override
            public void onSuccess(FIDUser result) {

            }

            @Override
            public void onCancel() {
               
            }
 });
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.login(object : FIDCallBack<FIDUser> {
            override fun onSuccess(result: FIDUser) {
                
            }

            override fun onFail(error: String?) {
                
            }

            override fun onCancel() {
             
            }
})
```

{% endtab %}
{% endtabs %}

## Logout

* When successful logout will be processed in callback `onSuccess()`
* When the logout fails, it will be handled at the callback `onFail()`

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.logout(new FIDCallBack<Boolean>() {
    @Override
    public void onCancel() {
    
    }
    
    @Override
    public void onFail() {
    
    }
    
    @Override
    public void onSuccess(Boolean result) {
        
    }
});
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.logout(object : FIDCallBack<Boolean> {
  override fun onSuccess(result: Boolean) {

  }

  override fun onFail() {

  }
  
   override fun onCancel() {
                   
  }
})
```

{% endtab %}
{% endtabs %}

## Refresh token (manual)

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.refreshToken(new FIDCallBack<FIDAuthentication>() {
            @Override
            public void onCancel() {
                
            }

            @Override
            public void onSuccess(FIDAuthentication result) {
                
            }

            @Override
            public void onFail() {

            }
});
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.refreshToken(object: FIDCallBack<FIDAuthentication>{
    override fun onSuccess(result: FIDAuthentication) {
                
    }

    override fun onFail() {
             
    }
})
```

{% endtab %}
{% endtabs %}

## Get user information

* Call the function to [Refresh token ](#refresh-token-manual)before call getCurrentAccount with the syntax
* Then call the function to get user information with the syntax

{% tabs %}
{% tab title="Java" %}

```java
 FID.INSTANCE.getCurrentAccount(Context, new FIDUserCallBack() {
   @Override
    public void onSuccess(FIDUser result) {
    
    }
    
    @Override
    public void onFail() {
    
    }
    
    @Override
    public void onSuggestionLinkAccount() {

     }
});
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.getCurrentAccount(Context, object : FIDUserCallBack {
    override fun onSuccess(result: FIDUser) {
  
    }
  
    override fun onFail() {
  
    }
    
     override fun onSuggestionLinkAccount() {
        
     }
})
```

{% endtab %}
{% endtabs %}

## In app purchase

* Before using payment, dev needs to call `setPlayerInfo` function. Where: `server_name`is the identifier of the game server and `character_name`is the name of the game character.

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.sendLogSelectServer("server_id");
FID.INSTANCE.sendLogSelectCharacter("character_name");
FID.INSTANCE.setPlayerInfo();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.sendLogSelectServer("server_name")
FID.sendLogSelectCharacter("character_name")
FID.setPlayerInfo()
```

{% endtab %}
{% endtabs %}

#### **The partner side uses the available UI**

* Function to get list of items:

{% tabs %}
{% tab title="Java" %}

```java
FPay.INSTANCE.getListProduct(Context context, new FPay.FPayCallBack<List<ProductItem>>() {
    @Override
            public void onPending() {
                
            }

            @Override
            public void onCancel() {

            }

            @Override
            public void onSuccess(List<ProductItem> result) {

            }

            @Override
            public void onFailure(@NonNull String error) {
                
            }
});
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FPay.getListProduct(context: Context, object: FPay.FPayCallBack<List<ProductItem>>{
            override fun onSuccess(result: List<ProductItem>) {

            }

            override fun onFailure(error: String) {

            }

            override fun onPending() {

            }

            override fun onCancel() {
                
            }
})
```

{% endtab %}
{% endtabs %}

* Item purchase handler function, passing in the paymentWithoutUISDK function the productInfo object is taken from the list item list in getListProduct function.

{% tabs %}
{% tab title="Java" %}

```java
private void purchaseItemWithoutUI(){
  ProductItem productItem = new ProductItem();
        productItem.setStorePackageID("eclazz.item1");
        productItem.setGameCode("FSDK");
        productItem.setIapPackageId(12);
        productItem.setInGamePackageID("152217");
        productItem.setDisplayAmount(23000);
        productItem.setDisplayCurrency("VND");

        FunzyIAPOrderInfo info = new FunzyIAPOrderInfo();
        FunzyIAPOrderInfo.FunzyIAPExtraDataOrder order = (FunzyIAPOrderInfo.FunzyIAPExtraDataOrder) info.getExtraDataInfo();
        if (order != null) {
            order.setCooOrderId("" + System.currentTimeMillis());
        }
        info.setExtraDataInfo(order);

        FPay.INSTANCE.purchaseItemWithoutUI(this, productItem, info, new FPay.FPayCallBack<String>() {
            @Override
            public void onPending() {

            }

            @Override
            public void onFailure(@NonNull String error) {

            }

            @Override
            public void onSuccess(@NonNull String transactionId) {

            }

            @Override
            public void onCancel() {

            }
        });
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
private fun purchaseItemWithoutUI() {
   val productItem = ProductItem().apply {
            storePackageID = "101000001"
            gameCode = "lcm"
            iapPackageId = 2
            inGamePackageID = "101000001"
            displayAmount = 23000
            displayCurrency = "VND"
        }
        FPay.purchaseItemWithoutUI(
            this,
            productItem = productItem,
            iapOrderInfo = FunzyIAPOrderInfo().apply {
                (this.extraDataInfo as? FunzyIAPOrderInfo.FunzyIAPExtraDataOrder)?.cooOrderId = "${System.currentTimeMillis()}"
            },
            callback = object : FPay.FPayCallBack<String> {
                override fun onSuccess(result: String) {
             
                }

                override fun onFailure(error: String) {
                  
                }

                override fun onCancel() {
              
                }

                override fun onPending() {
                  
                }
            })
}
```

{% endtab %}
{% endtabs %}

* The `onSuccess` callback returns a list of items as listProductItem. Dev takes this data to self build interface.
* When the item list cannot be obtained, the dev will process it in the `onFail` callback.
* Upon successful item purchase, it will be processed in the `onSuccess` callback. In this callback return 1 transaction code.
* In case of unsuccessful item purchase, it will be handled at the `onFailure`' callback.
* When purchasing items that the user cancels will be handled in the `onCancel` callback.

**How does the payment flow work?**

All payment methods share the same flow of operations as follows:

<figure><img src="/files/PRqglf0lHcPwWVgf7ub3" alt=""><figcaption></figcaption></figure>

> Game calls the payment interface from the SDK. The game will get the item list from the SDK or the SDK shows the item list for the game The SDK sends a payment request to the server. The server notifies the game server through an api payment callback, the game server processes the transaction to add money or in-game items and then tells the game to display the results in the game. At the same time, the server also receives information and returns it to the SDK. Sdk send packages to client game. The SDK still reports the results to the game through a delegate. The game that has received the results from the game server (which is the most accurate) does not need to use the information returned from this SDK anymore. It is used in special cases.

## Q\&A

This part the game will ask the SDK to open a webview form for users to create questions about the game, as well like refer to the issues that the community has answered before,...

* Call the Q\&A function with the syntax:

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.openQA(this);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.openQA(this)
```

{% endtab %}
{% endtabs %}

## Connect Account

* Call the Connect account function with the syntax:

{% tabs %}
{% tab title="Java" %}

```java
 FID.INSTANCE.connectAccount(new FIDCallBack<FIDUser>() {
            @Override
            public void onCancel() {
                FIDCallBack.super.onCancel();
            }

            @Override
            public void onSuccess(FIDUser result) {
                
            }

            @Override
            public void onFail() {

            }
 });
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.connectAccount(object : FIDCallBack<FIDUser> {
                override fun onSuccess(result: FIDUser) {
                  
                }

                override fun onFail() {
                    
                }

                override fun onCancel() {
                  
                }
})
```

{% endtab %}
{% endtabs %}

## Tracking

* Using this code for some events:

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.sendLogSelectServer("server_name");
FID.INSTANCE.sendLogSelectCharacter("character_name");
FID.INSTANCE.sendLogLevelUp("1");
FID.INSTANCE.sendLogTutorialCompleted();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.INSTANCE.sendLogSelectServer("server_name");
FID.INSTANCE.sendLogSelectCharacter("character_name");
FID.INSTANCE.sendLogLevelUp("1");
FID.INSTANCE.sendLogTutorialCompleted();
```

{% endtab %}
{% endtabs %}

* For custom events:

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.sendLogTracking("event_name", "extra_data")
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.sendLogTracking("event_name", "extra_data")
```

{% endtab %}
{% endtabs %}

## Language

* FID support multiple language: Vietnamese & English

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.configLanguage(LANGUAGE.VIETNAMESE)
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.configLanguage(LANGUAGE.VIETNAMESE)
```

{% endtab %}
{% endtabs %}

## Bubble support Button

* Bubble support is other layout support multiple function: IAP, news, connect account, ...
* For show bubble support:

{% tabs %}
{% tab title="Java" %}

```java
 FID.INSTANCE.showSupportButton(context, viewRoot);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
 FID.showSupportButton(context, viewRoot)
```

{% endtab %}
{% endtabs %}

* For hide bubble support:

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.hideSupportButton();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.hideSupportButton()
```

{% endtab %}
{% endtabs %}

## Deeplink

* For automatic handle deeplink like openApp or Ads, onelink, use code like beblow:

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.handleDeepLink(intent, context);
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.handleDeepLink(intent, context)
```

{% endtab %}
{% endtabs %}

## Change Password

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.changePassword(new FIDCallBack<FIDUser>() {
            @Override
            public void onFail(@Nullable String error) {

            }

            @Override
            public void onSuccess(FIDUser result) {

            }

            @Override
            public void onCancel() {
                FIDCallBack.super.onCancel();
            }
        });
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.changePassword(object : FIDCallBack<FIDUser> {
                override fun onSuccess(result: FIDUser) {
                  
                }

                override fun onFail(error: String?) {
                
                }

                override fun onCancel() {
                    
                }
 })
```

{% endtab %}
{% endtabs %}

## Update phone

* Using for user login by thirdparty then want to connect with FID phonenumber account

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.updatePhone(new FIDCallBack<FIDUser>() {
            @Override
            public void onFail(@Nullable String error) {

            }

            @Override
            public void onSuccess(FIDUser result) {

            }

            @Override
            public void onCancel() {
                FIDCallBack.super.onCancel();
            }
        });
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
 FID.updatePhone(object : FIDCallBack<FIDUser> {
                override fun onSuccess(result: FIDUser) {
                   
                }

                override fun onFail(error: String?) {
                   
                }

                override fun onCancel() {
                   
                }
 })
```

{% endtab %}
{% endtabs %}

## Handle Giftcode

Using this for event scan QRcode to get giftcode ingame.&#x20;

`FIDGiftCode`

| Params  | Type   | Description                |
| ------- | ------ | -------------------------- |
| code    | String | Giftcode ingame            |
| content | String | Description giftcode event |

{% tabs %}
{% tab title="Java" %}

```java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    handleGiftCode();
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleGiftCode();
}

void handleGiftCode() {
    FID.INSTANCE.onReceiveGiftCode(getIntent(), new FIDGiftCodeCallBack() {
        @Override
        public void onSuccess(@NonNull FIDGiftCode fidGiftCode) {
            
        }
    });
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
  // ...
   handleGiftCode()
  }
  
   override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        handleGiftCode()
    }

   fun handleGiftCode() {
        FID.onReceiveGiftCode(intent, object : FIDGiftCodeCallBack {
            override fun onSuccess(fidGiftCode: FIDGiftCode) {

            }
        })
    }
```

{% endtab %}
{% endtabs %}

## Playnow Account Not Linked

If App/Game require link account for play now account before make an In App Purchase, you can use this function:

{% tabs %}
{% tab title="Java" %}

```java
FID.INSTANCE.isPlayNowAccountNotLinked()
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
FID.isPlayNowAccountNotLinked()
```

{% endtab %}
{% endtabs %}

## Enable Debug Log

{% tabs %}
{% tab title="Java" %}

```
FID.INSTANCE.enableDebug(true)
```

{% endtab %}

{% tab title="Kotlin" %}

```
FID.enableDebug(true)
```

{% endtab %}
{% endtabs %}

## Proguard

```
##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}
##---------------End: proguard configuration for Gson  ----------

-optimizationpasses 5
-optimizations !code/simplification/arithmetic,!field/*,!class/merging*/
-allowaccessmodification
-repackageclasses ''
-verbose

-keep class **$$Parcelable { *; }

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

# To support Enum type of class members
-keep enum * { *; }
#Keep native
-keep public class * {
    public <methods>;
}

-keep class ai.ftech.FTechLog** {
public<fields>;
public<methods>;
}

-keep class ai.ftech.fid.FID** {
    public<fields>;
    public<methods>;
}
-keep interface * { *; }

-keep public class ai.ftech.fid.FidConfig** {
*;
}

-keep class ai.ftech.fid.domain.model.dynamicconfig.AuthConfigRequest** {

}

-keep class ai.ftech.fpay.FPay** {
    public<fields>;
    public<methods>;
}

-keep class com.appsflyer.** { *; }
-keep class kotlin.jvm.internal.** { *; }
```


---

# 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://docs.funzy.vn/android-fid-sdk/3.-sdk-features.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.
