# Laravel fix request: PlentyOne delivery confirmation **Endpoint affected:** `POST /api/sales/orders/mark-plentyone-order-delivery` ## The bug we're seeing Roughly **1–2 of every 100 PlentyOne deliveries** end up in a broken state on PlentyOne: - The **confirmation comment** ("we shipped …") **is posted** on the order. - The **shipping package / tracking number is NOT registered** on the order. From the Nuxt frontend's point of view this looks like a success because the Laravel endpoint returns 2xx, so we mark the shipment as confirmed (`IsCommissionedConfirmed = true`, `ack_commissioned_laravel = true`) and the issue stays invisible until a customer complains that the tracking number is missing on Plenty. ## What we suspect Laravel currently does (roughly, in order) on each call: 1. Send order comment to Plenty. 2. Send shipping package (tracking number) to Plenty. 3. Send the customer notification mail. If step 1 succeeds but step 2 silently fails (network blip, Plenty timeout, 2xx response without a real `packageId`, etc.), the order ends up with a comment but no package — exactly the state we see. ## What we'd like changed Please change `sales/orders/mark-plentyone-order-delivery` so that: ### 1. Reorder: tracking first, comment second, mail last Run the shipping-package call to Plenty **first**. Only if it returns 2xx **and** the response contains a valid `packageId`, continue to the next step. Suggested order: 1. POST shipping package (tracking number) to Plenty. 2. POST order comment to Plenty. 3. Send customer notification mail. ### 2. Verify-after-write (defence in depth) Right after the shipping-package POST returns 2xx, do a `GET` on the order's shipping packages and confirm the tracking number we sent is actually present in the returned list. If it isn't, treat that as a tracking failure — same as step 1 failing. This protects us against Plenty accepting a request but silently dropping the package, which we believe is part of what's happening. ### 3. Skip-on-failure If the tracking step fails (either non-2xx, or 2xx-but-not-verified): - **Do not** post the order comment. - **Do not** send the customer notification mail. - **Do not** return 2xx to Nuxt. The whole operation should be all-or-nothing from our perspective. ### 4. Return a structured error on failure When any step fails, return a non-2xx HTTP response with a body like: ```json { "status": 500, "step": "tracking", "trackingAdded": false, "commentAdded": false, "mailSent": false, "plentyResponse": { "...": "raw upstream response or error" }, "message": "Plenty did not register the shipping package" } ``` `step` is one of `"tracking" | "comment" | "mail"` so we can show operators what failed. ### 5. Log every step Persist the per-step result and raw Plenty payloads to wherever Laravel already keeps integration logs. Please key the log rows by: - `plentyone_order_id` (Plenty's internal order id; we send it as `id` in the request body). - our `referenceId` (the value we send under `details.referenceId` — this is `M_Inout.DocumentNo` in our ERP). That way when we get a customer complaint we can correlate a Plenty order id with whatever happened on Laravel's side at confirmation time. ### 6. Resubmit safety We already have a resubmit flow on our side (`server/api/commission/parcels/resubmit.post.ts`) that re-calls the same endpoint when a confirmation has gone wrong. Once you reorder the steps, this resubmit should be idempotent: if a previous partial run posted the comment, the resubmit should detect that and **not** post a second one, just complete the missing tracking package step. (Ideally the partial state can no longer occur once the reorder is in, but the resubmit needs to remain safe to call.) ## What we send you today (for reference) The request body Nuxt sends to `POST /api/sales/orders/mark-plentyone-order-delivery` looks like this: ```json { "orderSource": { /* full C_OrderSource record incl. marketplace_url, marketplace_key, marketplace_secret */ }, "id": "", "details": { "shippingDate": "YYYY-MM-DD", "carrierCode": "DHL", "shippingMethod": "Paket", "referenceId": "", "weight": 0 }, "trackingCodes": { "number": "", "url": "", "trackingNoArray": ["additional", "tracking", "numbers"] // optional, multi-parcel only }, "mail": { "email": "customer@example.com", "isSentCustomTrackingMail": true, "orderNumber": "...", "name": "...", "company": "LogYou GmbH", "carrier": "DHL", "lines": [ { "description": "...", "quantity": 1 } ], "address1": "...", "address2": "...", "city": "...", "country": "...", "postal": "..." } } ``` Nothing about that payload needs to change — the fix is purely in how Laravel sequences and gates the three downstream actions on Plenty. ## Acceptance criteria We'll consider this fixed when: - **Happy path:** Plenty's package endpoint succeeds → behaviour identical to today (200 returned, comment + mail + package all present on Plenty). - **Plenty 5xx / timeout on package:** non-2xx returned to Nuxt, **no comment posted**, **no mail sent**, log row written with `step=tracking`. - **Plenty 200 but missing on verify GET:** same as above — non-2xx, no comment, no mail, log row written with `step=tracking`. - **Resubmit safety:** calling the endpoint a second time for the same order doesn't produce a duplicate comment on Plenty. ## What we'll do on our side (Nuxt) For our own diagnostics we'll add a small log column on `M_Inout` (`marketplace_confirm_log`) and persist your full response body to it on every call. That gives us forensic data while the fix is rolling out and afterwards — but it changes nothing about how we call your endpoint or what we expect from it. Thanks!