Configuration des publicités récompensées

Contenu

Pourquoi utiliser les annonces interstitielles activables ?

Pour maximiser les revenus tout en conservant une excellente expérience utilisateur, Vungle recommande l'utilisation d'interstitiels activables. Apparaissant lors des interruptions naturelles dans l'application, les interstitiels activables offrent des revenus élevés, en particulier si vous suivez notre recommandation pour les rendre impossibles à ignorer. Cet emplacement offre également une excellente expérience utilisateur, car les utilisateurs choisissent de regarder une vidéo et sont récompensés par quelque chose de valeur, comme de la monnaie virtuelle, du contenu premium ou des produits intégrés à l'application. Le montant et le type de récompense accordés à un utilisateur pour la visualisation d'une vidéo dépend entièrement à vous. Il existe deux façons d'y parvenir : les récompenses intégrés à l'application ou les rappels serveur-à-serveur.

Option recommandée : récompenses intégrées aux applications

Vue d'ensemble

Il s'agit d'une alternative à l'utilisation des rappels serveur-à-serveur. Lorsqu'un utilisateur visionne complètement une annonce ou clique sur le bouton de téléchargement, vous pouvez le récompenser directement dans votre application. Le principal avantage de cette approche est qu'elle est assez simple à mettre en œuvre. Si vous désirez quelque chose de rapide et ne craignez pas les attaques par rejeu, ceci devrait suffire.

Vungle offre désormais une variété de formats d'annonces avec des annonces-modèles dynamiques. Contrairement au format d'annonce traditionnel dans lequel la diffusion d'une annonce se compose d'une lecture vidéo suivie d'un écran de fin, nous proposons des modèles où le bouton d'appel à l'action (CTA) est disponible pendant la lecture vidéo. Les utilisateurs qui visionnent toute l'annonce vidéo, de même que ceux qui font clic sur le bouton, devraient être récompensés.

Remarque : Les publicités récompensées sont parfois nommées publicités répondant au mécanisme d'incitation ; les deux termes font toujours référence au même type de publicité. Dans le code SDK et dans notre API de création, nous utilisons le terme « répondant au mécanisme d'incitation ».

Mise en œuvre pour iOS

Implémentez le délégué VungleSDK, consultez pour ça la section « Rappels de délégué » de « Démarrer avec Vungle - iOS SDK v. 5.1 + ».

- (void)vungleWillCloseAdWithViewInfo:(VungleViewInfo *)info placementID:(NSString *)placementID;

Si le rappel vungleSDKwillCloseAdWithViewInfo vous renvoie un dictionnaire viewInfo avec des valeurs « yes » pour les clés completedView ou didDownload, alors l'utilisateur a gagné une récompense.

Mise en œuvre pour Android

Implémentez l'interface EventListener, consultez « Démarrer avec Vungle - Android SDK v. 5.1 + ».

public void onAdEnd (String placementReferenceId, boolean wasSuccessfulView, boolean wasCallToActionClicked)

Si le rappel onAdEnd renvoie « true » pour wasSuccessfulView ou pour wasCallToActionClicked, alors l'utilisateur a gagné une récompense.

Rappels serveur-à-serveur

Vue d'ensemble

Les rappels de serveur-à-serveur permettent de récompenser les utilisateurs pour les visualisations d'annonces avec des monnaies intégrés au jeu ou d'autres récompenses. Lorsqu'un utilisateur visionne complètement une annonce, vous pouvez configurer un rappel des serveurs de Vungle vers le vôtre pour vous informer que l'utilisateur a terminé l'action.

L'un des avantages de cette approche est le contrôle. Cette méthode permet de faire des modifications et des mises à jour directement du côté du serveur, donc il n'est pas nécessaire d'effectuer une mise à jour par « push ». Un autre avantage est la sécurité dans la prévention des attaques par rejeu (lorsqu'une transmission de données valides est répétée ou retardée de façon malveillante ou frauduleuse).

Mise en œuvre

Remarque à propos de la version 5.x du SDK Vungle : À partir de la version 5.1 du SDK, nous avons déplacé l'option de récompense sur le tableau de bord, afin que les éditeurs puissent facilement la modifier sans changer le code. Dans votre tableau de bord, recherchez la case à cocher pour activer ou désactiver l'option de récompense au niveau de l'emplacement en accédant à Application StagePlacements Stage → clic sur l'icône du crayon image1.png → Edit Placement.

image2.jpg

Remarque à propos des versions 1.0 - 4.1 du SDK Vungle :

  • Pour iOS, commencez par définir l'option vunglePlayAdOptionKeyIncentivized de votre objet playAd à « yes ». (Retrouvez plus de détails sur cette option et sur les options de playAd associées ici.)

  • Pour Android, commencez par définir l'option setIncentivized de votre objet AdConfig à « true ». (Retrouvez plus de détails sur cette option et sur les options de playAd associées ici.)

Mise en œuvre de toutes les versions du SDK Vungle

Lorsqu'un utilisateur visionne au moins 80 % d'une publicité récompensée, cette dernière est considérée comme ayant été entièrement visionnée. Vungle effectue ensuite un « ping » sur vos serveurs avec une URL de rappel ressemblant à ceci :

http://acme.com/bugzBunny/reward?uid=%user%

ou à ceci :

http://acme.com/bugzBunny/reward? amount=1&uid=%user%&txid=%txid%&digest=%digest%

Nous recommandons l'utilisation de etxid et edigest, comme suit :

http://acme.com/bugzBunny/reward? amount=1&uid=%user%&etxid=%etxid%&edigest=%edigest%.

Configurez l'URL de rappel dans les paramètres de votre application sur le tableau de bord (comme indiqué ci-dessous).

CallbackURL.gif

La plupart des éditeurs utilisent seulement %user%, plus%txid%, et %digest% pour la sécurité, mais tous les éléments suivants sont disponibles :

Variables

Description

%user%

Le nom d'utilisateur fourni par le SDK Vungle via :

●      iOS : la clé VunglePlayAdOptionKeyUser du dictionnaire des options transmis à playAd()

●      Android : le définisseur setIncentivizedUserId de l'objet de configuration globale de l'annonce transmis à playAd()

%udid%

Un identificateur unique pour l'appareil

%ifa%

●      iOS : un identificateur unique Apple pour l'appareil.

●      Android : ceci renvoie l'ID publicitaire Google

%etxid%

Un ID de transaction unique pour la diffusion complète avec horodatage de serveur ; transactionID:timestamp (recommandé).

%edigest%

Jeton de sécurité pour vérifier que le rappel provient de Vungle ; consultez la section Sécurité pour plus de détails (recommandé).

%txid%

Un ID de transaction unique pour la diffusion complète

%digest%

Jeton de sécurité pour vérifier que le rappel provient de Vungle ; consulter la section Sécurité pour plus de détails

Notez que %user% est la seule variable que vous devez passer. Les autres sont renvoyées à partir des serveurs de Vungle si vous les incluez dans l'URL de rappel.

Configuration des récompenses

Maintenant que vous pouvez récompenser un utilisateur pour avoir regardé une annonce, que lui donnez-vous ? Si vous donnez des gemmes à chaque fois, c'est plutôt simple. Mais que faire si vous voulez faire mieux ? Nous ne disposons pas d'une option intégrée pour la configuration de la récompense, mais voici ce que nous recommandons :

Exemple : Pièces de monnaie et vies

Supposons que votre application contienne à plusieurs niveaux des publicités récompensées : à la fois dans votre boutique et à la fin de toutes les troisièmes parties (« game-over »). Supposons aussi que vous souhaitiez récompenser le joueur avec des pièces de monnaie dans le magasin et avec une vie à la fin du jeu. Pour chaque instance de playAd(), configurez l'utilisateur comme suit :

userName123:coins or userName123:lives 

Ensuite, lorsque vos serveurs reçoivent le rappel de Vungle, analysez la valeur de %user% pour savoir quelle récompense attribuer !

Sécurité

Authentification des rappels

Afin de vérifier que les rappels que vous recevez proviennent de Vungle, cochez la case « Secret Key for Secure Callback » pour votre application. Cela génère une clé secrète semblable à ceci :

4YjaiIualvm8/4wkMBRH8pctlqB1NyzhK3qUGUar+Zc=

Vous pouvez utiliser la clé pour vérifier l'origine du rappel comme suit :

  1. Créez la chaîne brute de vérification de la transaction en concaténant votre clé secrète avec l'ID de la transaction, les deux étant séparés par deux-points comme ceci :
    transactionString = secretKey + ":" + %txid% or %etxid%
  2. Hachez les octets de transactionString en utilisant deux fois l'algorithme SHA-256.

  3. Générez le jeton de vérification de la transaction en codant en hexadécimal les octets de sortie des deux hachages séquentiels SHA-256, et vous obtenez quelque chose comme ceci :
    transactionToken = 870a0d13da1f1670b5ba35f27604018aeb804d5e6ac5c48194b2358e6748e2a8
  4. Vérifiez que le jeton transactionToken généré équivaut au jeton envoyé dans la chaîne de requête du rappel en tant que %digest% ou %edigest%

