Tunaweza kukusaidiaje?

Tuambie kwa ufupi linahusu nini — tutakujibu kwa barua pepe.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Nenda kwenye ukurasa wa mwanzo

Endpoint de datos de clientes: pedidos, envíos y facturas

Referencia técnica de la conexión de datos de clientes: solicitud firmada de reactive.chat a su tienda, esquema de respuesta, límites, motivos de fallo, endpoint de referencia completo en PHP y Node e importación mediante API.

Con los datos de clientes, el asistente de IA responde a preguntas como «¿Dónde está mi paquete?» o «¿Está pagada mi factura?», solo para visitantes confirmados y solo sobre sus propios pedidos. Esta página es la referencia técnica para quien construye el endpoint. Qué muestra el asistente y qué campos habilita en su cuenta se describe en Datos de clientes en el chat.

Disponible en los planes de pago (también durante la prueba).

Requisitos

  1. Verificación de identidad con exp. El visitante debe estar identificado con un userHash válido y exp (Identificar visitantes). Sin exp se considera confirmado, pero sin verificación vigente; en ese caso no se consulta nada.
  2. Ajuste en su cuenta, en Proyecto → Datos de clientes: elegir el modo, introducir el endpoint, habilitar campos y establecer el aviso para visitantes sin sesión iniciada.

Modos:

La solicitud

reactive.chat llama a su endpoint con POST:

POST /rc-kundendaten HTTP/1.1
Host: shop.your-company.com
Content-Type: application/json
Accept: application/json
User-Agent: reactive.chat-kundendaten/1
X-RC-Zeit: 1792503600
X-RC-Signatur: 5d41402abc4b2a76b9719d911017c592...

{"user_id":"4711","anliegen":"lieferung","referenz":"2026-0815","sprache":"en","nonce":"9f86d081884c7d659a2feaa0c55ad015"}

Cabeceras:

Cuerpo:

Su endpoint debe:

  1. verificar la firma sobre el cuerpo sin procesar (en tiempo constante, por ejemplo, con hash_equals), no sobre JSON recodificado;
  2. rechazar solicitudes cuyo X-RC-Zeit difiera más de 300 segundos de su reloj;
  3. aceptar cada nonce una sola vez (recomendado);
  4. devolver únicamente datos del user_id indicado;
  5. ser accesible mediante https en el puerto 443, en una dirección pública, sin redirecciones ni nombre de usuario/contraseña en la dirección;
  6. responder por completo en 3 segundos (establecimiento de conexión en un máximo de 2 segundos).

La respuesta

Exactamente HTTP 200 con un objeto JSON. Si no hay coincidencias, {"bestellungen":[]}, también con 200.

{
  "bestellungen": [
    {
      "nummer": "2026-0815",
      "datum": "2026-09-28",
      "status": "shipped",
      "summe": "49.90",
      "waehrung": "EUR",
      "positionen": [ { "name": "Balance bike blue", "menge": 1 } ],
      "versand": { "dienst": "DHL", "sendungsnummer": "00340434161234567890", "status": "out for delivery" },
      "rechnung": { "nummer": "R-2026-1201", "status": "paid", "faellig_am": "2026-10-12" }
    }
  ]
}

Campos por pedido:

Límites: un máximo de 10 pedidos, 20 artículos por pedido, textos de hasta 200 caracteres y una respuesta completa de un máximo de 16 384 bytes. Los campos desconocidos y los no habilitados en su cuenta se descartan sin error.

Nunca se incorporan, aunque los envíe: calle y otras líneas de dirección, nombres, correo electrónico, teléfono, IBAN/BIC/cuenta y datos de tarjeta, fecha de nacimiento, contraseñas/tokens/claves, números de identificación fiscal y de documentos de identidad. Es preferible no enviarlos.

Qué ocurre con la respuesta: se utiliza solo para esa única respuesta del asistente y no se almacena, ni en la conversación ni en la base de conocimiento; el registro solo recoge la cantidad y la duración. Si su endpoint no responde a tiempo o responde con un error, el asistente contesta como si no hubiera datos.

Endpoint de referencia en PHP

<?php
declare(strict_types=1);

$GEHEIMNIS = (string) getenv('RC_KD_GEHEIMNIS');   // rckd_... from the account

