Comentario del usuario sobre el pedido.
Webhooks (1.0)
Los webhooks son notificaciones sobre eventos que se producen en el sistema. Cuando ocurre un evento específico, Xsolla envía una solicitud HTTP, en la cual se transmiten los datos del evento, a su aplicación. Generalmente, se trata de una solicitud POST en formato JSON.
Ejemplos de eventos:
- interacción del usuario con un catálogo de artículos
- pago o cancelación de un pedido
Cuando se produce un evento definido, Xsolla lo notifica a su sistema mediante un webhook. En consecuencia, puede realizar acciones como:
- reponer el saldo del usuario
- efectuar una devolución de pago
- abonar o cargar nuevos artículos en la cuenta de usuario
- empezar a proveer una suscripción
- bloquear a un usuario si hay una sospecha de fraude
Ejemplo de un flujo de trabajo de webhook de procesamiento de pagos:
Nota
Dependiendo de la solución utilizada y del tipo de integración, el conjunto de webhooks y la secuencia de interacciones pueden ser distintos a los del ejemplo proporcionado.
Videoguía para la integración de webhooks con Xsolla:
Configuración de Webhooks para trabajar con productos y soluciones de Xsolla:
| Producto/Solución | Obligatorio/Opcional | ¿Para qué se utilizan los webhooks? |
|---|---|---|
| Payments | Obligatorio |
|
| Store | Obligatorio |
|
| Game Sales | Opcional | Para vender claves del juego, la validación del usuario y el abono de los artículos no son necesarios. Puede conectar los webhooks si desea recibir información sobre eventos, como el pago o la cancelación de pedidos. Si conecta webhooks, es esencial procesar todos los webhooks requeridos entrantes. |
| Suscripciones | Opcional | Recibir información sobre la creación, actualización o cancelación de una suscripción. También puede solicitar información mediante la API. |
| Web Shop | Obligatorio |
|
| Digital Distribution Hub | Obligatorio |
Consulte la documentación para obtener información detallada sobre cómo establecer webhooks para el Digital Distribution Hub. |
| Login | Opcional | Recibir información sobre un evento:
Consulte la documentación de Login para obtener información detallada sobre cómo establecer webhooks. |
Si utiliza productos y soluciones que requieren trabajar con webhooks, active y pruebe los webhooks en su Cuenta del editor y establezca su procesamiento. Cuando se producen eventos específicos, los webhooks se envían secuencialmente. Por lo tanto, si no procesa uno de los webhooks, no se enviarán los posteriores. La lista de webhooks requeridos se muestra a continuación.
Se han configurado dos opciones de envío de webhooks del lado de Xsolla para la compra y la devolución de artículos en el sitio web: los datos de pago y de la transacción, así como la información sobre los artículos comprados, se pueden enviar por separado o se pueden combinar en un webhook.
Recibir información en webhooks combinados:
Si se registró en Cuenta del editor después del 22 de enero de 2025, recibirá toda la información en los webhooks Successful payment for order (order_paid) y Order cancellation (order_canceled). En este caso, no es necesario procesar los webhooks Pago (payment) y Reembolso (refund).
Recibir información en webhooks separados:
Si se registró en Cuenta del editor el 22 de enero de 2025 o antes, recibirá los siguientes webhooks:
- Pago (
payment) y Reembolso (refund) con información sobre los datos de pago y los detalles de la transacción. - Successful payment for order (
order_paid) y Order cancellation (order_canceled) con información sobre los artículos comprados.
Debe procesar todos los webhooks entrantes. Para cambiar a la nueva opción con la recepción de webhooks combinados, contacte con sus gestores de éxito del cliente o escriba a csm@xsolla.com.
Para el funcionamiento completo de la tienda del juego y la gestión de pagos, es necesario implementar el procesamiento de los principales webhooks.
Si recibe webhooks combinados:
| Nombre y tipo de webhook | Descripción |
|---|---|
Validación del usuario >Validación del usuario (user_validation) | Se envía en diferentes etapas del proceso de pago para garantizar que el usuario está registrado en el juego. |
> Webhooks combinados > de Game services Successful payment for order (order_paid) | Contiene datos de pago, detalles de la transacción e información sobre los artículos comprados. Utilice los datos del webhook para añadir artículos al usuario. |
Servicios de juego > Webhooks combinados >Cancelación del pedido (order_canceled) | Contiene datos del pago cancelado, detalles de la transacción e información sobre los artículos comprados. Utilice los datos del webhook para eliminar los artículos comprados. |
Si recibe webhooks separados:
| Nombre y tipo de webhook | Descripción |
|---|---|
Validación del usuario >Validación del usuario (user_validation) | Se envía en diferentes etapas del proceso de pago para garantizar que el usuario está registrado en el juego. |
Pagos >Pago (payment) | Contiene los datos del pago y los detalles de la transacción. |
> Webhooks separados >de Game services Successful payment for order (order_paid) | Contiene información sobre los artículos comprados. Utilice los datos del webhook para añadir artículos al usuario. |
Pagos >Reembolso (refund) | Contiene los datos del pago y los detalles de la transacción. |
Servicios de juego > Webhooks separados >Cancelación del pedido (order_canceled) | Contiene información sobre los artículos comprados y el ID de la transacción cancelada. Utilice los datos del webhook para eliminar los artículos comprados. |
Si la personalización del catálogo de artículos se implementa en el lado de su aplicación, establezca el procesamiento del webhook Personalización del catálogo en el lado del socio.
Nota
Para recibir pagos reales, solo es necesario firmar el acuerdo de licencia e implementar el procesamiento de los webhooks:
- Payment, Successful payment for order y User validation si recibe webhooks separados
- Successful payment for order y User validation si recibe webhooks combinados.
Para gestionar automáticamente los planes de suscripción, es necesario implementar el procesamiento de los principales webhooks:
- Validación del usuario (
user_validation): se envía en diferentes etapas del proceso de pago para garantizar que el usuario esté registrado en el juego. - Pago (
payment): se envía cuando se paga un pedido y contiene los datos del pago y los detalles de la transacción. - Suscripción creada (
create_subscription): se envía cuando se ha procesado correctamente un webhook de Pago o el usuario ha adquirido una suscripción con un periodo de prueba. Contiene los detalles de la suscripción adquirida y los datos del usuario. Use los datos del webhook para agregar una suscripción al usuario. - Suscripción actualizada (
update_subscription): se envía cuando se renueva o modifica una suscripción, cuando se ha procesado correctamente un webhook de Pago. Contiene los detalles de la suscripción adquirida y los datos del usuario. Use los datos del webhook para ampliar la suscripción del usuario o cambiar los parámetros de la suscripción. - Reembolso (
refund): se envía cuando se cancela un pedido y contiene los datos del pago cancelado y los detalles de la transacción. - Suscripción cancelada (
cancel_subscription): se envía cuando se ha procesado correctamente un webhook de Reembolso o se ha cancelado la suscripción por otro motivo. Contiene información sobre la suscripción y los datos del usuario. Use los datos del webhook para sustraer al usuario las suscripciones adquiridas.
Para habilitar la recepción de webhooks:
- En el proyecto en Cuenta del editor vaya a Project settings > Webhooks.
- En el campo Webhook server, especifique la URL de su servidor en el que desea recibir webhooks en el formato
https://example.com. También puede especificar la URL que encuentre en una herramienta para probar webhooks.
Aviso
El protocolo HTTPS se utiliza para transferir datos; el protocolo HTTP no es compatible.
- Genere una clave secreta:
- En la sección Secret keys, haga clic en Add key.
- En la ventana modal que se abre, introduzca el nombre de la clave que le permita identificarla en la lista general.
- Haga clic en Create key.
- Haga clic en Copy secret y guarde la clave creada en un lugar seguro.
- Haga clic en Done.
- Confirme que ha guardado la clave y haga clic en Ok, close.
Aviso
Recomendaciones:
- Guarde la clave secreta generada en un lugar seguro. Puede ver la clave en Cuenta del editor solo una vez cuando se crea.
- No comparta su clave secreta con nadie.
- La clave secreta debe guardarse en su servidor y nunca en binarios o en el front-end.
- Haga clic en Enable webhooks.
Tenga en cuenta
Para probar los webhooks, puede seleccionar cualquier sitio web específico, como webhook.site, o una plataforma, como ngrok.
Aviso
No es posible enviar webhooks a diferentes URL a la vez. Lo que sí se puede hacer en Cuenta del editor es indicar primero una URL de prueba y, a continuación, sustituirla por la URL real.
Para deshabilitar la recepción de webhooks:
- En el proyecto en Cuenta del editor vaya a Project settings > Webhooks.
- Haga clic en Disable webhooks.
Actualizar las claves secretas con frecuencia mejora la seguridad de su integración. Puede crear hasta 5 claves secretas en su proyecto para habilitar su rotación. Para ello:
- En Project settings > Webhooks, haga clic en Add key.
- En la ventana modal que se abre, introduzca el nombre de la clave que le permita identificarla en la lista general.
- Haga clic en Create key.
- Haga clic en Copy secret y guarde la clave creada.
- Haga clic en Done.
- Confirme que ha guardado la clave y haga clic en Ok, close.
Aviso
Recomendaciones:
- Guarde la clave secreta generada en un lugar seguro. Puede ver la clave en Cuenta del editor solo una vez cuando se crea.
- No comparta su clave secreta con nadie.
- La clave secreta debe guardarse en su servidor y nunca en binarios o en el front-end.
Solo puede haber una clave secreta activa por proyecto. Si quiere cambiarla, haga clic en Set as active en la fila de otra clave y confirme la acción. Cuando haya completado correctamente la migración a una nueva clave, le recomendamos que elimine las claves desactivadas.
Para los webhooks de la sección Payments and Store, hay opciones de configuración avanzada disponibles. Aparecerán automáticamente en el bloque General settings después de hacer clic en el botón Get webhooks.
Nota
Si no se muestra la configuración avanzada, asegúrese de que la recepción de webhooks está conectada en la configuración general y de que se encuentra en la pestaña Testing > Payments and Store.
En esta sección puede configurar la recepción de información adicional en webhooks. Para ello, active la opción. La línea de cada permiso indica los webhooks que se verán afectados al cambiar la configuración.
| Conmutador | Descripción |
|---|---|
| Mostrar información sobre la cuenta de pago guardada (solo se muestra si se registró en Cuenta del editor el 22 de enero de 2025 o antes y recibe webhooks separados). | La información sobre el método de pago guardado se transmite en el objeto personalizado payment_account. |
| Mostrar información sobre las transacciones realizadas a través de los métodos de pago guardados. | La información se transmite en los siguientes parámetros personalizados del webhook:
|
Añadir el objeto order al webhook (solo se muestra si se registró en Cuenta del editor el 22 de enero de 2025 o antes y recibe webhooks separados). | La información sobre el pedido se transmite en el objeto order del webhook Pago. |
| Enviar solo los parámetros de usuario necesarios, sin incluir datos confidenciales. | Solamente la siguiente información sobre el usuario se transmite en el webhook:
|
| Enviar parámetros personalizados. | La información sobre los parámetros de token personalizados se transmite en el webhook. |
| Mostrar el número BIN y el sufijo de la tarjeta. | La siguiente información sobre el número de tarjeta bancaria se transmite en el webhook:
|
| Mostrar la marca de la tarjeta. | La marca de la tarjeta empleada para realizar el pago. Por ejemplo, Mastercard o Visa. |
| Mostrar información sobre el motivo del reembolso. | Información detallada sobre los motivos del reembolso. |
| Mostrar la retención fiscal del país y las comisiones de adquisición de usuarios. | Los objetos payment_details.country_wht y payment_details.user_acquisition_fee se transmitirán en el webhook. Esta opción está activada por defecto. |
| Enviar información de 3DS. | El objeto cards que contiene datos sobre la verificación 3-D Secure se transmitirá en el webhook. |

