چطور می‌توانیم کمک کنیم؟

کوتاه بفرمایید موضوع چیست؛ از طریق ایمیل با شما تماس می‌گیریم.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
رفتن به صفحه اصلی

Slutpunkt för kunddata: beställningar, leverans och fakturor

Teknisk referens för kunddatakopplingen – det signerade anropet från reactive.chat till din butik, svarsschema, gränser, felorsaker, en fullständig referensslutpunkt i PHP och Node samt import via API.

Med kunddata besvarar AI-assistenten frågor som ”Var är mitt paket?” eller ”Är min faktura betald?” – bara för bekräftade besökare och bara om deras egna beställningar. Den här sidan är den tekniska referensen för den som bygger slutpunkten. Vad assistenten visar och vilka fält du aktiverar i kontot beskrivs i Kunddata i chatten.

Tillgängligt i betalplanerna (även under provperioden).

Förutsättningar

  1. Identitetsverifiering med exp. Besökaren måste vara inloggad med giltig userHash och exp (Identifiera besökare). Utan exp räknas hen som bekräftad men inte som aktuell – då hämtas inget.
  2. Inställning i kontot under Projekt → Kunddata: välj läge, ange slutpunkt, aktivera fält och ställ in informationen för besökare som inte är inloggade.

Lägen:

Anropet

reactive.chat anropar din slutpunkt med 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"}

Huvuden:

Meddelandekropp:

Din slutpunkt måste:

  1. verifiera signaturen över den råa meddelandekroppen (i konstant tid, till exempel hash_equals) – inte över JSON som kodats om;
  2. avvisa anrop vars X-RC-Zeit avviker mer än 300 sekunder från din klocka;
  3. godta varje nonce bara en gång (rekommenderas);
  4. bara returnera data för angivet user_id;
  5. kunna nås via https på port 443 på en offentlig adress – utan omdirigeringar och utan användarnamn/lösenord i adressen;
  6. svara fullständigt inom 3 sekunder (upprättande av anslutningen högst 2 sekunder).

Svaret

Exakt HTTP 200 med ett JSON-objekt. Ingen träff är {"bestellungen":[]}, också med 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" }
    }
  ]
}

Fält per beställning:

Gränser: högst 10 beställningar, 20 artiklar per beställning, texter högst 200 tecken, hela svaret högst 16 384 byte. Okända fält och fält som inte är aktiverade i kontot slopas utan fel.

Tas aldrig med, även om du skickar dem: gata och ytterligare adressrader, namn, e-post, telefon, IBAN/BIC/konto- och kortuppgifter, födelsedatum, lösenord/token/nycklar, skattenummer och ID-nummer. Skicka dem helst inte alls.

Vad som händer med svaret: det används bara för assistentens enda svar och lagras inte – varken i konversationen eller i kunskapsbasen; loggen registrerar bara antal och varaktighet. Om slutpunkten inte svarar i tid eller svarar med ett fel svarar assistenten som om det inte fanns några data.

Referensslutpunkt i 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]);

Referensslutpunkt i 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) });
});

Testa anslutningen

Testa anslutning i kontot anropar slutpunkten med user_id rc-test och anliegen test. Returnera en tom lista eller en exempelbeställning för detta. Om anropet misslyckas anger kontot någon av dessa orsaker:

Import via API

Om butiken inte kan erbjuda en slutpunkt skickar du data till oss. Du behöver en API-nyckel med omfattningen bara import av kunddata (kundendaten:schreiben) och läget Import eller Båda.

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

Importerade data lagras krypterade (kundnumret bara som hash) och raderas 90 dagar efter den senaste importen. Skicka därför ändringar löpande, till exempel vid varje statusändring.

Fel: 402 plan_required (ingen betalplan), 409 import_disabled (läget tillåter inte import, med kd_modus), 400 bad_request med param (user_id, project_id, bestellungen), 404 (någon annans projekt). Det allmänna felformatet beskrivs under Server-API.

Besökare som inte är inloggade

Om någon utan bekräftad identitet frågar om en beställning hämtar assistenten inget; i stället visar den informationen från kontot (text per språk, valfritt en inloggningslänk) eller lämnar över till teamet.

Nästa: Webhooks · Server-API

آخرین به‌روزرسانی: