כל תשלום PayPal שהושלם הופך למסמך מס, וכל החזר הופך לחשבונית זיכוי.
התקנה (5 דקות)#
- ב-developer.paypal.com → Apps & Credentials → בחרו את האפליקציה (מצב Live) → Add Webhook.
- ב-Webhook URL הדביקו את כתובת ה-Webhook שמוצגת אצלכם במסך החיבור.
- סמנו את האירועים Payment capture completed (
PAYMENT.CAPTURE.COMPLETED) ו-Payment capture refunded (PAYMENT.CAPTURE.REFUNDED). - אחרי השמירה העתיקו את ה-Webhook ID (מחרוזת כמו
8PT597110X687430LKGECATA) והדביקו אותו בשדה הסוד. - אופציונלי: הדביקו גם Client ID ו-Client Secret של אותה אפליקציה. אירוע התשלום של PayPal לא כולל פרטי קונה ולא פריטים. עם ה-credentials המסמך יכלול את שם הלקוח, האימייל, הכתובת והפריטים. בלי הם המסמך יופק עם שורה אחת "תשלום PayPal" ושם "לקוח/ה".
- בחרו סוג מסמך.
שימו לב: כפתור "שליחת הזמנת בדיקה" לא עובד עבור PayPal. רק PayPal יכול לחתום על אירוע, ולכן הבדיקה נדחית ב-401. בדקו עם תשלום אמיתי ב-Sandbox.
Developer reference#
Endpoint: POST /api/billing/ecommerce/paypal/<hookId>
Auth (transmission signature, verified offline): PayPal sends PAYPAL-TRANSMISSION-ID, PAYPAL-TRANSMISSION-TIME, PAYPAL-TRANSMISSION-SIG (base64), PAYPAL-CERT-URL, PAYPAL-AUTH-ALGO (SHA256withRSA). The signed message is
<transmission id>|<transmission time>|<webhook id>|<crc32(rawBody)> with CRC32 as an unsigned decimal. Steps in verify:
- Algorithm must be
SHA256withRSA. - The cert URL must be
https, host ending in.paypal.com, no port or credentials, path under/v1/notifications/certs/. Anything else is rejected without a request. - The PEM is fetched once and cached for 24h per URL.
RSA-SHA256verification of the message with the certificate's public key.
The Webhook ID is the connector secret, so a delivery meant for a different webhook cannot verify. A failing cert fetch fails closed (401, PayPal retries). Docs: https://developer.paypal.com/api/rest/webhooks/rest/, events: https://developer.paypal.com/api/rest/webhooks/event-names/.
| Event | Result |
|---|---|
PAYMENT.CAPTURE.COMPLETED (status COMPLETED) | document |
PAYMENT.CAPTURE.REFUNDED | credit note |
CHECKOUT.ORDER.APPROVED and all others | 200 {skipped} |
| PayPal field | Document |
|---|---|
resource.id | externalId; apiIdentifier = paypal-<id>[-<invoice_id or custom_id>] (sanitised, max 40 chars) |
resource.amount.value (decimal string) | agorot by string arithmetic ("236.00" → 23600); payment type PayPal |
pulled order payer.name / email_address | customer name / email |
pulled order purchase_units[0].shipping.address | customer address |
pulled order items[] (unit_amount + tax) × quantity | lines; a positive residual against the capture becomes משלוח / תוספת, a negative one falls back to one line |
pulled order breakdown.tax_total | document VAT rate (nearest 18% / 17%, else statutory) |
Pull (optional): with client_id + client_secret, fetchOrder runs POST /v1/oauth2/token (client credentials) and GET /v2/checkout/orders/{order_id} (from supplementary_data.related_ids.order_id) on the same environment as the event's own links (live or sandbox host from an allowlist). 401 / 403 / 404: falls back to the bare capture. Transport error or 5xx: 502, PayPal retries.
Refunds: refund id = resource.id; the original capture id is the rel: "up" link. The event does not state the capture total, so the credit note is always issued for the refunded amount (a full refund is a credit for the full amount).
Test: a valid signature can only come from PayPal. Use a Sandbox payment, or the developer dashboard's webhook event history to resend a real event. A hand-made curl is answered 401:
curl -i -X POST "https://<your-host>/api/billing/ecommerce/paypal/<hookId>" \
-H "Content-Type: application/json" -d '{"event_type":"PAYMENT.CAPTURE.COMPLETED"}'
# → 401 {"error":"invalid signature"}Troubleshooting#
- 401 invalid signature: ה-Webhook ID בשדה הסוד לא של ה-Webhook ששולח (לכל Webhook ID משלו, וגם ל-Sandbox ול-Live). בדקו שאין רווח בהעתקה.
- 401 secret is not configured: עדיין לא הדבקתם Webhook ID.
- 404 unknown hook: כתובת ה-Webhook לא הועתקה במלואה.
- המסמך ללא שם לקוח ופריטים: לא הוגדרו Client ID ו-Client Secret, או שהם שייכים לסביבה אחרת (Sandbox מול Live).
- הזמנה בסטטוס blocked או dormant: מגבלת החבילה או מודול החיוב כבוי. אפשר להפיק ידנית מיומן ההזמנות.
- זיכוי בסטטוס error: התשלום המקורי לא הופק אצלנו, ולכן אין מסמך לזכות.