Probar los webhooks ayuda a asegurar la correcta configuración del proyecto tanto en su lado como en el lado de Xsolla.
Si los webhooks están establecidos correctamente, aparecerá una sección de prueba de webhooks bajo la sección de configuración de webhooks.
La sección de pruebas de Cuenta del editor varía en función de la opción de recepción de webhooks.
Si se registró en Cuenta del editor después del 22 de enero de 2025, recibirá webhooks combinados:
| Nombre de la pestaña para pruebas de webhooks | Nombre y tipo de webhook |
|---|---|
| Payments and store | Validación del usuario >Validación del usuario (user_validation) |
> Webhooks combinados > de Game services Successful payment for order (order_paid) | |
Servicios de juego > Webhooks combinados >Cancelación del pedido (order_canceled) | |
| Subscriptions | Validación del usuario >Validación del usuario (user_validation) |
Pagos >Pago (payment) |
Si se registró en Cuenta del editor el 22 de enero de 2025 o antes, recibirá webhooks separados:
| Nombre de la pestaña para pruebas de webhooks | Nombre y tipo de webhook |
|---|---|
| Store | > Webhooks separados >de Game services Successful payment for order (order_paid) |
Servicios de juego > Webhooks separados >Cancelación del pedido (order_canceled) | |
| Payments | Validación del usuario >Validación del usuario (user_validation) |
Pagos >Pago (payment) | |
| Subscriptions | Validación del usuario >Validación del usuario (user_validation) |
Pagos >Pago (payment) |
Tenga en cuenta
Si aparece un aviso de que la prueba no se ha superado en la sección de pruebas, verifique la configuración de la respuesta del webhook en su agente de escucha del webhook. Los motivos de los errores en la prueba se indican en los resultados de la prueba.
Ejemplo:
Cuando utiliza el sitio especializado webhook.site para la prueba.
Aparece un error en la sección Testing response to invalid signature.
Esto ocurre porque Xsolla envía un webhook con una firma incorrecta y espera que su controlador responda con un código HTTP 4xx que especifique el código de error INVALID_SIGNATURE.
webhook.site envía un código HTTP 200 en respuesta a todos los webhooks, incluyendo un webhook con una firma incorrecta. No se puede obtener el código HTTP 4xx esperado, por lo que aparece un error en el resultado de la prueba.
A continuación se describe el proceso de pruebas para el escenario con webhooks combinados.
En la pestaña Payments and Store, puede probar los siguientes webhooks:
- Validación del usuario (
user_validation) - Successful payment for order (
order_paid) - Cancelación del pedido (
order_canceled)
Para probar los webhooks:
- En la sección de pruebas de webhooks, vaya a la pestaña Payments and Store.
- En la lista desplegable, seleccione el tipo de artículo. Si aún no ha configurado este tipo de artículo en Cuenta del editor, haga clic en el botón para configurarlo. Una vez creado el artículo, vuelva a la sección de pruebas del webhook y continúe con el siguiente paso.
- Rellene los campos necesarios:
- User ID: al realizar pruebas, puede utilizar cualquier combinación de letras y números.
- Introduzca cualquier valor en el campo Xsolla order ID.
- Xsolla invoice ID: ID de la transacción en Xsolla. Al hacer pruebas, puede usar cualquier valor numérico.
- Invoice ID: ID de transacción en el juego. Al hacer pruebas, puede usar cualquier combinación de letras y números. No es un parámetro obligatorio para que el pago se realice correctamente, pero puede indicarlo para vincular el ID de transacción de su sistema con el de Xsolla.
- Amount: importe del pago. Al hacer pruebas, puede utilizar cualquier valor numérico.
- Currency: seleccione una moneda de la lista desplegable.
- Seleccione el SKU de los artículos en la lista desplegable e indique la cantidad. Puede seleccionar varios artículos del mismo tipo haciendo clic en + y añadiéndolos en una nueva línea.
- Haga clic en Test webhooks.
Los webhooks User validation, Successful payment for order y Order cancellation con los datos especificados se envían a la URL facilitada. Los resultados de la prueba de cada tipo de webhook se muestran debajo del botón Test webhooks.
Si la casilla Use public user ID está marcada en Project settings
Integration settings, el webhook Búsqueda del usuario también se enviará a la URL de su servidor de webhooks y se mostrará el resultado de la prueba.
Para cada webhook, tiene que establecer el procesamiento de ambos escenarios: uno, satisfactorio, y el otro, fallido.
Nota
Para probar los webhooks, debe tener al menos un plan de suscripción creado en Cuenta del editor en Items catalog > Subscriptions.
En la pestaña Subscriptions puede probar los siguientes webhooks:
- Validación del usuario (
user_validation) - Pago (
payment)
Nota
Encontrará información detallada sobre cómo probar otros escenarios de gestión de suscripciones en la guía de integración.
Para probar los webhooks:
- En la sección de pruebas, vaya a la pestaña Subscriptions.
- Rellene los campos necesarios:
- User ID: al realizar pruebas, puede utilizar cualquier combinación de letras y números.
- Xsolla invoice ID: ID de la transacción en Xsolla. Al hacer pruebas, puede usar cualquier valor numérico.
- Public user ID: ID conocido por el usuario, por ejemplo, una dirección de correo electrónico o un apodo. Este campo se muestra si ha marcado la casilla Use public user ID en su proyecto en Project settings > Integration settings.
- Amount: importe del pago. Al hacer pruebas, puede utilizar cualquier valor numérico.
- Currency: seleccione una moneda de la lista desplegable.
- Plan ID: un plan de suscripción. Elija un plan de la lista desplegable.
- Subscription product: seleccione un producto de la lista desplegable (opcional). La lista se muestra si hay productos configurados en su proyecto.
- Invoice ID: ID de transacción en el juego. Al hacer pruebas, puede usar cualquier combinación de letras y números. No es un parámetro obligatorio para que el pago se realice correctamente, pero puede indicarlo para vincular el ID de transacción de su sistema con el de Xsolla.
- Trial period. Para probar la compra de una suscripción sin periodo de prueba o para probar la renovación de una suscripción , indique el valor
0.
- Haga clic en Test.
En la URL indicada, recibirá webhooks con los datos rellenados. Los resultados de las pruebas de cada webhook, tanto en el caso de que se haya realizado correctamente como en el de que se haya producido un error, se muestran debajo del botón Test.
El agente de escucha es un código de programa que permite recibir webhooks entrantes en una dirección URL especificada, generar una firma y enviar una respuesta al servidor de webhooks de Xsolla.
Nota
Puede utilizar la biblioteca de SDK para PHP de Pay Station, que contiene clases predefinidas para procesar webhooks.
En el lado de su aplicación, implemente la recepción de webhooks desde las siguientes direcciones IP:
185.30.20.0/24185.30.21.0/24185.30.22.0/24185.30.23.0/2434.102.38.17834.94.43.20735.236.73.23434.94.69.4434.102.22.197
Si ha integrado el producto Login, añada webhooks de procesamiento desde las siguientes direcciones IP:
34.94.0.8534.94.14.9534.94.25.3334.94.115.18534.94.154.2634.94.173.13234.102.48.3035.235.99.24835.236.32.13135.236.35.10035.236.117.164
Limitaciones:
- En la base de datos de su aplicación no debería haber varias transacciones aceptadas con el mismo ID.
- Si el agente de escucha de webhooks recibió un webhook con un ID que ya existe en la base de datos, deberá devolver el resultado del procesamiento anterior de esta transacción. No se recomienda acreditar al usuario una compra duplicada ni crear registros duplicados en la base de datos.
Para garantizar una transmisión de datos segura, debe comprobar que el webhook se ha enviado desde el servidor de Xsolla y que no ha sido manipulado durante el tránsito. Para ello, genere su propia firma basada en la carga útil del cuerpo de la solicitud y compárela con la firma proporcionada en el encabezado authorization de la solicitud entrante. Si las firmas coinciden, significa que el webhook es auténtico y seguro de procesar.
Pasos de verificación:
Obtenga la firma del encabezado
authorizationde la solicitud de webhook entrante. El formato del encabezado esSignature <signature_value>.Obtenga el cuerpo de la solicitud del webhook en formato JSON.
Aviso
Use la carga JSON tal y como la ha recibido. No analice ni recodifique la carga, ya que esto alteraría el formato y provocaría que la verificación de la firma fallara.
Genere su propia firma para comparar:
- Concatene la carga JSON con la clave secreta de su proyecto al añadir la clave al final de la cadena.
- Aplique la función hash criptográfica SHA-1 a la cadena obtenida. El resultado será una cadena hexadecimal en minúsculas.
Compare su firma generada con la del encabezado
authorization. Si coinciden, significa que el webhook es auténtico.
A continuación encontrará ejemplos de implementación de generación de firmas para los siguientes lenguajes: C#, C++, Go, PHP y Node.js.
POST /your_uri HTTP/1.1
host: your.host
accept: application/json
content-type: application/json
content-length: 165
authorization: Signature 52eac2713985e212351610d008e7e14fae46f902
{
"notification_type":"user_validation",
"user":{
"ip":"127.0.0.1",
"phone":"18777976552",
"email":"email@example.com",
"id":1234567,
"name":"Xsolla User",
"country":"US"
}
}curl -v 'https://your.hostname/your/uri' \
-X POST \
-H 'authorization: Signature 52eac2713985e212351610d008e7e14fae46f902' \
-d '{
"notification_type":
"user_validation",
"user":
{
"ip": "127.0.0.1",
"phone": "18777976552",
"email": "email@example.com",
"id": 1234567,
"name": "Xsolla User",
"country": "US"
}
}'Nota
Este ejemplo de código es compatible con .NET Framework 4.0 y versiones posteriores, así como con .NET Core y otras versiones modernas de .NET. La verificación de firmas utiliza la comparación en tiempo constante a través del método ConstantTimeEquals para ayudar a prevenir ataques de temporización.
using System;
using System.Security.Cryptography;
using System.Text;
public static class XsollaWebhookSignature
{
public static string ComputeSha1(string jsonBody, string secretKey)
{
// Concatenation of the JSON from the request body and the project's secret key
string dataToSign = jsonBody + secretKey;
using (SHA1 sha1 = SHA1.Create())
{
byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(dataToSign));
// Convert hash bytes to lowercase hexadecimal string
var hexString = new StringBuilder(hashBytes.Length * 2);
foreach (byte b in hashBytes)
{
hexString.Append(b.ToString("x2"));
}
return hexString.ToString();
}
}
public static bool VerifySignature(string jsonBody, string secretKey, string receivedSignature)
{
string computedSignature = ComputeSha1(jsonBody, secretKey);
string receivedSignatureLower = receivedSignature.ToLower();
// Use constant-time comparison to prevent timing attacks
return ConstantTimeEquals(computedSignature, receivedSignatureLower);
}
private static bool ConstantTimeEquals(string a, string b)
{
if (a.Length != b.Length)
{
return false;
}
int result = 0;
for (int i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}
}Nota
Para utilizar el método Convert.ToHexString, necesita .NET 5.0 y posterior.
CryptographicOperations.FixedTimeEquals en lugar de ConstantTimeEquals.// For .NET 5.0 and later, you can use the more concise Convert.ToHexString method:
using System;
using System.Security.Cryptography;
using System.Text;
public static class XsollaWebhookSignature
{
public static string ComputeSha1(string jsonBody, string secretKey)
{
string dataToSign = jsonBody + secretKey;
using var sha1 = SHA1.Create();
byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(dataToSign));
return Convert.ToHexString(hashBytes).ToLower();
}
public static bool VerifySignature(string jsonBody, string secretKey, string receivedSignature)
{
string computedSignature = ComputeSha1(jsonBody, secretKey);
string receivedSignatureLower = receivedSignature.ToLower();
// Use constant-time comparison to prevent timing attacks
return ConstantTimeEquals(computedSignature, receivedSignatureLower);
}
private static bool ConstantTimeEquals(string a, string b)
{
if (a.Length != b.Length)
{
return false;
}
int result = 0;
for (int i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}
}Nota
Si dispone de .NET 7.0 y versiones posteriores, puede utilizar el método CryptographicOperations.FixedTimeEquals.
// For .NET 7.0+, you can use the built-in CryptographicOperations.FixedTimeEquals:
using System.Security.Cryptography;
public static bool VerifySignature(string jsonBody, string secretKey, string receivedSignature)
{
string computedSignature = ComputeSha1(jsonBody, secretKey);
byte[] computedBytes = Encoding.UTF8.GetBytes(computedSignature);
byte[] receivedBytes = Encoding.UTF8.GetBytes(receivedSignature.ToLower());
return CryptographicOperations.FixedTimeEquals(computedBytes, receivedBytes);
}#include <string>
#include <sstream>
#include <iomanip>
#include <openssl/sha.h>
class XsollaWebhookSignature {
public:
static std::string computeSha1(const std::string& jsonBody, const std::string& secretKey) {
// Concatenation of the JSON from the request body and the project's secret key
std::string dataToSign = jsonBody + secretKey;
unsigned char digest[SHA_DIGEST_LENGTH];
// Create SHA1 hash
SHA1(reinterpret_cast<const unsigned char*>(dataToSign.c_str()),
dataToSign.length(), digest);
// Convert to lowercase hexadecimal string
std::ostringstream hexStream;
hexStream << std::hex << std::setfill('0');
for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) {
hexStream << std::setw(2) << static_cast<unsigned int>(digest[i]);
}
return hexStream.str();
}
static bool verifySignature(const std::string& jsonBody, const std::string& secretKey, const std::string& receivedSignature) {
std::string computedSignature = computeSha1(jsonBody, secretKey);
// Timing-safe comparison
if (computedSignature.length() != receivedSignature.length()) {
return false;
}
volatile unsigned char result = 0;
for (size_t i = 0; i < computedSignature.length(); ++i) {
result |= (computedSignature[i] ^ receivedSignature[i]);
}
return result == 0;
}
};package main
import (
"crypto/sha1"
"crypto/subtle"
"encoding/hex"
"strings"
)
type XsollaWebhookSignature struct{}
func (x *XsollaWebhookSignature) ComputeSha1(jsonBody, secretKey string) string {
// Concatenation of the JSON from the request body and the project's secret key
dataToSign := jsonBody + secretKey
// Create SHA1 hash
h := sha1.New()
h.Write([]byte(dataToSign))
signature := h.Sum(nil)
// Convert to lowercase hexadecimal string
return strings.ToLower(hex.EncodeToString(signature))
}
func (x *XsollaWebhookSignature) VerifySignature(jsonBody, secretKey, receivedSignature string) bool {
computedSignature := x.ComputeSha1(jsonBody, secretKey)
receivedSignatureLower := strings.ToLower(receivedSignature)
// Use constant time comparison to prevent timing attacks
return subtle.ConstantTimeCompare([]byte(computedSignature), []byte(receivedSignatureLower)) == 1
}<?php
class XsollaWebhookSignature
{
/**
* Compute SHA1 signature from webhook JSON body and secret key
*
* @param string $jsonBody The raw JSON body from webhook
* @param string $secretKey The project's secret key
* @return string The lowercase SHA1 signature
*/
public static function computeSha1(string $jsonBody, string $secretKey): string
{
// Concatenation of the JSON from the request body and the project's secret key
$dataToSign = $jsonBody . $secretKey;
// Generate SHA1 signature
$signature = sha1($dataToSign);
return strtolower($signature);
}
/**
* Verify webhook signature using timing-safe comparison
*
* @param string $jsonBody The raw JSON body from webhook
* @param string $secretKey The project's secret key
* @param string $receivedSignature The signature from authorization header
* @return bool True if signature is valid, false otherwise
*/
public static function verifySignature(string $jsonBody, string $secretKey, string $receivedSignature): bool
{
$computedSignature = self::computeSha1($jsonBody, $secretKey);
// Use hash_equals for timing-safe comparison
return hash_equals($computedSignature, strtolower($receivedSignature));
}
}
?>const crypto = require('crypto');
class XsollaWebhookSignature {
// IMPORTANT: jsonBody must be the raw JSON string exactly as received from Xsolla
static computeSha1(jsonBody, secretKey) {
// Concatenation of the JSON from the request body and the project's secret key
const dataToSign = jsonBody + secretKey;
// Create SHA1 hash
const hash = crypto.createHash('sha1');
hash.update(dataToSign, 'utf8');
// Convert to lowercase hexadecimal string
return hash.digest('hex').toLowerCase();
}
static verifySignature(jsonBody, secretKey, receivedSignature) {
const computedSignature = this.computeSha1(jsonBody, secretKey);
const cleanReceivedSignature = receivedSignature.toLowerCase();
// Check if signatures have the same length before using timingSafeEqual
if (computedSignature.length !== cleanReceivedSignature.length) {
return false;
}
try {
return crypto.timingSafeEqual(
Buffer.from(computedSignature, 'hex'),
Buffer.from(cleanReceivedSignature, 'hex')
);
} catch (error) {
// Return false if there's any error (e.g., invalid hex characters)
return false;
}
}
}Para confirmar la recepción del webhook, su servidor debe devolver:
- Código HTTP "200", "201" o "204" en el caso de una respuesta satisfactoria.
- Código HTTP "400" con la descripción del problema si no se encontró el usuario especificado o se transmitió una firma no válida. Su gestor de webhooks también puede devolver un código HTTP "5xx" si hay problemas temporales en su servidor.
Si el servidor de Xsolla no ha recibido respuesta a los webhooks Successful payment for order y Order cancellation o ha recibido una respuesta con un código 5xx, los webhooks se reenvían de la siguiente manera:
- 2 intentos con un intervalo de 5 minutos
- 7 intentos con un intervalo de 15 minutos
- 10 intentos con un intervalo de 60 minutos
Se realiza un máximo de 20 intentos de envío de webhooks en un plazo de 12 horas desde el primer intento.
Aviso
El pago se reembolsará al usuario si se cumplen las siguientes condiciones:
- El reembolso lo inició Xsolla.
- En respuesta a un webhook, se devolvió un código de estado
4xx, o no se recibió ninguna respuesta tras todos los intentos, o se devolvió un código de estado5xx.
Si el servidor de Xsolla no ha recibido respuesta al webhook User validation o ha recibido una respuesta con un código 400 o 5xx, el webhook User validation no se reenvía. En este caso, el usuario ve un error y los webhooks Payment y Successful payment for order no se envían.
Códigos de error para el código HTTP 400:
| Código | Mensaje |
|---|---|
| INVALID_USER | Usuario no válido |
| INVALID_PARAMETER | Parámetro no válido |
| INVALID_SIGNATURE | Firma no válida |
| INCORRECT_AMOUNT | Importe incorrecto |
| INCORRECT_INVOICE | Factura incorrecta |
HTTP/1.1 400 Bad Request
{
"error":{
"code":"INVALID_USER",
"message":"Invalid user"
}
}Siga estas directrices:
- Utilice solo HTTPS, con un certificado válido.
- Verifique siempre la firma comparándola con el cuerpo de la solicitud sin procesar; no analice ni recodifique los datos.
- No transmita datos sensibles en las URL y evite revelar detalles técnicos en los mensajes de error.
- Excluya el punto final del webhook del middleware CSRF: las solicitudes de Xsolla no incluyen un token CSRF y se rechazarán si no se aplica esta configuración.
- Incluya las direcciones IP de Xsolla en la lista de permitidos.
Siga estas directrices:
- Acepte la solicitud
POSTcon el cuerpo y los encabezados tal cual, sin modificaciones. - Verifique la firma del webhook y devuelva el código de estado correspondiente:
4xx: si las firmas no coinciden;2xx: en casos de éxito. Recomendamos devolver204 No Contentantes de ejecutar la lógica de negocio principal. También es aceptable200 OK.
- Transmita la carga útil a una tarea asíncrona o a una cola para su posterior procesamiento.
- Implemente la idempotencia. Asegúrese de que su sistema admite recibir el mismo webhook más de una vez.
Ejemplo de flujo:
HTTP POST /webhooks/xsolla
read raw_body, headers
if !verify_signature(raw_body, headers['authorization']):
return 400 {"error":{"code":"INVALID_SIGNATURE","message":"Invalid signature"}}
enqueue(raw_body)
return 204 # or 200Siga estas directrices:
- Utilice el ID de transacción o el ID externo, ID de pedido como claves de idempotencia.
- Almacene los ID procesados y devuelva el resultado anterior si se recibe un duplicado.
- Evite la concesión duplicada de artículos, las entradas duplicadas en la base de datos y los cobros duplicados.
- Tenga en cuenta que, en el caso de la entrega secuencial, un fallo en un evento anterior bloquea el procesamiento de todos los siguientes.
Siga estas directrices:
- Utilice colas y el procesamiento asíncrono para operaciones que consumen muchos recursos, como llamadas API de terceros, facturación y concesión de artículos.
- Configure los tiempos de espera en el controlador de webhook (1-3 s). En caso de fallos temporales, utilice el mecanismo de reintento de Xsolla.
- No implemente reintentos en el controlador del webhook: la reentrega la gestiona Xsolla.
- Registre las marcas de tiempo de entrega de los webhooks y los estados de procesamiento; configure avisos para los picos de errores
5xxy los reenvíos. - Propague los ID de correlación desde el webhook a sus registros y a su sistema de monitorización (APM).
- Configure el registro y la supervisión de errores. En los casos de fallos irrecuperables, traslade los trabajos a una cola de mensajes no entregados (DLQ). Desarrolle una herramienta segura para reproducir eventos que esté protegida por un mecanismo de idempotencia.
Compra realizada con éxito: artículo concedido en el primer intento:
Entrega duplicada (tiempo de espera agotado en el primer intento por parte del socio):
Reembolso:
Interrupción del servicio del socio:
No. Los webhooks de pago utilizan el protocolo de servidor a servidor y se envían a una única URL especificada en la configuración del proyecto . Si quiere recibir notificaciones en su juego, sitio web o aplicación móvil, configure el envío de webhooks en su servidor para transferir datos entre Xsolla y su juego. También puede probar los webhooks desde la consola de desarrollador.
Nota
Si está probando la integración de forma local, las solicitudes `POST` de Xsolla no llegan a direcciones URL como http://localhost:3000/my-webhook-endpoint. Utilice servicios como ngrok que permiten crear un túnel de acceso externo para recibir solicitudes de Xsolla en su entorno local. Encontrará más información al respecto en la documentación de ngrok.
Asegúrese de que su servidor de webhooks admita los tipos de solicitudes HTTP POST y GET.
Utilice el ID externo: este es el ID de la transacción en su juego, asignado al pedido en su sistema. Del lado de Xsolla, el ID externo está vinculado al ID de la transacción, lo que permite a Xsolla evitar pagos duplicados para la misma transacción. Para obtener más información sobre la configuración, consulte nuestra documentación.
Recomendamos:
- Devolver
204o200inmediatamente después de la verificación de la firma. - Comprobar la firma del webhook en relación con el cuerpo de la solicitud, sin modificaciones.
- Implementar la idempotencia para todas las operaciones.
- Registrar todos los eventos y configurar la supervisión de errores.
- Evitar incluir datos sensibles en las URL y no exponer detalles técnicos en los mensajes de error.
Consulte la sección Prácticas recomendadas para obtener información detallada.
Para que los webhooks funcionen correctamente, asegúrese de tener lo siguiente configurado antes de activarlos:
- Se utiliza HTTPS.
- La verificación de firma del webhook se implementa en función del cuerpo de la solicitud sin procesar y sin modificación.
- Se devuelve una respuesta
204/200en cuanto se confirma la firma. - La idempotencia se aplica a todas las operaciones.
- Se han configurado el registro de errores y la supervisión.
- No se transmiten datos sensibles en las URL ni se exponen detalles técnicos en los mensajes de error.
- Se admiten reintentos de webhooks conforme a la lógica de reintentos de Xsolla.
- Toda la integración está documentada.
Observación
El tipo de notificación se envía en el parámetro notification_type.
| Webhook | Tipo de notificación | Descripción |
|---|---|---|
| Validación del usuario | user_validation | Se envía para comprobar si un usuario existe en el juego. |
| Búsqueda de usuario | user_search | Se envía para obtener información de usuario basada en el ID público del usuario. |
| Pago | payment | Se envía cuando un usuario realiza un pago. |
| Reembolso | refund | Se envía cuando un pago debe cancelarse por cualquier motivo. |
| Reembolso parcial | partial_refund | Se envía cuando un pago debe cancelarse parcialmente por cualquier motivo. |
| Pago rechazado | ps_declined | Se envía cuando un pago es rechazado por el sistema de pago. |
| Transacción de AFS rechazada | afs_reject | Se envía cuando se rechaza una transacción durante una comprobación de AFS. |
| Lista de bloqueo de AFS rechazada | afs_black_list | Se envía cuando se actualiza la lista de bloqueo de AFS. |
| Suscripción creada | create_subscription | Se envía cuando un usuario crea una suscripción. |
| Suscripción actualizada | update_subscription | Se envía cuando se renueva o modifica una suscripción. |
| Suscripción cancelada | cancel_subscription | Se envía cuando se cancela una suscripción. |
| Suscripción con renovación suspendida | non_renewal_subscription | Se envía cuando el estado es de renovación suspendida. |
| Añadir cuenta de pago | payment_account_add | Se envía cuando un usuario añade o guarda una cuenta de pago. |
| Eliminar cuenta de pago | payment_account_remove | Se envía cuando un usuario elimina la cuenta de pago de las cuentas guardadas. |
| Validación del usuario en Web Shop | - | Se envía desde un sitio de Web Shop para comprobar si un usuario existe en el juego. |
| Personalización del catálogo en el lado del socio | partner_side_catalog | Se envía cuando un usuario interactúa con la tienda. |
| Successful payment for order | order_paid | Se envía cuando se paga un pedido. |
| Order cancellation | order_canceled | Se envía cuando se cancela un pedido. |
| Dispute | dispute | Se envía cuando se abre una nueva disputa. |
Solicitud
Xsolla envía el webhook order_canceled a la URL especificada cuando el pago ha sido cancelado por el usuario, el socio o automáticamente. El webhook contiene información sobre los artículos devueltos y los detalles del pedido cancelado.
El webhook no se envía si el pago no se ha realizado correctamente, por ejemplo:
- se abrió la interfaz de pago, pero el usuario no pagó el pedido
- se abrió la interfaz de pago, pero hubo errores durante el pago
El tiempo de procesamiento recomendado del webhook es de menos de 3 segundos.
Lista de artículos adquiridos por el usuario.
El conjunto de parámetros incluidos en la matriz depende de la versión del webhook. La versión 2 incluye parámetros adicionales: is_free, is_bonus y is_bundle_content. Para cambiar de versión, introduzca su número en el parámetro version en la llamada API Update information about webhook settings.
Promociones aplicadas para artículos específicos en el pedido. La matriz se devuelve en los siguientes casos:
- Se configura una promoción de descuento para un artículo específico.
- Se aplica un código promocional con el parámetro Descuento en artículos seleccionados.
Si no se aplican promociones a nivel de artículo, se devuelve una matriz vacía.
ID único del artículo. Para los artículos con el tipo game_key, se utiliza un valor con el formato sku_drm.
Tipo de artículos. Para los artículos del tipo bundle, incluidos los paquetes de monedas virtuales, la matriz items mostrará:
- parámetros del lote o del paquete de monedas virtuales
- artículos incluidos en el lote o monedas incluidas en el paquete
El tipo value_point se utiliza en operaciones de puntos de fidelidad - cuando se gastan o conceden puntos.
Información sobre el pedido.
Cupones aplicados. Si no se aplica el cupón, la matriz no devuelve.
Moneda del pedido. La moneda virtual utiliza el código de artículo (SKU) y la moneda real usa un código de tres letras según la ISO 4217.
Tipo de moneda de pago. Para realizar un pedido gratuito, se especifica el valor unknown.
| Enumeración Valor | Descripción |
|---|---|
| loyalty_point | puntos de fidelidad |
| real | moneda real |
| unknown | pedido gratuito |
| virtual | moneda virtual |
ID de factura de pagos en moneda real. Los pagos en moneda virtual o los artículos gratuitos tienen un valor de null.
Modo de pago. default se utiliza para pagos reales; sandbox para pagos de prueba.
Plataforma de pago. Para realizar pagos vía Xsolla se utiliza el valor xsolla. Para otros pagos se usa el valor correspondiente al nombre de la plataforma de publicación de juegos: playstation_network, xbox_live, pc_standalone, nintendo_shop, google_play, app_store_ios, android_standalone, ios_standalone, android_other, ios_other, pc_other.
Códigos promocionales aplicados. Si no se aplica el código promocional, la matriz no devuelve.
Promociones aplicadas para todo el pedido. La matriz se devuelve en los siguientes casos:
- Una promoción afecta al importe total de la compra, como un código promocional con el parámetro Descuento en compras.
- No se aplica ningún descuento a la compra, pero se añaden artículos de bonificación al pedido. En este caso, se devuelven los valores del coste con descuento (amount_with_discount) y sin descuento (amount_without_discount) e idénticos, ya que no se aplica ningún descuento.
Si no se aplican promociones a nivel de pedido, se devuelve una matriz vacía.
Información del usuario.
Código de país. Dos letras mayúsculas de conformidad con la norma ISO 3166-1 alpha-2.
- https://api.xsolla.com/merchant/v2/order-cancellation-separate
- Mock serverhttps://xsolla.redocly.app/_mock/es/webhooks/order-cancellation-separate
- CURL
- Payload
curl -v 'https://your.hostname/your/uri' \
-X POST \
-H 'accept: application/json' \
-H 'content-type: application/json' \
-H 'authorization: Signature d09695066c52c1b8bdae92f2d6eb59f5b5f89843' \
-d '{
"notification_type": "order_canceled",
"items": [
{
"sku": "com.xsolla.v.item_1",
"type": "virtual_good",
"is_pre_order": false,
"quantity": 3,
"amount": "1000",
"promotions": [
{
"amount_without_discount": "6000",
"amount_with_discount": "5000",
"sequence": 1
},
{
"amount_without_discount": "5000",
"amount_with_discount": "4000",
"sequence": 2
}
],
"custom_attributes": {
"purchased": 0,
"attr": "value"
}
},
{
"sku": "com.xsolla.v.item_new_1",
"type": "bundle",
"is_pre_order": false,
"quantity": 1,
"amount": "1000",
"promotions": []
},
{
"sku": "com.xsolla.gold_1",
"type": "virtual_currency",
"is_pre_order": false,
"quantity": 1500,
"amount": "[null]",
"promotions": []
}
],
"order": {
"id": 1,
"mode": "default",
"currency_type": "virtual",
"currency": "sku_currency",
"amount": "2000",
"status": "paid",
"platform": "xsolla",
"comment": null,
"invoice_id": "1",
"promotions": [
{
"amount_without_discount": "4000",
"amount_with_discount": "2000",
"sequence": 1
}
],
"promocodes": [
{
"code": "promocode_some_code",
"external_id": "promocode_sku"
}
],
"coupons": [
{
"code": "WINTER2021",
"external_id": "coupon_sku"
}
]
},
"user": {
"external_id": "id_xsolla_login_1",
"email": "email@example.com",
"country": "US"
}
}'Solicitud
Xsolla envía el webhook order_paid a la URL especificada cuando se cumplen las siguientes condiciones:
- El usuario pagó correctamente el pedido.
- Xsolla recibió una respuesta sobre el procesamiento correcto del webhook payment.
El webhook order_paid contiene información sobre los artículos comprados y los datos de la transacción.
El webhook order_paid no se envía si:
- El pago no se realizó correctamente, por ejemplo:
- se abrió el formulario de pago, pero el usuario no pagó el pedido
- se abrió el formulario de pago, pero hubo errores durante el pago
- No se ha recibido la respuesta sobre el procesamiento correcto del webhook payment.
Se recomienda que el tiempo de procesamiento del webhook order_paid sea inferior a 3 segundos.
Las respuestas esperadas se describen en la sección Responses. Puede utilizar otros códigos de respuesta. Dependiendo del código de respuesta y de la conexión de la función de reembolso automático de pagos, la lógica de procesamiento del webhook por parte de Xsolla es la siguiente:
| Código de respuesta | El reembolso automático de pagos está desactivado (por defecto) | El reembolso automático de pagos está activado |
|---|---|---|
400, 401, 402, 403, 404, 409, 422, 415 | Ninguna acción | Reembolso automático al usuario |
200, 201, 204 | Ninguna acción | Ninguna acción |
| Diferente código o ninguna respuesta al webhook | Se envían varios webhooks en un intervalo de tiempo especificado: 2 intentos con un intervalo de 5 minutos, 7 intentos con un intervalo de 15 minutos, 10 intentos con un intervalo de 60 minutos. | Se envían varios webhooks en un intervalo especificado: 2 intentos con un intervalo de 5 minutos, 7 intentos con un intervalo de 15 minutos, 10 intentos con un intervalo de 60 minutos. Si se envían todos los webhooks pero no se recibe una respuesta satisfactoria, se emite un reembolso automático al usuario. |
Para conectar la función de reembolso automático, contacte con sus gestores de éxito del cliente o escriba a csm@xsolla.com.
Lista de artículos adquiridos por el usuario.
El conjunto de parámetros incluidos en la matriz depende de la versión del webhook. La versión 2 incluye parámetros adicionales: is_free, is_bonus y is_bundle_content. Para cambiar de versión, introduzca su número en el parámetro version en la llamada API Update information about webhook settings.
Promociones aplicadas para artículos específicos en el pedido. La matriz se devuelve en los siguientes casos:
- Se configura una promoción de descuento para un artículo específico.
- Se aplica un código promocional con el parámetro Descuento en artículos seleccionados.
Si no se aplican promociones a nivel de artículo, se devuelve una matriz vacía.
ID único del artículo. Para los artículos con el tipo game_key, se utiliza un valor con el formato sku_drm.
Tipo de artículos. Para los artículos del tipo bundle, incluidos los paquetes de monedas virtuales, la matriz items mostrará:
- parámetros del lote o del paquete de monedas virtuales
- artículos incluidos en el lote o monedas incluidas en el paquete
El tipo value_point se utiliza en operaciones de puntos de fidelidad - cuando se gastan o conceden puntos.
Información sobre el pedido.
Cupones aplicados. Si no se aplica el cupón, la matriz no devuelve.
Moneda del pedido. La moneda virtual utiliza el código de artículo (SKU) y la moneda real usa un código de tres letras según la ISO 4217.
Tipo de moneda de pago. Para realizar un pedido gratuito, se especifica el valor unknown.
| Enumeración Valor | Descripción |
|---|---|
| loyalty_point | puntos de fidelidad |
| real | moneda real |
| unknown | pedido gratuito |
| virtual | moneda virtual |
ID de factura de pagos en moneda real. Los pagos en moneda virtual o los artículos gratuitos tienen un valor de null.
Modo de pago. default se utiliza para pagos reales; sandbox para pagos de prueba.
Plataforma de pago. Para pagos a través de Xsolla se utiliza el valor xsolla. Para otros pagos, se utiliza el valor correspondiente al nombre de la plataforma de publicación de juegos.
Códigos promocionales aplicados. Si no se aplica el código promocional, la matriz no devuelve.
Promociones aplicadas para todo el pedido. La matriz se devuelve en los siguientes casos:
- Una promoción afecta al importe total de la compra, como un código promocional con el parámetro Descuento en compras.
- No se aplica ningún descuento a la compra, pero se añaden artículos de bonificación al pedido. En este caso, se devuelven los valores del coste con descuento (amount_with_discount) y sin descuento (amount_without_discount) e idénticos, ya que no se aplica ningún descuento.
Si no se aplican promociones a nivel de pedido, se devuelve una matriz vacía.
Información del usuario.
Código de país. Dos letras mayúsculas de conformidad con la norma ISO 3166-1 alpha-2.
- https://api.xsolla.com/merchant/v2/successful-order-payment-separate
- Mock serverhttps://xsolla.redocly.app/_mock/es/webhooks/successful-order-payment-separate
- CURL
- Payload
curl -v 'https://your.hostname/your/uri' \
-X POST \
-H 'accept: application/json' \
-H 'content-type: application/json' \
-H 'authorization: Signature d09695066c52c1b8bdae92f2d6eb59f5b5f89843' \
-d '{
"notification_type": "order_paid",
"items": [
{
"sku": "com.xsolla.v.item_1",
"type": "virtual_good",
"is_pre_order": false,
"quantity": 3,
"amount": "1000",
"promotions": [
{
"amount_without_discount": "6000",
"amount_with_discount": "5000",
"sequence": 1
},
{
"amount_without_discount": "5000",
"amount_with_discount": "4000",
"sequence": 2
}
],
"custom_attributes":
{
"purchased": 0,
"attr": "value"
}
},
{
"sku": "com.xsolla.v.item_new_1",
"type": "bundle",
"is_pre_order": false,
"quantity": 1,
"amount": "1000",
"promotions": []
},
{
"sku": "com.xsolla.gold_1",
"type": "virtual_currency",
"is_pre_order": false,
"quantity": 1500,
"amount": "[null]",
"promotions": []
}
],
"order": {
"id": 1,
"mode": "default",
"currency_type": "virtual",
"currency": "sku_currency",
"amount": "2000",
"status": "paid",
"platform": "xsolla",
"comment": null,
"invoice_id": "1",
"promotions": [
{
"amount_without_discount": "4000",
"amount_with_discount": "2000",
"sequence": 1
}
],
"promocodes": [
{
"code": "promocode_some_code",
"external_id": "promocode_sku"
}
],
"coupons": [
{
"code": "WINTER2021",
"external_id": "coupon_sku"
}
]
},
"user": {
"external_id": "id_xsolla_login_1",
"email": "gc_user@xsolla.com",
"country": "US"
}
}'