Prévention des attaques par rejeu

Pour éviter qu'un seul rappel ne soit reproduit à plusieurs reprises par rapport à votre serveur, enregistrez les ID des transactions authentifiées et rejetez les rappels futurs ayant des ID de transaction en double. Étant donné qu'un ID de transaction contient un horodatage, vous pouvez limiter le nombre d'ID de transaction à stocker et vérifier les doublons en appliquant un temps d'arrêt pour les rappels opportuns comme suit :

  1. Faites l'extraction de l'horodatage (en millisecondes) à partir de l'ID de transaction comme ceci :
    transactionMillis = transactionId.substringAfter(":")
  2. Vérifiez que transactionMillis est plus grand que le temps d'arrêt et que transactionId n'a pas été rencontré depuis le temps d'arrêt.

Exemple de code

Ces extraits d'exemples de code vous aideront à programmer la sécurité des rappels serveur-à-serveur. Il existe des exemples pour : Node.js, Java, Python et Ruby. Notez que :

  • L'ID transaction_id peut être %txid% ou %etxid% (recommandé).
  • Pour %etxid%, le hachage de vérification doit être %edigest%.

Node.js

var crypto = require('crypto');

function isTransactionValid(secret, transaction_id, provided_hash) {
  return isTransactionRecent(transaction_id) &&
         isTransactionNew(transaction_id)    &&
         createSecurityDigest(secret, transaction_id) === provided_hash;
}

function getTransactionTimestamp(transaction_id) {
  return parseInt(transaction_id.split(":")[1], 10) || null;
}

function isTransactionRecent(transaction_id) {
  // Does the transaction have a reasonable format?
  var tx_timestamp = getTransactionTimestamp(transaction_id);
  if (tx_timestamp === null) { return false; }

  // Is the transaction within a reasonable time range?
  var now = new Date().getTime();
  var time_diff = now - tx_timestamp;
  var hour_in_future = -1000 * 60 * 60, three_days_ago = 1000 * 60 * 60 * 24 * 3;
  return (time_diff < three_days_ago && time_diff > hour_in_future);
}

// Have we seen this transaction before?
// NOTE: To scale beyond just this one node process, you'll need to put this in some
// sort of centralized datastore. Any one that supports atomic insertions into a set
// should do. The Redis database, with its SADD command, would be a good place to start.
var known_transactions = {};
function isTransactionNew(transaction_id) {

// For %etxid%, extract unique event id from etxid, which is the string before “:”, and use it to identify unique ad events
if (known_transactions[transaction_id]) { return false; } known_transactions[transaction_id] = true; return true; } function createSecurityDigest(secret, transaction_id) { var firsthash = crypto.createHash("sha256").update(secret + ":" + transaction_id).digest("binary"); return crypto.createHash("sha256").update(firsthash,"binary").digest("hex"); }

Java

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;

public class ServerCallbackSecurityExample {
  private static final long MAX_CALLBACK_AGE_MILLIS = 7 * 24 * 60 * 60 * 1000;  // 7 days

  /**
   * Checks that a transaction is recent enough, signed with the secretKey, and not a duplicate.
   * 
   * @param transactionId the transaction ID.
   * @param secretKey a shared secret.
   * @param verificationDigest the verification digest sent in the callback querystring.
   */
  public boolean isValidTransaction(String transactionId, String secretKey, String verificationDigest) throws NoSuchAlgorithmException {
    return isRecentTransaction(transactionId)
      && isDigestValid(transactionId, secretKey, verificationDigest)
      && isNewTransaction(transactionId);
  }

  protected boolean isRecentTransaction(String transactionId) {
    boolean isRecent = false;
    try {
      final long minCallbackAgeMillis = System.currentTimeMillis() - MAX_CALLBACK_AGE_MILLIS;
      final long transactionMillis = getTransactionMillis(transactionId);
      isRecent = (transactionMillis > minCallbackAgeMillis);
    }
    catch (ParseException exception) {
      // invalid transaction ID format
    }
    return isRecent;
  }

  protected boolean isDigestValid(String transactionId, String secretKey, String verificationDigest) throws NoSuchAlgorithmException {
    return createSecurityDigest(transactionId, secretKey)
      .equals(verificationDigest);
  }

  protected boolean isNewTransaction(String transactionId) {
    // TODO if transactionId is new, store the transactionId with its associated transactionMillis
// For %etxid%, extract unique event ID from etxid, which is the string before ":", and use it to identify unique ad events
return true; } protected long getTransactionMillis(String transactionId) throws ParseException { final int transactionMillisIndex = transactionId.lastIndexOf(":") + 1; try { if (transactionMillisIndex > 0 && transactionMillisIndex < transactionId.length()) { return Long.parseLong( transactionId.substring(transactionMillisIndex)); } else { throw new ParseException("No timestamp in transaction ID", transactionMillisIndex); } } catch (NumberFormatException exception) { throw new ParseException("Invalid transaction ID timestamp", transactionMillisIndex); // invalid timestamp } } protected String createSecurityDigest(String transactionId, String secretKey) throws NoSuchAlgorithmException { final String verificationString = secretKey + ":" + transactionId; final MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); return toHexString( messageDigest.digest( messageDigest.digest( verificationString.getBytes(Charset.forName("US-ASCII"))))); } protected String toHexString(byte[] bytes) { final StringBuffer hexStringBuffer = new StringBuffer(); for (final byte byt : bytes) { hexStringBuffer.append( Integer.toString((byt & 0xff) + 0x100, 16) .substring(1)); } return hexStringBuffer.toString(); } }

Python

import hashlib, time

def isTransactionValid(secret, transaction_id, input_hash):
  """Returns whether this transaction id / hash should be considered valid"""
  return isTransactionIDRecent(transaction_id) and \
         isTransactionIDNew(transaction_id)    and \
         createSecurityDigest(secret, transaction_id) == input_hash

def getTransactionTimestamp(transaction_id):
  """Will return the unix time (in milliseconds) of this transaction, or None if invalid"""
  parsed = transaction_id.split(":")
  try:
    return int(parsed[1]) if len(parsed) == 2 else None
  except ValueError as e:
    return None

def isTransactionIDRecent(transaction_id):
  """Is this transaction within a reasonable time range?"""
  tx_time = getTransactionTimestamp(transaction_id)

  # Handle bad transaction:
  if tx_time is None:
    return False

  # Handle bad transaction times:
  now = int(time.time() * 1000)
  three_days_ago = now - (1000 * 60 * 60 * 24 * 3)
  one_hour_from_now = now + (1000 * 60 * 60)
  return ( three_days_ago < tx_time < one_hour_from_now )

def isTransactionIDNew(transaction_id, known_transaction_ids=set()):
  """Is this a duplicate transaction?
     NOTE: We only use the Python set for simplicity. For better / more centralized solutions,
     you can use any datastore that supports atomic insertion into a set. For starters, try
     Redis with its "SADD" command."""

"""For %etxid%, extract unique event ID from etxid, which is the string before ":", and use it to identify unique ad events"""
if transaction_id in known_transaction_ids: return False # Else, valid: known_transaction_ids.add(transaction_id) return True def createSecurityDigest(secret, transaction_id): """Will return the string that the security hash should have been""" firsthash = hashlib.sha256() firsthash.update(secret + ":" + transaction_id) secondhash = hashlib.sha256() secondhash.update(firsthash.digest()) return secondhash.hexdigest()

Ruby

require "openssl"
require "digest/sha2"
require "base64"
require "time"

# just some helper methods, ignore if using Rails
class Fixnum
  SECONDS_IN_DAY = 24 * 60 * 60
  HOURS_IN_DAY = 24 * 60

  def days
    self * SECONDS_IN_DAY
  end

  def hour
    self * 60 * 60
  end

  def ago
    (Time.now - self)
  end

  def from_now
    (Time.now + self)
  end
end

def transaction_valid?(secret, txid, input_hash)
  transaction_id_recent?(txid) && transaction_id_new?(txid) && create_security_digest(secret, txid) == input_hash
end

def transaction_timestamp(txid)
  arr = txid.split(":")
  return arr[1].to_i if arr.size == 2
end

def transaction_id_recent?(txid)
  tx_time = transaction_timestamp(txid)
  return false if tx_time.nil?

  now = Time.now.to_i
  three_days_ago = 3.days.ago
  one_hour_from_now = 1.hour.from_now
  three_days_ago.to_i < tx_time && tx_time < one_hour_from_now.to_i
end

# For %etxid%, extract unique event ID from etxid, which is the string before ":", and use it to identify unique ad events def transaction_id_new?(txid, transactions = []) return false if transactions.include?(txid) transactions << txid return true end def create_security_digest(secret, txid) verification_string = "#{secret}:#{txid}" first_digest = Digest::SHA2.new(256).update(verification_string) Digest::SHA2.new(256).hexdigest(first_digest.digest) end
Vous avez d’autres questions ? Envoyer une demande

Commentaires