In-game Rewards
Receiving In-game Rewards
Any websites can provide rewards into supporting games that are published by Noctua. To make your game support delivery of the rewards, this document will guide you on the implementation.
Note
In-game rewards are delivered by Noctua SDK Backend to your game server, and not directly to the game client.
The delivery from the game server to the game client is out of the scope of this document.
Callback for Reward Items Delivery
To ensure players receive their reward items, you need to set up a callback webhook. This webhook will be notified by the Noctua SDK Backend when a reward item needs to be delivered.
- Implement an API endpoint in your game server to receive reward notifications
- Register this webhook URL in the Noctua Developer Dashboard. You can contact us if you need help with this.
- When a reward is granted, your webhook will receive a
POST
request with reward details
Callback Idempotency
Please ensure that your callback handler is idempotent. This means that when it is called multiple times with the same exact request payload, it should produce an effect only once—for example, ensuring that an item is delivered only once per reward.
Request Headers
The webhook request will include a custom header for security validation:
X-CALLBACK-TOKEN: <your_secret_token>
Validating the X-CALLBACK-TOKEN
To ensure the callback is genuine and comes from the Noctua SDK Backend:
- Retrieve the
X-CALLBACK-TOKEN
from the request headers. - Compare this token with the secret token provided in your Noctua Developer Dashboard.
- If the tokens match, process the webhook. If not, reject the request.
Here's a simple example of how to validate the token
<?php
function validate_callback_token($headers) {
$expected_token = 'your_secret_token_from_noctua_dashboard';
// Check if the X-CALLBACK-TOKEN header exists
if (isset($headers['X-CALLBACK-TOKEN'])) {
$received_token = $headers['X-CALLBACK-TOKEN'];
if ($received_token === $expected_token) {
// Token is valid, process the webhook
return true;
}
}
// Invalid token or missing header, reject the request
return false;
}
// Usage example:
$headers = getallheaders(); // This function gets all HTTP headers
if (validate_callback_token($headers)) {
// Process the webhook
process_webhook(json_decode(file_get_contents('php://input'), true));
} else {
// Reject the request
http_response_code(403);
echo 'Invalid token';
}
?>
Request Body
The webhook will receive a JSON payload with the following structure:
{
"data": {
"reward_id": 12345678,
"player_id": 485934,
"ingame_role_id": "gmu8950",
"ingame_server_id": "srv8976",
"reward_time": "2024-01-01T12:00:00Z",
"title": "Birthday Reward",
"body": "This is our gift to you.",
"type": "ingame_item",
"items": [
{
"id": "ingame_reward_id_123",
"product_id": "foobar.pack1",
"image_url": "https://upload.wikimedia.org/wikipedia/en/6/63/Feels_good_man.jpg",
"quantity": 1
}
],
},
"signed_data": "eyJhbGciOiJFUzI1NiIsIm..."
}
The data
field contains raw data while the signed_data
is the JWS (JSON Web Signature) version of the same data. It is recommended to load from signed_data
instead of from data
.
JWKS
The JWKS used to verify signed_data
is the same as the one used to verify user access token.
Here are some examples on how to verify and load the data from signed_data
.
<?php
use Jose\Component\Core\JWK;
use Jose\Component\Core\JWKSet;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\Serializer\CompactSerializer;
// Fetch JWKS from the server
$jwksJson = file_get_contents('https://sdk-api-v2.noctuaprojects.com/api/v1/auth/jwks');
$jwks = JWKSet::createFromJson($jwksJson);
$signedData = 'SIGNED_DATA_STRING';
// Parse the signed data
$serializer = new CompactSerializer();
$jws = $serializer->unserialize($signedData);
// Get the key ID from the signed data header
$headers = $jws->getSignature(0)->getProtectedHeader();
$kid = $headers['kid'] ?? null;
if ($kid === null) {
throw new Exception('No "kid" found in signed data header');
}
// Find the corresponding key in the JWKS
$jwk = $jwks->get($kid);
if ($jwk === null) {
throw new Exception('No matching key found in JWKS');
}
// Verify the signature
$algorithm = new ES256();
$algorithmManager = new \Jose\Component\Core\AlgorithmManager([$algorithm]);
$jwsVerifier = new JWSVerifier($algorithmManager);
if (!$jwsVerifier->verifyWithKey($jws, $jwk, 0)) {
throw new Exception('Signed data signature verification failed');
}
// If we reach here, the signed data is valid
$payload = json_decode($jws->getPayload(), true);
// Now $payload contains the decoded signed data