diff --git a/CHANGELOG.md b/CHANGELOG.md index 96862e1..b214b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 without performing an HTTP request, for embedding in an already- authenticated UI via ``. +### Fixed +- `PriceRequest::toQuery()` now serialises the `product` query parameter + as Bring's numeric service code (via the new `Product::shippingGuideCode()`) + instead of the v2 string name. Shipping Guide v2 prices by numeric code, + so sending `EXPRESS_NORDIC_0900` (and the other string names) returned an + empty product list — i.e. "no price". Products without a confirmed numeric + code fall back to the string value, unchanged. Note: Express Nordic 09:00 + (`0335`) is being decommissioned by Bring on 2026-09-01 in favour of Bring + Courier & Express (`3620` + VAS `1171`). + +### Added +- `Product::shippingGuideCode(): ?string` — the numeric service code the + Shipping Guide v2 endpoints expect, string-typed so leading zeros survive + (`0335`). Distinct from `legacyNumericCode()`, which carries the historical + in-app codes (Express Nordic 09:00 is `4850` there, `0335` here). + ### Changed - `TrackingApi::signature(string)` and `SignatureEndpoint::__construct` now expect the **signature-link path** (the value Bring returns on diff --git a/src/v4/Endpoint/Shipping/PriceRequest.php b/src/v4/Endpoint/Shipping/PriceRequest.php index dc8652f..d2c82bf 100644 --- a/src/v4/Endpoint/Shipping/PriceRequest.php +++ b/src/v4/Endpoint/Shipping/PriceRequest.php @@ -81,7 +81,14 @@ public function toQuery(): array } if ($this->products !== []) { - $q['product'] = array_map(static fn (Product $p): string => $p->value, $this->products); + // Shipping Guide v2 prices by Bring's numeric service code, not the + // v2 string name (sending EXPRESS_NORDIC_0900 et al. yields an empty + // product list / "no price"). Fall back to the string value only for + // products without a confirmed numeric code. + $q['product'] = array_map( + static fn (Product $p): string => $p->shippingGuideCode() ?? $p->value, + $this->products, + ); } if ($this->additional !== []) { $q['additional'] = array_map(static fn (AdditionalService $a): string => $a->value, $this->additional); diff --git a/src/v4/Enum/Product.php b/src/v4/Enum/Product.php index d89990a..109b712 100644 --- a/src/v4/Enum/Product.php +++ b/src/v4/Enum/Product.php @@ -44,7 +44,14 @@ enum Product: string case RETURN_BUSINESS_PARCEL = 'BUSINESS_PARCEL_RETURN'; case RETURN_BUSINESS_PALLET = 'BUSINESS_PALLET_RETURN'; - /** Numeric legacy codes still accepted by some endpoints. */ + /** + * In-app/legacy numeric codes some callers still send (e.g. the price + * calculator). These are the historical service numbers carried over from + * the v3 era — NOT necessarily the codes the current Shipping Guide v2 + * endpoint prices by. Use {@see shippingGuideCode()} when building a + * Shipping Guide request; the two diverge for Express Nordic 09:00 + * (legacy 4850 vs Shipping Guide 0335). + */ public function legacyNumericCode(): ?int { return match ($this) { @@ -57,6 +64,39 @@ public function legacyNumericCode(): ?int }; } + /** + * Product identifier as the Shipping Guide v2 endpoints + * (/shippingguide/v2/products[/price]) expect it in the `product` query + * parameter. + * + * Shipping Guide v2 identifies products by Bring's numeric service codes + * (e.g. "5800" Pickup Parcel, "5600" Home Delivery, "0335" Express Nordic + * 09:00). It does NOT price the v2 string names (EXPRESS_NORDIC_0900, …) — + * sending those returns an empty product list ("no price"). Codes are + * returned as strings so leading zeros survive ("0335" must not become 335). + * + * Returns null for products whose Shipping Guide code is not yet confirmed; + * callers should fall back to the enum string value for those so unknown + * products are no worse off than before. + * + * NOTE: Express Nordic 09:00 (0335) is being decommissioned by Bring on + * 2026-09-01. The successor is Bring Courier & Express (3620) with VAS 1171 + * — that migration needs both a product and an additional-service change, + * so it is intentionally NOT silently mapped here. + */ + public function shippingGuideCode(): ?string + { + return match ($this) { + self::PICKUP_PARCEL => '5800', + self::HOME_DELIVERY_PARCEL => '5600', + self::BUSINESS_PARCEL => '5000', + self::EXPRESS_NORDIC_0900 => '0335', + self::MAILBOX_PARCEL => '3584', + self::MAILBOX_PARCEL_TRACKED => '3570', + default => null, + }; + } + /** * Product identifier as Bring's Booking API expects it in product.id. * diff --git a/tests/v4/Endpoint/Shipping/PriceRequestTest.php b/tests/v4/Endpoint/Shipping/PriceRequestTest.php new file mode 100644 index 0000000..ebc3aa1 --- /dev/null +++ b/tests/v4/Endpoint/Shipping/PriceRequestTest.php @@ -0,0 +1,57 @@ + 1000]], + products: array_values($products), + ); + } + + public function testProductsSerialiseToBringNumericServiceCodes(): void + { + $q = $this->request(Product::BUSINESS_PARCEL, Product::PICKUP_PARCEL)->toQuery(); + + // Shipping Guide v2 prices by numeric code, not the v2 string name. + self::assertSame(['5000', '5800'], $q['product']); + } + + public function testExpressNordicKeepsLeadingZeroAndUsesShippingGuideCode(): void + { + $q = $this->request(Product::EXPRESS_NORDIC_0900)->toQuery(); + + // 0335 (Shipping Guide v2), NOT the string name and NOT the legacy + // in-app code 4850 — and the leading zero must survive. + self::assertSame(['0335'], $q['product']); + } + + public function testUnmappedProductFallsBackToStringValue(): void + { + // No confirmed Shipping Guide numeric code: keep prior behaviour. + $q = $this->request(Product::EXPRESS_INTERNATIONAL)->toQuery(); + + self::assertSame(['EXPRESS_INTERNATIONAL'], $q['product']); + } + + public function testNoProductParamWhenProductsEmpty(): void + { + self::assertArrayNotHasKey('product', $this->request()->toQuery()); + } +}