How To Integrate
The eSkills platform allows you to integrate the Cache-Based Skilled games business model within your app. We provide you 3 main features:
- Payment system using appcoins payment system
- Matchmaking system
- Revenue share system
This document will help you to integrate your app with eSkills platform.
Platform Overview
There are 4 components illustrated in this integration:
- Game Client: game installed in user’s device;
- Game Server: backend used by the Game Client;
- AppCoins Wallet: app that allows users to perform payments
- eSkills: The backend that manages all the matches
Create URL (1)
In this step is where we set up all the game settings such as the entry fee, user name and other properties. We will use an URL to communicate with AppCoins Wallet and send all the data needed.
All we need to do is create a String object representing the URL containing the essential information to create a match.
URL specification
Scheme: https
Host: apichain.catappult.io
Path: /transaction/eskills
Query string arguments: value, currency, product, domain, metadata, timeout, user_name,
user_id, environment
Example:
https://apichain.catappult.io/transaction/eskills?value=1¤cy=USD&product=summer_battle&user_name=faker&user_id=a25cd286&domain=com.appcoins.eskills2048
Parameter | Type | Description | Static | Optional | Default | Example |
---|---|---|---|---|---|---|
value | float | Entry fee value | N | Y | 1 | 2 |
currency | string | Entry fee currency | Y | Y | USD | USD |
product | string | Match type id | N | Y | 1v1 | summer_battle |
domain | string | The application id, also known as package name. | N | N | com.appcoins.eskills2048 | |
metadata | string | A map in json format with user defined metadata | Y | {"metaKey":"metaValue"} | ||
timeout | long | Max time a match should run in seconds | N | Y | 3600 | 86400 |
user_name | string | User’s nickname | N | Y | faker | |
user_id | string | Game user id | a25cd286 | |||
environment | string | Set the environment | N | Y | LIVE | SANDBOX |
number_of_users | integer | Number of players per match | Y | N | 2 |
Parameters description:
value: value of the entry fee that will be charged to each player entering the room;
currency: Currency of the value parameter, only USD is supported for now
product: This corresponds to sku. Players will queue up vs players with the same sku and value
domain: your application id (package name)
* metadata: Map<String, String> with user defined metadata.
timeout: max time allowed for a match. Once the time is reached the player with the higher score will win.
user_name: User’s user name. You will be able to get it later from room information.
user_id: Your user id. You will be able to get it later from room information.
environment: Allows you to set the environment. The value can be “LIVE” for releases or
“SANDBOX” for testing purposes. Only players from the same environment can face each other. When playing in “SANDBOX” mode, no entry fee will be charged and no revenue share will take place.
number_of_users: Amount of players per match, including current user.
- metadata example using google library gson:
Map<String, String> metadataMap = new HashMap<>();
metadataMap.put("metaKey", "metaValue");
Gson gson = new Gson();
String metadata = gson.toJson(metadataMap);
The resulting String URL should look similar to this:
Player usernames must be restricted to the range:
a-z
A-Z
_
and spaces, or else step 3 might fail.
Call URL (2)
Inside the app/game, an intent with the URL should be opened either using the web browser (whenever the AppCoins Wallet is not installed), or via the AppCoins Wallet.
In some cases, the user chooses to open the URL with the browser when the AppCoins Wallet is already installed or even chooses the option to always open the URL with the browser. In order to avoid this, we suggested adding the following sample to trigger the One Step billing flow.
private static final int REQUEST_CODE = 3124;
public void startMatch(String url) {
Intent intent = buildTargetIntent(url);
try {
startActivityForResult(intent, REQUEST_CODE);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method generates the intent with the provided One Step URL to target
the
* AppCoins Wallet.
* @param url The url that generated by following the One Step payment rules
*
* @return The intent used to call the wallet
*/
private Intent buildTargetIntent(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
// Check if there is an application that can process the AppCoins Billing
// flow
PackageManager packageManager =
getApplicationContext().getPackageManager();
List<ResolveInfo> appsList = packageManager
.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo app : appsList) {
if (app.activityInfo.packageName.equals("cm.aptoide.pt")) {
// If there's aptoide installed always choose Aptoide as default to open
// url
intent.setPackage(app.activityInfo.packageName);
break;
} else if (app.activityInfo.packageName.equals("com.appcoins.wallet")){
// If Aptoide is not installed and wallet is installed then choose Wallet
// as default to open url
intent.setPackage(app.activityInfo.packageName);
}
}
return intent;
}
Target SDK 30 and over
When the app targets SDK 30 or above you need to add the intent to AndroidManifest as such:
<manifest>
...
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
</intent>
...
</queries>
...
</manifest>
Process Payment (3)
Once the URL is called, AppCoins Wallet will take over and process the payment and wait for an opponent to match against the current user. Once the match is ready to start, the AppCoins wallet will return an intent where you can extract a session token that will allow you to interact with our services on the user's behalf.
Extract session token
In order to communicate with eSkills API, you need a session token that will be used as a Bearer token, you should send it over the Authorization header in the requests.
This token is valid according to the value sent on timeout field the default is 12 hours. The session identifies the user and the match he is currently playing.
public static final String SESSION = "SESSION";
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent
data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == 0) {
String sessionToken = data.getStringExtra(SESSION);
}
}
Inside "onActivityResult" you can also see that there's a parameter called "resultCode". This represents the payment's current state, as well as any actions you should take. Below you can find all the possible values and their correspondent explanation:
RESULT_OK = 0 // user payed successfuly for the entry ticket
RESULT_USER_CANCELED = 1 // user canceled, before or after paying. if the user canceled after paying, we will automatically refund the transaction
RESULT_REGION_NOT_SUPPORTED = 2 // due to legal limitations, user region (country or sub-division) may not allow platforms like e-Skills
RESULT_NO_NETWORK = 3 // connection failed while reaching our backend services
RESULT_ERROR = 6 // other errors that were not described earlier
Start Game (4)
Once everything is ready, you should start your game logic.
You must use the session token returned on step 3 to use the eSkills API.
Refer to Best Practices for a hint on how to structure your game's flow.
Checking the room status
At any given time, you can check the room status using the https://api.eskills.catappult.io/room/redoc#operation/get_room__get endpoint.
Periodic Score Update (5)
You should periodically update the user's score by calling our api, everytime you update a user's score, you’ll receive all the match data up to date and you’ll be able to get all the user’s score, the winner (if any) and some other info.
See all the documentation about our api here.
public Single<RoomResponse> patch(String session, long score, UserStatus status) {
PatchRoomRequest patchRoomRequest = new PatchRoomRequest();
patchRoomRequest.setScore(score);
// when updating user's score, status will be "PLAYING"
patchRoomRequest.setStatus(status);
//1st argument is the authorization header (see step 3)
//2nd argument is the request body
return roomApi.patchRoom(BEARER_ + session, patchRoomRequest);
}
Snippet from RoomRepository.java
Finish Game (6)
Once a user finishes the game by losing or winning, you should call our api informing us. To do so, you need to call set score api setting the status field to “COMPLETED”.
See all the documentation about our api here.
public Single<RoomResponse> patch(String session, long score, UserStatus status) {
PatchRoomRequest patchRoomRequest = new PatchRoomRequest();
patchRoomRequest.setScore(score);
// when finishing the game, status will be "COMPLETED"
patchRoomRequest.setStatus(status);
//1st argument is the authorization header (see step 3)
//2nd argument is the request body
return roomApi.patchRoom(BEARER_ + session, patchRoomRequest);
}
Snippet from RoomRepository.java
End game flow (7) - Beta
Once the player finishes the game, the wallet app needs to wait for all players to finish the game and then communicate the winner. This flow is managed by the wallet app through a simple intent call. After completing the game, users will be able to see their opponent's score, the match result, leaderboards, and other features.
URL specification
Scheme: https
Host: apichain.catappult.io
Path: /transaction/eskills/endgame
Query string arguments: session, domain
Example:
https://apichain.catappult.io/transaction/eskills/endgame?session={session_token}&domain=com.appcoins.eskills2048
Parameter | Type | Description | Static | Optional | Default | Example |
---|---|---|---|---|---|---|
session | Jwt | Session Token | N | Y | ||
domain | string | The application id, also known as package name. | N | N | com.appcoins.eskills2048 |
Parameters description:
session: Session token extracted in step 4.
domain: your application id (package name)
private void launchEskillsEndgameFlow(String session) {
String url = BuildConfig.BASE_HOST_PAYMENT
+ "/transaction/eskills/endgame?"
+ "session="
+ session
+ "&domain="
+ BuildConfig.APPLICATION_ID;
Intent intent = buildTargetIntent(url);
try {
mLauncher.launch(intent);
} catch (Exception e) {
e.printStackTrace();
}
Activity Results
It is important to handle the activity result correctly so that specific responses lead to the appropriate flows, such as restarting a match or going back to the first screen.
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
switch (result.getResultCode()) {
case RESULT_OK:
launchInitialActivity();
break;
case RESULT_RESTART:
// TODO: Handle restart flow
break;
case RESULT_SERVICE_UNAVAILABLE:
// TODO: Handle service unavailable flow
break;
case RESULT_ERROR:
// TODO: Handle error flow
break;
case RESULT_INVALID_URL:
// TODO: Handle invalid URL flow
break;
default:
launchInitialActivity();
break;
}
});
RESULT_OK and default: In these cases, the user should be directed to the main menu of the game.
RESULT_RESTART: When this result is received, a new match should be started with the same parameters as the previous match. You can create a new URL using the instructions provided in Create URL (1).
RESULT_SERVICE_UNAVAILABLE, RESULT_ERROR, and RESULT_INVALID_URL: In all error situations, users should be presented with an error screen that allows them to retry launching the flow. You can implement an error handling mechanism to display an appropriate error message and provide the option to retry.
ESkills API
eSkills api provides all the methods needed to update a user's actions. Learn more about our api in the following url: https://api.eskills.catappult.io/room/redoc
For this integration, we need 2 APIs:
Get room stats
You can find the documentation in the following url:
https://api.eskills.catappult.io/room/redoc#operation/get_room__get
Set score
You can find the documentation in the following url:
https://api.eskills.catappult.io/room/redoc#operation/set_store_room_score__patch
Code References
Feel free to check out our working demo app at https://github.com/Aptoide/2048.
Updated over 1 year ago