ہم کیسے مدد کر سکتے ہیں؟

مختصراً بتائیں کہ معاملہ کیا ہے—ہم آپ کو ای میل کے ذریعے جواب دیں گے۔

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
ابتدائی صفحے پر جائیں

Endpoint per i dati dei clienti: ordini, spedizioni e fatture

Riferimento tecnico per il collegamento dei dati dei clienti: richiesta firmata da reactive.chat al Suo negozio, schema della risposta, limiti, motivi di errore, endpoint completo di riferimento in PHP e Node e importazione tramite API.

Con i dati dei clienti, l’assistente IA risponde a domande come «Dov’è il mio pacco?» o «La mia fattura è stata pagata?», solo per visitatori confermati e solo sui propri ordini. Questa pagina è il riferimento tecnico per chi realizza l’endpoint. Ciò che mostra l’assistente e i campi che abilita nell’account sono descritti in Dati dei clienti nella chat.

Disponibile nei piani a pagamento (anche durante la prova).

Requisiti

  1. Verifica dell’identità con exp. Il visitatore deve essere autenticato con un userHash valido e exp (Identificare i visitatori). Senza exp è considerato confermato, ma senza verifica recente: non viene quindi recuperato nulla.
  2. Impostazione nell’account in Progetto → Dati dei clienti: scelga la modalità, inserisca l’endpoint, abiliti i campi e imposti l’avviso per i visitatori non autenticati.

Modalità:

La richiesta

reactive.chat chiama il Suo 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"}

Header:

Corpo:

Il Suo endpoint deve:

  1. verificare la firma sul corpo grezzo (a tempo costante, ad esempio hash_equals), non sul JSON ricodificato;
  2. rifiutare richieste il cui X-RC-Zeit differisce dall’orologio di oltre 300 secondi;
  3. accettare ogni nonce una sola volta (consigliato);
  4. restituire solo i dati dello user_id indicato;
  5. essere raggiungibile tramite https sulla porta 443 a un indirizzo pubblico, senza reindirizzamenti né nome utente/password nell’indirizzo;
  6. rispondere completamente entro 3 secondi (connessione stabilita in massimo 2 secondi).

La risposta

Esattamente HTTP 200 con un oggetto JSON. Nessuna corrispondenza è {"bestellungen":[]}, sempre 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" }
    }
  ]
}

Campi per ordine:

Limiti: massimo 10 ordini, 20 articoli per ordine, testi di massimo 200 caratteri, intera risposta di massimo 16.384 byte. I campi sconosciuti o non abilitati nell’account vengono scartati senza errore.

Mai acquisiti, anche se li invia: via e ulteriori righe dell’indirizzo, nomi, e-mail, telefono, IBAN/BIC/conto e dati delle carte, data di nascita, password/token/chiavi, codici fiscali e numeri di documenti d’identità. È preferibile non inviarli affatto.

Che cosa succede alla risposta: viene usata solo per quella risposta dell’assistente e non viene memorizzata, né nella conversazione né nella knowledge base; il registro annota solo numero e durata. Se l’endpoint non risponde in tempo o restituisce un errore, l’assistente risponde come se non ci fossero dati.

Endpoint di riferimento in 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 di riferimento in 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) });
});

Testare la connessione

Verifica la connessione nell’account chiama il Suo endpoint con user_id rc-test e anliegen test. Restituisca un elenco vuoto o un ordine di esempio. Se la chiamata fallisce, l’account indica uno di questi motivi:

Importazione tramite API

Se il Suo negozio non può offrire un endpoint, ci invii i dati. Serve una chiave API con l’ambito solo importazione dati dei clienti (kundendaten:schreiben) e la modalità Importazione o Entrambe.

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"}]}'

I dati importati vengono memorizzati in forma cifrata (il numero cliente solo come hash) ed eliminati 90 giorni dopo l’ultima importazione. Invii quindi le modifiche continuamente, ad esempio a ogni cambio di stato.

Errori: 402 plan_required (nessun piano a pagamento), 409 import_disabled (la modalità non consente l’importazione, con kd_modus), 400 bad_request con param (user_id, project_id, bestellungen), 404 (progetto di un’altra persona). Il formato generale degli errori è descritto in API server.

Visitatori non autenticati

Se una persona senza identità confermata chiede informazioni su un ordine, l’assistente non recupera nulla; mostra invece l’avviso configurato nell’account (testo per lingua, facoltativamente con link di accesso) oppure passa al team.

Prosegua: Webhook · API server

آخری بار تازہ کاری: