Documentație parteneri

Tot ce aveți nevoie pentru a publica anunțurile dvs. de vânzare și răscumpărare pe QuizzBuy și pentru a ne notifica comenzile atribuite traficului nostru.

1. Obținerea unei chei API

Contactați interlocutorul dvs. QuizzBuy (sau formularul de contact). Generăm pentru dvs. o cheie în formatul zzb_live_… — vă este comunicată o singură dată, păstrați-o în siguranță. Transmiteți-o la fiecare cerere în antetul HTTP:

Authorization: Bearer zzb_live_VOTRE_CLE

2. Verificarea cheii dvs.

curl https://quizzbuy.com/api/v1/me \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Réponse
{
  "company": { "id": "…", "name": "Votre société", "slug": "votre-societe" },
  "commission_rate": 0.05,
  "click_param": "zzb_click"
}

3. Publicarea anunțurilor

Un anunț este fie o ofertă de răscumpărare (BUYBACK — preluați un dispozitiv la un preț dat), fie o ofertă de vânzare (SALE — un produs recondiționat pe care îl vindeți). Trimiterea este un upsert: retrimiterea aceluiași externalId actualizează anunțul.

curl -X POST https://quizzbuy.com/api/v1/listings \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "ref-interne-123",
    "type": "BUYBACK",
    "title": "Reprise iPhone 15 Pro 256 Go",
    "brand": "Apple",
    "model": "iPhone 15 Pro",
    "category": "SMARTPHONE",
    "storage": "256 GB",
    "condition": "GOOD",
    "priceCents": 52000,
    "currency": "EUR",
    "url": "https://votre-site.fr/reprise/iphone-15-pro",
    "country": "FR"
  }'
  • category: SMARTPHONE, LAPTOP, SMARTWATCH, AUDIO sau GAMING.
  • condition: LIKE_NEW, EXCELLENT, GOOD, FAIR sau BROKEN.
  • priceCents: preț în cenți (52000 = 520 €).
  • Fiecare anunț nou (sau modificare) trece în moderare înainte de publicare.

Actualizare, listare sau dezactivare:

# Lister vos annonces
curl "https://quizzbuy.com/api/v1/listings?type=BUYBACK&status=APPROVED" \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Mise à jour partielle
curl -X PATCH https://quizzbuy.com/api/v1/listings/ID_ANNONCE \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "priceCents": 49000 }'

# Désactiver (soft delete)
curl -X DELETE https://quizzbuy.com/api/v1/listings/ID_ANNONCE \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

4. Urmărirea click-urilor

Când un vizitator QuizzBuy dă click către site-ul dvs., URL-ul de destinație conține un identificator de click:

https://votre-site.fr/vendre?zzb_click=6f1e0c9a-3b2d-4e8f-9a10-abcdef123456

Stocați această valoare (cookie sau sesiune pe partea site-ului dvs., durată recomandată: 45 de zile). Aceasta este cea care va permite atribuirea comenzii. Numele parametrului (zzb_click implicit) este configurabil la cerere.

5. Notificarea unei comenzi (postback S2S)

Imediat ce un client plasează o comandă (achiziție sau cerere de răscumpărare validată) și dispuneți de un zzb_click, apelați postback-ul nostru de pe serverul dvs.:

curl -X POST https://quizzbuy.com/api/v1/postback \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "click_id": "6f1e0c9a-3b2d-4e8f-9a10-abcdef123456",
    "type": "BUYBACK",
    "amount_cents": 52000,
    "currency": "EUR",
    "order_ref": "CMD-2026-000123"
  }'

# Réponse 201
{ "conversion_id": "…", "status": "TO_INVOICE", "commission_cents": 2600 }
  • Fereastră de atribuire: 45 de zile după click; peste această limită, răspuns 422 attribution_window_expired.
  • order_ref trebuie să fie unic: un duplicat returnează 409 duplicate_order_ref (idempotență — puteți reîncerca fără risc).
  • type: SALE pentru o achiziție, BUYBACK pentru o răscumpărare.
  • currency: doar EUR — orice altă monedă este refuzată (422 unsupported_currency).

6. Testare în sandbox

Adăugați "test": true la postback: cererea este validată integral (cheie, click_id, fereastra de 45 zile), dar nicio conversie nu este înregistrată sau facturată.

{ "click_id": "…", "type": "SALE", "amount_cents": 10000, "order_ref": "TEST-1", "test": true }
# → 200 { "test": true, "valid": true, "commission_cents": 500 }

7. Coduri de eroare

CodEroareExplicație
400invalid_inputCorp de cerere invalid (detalii în răspuns).
401unauthorizedCheie API absentă, revocată sau invalidă.
404click_not_found / not_foundclick_id sau anunț necunoscut (sau nu vă aparține).
409duplicate_order_refComandă deja notificată.
422attribution_window_expiredClick mai vechi de 45 de zile.
429rate_limitedPrea multe cereri — reîncercați într-un minut.

8. Facturare

Fiecare conversie atribuită generează un comision (rată contractuală, vizibilă prin /api/v1/me). QuizzBuy vă trimite o factură recapitulativă periodică a conversiilor validate. Pentru orice întrebare: contactați-ne.

9. Marketplace — vânzare pe QuizzBuy

Anunțurile SALE cu un quantity > 0 sunt vândute direct pe QuizzBuy (plata clientului la noi, virarea sumei nete de comision). Câmpuri suplimentare: quantity (stoc), color, grade (A/B/C), batteryHealth (%), warrantyMonths. Anunțul este atașat automat fișei de produs corespunzătoare (marcă + model + capacitate + culoare).

# Mettre à jour le stock (sans re-modération)
curl -X PATCH https://quizzbuy.com/api/v1/listings/ID_ANNONCE/stock \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "quantity": 12 }'

# Ajouter une photo (multipart, max 5 × 5 Mo, jpeg/png/webp)
curl -X POST https://quizzbuy.com/api/v1/listings/ID_ANNONCE/images \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -F "file=@photo.jpg"

10. Webhook-uri — fiți informat despre vânzări

Înregistrați un endpoint HTTPS; vă notificăm la fiecare etapă a unei comenzi care conține produsele dvs. (order.created, order.paid, order.cancelled). Secretul este returnat doar la creare — stocați-l.

curl -X POST https://quizzbuy.com/api/v1/webhooks \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://votre-site.fr/webhooks/quizzbuy", "events": ["order.paid"] }'

# Réponse (secret affiché une seule fois)
{ "webhook": { "id": "…", "secret": "whsec_…", "events": ["order.paid"] } }

# Tester
curl -X POST https://quizzbuy.com/api/v1/webhooks/ID_WEBHOOK/test \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

Fiecare livrare este semnată. Verificați antetul X-ZZbuy-Signature (t=timestamp,v1=hex):

// Node.js
const [t, v1] = signature.split(",").map((p) => p.split("=")[1]);
const expected = crypto
  .createHmac("sha256", WEBHOOK_SECRET)
  .update(`${t}.${rawBody}`)
  .digest("hex");
const valid =
  crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) &&
  Math.abs(Date.now() / 1000 - Number(t)) < 300; // anti-replay 5 min

În caz de eșec (≠ 2xx), reîncercăm cu backoff: 1 min, 5 min, 30 min, 2 h, 12 h. Primiți de asemenea un email de notificare de vânzare.

11. Procesarea comenzilor dvs.

Listați liniile de comandă, confirmați primirea, apoi expediați cu un număr de urmărire (clientul este notificat automat). Adresa de livrare este vizibilă doar după plată.

# Lister les commandes à traiter
curl "https://quizzbuy.com/api/v1/orders?status=PENDING" \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Accuser réception
curl -X POST https://quizzbuy.com/api/v1/orders/ID_LIGNE/ack \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Expédier
curl -X POST https://quizzbuy.com/api/v1/orders/ID_LIGNE/ship \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "carrier": "Colissimo", "trackingNumber": "6A123456789FR" }'

12. Reprise gérée (trade-in)

Reprise gérée merge mai departe decât simpla răscumpărare prin redirecționare: persoana fizică își trimite dispozitivul de pe QuizzBuy, iar dvs. gestionați întregul dosar prin API (acceptare, primire, contraofertă, plată). Prețul afișat clientului este blocat la trimitere pe baza grilei dvs. de răscumpărare (a se vedea §14); îl puteți revizui în scădere doar după primire, printr-o contraofertă motivată.

Toate endpoint-urile se autentifică cu cheia dvs. API (Authorization: Bearer zzb_live_…) și returnează doar dosarele dvs.

Ciclu de viață

SUBMITTEDACCEPTEDSHIPPEDRECEIVEDPAID. Două ramificații: REJECTED (refuz înainte de expediere) și COUNTER_OFFER (contraofertă după inspecție, pe care persoana fizică o acceptă — apoi PAID — sau o refuză — returnare dispozitiv, CANCELLED).

StatutSemnificație
SUBMITTEDCerere creată de persoana fizică, în așteptarea deciziei dvs.
ACCEPTEDAți confirmat răscumpărarea; clientul trebuie să expedieze dispozitivul.
SHIPPEDPersoana fizică a introdus numărul de urmărire.
RECEIVEDAți primit dispozitivul; inspecție în curs.
COUNTER_OFFERDupă inspecție, propuneți o sumă revizuită (mai mică).
PAIDPlată efectuată către persoana fizică — dosar închis.
REJECTEDCerere refuzată înainte de expediere (neeligibil, fraudă…).
CANCELLEDAnulată (refuzul unei contraoferte, returnare dispozitiv).

Listare și consultare

# Lister vos reprises (plus récentes d'abord)
curl "https://quizzbuy.com/api/v1/trade-ins?status=SUBMITTED&limit=50&offset=0" \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Réponse
{
  "total": 3,
  "trade_ins": [
    {
      "trade_in_id": "…",
      "reference": "TI-2026-000042",
      "status": "SUBMITTED",
      "device": {
        "brand": "Apple", "category": "SMARTPHONE", "model": "iPhone 15 Pro",
        "storage": "256 GB", "condition": "GOOD",
        "functional_status": "FULLY_WORKING", "battery_health": 92, "imei": "…"
      },
      "offer_cents": 52000,
      "currency": "EUR",
      "price_locked_until": "2026-08-05T12:00:00.000Z",
      "country": "FR",
      "customer": { "email": "…", "first_name": "…", "last_name": "…", "locale": "fr" },
      "comment": null,
      "counter_offer_cents": null, "counter_reason": null,
      "tracking_carrier": null, "tracking_number": null,
      "shipping_label_url": null, "payout_ref": null,
      "accepted_at": null, "shipped_at": null, "received_at": null,
      "paid_at": null, "cancelled_at": null,
      "created_at": "2026-07-21T12:00:00.000Z"
    }
  ]
}

# Détail d'une reprise
curl https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"
# → { "trade_in": { … } }
  • status (filtru opțional): o valoare din tabelul de mai sus — altfel 400 invalid_status.
  • limit: max 200 (implicit 50); offset pentru paginare.
  • customer (contactul persoanei fizice) este vizibil doar în acest API partener, niciodată în webhook-uri.

Tranziții

Fiecare acțiune este un POST și returnează { "ok": true, "trade_in": { … } }. O tranziție dintr-un statut incompatibil returnează 409 invalid_status (cu current real). Persoana fizică este notificată prin email la fiecare etapă.

# 1. Accepter (depuis SUBMITTED) — shippingLabelUrl facultatif (étiquette prépayée)
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/accept \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "shippingLabelUrl": "https://votre-site.fr/labels/ti-42.pdf" }'

# 2. Réceptionner l'appareil (depuis SHIPPED ou ACCEPTED)
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/receive \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# 3a. Payer le montant garanti (depuis RECEIVED) — clôt la reprise
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/pay \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "payoutRef": "VIR-2026-000123" }'

# 3b. …ou contre-offrer après inspection (depuis RECEIVED) — montant < offre garantie
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/counter \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "amountCents": 42000, "reason": "Rayures écran non déclarées" }'

# Refuser avant envoi (depuis SUBMITTED uniquement)
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/reject \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"
  • accept: doar din SUBMITTED. shippingLabelUrl opțional (URL, ≤ 500 car.).
  • receive: din SHIPPED sau ACCEPTED (depunere/expediere nedeclarată de client).
  • counter: din RECEIVED. amountCents trebuie să fie strict inferior lui offer_cents, altfel 400 counter_not_lower; reason obligatoriu (1–500 car.). Ulterior, persoana fizică este cea care acceptă sau refuză din pagina sa de urmărire.
  • pay: din RECEIVED. payoutRef obligatoriu (referință de virament, 1–120 car.).
  • reject: doar din SUBMITTED — niciun corp necesar.

13. Webhook-uri reprise (trade_in.*)

Aceleași webhook-uri (§10, semnătură X-ZZbuy-Signature identică) acoperă reprise gérée. Abonați-vă la toate sau o parte din evenimentele trade_in.* la crearea unui endpoint:

curl -X POST https://quizzbuy.com/api/v1/webhooks \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://votre-site.fr/webhooks/quizzbuy",
    "events": ["trade_in.created", "trade_in.shipped", "trade_in.counter_accepted"]
  }'
EvenimentDeclanșator
trade_in.createdCerere nouă trimisă de o persoană fizică (statut SUBMITTED).
trade_in.shippedPersoana fizică a introdus numărul de urmărire al expedierii.
trade_in.counter_acceptedPersoana fizică a acceptat contraoferta dvs.
trade_in.counter_declinedPersoana fizică a refuzat contraoferta dvs. (returnare dispozitiv).
trade_in.cancelledRăscumpărare anulată.

Corpul livrării (contactul persoanei fizice nu figurează aici — obțineți-l prin GET /api/v1/trade-ins/:id):

{
  "trade_in_id": "…",
  "reference": "TI-2026-000042",
  "status": "SHIPPED",
  "device": {
    "brand": "Apple", "category": "SMARTPHONE", "model": "iPhone 15 Pro",
    "storage": "256 GB", "condition": "GOOD",
    "functional_status": "FULLY_WORKING", "battery_health": 92, "imei": "…"
  },
  "offer_cents": 52000,
  "currency": "EUR",
  "price_locked_until": "2026-08-05T12:00:00.000Z",
  "country": "FR"
}

14. Trimiterea grilei dvs. de răscumpărare

Alternativă „push” la fluxul CSV preluat de noi: trimiteți direct grila dvs. de prețuri de răscumpărare. Fiecare linie conține 4 sume în funcție de starea dispozitivului. Prețurile alimentează același tabel ca sincronizarea automată — apar deci imediat în comparator și servesc drept preț blocat pentru reprise gérée (§12). Operația este un upsert pe (marcă, categorie, model, capacitate).

curl -X PUT https://quizzbuy.com/api/v1/buyback/prices \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": [
      {
        "brand": "Apple",
        "category": "SMARTPHONE",
        "model": "iPhone 15 Pro",
        "storage": "256 GB",
        "priceNewCents": 60000,
        "priceGoodCents": 52000,
        "priceFairCents": 40000,
        "priceBrokenCents": 15000,
        "currency": "EUR",
        "url": "https://votre-site.fr/reprise/iphone-15-pro"
      }
    ]
  }'

# Réponse
{ "ok": true, "upserted": 1 }
  • prices: 1 până la 2000 de linii per cerere.
  • category: SMARTPHONE, TABLET, LAPTOP, SMARTWATCH, AUDIO sau GAMING.
  • priceNewCents (nou), priceGoodCents (bun), priceFairCents (marcat), priceBrokenCents (spart) — în cenți, ≥ 0.
  • storage și url opționale; capacitatea este normalizată automat (ex. „256go” → „256 GB”).