Server-to-Server Payouts

Once a user meets the campaign criteria for being rewarded, it's time to initiate the payout—sending the rewards to the user. Publishers issue these payouts upon receiving a payout request callback from adjoe's backend. The request is made to the S2S API URL configured for the publisher for their SDK in the Publisher Dashboard.

Set Up Server-to-Server (S2S) Payouts

To enable S2S payouts, set up an endpoint on your server to receive notifications about user rewards from adjoe. When a request is made to your endpoint, you then must manage the distribution of these rewards to the users. Be aware that once a reward is distributed, it cannot be allocated again.

Endpoint Structure

The key names can be customized, or you can use the default names. Please ensure that no special characters should be included in the values, otherwise our backend may transform the request, which could cause issues with the payout. Some of these values include: [] . =

Default

GET / https://example.com/example?sid={sid}&trans_uuid={trans_uuid}&user_uuid={user_uuid}&currency={currency}&coin_amount={coin_amount}&device_id={device_id}&sdk_app_id={sdk_app_id}&app_id={app_id}&reward_level={reward_level}&app_name={app_name}&reward_type={reward_type}&publisher_sub_id1={publisher_sub_id1}&publisher_sub_id2={publisher_sub_id2}&publisher_sub_id3={publisher_sub_id3}&publisher_sub_id4={publisher_sub_id4}&publisher_sub_id5={publisher_sub_id5}&placement={placement}&ua_network={ua_network}&ua_channel={ua_channel}&ua_subpublisher_encrypted={ua_subpublisher_encrypted}&ua_subpublisher_cleartext={ua_subpublisher_cleartext}

Customized (Example)

GET / https://example.com/example?user_id={user_uuid}&sid={sid}&point_amount={coin_amount}&points={currency}&trans_uuid={trans_uuid}&ua_network={ua_network}&ua_channel={ua_channel}&playtime_placement={placement}&custom_id={publisher_sub_id1}

To achieve successful payouts, the user user_uuid must match the value set in the SDK initialization. More details can be found in Initialize the SDK.

Parameter
Detail
Format
Type

user_uuid

Identifies the user eligible for rewards. This ID must align with the one provided during SDK initialization.

String

Required

sid

String

Required

trans_uuid

The unique transaction ID.

UUID v4

Required

coin_amount

The amount of virtual currency the user should get.

Integer

Required

currency

The name of the virtual currency.

String

Required

sdk_app_id

App ID of the SDK. Example: com.example.android.gamename

String

Optional

app_id

App ID that is used to generate usage.

String

Optional

device_id

Optional external device ID.

String

Optional

app_name

URL encoded app name in English

String

Optional

reward_level

The name of the reward level of the game.

Integer

Optional

reward_type

The type of reward given. Example: Advance, Playtime, AdvancePlus, AdvancePlusWeb, CoinStreakBonus, Event.

String

Optional

ua_network

The network from which the user was acquired, e.g., adwords, facebook, organic.

String

Optional

ua_channel

The channel of the campaign in the network that led to the user's acquisition, e.g., incent, video.

String

Optional

ua_subpublisher_encrypted

The encrypted package name or app id of the sub-publisher app within the network from which the user was acquired.

String

Optional

ua_subpublisher_cleartext

The cleartext package name of app id of the sub publisher app in the network where the user was acquired, e.g., com.domain.appName

String

Optional

placement

The placement of Playtime inside your app, e.g., "Main Screen", "Shop", "Interstitial", "More Diamonds Screen", etc.

String

Optional

app_category

Category of the App that user played. Example: Board, Casino, Casual, Entertainment, Puzzle, Other

String

Optional

usage_seconds

Amount of seconds the current level requires. Time-based campaigns (Playtime) only.

Integer

Optional

total_usage_seconds

Total amount of seconds that user has played this game. Time-based campaigns (Playtime) only.

Integer

Optional

advanceplus_event_name

The name of the milestone/event that user completed to receive this reward. Event-based campaigns (Advance+) only.

String

Optional

advanceplus_event_description

The description of the milestone/event that user completed to receive this reward. Event-based campaigns (Advance+) only.

String

Optional

publisher_sub_id1

Value set in the extension object to be returned in the S2S payout URL.

String

Optional

publisher_sub_id2

Value set in the extension object to be returned in the S2S payout URL.

String

Optional

publisher_sub_id3

Value set in the extension object to be returned in the S2S payout URL.

String

Optional

publisher_sub_id4

Value set in the extension object to be returned in the S2S payout URL.

String

Optional

publisher_sub_id5

Value set in the extension object to be returned in the S2S payout URL.

String

Optional

event_boost_factor

The boost factor for the current promotion. Example: 2x, returned as 2.

Integer

Optional

event_boost_start_date

Date when the boost factor began.

String

Optional

event_boost_stop_date

Date when the boost factor stopped.

String

Optional

Example Response URL:

https://s2sexamplerequestendpoint.com/payout?user_uuid=a79d7158-6f9c-4e5b-ae7a-98143c77d396&sid=0de7e36d2965269f172d2d89bc9a8026da7f0819&coin_amount=100&currency=dollars&trans_uuid=e7b1a95f-8c72-4ed8-af69-ecf8d06b1d89&sdk_app_id=example.test.app&ua_network=tiktok&ua_channel=direct&publisher_sub_id1=RandomString

Calculate the SID

For security measures, a request signature is necessary to verify the authenticity of the request. It's important to calculate the sid parameter in your backend and compare it with the parameter received in the callback URL. The sid is generated as an SHA1 hash using the required query parameters and the encoding is hexadecimal. Your Account Manager will provide you with the necessary parameter, the s2s_token. The general format for this is as follows - no other parameters are included in the calculation:

sid = sha1(concatenate(trans_uuid, user_uuid, currency, coin_amount, device_id, sdk_app_id, s2s_token))

If sdk_app_id and/or device_id are not present in the endpoint URL, they will be excluded from the sid calculation. The sid will then be calculated as follows:

sid = sha1(concatenate(trans_uuid, user_uuid, currency, coin_amount, s2s_token))

Below is an example script that shows how to calculate the sid and compare it to the value in the callback URL sent by adjoe. You would implement some version of this in your backend.

import hashlib
from urllib.parse import urlparse, parse_qs

def calculate_sid(user_uuid, trans_uuid, currency, coin_amount, device_id, sdk_app_id, s2s_token):
    data = ''.join([trans_uuid, user_uuid, currency, str(coin_amount), device_id, sdk_app_id, s2s_token])
    hashed_data = hashlib.sha1(data.encode()).hexdigest()
    return hashed_data

def extract_parameters(url):
    parsed_url = urlparse(url)
    query_params = parse_qs(parsed_url.query)
    return query_params

def compare_sids(url, calculated_sid):
    query_params = extract_parameters(url)
    sid_from_url = query_params.get('sid', [None])[0]
    return sid_from_url == calculated_sid

# Example URL
url = "https://s2sexamplerequestendpoint.com/payout?user_uuid=a79d7158-6f9c-4e5b-ae7a-98143c77d396&sid=2477185c00a5b1ac94fdf94798798d5e846d8aee&coin_amount=100&currency=dollars&trans_uuid=e7b1a95f-8c72-4ed8-af69-ecf8d06b1d89&device_id=9a7e993a-80b4-4c3b-832f-97a5f501e2f1&sdk_app_id=com.example.android.gamename&ua_network=tiktok&ua_channel=direct&publisher_sub_id1=RandomString"

# Extract parameters from URL
query_params = extract_parameters(url)
user_uuid = query_params.get('user_uuid', [None])[0]
sid = query_params.get('sid', [None])[0]
trans_uuid = query_params.get('trans_uuid', [None])[0]
currency = query_params.get('currency', [None])[0]
coin_amount = int(query_params.get('coin_amount', [None])[0])
device_id = query_params.get('device_id', [None])[0]
sdk_app_id = query_params.get('sdk_app_id', [None])[0]
s2s_token = " " #the s2s_token will be supplied by your Account Manager.

# Calculate SID
calculated_sid = calculate_sid(user_uuid, trans_uuid, currency, coin_amount, device_id, sdk_app_id, s2s_token)

# Compare SIDs
result = compare_sids(url, calculated_sid)
print("SID Comparison Result:", result)

S2S Security

When we make payout request to publishers, we send an HTTP request from any of the following IP addresses:

3.121.65.44
18.185.166.67
52.29.52.48

To enhance security, use these IP addresses for IP-level blocking on endpoints receiving adjoe's S2S payout requests. Allow only these specific IPs access.

Retry Logic

If we receive a 4xx / 5xx from your endpoint, we have an established retry logic for the failed S2S requests.

For Playtime Campaigns

If a request fails, we retry after 10 minutes. A second failure prompts a retry in another 10 minutes. Subsequent failures lead to retries every 2 hours from the initial failed attempt. The time windows are additional, so it's roughly 12hrs in total.

For Advance and Advance+ Campaigns

If a request fails, we retry five more times, with each attempt spaced 30 seconds apart.

If you observe an extended period of unsuccessful requests, contact your Account Manager for further investigation into the issue.

Best Practices for Rewarding Users

If you payout with real money, we recommend that you implement KYC processes, reward cooldowns, and individual limits on the amount of rewards the user can get:

  • KYC or Know Your Customer: This verification process, used by businesses and financial institutions, confirms the identity of your users to prevent fraud. It involves collecting and verifying identification documents, such as government-issued IDs or proof of address, ensuring that users are who they claim to be.

  • Reward Cooldowns: Temporarily restrict users from receiving additional rewards after receiving one. This helps maintain fairness and prevents exploitation of the rewards system, like receiving rewards too frequently.

  • Individual Limits: To prevent abuse of the rewards systems, set daily limits on the amount of rewards a user can earn. For example, you might cap rewards at $50 USD per day.

Last updated