חשבוניות SLK

חיבור מותאם אישית

כל פלטפורמה ששולחת Webhook — ממפים בלחיצות, בלי קוד

חיבור מותאם אישיתWebhook

מה צריך

אימות
אימות חתימה ייעודי לפלטפורמה
מה מדביקים
הסוד נקבע לכל חיבור בנפרד במסך בניית החיבור
החזרים
חשבונית זיכוי מפיקים ידנית
הזמנת בדיקה
כפתור אחד במסך החיבור שולח הזמנה חתומה לדוגמה
בעמוד הזה

כל פלטפורמה ששולחת Webhook — מדביקים דוגמה, ממפים בלחיצות, וכל תשלום הופך למסמך.

מתאים לכל מערכת שאין לה חיבור מובנה: Magento, PrestaShop, OpenCart, Konimbo, Typeform, Jotform, Tally, Google Forms (דרך Apps Script), Bubble, Softr, Zapier ("Webhooks by Zapier") או האתר שלכם.

חיבור כל פלטפורמה ב-10 דקות#

  1. יצירת חיבור. חנויות ואינטגרציות → חיבור מותאם אישית → "חיבור חדש". תנו שם ובחרו אימות (ההמלצה: חתימת HMAC, אם הפלטפורמה תומכת). הסוד מוצג פעם אחת — העתיקו אותו.
  2. הכתובת. במסך החיבור מעתיקים את כתובת ה-Webhook ומדביקים אותה בפלטפורמה (שיטה POST). אם בחרתם טוקן או חתימה — מגדירים שם את הסוד ואת שם הכותרת (header).
  3. דוגמה. מדביקים בקשה אמיתית שהפלטפורמה שולחת (JSON או מחרוזת טופס). רוב הפלטפורמות מציגות דוגמה במסך ה-Webhook; אפשר גם לשלוח הזמנת בדיקה לאתר כמו webhook.site ולהעתיק משם.
  4. מיפוי. לוחצים "הצעת מיפוי אוטומטית" — או בוחרים "מתכון מוכן". עוברים על השדות המסומנים ב"כדאי לבדוק". מזהה ההזמנה חובה, והוא מונע מסמך כפול כשהפלטפורמה שולחת שוב.
  5. תצוגה מקדימה. בצד המסך רואים את המסמך שייצא: לקוח, שורות, מע"מ וסה"כ. אם הסכום ששולם שונה מסכום המסמך מופיעה אזהרה — מתקנים לפני שממשיכים.
  6. "להפיק רק כאשר". אם הפלטפורמה שולחת גם הזמנות שעוד לא שולמו, מגדירים שדה וערך (למשל status = paid). כל השאר מתקבל ולא מופק.
  7. שמירה ובדיקה. "שמירה", ואז "שליחת בדיקה" — הדוגמה נשלחת חתומה דרך הכתובת האמיתית ומפיקה מסמך אמיתי. התוצאה מופיעה ביומן ההזמנות.

Developer reference#

Receiver#

POST /api/billing/ecommerce/custom/<hookId> — one hookId per connector (a tenant may have many). Body: JSON, application/x-www-form-urlencoded, or multipart/form-data text fields (Jotform). Limits, body cap (512 KB) and per-IP rate limits are the generic receiver's (src/ecommerce/connectors/inbound.ts). Answers: 401 bad/missing auth · 404 unknown or deleted hookId · 200 {status:"issued", documentId} · 200 {skipped} for an event the "paid when" filter ignores, a paused connector, or a mapping failure (recorded as an error row in the orders log with the Hebrew reasons — a platform retry cannot fix a mapping).

Authentication (per connector)#

KindWhat the platform sends
hmac-hex<header>: hex(HMAC-SHA256(secret, rawBody)) (a sha256= prefix is accepted)
hmac-base64same, base64 — Typeform (Typeform-Signature: sha256=…), Tally (Tally-Signature)
timestamped<header>: t=<unix>,v1=hex(HMAC-SHA256(secret, "<t>.<rawBody>")), 5-minute tolerance
token<header>: <secret> (or Bearer <secret>), or ?token=<secret> on the URL
hookidnothing — the URL is the credential; per-hook rate limit (60/h)

The body-format setting is enforced too: a json connector refuses a form body and vice versa (401).

Mapping DSL#

One expression per document field:

expr      := source ( "|" transform )*
source    := alt ( "," alt )*          several = first non-empty, or joined by | concat
alt       := "literal" | 123 | true | false | now | path
path      := key(.key)*  with  [n]  [-1]  []  [key=value]  ["key with.dots"]
  • customer.email, items[0].name, items[-1].price — dot paths and indexes.
  • items[] — "for each element" — only in רשימת הפריטים (lines.each). Line fields are relative to the element (name, price), and the items[]. prefix is also accepted (items[].name). $.path inside a line field reads from the payload root.
  • answers[field.ref=email].email — the first element whose field.ref equals email (Typeform, Tally). Quote values with spaces: fields[label="Payment (price)"].value.
  • A JSON string met while walking is parsed (rawRequest.q3_name.first on Jotform).
  • A flat form key containing dots/brackets (billing[first_name]) is read verbatim.
  • "תשלום באתר" — a literal; 1800, true — literal number / boolean; now — today.
TransformEffect
shekelsdecimal major units → integer agorot ("1,234.50" → 123450). Default for money fields with no unit.
centsalready integer minor units (Stripe-style) — refuses a fraction
numberparse a number ("₪ 12,5" → 12.5)
percent18 → 1800 basis points
rate0.18 → 1800 basis points
trim / lower / upperstring cleanup
default:"…"value when empty
concat:" "join the comma-separated sources, skipping empty ones

Fields: externalId (required, [A-Za-z0-9._:@-]{1,100}), customerName (default לקוח/ה), customerEmail, customerVatId, customerAddress, subject (default <connector name> #<id>), currency (default ILS), taxIncluded (default true), vatBp (default the statutory rate), issueDate (ISO, DD/MM/YYYY, unix seconds/ms or now; default today), lines — {mode:"items", each, name, quantity (default 1), unitPrice, vatBp?, description?} or {mode:"single", name, amount} — shipping (adds a "משלוח" line), discount (fixed, document level), total (the platform's paid total), paymentType (literal 1..22 or a path + paymentTypeMap {"paypal":10,"cash":4}; unknown values fall back to 1 with a warning), paymentDate.

Money is never fudged: the payment row is the mapped total; if it differs from the document computed by the same engine that issues it (computeTotals), the preview warns and a payment document (receipt / invoice-receipt) is refused rather than issued with a wrong amount.

Stored ids: external_id = "<slug>:<order id>", apiIdentifier = "custom-<slug>-<order id>" — two connectors in one business never collide, and a redelivery returns the first document.

Examples#

Nested order (Magento/PrestaShop module, custom site)

json
{"order_id":"A-10045","status":"paid","customer":{"first_name":"נועה","last_name":"לוי","email":"[email protected]"},
 "items":[{"name":"כוס קרמיקה","quantity":2,"price":"59.90"}],"shipping":"25.00","total":"144.80"}
FieldExpression
externalIdorder_id
customerNamecustomer.first_name,customer.last_name | concat:" "
lines.each / name / quantity / unitPriceitems[] / name / quantity / price | shekels
shipping / totalshipping | shekels / total | shekels
paid whenstatus = paid

Flat gateway form: transaction_id=TX-1&sum=250.00&full_name=יוסי+כהן&email=y%40example.com

FieldExpression
externalIdtransaction_id
customerNamefull_name | default:"לקוח/ה"
lines (single)name "תשלום באתר", amount sum | shekels

Typeform with a payment question

FieldExpression
externalIdform_response.token
customerEmailform_response.answers[type=email].email
lines (single)name "תשלום בטופס", amount form_response.answers[type=payment].payment.amount | shekels
paid whenform_response.answers[type=payment].payment.success = true

Test with curl (hmac-hex)#

bash
URL='https://invoice.slk.co.il/api/billing/ecommerce/custom/<hookId>'
SECRET='<the connector secret>'
BODY='{"order_id":"T-1","status":"paid","items":[{"name":"בדיקה","quantity":1,"price":"10.00"}],"total":"10.00"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -sS -X POST "$URL" -H 'content-type: application/json' -H "x-signature: $SIG" --data "$BODY"

Token auth: curl -X POST "$URL?token=$SECRET" -H 'content-type: application/json' --data "$BODY".

Troubleshooting#

  • 401 — הסוד, שם הכותרת או פורמט הנתונים לא תואמים. חתימת HMAC מחושבת על הגוף המדויק שנשלח — פלטפורמה שמשנה רווחים או סדר שדות אחרי החתימה לא תעבור.
  • 404 — הכתובת שגויה, או שהחיבור נמחק.
  • שגיאה ביומן ההזמנות — המיפוי לא הצליח; הסיבה כתובה בעברית ליד ההזמנה. מתקנים את המיפוי, שומרים, והפלטפורמה או "שליחת בדיקה" שולחות שוב.
  • "payments … must equal total" — הסכום ששולם שונה מסכום המסמך. בודקים את "המחירים כוללים מע"מ", משלוח והנחה בתצוגה המקדימה.
  • חסום / כבוי — המסלול לא כולל חנויות, או שמודול החיוב כבוי. אחרי שדרוג מפיקים מחדש מיומן ההזמנות.

פותחים חשבון בדקה, ואז מדביקים את הכתובת.

חברו את חיבור מותאם אישית
חיבור חיבור מותאם אישית לחשבוניות SLK — הוראות התקנה | פורטל המפתחים