@pandait.tech/payment-nuvei 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -110,11 +110,17 @@ export const POST = createChargeHandler({
110
110
 
111
111
  Apply the same pattern to the remaining handlers — each one is documented inline with its own `XHandlerDeps` interface.
112
112
 
113
- ### Securing the webhook (important)
113
+ ### The webhook is OPTIONAL — and if you expose it, secure it
114
114
 
115
- Paymentez/Nuvei DMNs (webhooks) are **not signed**, so the webhook endpoint must be protected with a shared secret — otherwise anyone who knows an `orderId` could POST a fake "paid" notification.
115
+ First, set expectations correctly:
116
116
 
117
- 1. Set a secret and pass it to the handler (or via `NUVEI_WEBHOOK_SECRET`):
117
+ - **Payments are confirmed by the synchronous `/debit` response** (`createChargeHandler` reads `transaction.status === "success" && status_detail === 3`), plus the 3DS `term_url` callback + `verify` polling for challenged transactions. **That path fully confirms a payment without any webhook.**
118
+ - Paymentez's transaction-notification **callback** (`developers.paymentez.com/api/#webhook`) is an **optional, redundant async channel** — Paymentez POSTs transaction status to a URL you register *with Paymentez during integration* (there is no documented self-serve dashboard; for many accounts it is simply never wired up). In a real production consumer we observed Paymentez never calling it at all.
119
+ - So `createWebhookHandler` is a reconciliation backstop you can mount **if** your Paymentez account is configured to notify it. It is **not required** to take payments.
120
+
121
+ Because the endpoint is nonetheless a **public POST** route, if you mount it you must authenticate it — otherwise anyone who knows an `orderId` could POST a fake "paid" notification (`status_detail: 3`) and mark an order paid. Paymentez callbacks are **not signed**, so use a shared secret:
122
+
123
+ 1. Pass a secret to the handler (or via `NUVEI_WEBHOOK_SECRET`):
118
124
 
119
125
  ```ts
120
126
  export const POST = createWebhookHandler({
@@ -125,7 +131,7 @@ Paymentez/Nuvei DMNs (webhooks) are **not signed**, so the webhook endpoint must
125
131
  });
126
132
  ```
127
133
 
128
- 2. In the **Nuvei dashboard**, configure your DMN/callback URL with the secret as a query param:
134
+ 2. If/when you register the notification URL with Paymentez, append the secret as a query param:
129
135
 
130
136
  ```
131
137
  https://your-shop.com/api/webhooks/nuvei?key=YOUR_SECRET
@@ -133,9 +139,9 @@ Paymentez/Nuvei DMNs (webhooks) are **not signed**, so the webhook endpoint must
133
139
 
134
140
  (The secret is also accepted as the `x-webhook-key` header.)
135
141
 
136
- Requests without a matching key get `401` and are not processed (constant-time compared). If `webhookSecret` is **unset**, the handler still processes but logs a loud warning — treat that as not-production-ready.
142
+ Requests without a matching key get `401` and are not processed (constant-time compared). If `webhookSecret` is **unset**, the handler still processes but logs a loud warning. If you never register the URL with Paymentez, setting the secret simply seals the endpoint (everything → 401), which is a safe default.
137
143
 
138
- > `onPaymentSucceeded` fires once, on the transition into `paid` (idempotent across Nuvei's webhook retries).
144
+ > `onPaymentSucceeded` fires once, on the transition into `paid` (idempotent across retries).
139
145
 
140
146
  ## Hosting & deployment (3DS callback + Nuvei egress)
141
147