Configurar anuncios con recompensa

Contenido

¿Por qué usar anuncios intersticiales a opción de los usuarios?

Para maximizar los ingresos mientras se mantiene una excelente UX, Vungle recomienda el uso de anuncios intersticiales a opción de los usuarios. Se muestra en pausas naturales en la aplicación, los anuncios intersticiales a opción de los usuarios proporcionan altos ingresos, especialmente si sigue nuestra recomendación para hacer que no se puedan omitir. Esta ubicación también proporciona una excelente UX, porque los usuarios deciden mirar un video y son recompensados con algo de valor, como divisa virtual, contenido premium o bienes en la aplicación. El monto y el tipo de recompensa proporcionada a un usuario por completar la visualización de un video es completamente su decisión. Hay dos formas de lograr esto: recompensas en la aplicación o devoluciones de llamadas de servidor a servidor.

Opción recomendada: Recompensas en la aplicación

Descripción general

Esta es una alternativa al uso de las devoluciones de llamadas de servidor a servidor. Cuando un usuario completa correctamente la visualización de un anuncio o hace clic en el botón de descarga, puede recompensarlo directamente en la aplicación. El beneficio principal de este método es que es bastante simple de implementar. Si desea un método rápido y no está preocupado por los ataques de repetición, esto debería funcionar.

Vungle ahora ofrece una variedad de formatos de anuncios con anuncios de plantilla dinámica. A diferencia del formato tradicional de los anuncios, en el que la reproducción de un anuncio consiste en la reproducción de un video seguida de una tarjeta de objetivos, ofrecemos plantillas en las que el botón llamada de acción (CTA, por sus siglas en inglés) está disponible durante la reproducción del video. Los usuarios que finalizan la visualización del anuncio de video, así como aquellos que hacen clic en el botón, deberían recibir una recompensa.

Nota: Los anuncios con recompensa se mencionan en algunos casos como anuncios incentivados; ambos términos se refieren siempre al mismo tipo de anuncio publicitario. En el código de SDK y en nuestro API de reportes, usamos el término 'anuncios incentivados'.

Implementación para iOS

Implemente el delegado VungleSDK, que se analiza en la sección “Devoluciones de llamadas de delegados” de Introducción a SDK v. 5.1+ de Vungle para iOS.

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

Si la devolución de llamadas vungleSDKwillCloseAdWithViewInfo le pasa un diccionario viewInfo con valores de ‘sí’ para las claves completedView o didDownload, el usuario ha ganado la recompensa.

Implementación para Android

Implemente la interfaz de EventListener que se analiza en la sección Introducción a SDK v. 5.1+ de Vungle para Android.

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

Si la devolución de llamadas onAdEnd devuelve ‘verdadero’ para wasSuccessfulView o wasCallToActionClicked, el usuario ganó la recompensa.

Devoluciones de llamadas de servidor a servidor

Descripción general

Las devoluciones de llamadas de servidor a servidor le permiten recompensar a los usuarios por las visualizaciones de anuncios con divisas en el juego u otras recompensas. Cuando un usuario completa correctamente la visualización de un anuncio, puede configurar una devolución de llamada desde los servidores de Vungle al suyo para notificarle que el usuario ha completado la acción.

Un beneficio de este método es el control. Este método le permite realizar cambios y actualizaciones directamente del lado del servidor, de modo que no hay necesidad de motivar una actualización. Otro beneficio es la seguridad para evitar ataques de repetición (cuando una transmisión de datos válida es repetida o demorada de manera maliciosa o fraudulenta).

Implementación

Nota para Vungle SDK v. 5.x: Desde el SDK v. 5.1, hemos trasladado la opción con recompensa al panel de control para que los editores puedan cambiar fácilmente la opción sin cambiar el código. En el panel de control, busque la casilla de verificación para habilitar o deshabilitar la opción con recompensa en el nivel de la ubicación al navegar hasta Etapa de aplicaciónEtapa de ubicaciones → haga clic en el ícono del lápiz image1.png → Editar ubicación.

image2.jpg

Nota para Vungle SDK v.1.0 - v.4.1:

  • Para iOS, comience al configurar la opción vunglePlayAdOptionKeyIncentivized en su objeto playAd a ‘sí’. (Lea más detalles sobre esta opción, y sobre las opciones playAd relacionadas aquí).

  • Para Android, comience al configurar la opción setIncentivized en su objeto AdConfig a ‘verdadero’. (Lea más detalles sobre esta opción, y sobre las opciones playAd relacionadas aquí).

Implementación para todas las versiones del SDK de Vungle

Cuando un usuario mira el 80% o más de un anuncio incentivado, se considera una visualización finalizada. Luego, Vungle mandará ping a sus servidores con una URL de devolución de llamadas, similar a esta:

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

o a esta:

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

Recomendamos utilizar etxid y edigest, como esta:

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

Configure la URL de devolución de llamadas en la Configuración de su aplicación en el panel de control (como se muestra a continuación).

CallbackURL.gif

La mayoría de los editores solo utilizan %user%, plus%txid% y %digest% por seguridad, pero todos los siguientes están disponibles:

Variables

Descripción

%user%

El nombre de usuario proporcionado al SDK de Vungle a través de:

●      iOS: la clave VunglePlayAdOptionKeyUser del diccionario de opciones pasó a playAd()

●      Android: el setter setIncentivizedUserId del objeto de configuración del anuncio global pasó a playAd()

%udid%

Un identificador único para el dispositivo

%ifa%

●      iOS: identificador único de Apple para el dispositivo.

●      Android: devolverá la ID de anunciante de Google

%etxid%

Una ID de transacción única para la visualización finalizada con la marca de la hora del servidor; transactionID:timestamp (recomendada).

%edigest%

Token de seguridad para verificar que la devolución de llamadas vino de Vungle; consulte la sección de Seguridad para ver más detalles (recomendado).

%txid%

Un ID de transacción único para la visualización finalizada

%digest%

Token de seguridad para verificar que la devolución de llamadas vino de Vungle; consulte la sección Seguridad para obtener más detalles

Tenga en cuenta que %user% es la única variable que debe transmitir. El resto volverá desde los servidores de Vungle si las incluye en la URL de devolución de llamadas.

Configuración de la recompensa

Ahora que puede recompensar a un usuario por ver un anuncio, ¿qué le daría? Si va a entregar gemas es bastante simple. ¿Pero qué sucede si desea usar opciones más avanzadas? No tenemos una opción incorporada para la configuración de recompensa, pero esto es lo que recomendamos:

Ejemplo: monedas vs. vidas

Supongamos que su aplicación tiene anuncios incentivados en varios lugares: en su tienda y cada tercer “fin del juego”. Usted desea recompensar al jugador con monedas en la tienda y con una vida al terminar el juego. Por cada caso de playAd(), configure al usuario de la siguiente manera:

userName123:coins or userName123:lives 

Luego, cuando sus servidores reciban la devolución de llamadas de Vungle, analice %user% para buscar la recompensa correcta.

Seguridad

Autenticar devoluciones de llamadas

Para verificar las devoluciones de llamadas que reciba originadas desde Vungle, seleccione la Clave secreta para la casilla de verificación Asegurar devolución de llamadas para su aplicación. Esto generará una clave secreta como esta:

4YjaiIualvm8/4wkMBRH8pctlqB1NyzhK3qUGUar+Zc=

Puede usar la clave para verificar el origen de la devolución de llamadas de la siguiente manera:

  1. Cree la cadena de verificación de transacción en bruto al concatenar su clave secreta con la ID de la transacción, separada por dos puntos, de esta manera:
    transactionString = secretKey + ":" + %txid% or %etxid%
  2. Use la función hash con los bytes de la transactionString dos veces usando el algoritmo SHA-256.

  3. Genere el token de verificación de la transaction al codificar hexadecimalmente los bytes de salida de dos rondas secuenciales de hashing SHA-256, que se parecerá a este:
    transactionToken = 870a0d13da1f1670b5ba35f27604018aeb804d5e6ac5c48194b2358e6748e2a8
  4. Verifique que el transactionToken que generó sea igual al que envió en la cadena de consulta de la devolución de llamadas como %digest% o %edigest%

Evitar ataques de repetición

Para evitar que una sola devolución de llamadas se repita múltiples veces en su servidor, almacene las ID de transacción autenticadas y rechace las devoluciones de llamadas con ID de transacción duplicadas. Debido a que una ID de transacción contiene una marca de tiempo, puede limitar la cantidad de ID de transacción que debe almacenar y buscar duplicados al aplicar un punto límite para las devoluciones de llamadas temporales de la siguiente manera:

  1. Extraiga la marca de tiempo (en milisegundos) de la ID de transacción de la siguiente manera: 
    transactionMillis = transactionId.substringAfter(":")
  2. Verifique que transactionMillis sea posterior a su punto límite y que transactionId no se haya encontrado desde su punto límite.

Ejemplo de código

Estos bits del código de muestra lo ayudarán a implementar la seguridad de las devoluciones de llamadas de servidor a servidor. Tenemos ejemplos para: Node.js, Java, Python y Ruby. Tome en cuenta que:

  • El transaction_id puede ser %txid% o %etxid% (recomendado).
  • Para %etxid%, el resumen criptográfico de la verificación debe ser %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
¿Tiene más preguntas? Enviar una solicitud

Comentarios