function antworte(int $status, array $daten): never {
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');
    echo json_encode($daten, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { antworte(405, ['fehler' => 'POST only']); }

$body = (string) file_get_contents('php://input', false, null, 0, 4097);
if (strlen($body) > 4096) { antworte(413, ['fehler' => 'too large']); }

$zeit = (string) ($_SERVER['HTTP_X_RC_ZEIT'] ?? '');
$sig  = strtolower((string) ($_SERVER['HTTP_X_RC_SIGNATUR'] ?? ''));
if (!preg_match('/^\d{9,11}$/', $zeit) || abs(time() - (int) $zeit) > 300) {
    antworte(401, ['fehler' => 'zeit']);
}
if (!hash_equals(hash_hmac('sha256', $zeit . '.' . $body, $GEHEIMNIS), $sig)) {
    antworte(401, ['fehler' => 'signatur']);
}

$j        = json_decode($body, true);
$userId   = is_string($j['user_id'] ?? null) ? $j['user_id'] : '';
$referenz = is_string($j['referenz'] ?? null) ? trim($j['referenz']) : '';
$nonce    = is_string($j['nonce'] ?? null) ? $j['nonce'] : '';
if ($userId === '' || !preg_match('/^[0-9a-f]{32}$/', $nonce)) {
    antworte(400, ['fehler' => 'anfrage']);
}

$pdo = new PDO(getenv('SHOP_DB_DSN'), getenv('SHOP_DB_USER'), getenv('SHOP_DB_PASS'),
               [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);

// nonce only once: CREATE TABLE rc_nonce (nonce CHAR(32) PRIMARY KEY, zeit INT NOT NULL);
$pdo->prepare('DELETE FROM rc_nonce WHERE zeit < ?')->execute([time() - 600]);
try {
    $pdo->prepare('INSERT INTO rc_nonce (nonce, zeit) VALUES (?, ?)')->execute([$nonce, time()]);
} catch (PDOException $e) {
    antworte(409, ['fehler' => 'nonce already used']);
}

if ($userId === 'rc-test') { antworte(200, ['bestellungen' => []]); }   // test button

// ONLY this customer; the reference is only a filter
$sql = 'SELECT id, nummer, datum, status, summe, waehrung, versand_dienst, sendungsnummer,
               versand_status, rechnung_nummer, rechnung_status, faellig_am
          FROM bestellungen WHERE kunde_id = :u';
$w = [':u' => $userId];
if ($referenz !== '') {
    $sql .= ' AND (nummer = :r1 OR rechnung_nummer = :r2 OR sendungsnummer = :r3)';
    $w += [':r1' => $referenz, ':r2' => $referenz, ':r3' => $referenz];
}
$st = $pdo->prepare($sql . ' ORDER BY datum DESC LIMIT 10');
$st->execute($w);

$pos = $pdo->prepare('SELECT name, menge FROM bestell_positionen WHERE bestellung_id = ? LIMIT 20');
$aus = [];
foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $b) {
    $pos->execute([$b['id']]);
    $aus[] = [
        'nummer'     => (string) $b['nummer'],
        'datum'      => $b['datum'],
        'status'     => $b['status'],
        'summe'      => $b['summe'],
        'waehrung'   => $b['waehrung'],
        'positionen' => $pos->fetchAll(PDO::FETCH_ASSOC),
        'versand'    => ['dienst' => $b['versand_dienst'], 'sendungsnummer' => $b['sendungsnummer'],
                         'status' => $b['versand_status']],
        'rechnung'   => ['nummer' => $b['rechnung_nummer'], 'status' => $b['rechnung_status'],
                         'faellig_am' => $b['faellig_am']],
    ];
}
antworte(200, ['bestellungen' => $aus]);

Endpoint de referencia en Node.js (Express)

const crypto = require('crypto');
const express = require('express');
const app = express();
const GEHEIM = process.env.RC_KD_GEHEIMNIS;
const gesehen = new Map();   // nonce -> time; a database in production

app.post('/rc-kundendaten', express.raw({ type: 'application/json', limit: '4kb' }), async (req, res) => {
  const body = req.body.toString('utf8');          // RAW body
  const zeit = String(req.get('X-RC-Zeit') || '');
  const sig  = String(req.get('X-RC-Signatur') || '').toLowerCase();
  if (!/^\d{9,11}$/.test(zeit) || Math.abs(Date.now() / 1000 - Number(zeit)) > 300) {
    return res.status(401).json({ fehler: 'zeit' });
  }
  const soll = crypto.createHmac('sha256', GEHEIM).update(zeit + '.' + body).digest('hex');
  if (sig.length !== soll.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(soll))) {
    return res.status(401).json({ fehler: 'signatur' });
  }
  const j = JSON.parse(body);
  if (!/^[0-9a-f]{32}$/.test(j.nonce || '') || gesehen.has(j.nonce)) {
    return res.status(409).json({ fehler: 'nonce' });
  }
  gesehen.set(j.nonce, Date.now());
  if (j.user_id === 'rc-test') { return res.json({ bestellungen: [] }); }

  const bestellungen = await ladeBestellungen(j.user_id, j.referenz);   // ONLY this customer
  res.json({ bestellungen: bestellungen.slice(0, 10) });
});

Probar la conexión

Probar la conexión, en su cuenta, llama a su endpoint con user_id rc-test y anliegen test. Devuelva una lista vacía o un pedido de ejemplo. Si falla la llamada, su cuenta indica uno de estos motivos:

Importación mediante API

Si su tienda no puede ofrecer un endpoint, envíenos los datos. Necesita una clave de API con el alcance solo importación de datos de clientes (kundendaten:schreiben) y el modo Importación o Ambos.

curl -X PUT "https://reactive.chat/v1/kundendaten/4711?project_id=123" \
  -H "Authorization: Bearer rc_live_..." \
  -H "Content-Type: application/json" \
  -d '{"bestellungen":[{"nummer":"2026-0815","datum":"2026-09-28","status":"shipped"}]}'

Los datos importados se almacenan cifrados (el número de cliente solo como hash) y se eliminan 90 días después de la última importación. Por eso, envíe los cambios continuamente, por ejemplo, con cada cambio de estado.

Errores: 402 plan_required (sin plan de pago), 409 import_disabled (el modo no permite importar, con kd_modus), 400 bad_request con param (user_id, project_id, bestellungen), 404 (proyecto ajeno). El formato general de errores se describe en API de servidor.

Visitantes sin sesión iniciada

Si alguien sin identidad confirmada pregunta por un pedido, el asistente no consulta nada; en su lugar muestra el aviso de su cuenta (texto por idioma y, opcionalmente, un enlace para iniciar sesión) o transfiere la conversación a su equipo.

Siguiente: Webhooks · API de servidor

Ilisasishwa mara ya mwisho: