Move customerNumber from request root into each consignment's product#61
Conversation
Bring's Booking API expects customerNumber inside consignments[].product.customerNumber. The SDK was emitting it at the request root, which Bring's gateway silently ignores — it reads from the product slot, finds nothing, and rejects every consignment with BOOK-INPUT-019 "Customer number must be provided". The constructor invariant (non-empty customerNumber) prevented the local failure but couldn't help the wire shape. The public SDK shape is unchanged: customerNumber stays a single request-level field on BookingRequest. At toArray() time we inject it into each consignment's product object, so callers keep modelling it once even though Bring's schema repeats it per consignment. The existing BookingApiTest only checked the (incorrect) root placement against a mock client, so the bug went unnoticed. The assertion is updated to pin the field to consignments[0].product and to assert it is NOT present at the root, locking in the correct shape against the live API. Confirmed against https://developer.bring.com/api/booking/ — schema lists customerNumber as a required property of the product object inside each consignment.
There was a problem hiding this comment.
Code Review
This pull request updates the serialization of BookingRequest to nest the customerNumber inside each consignment's product object rather than placing it at the request root, aligning with the expectations of Bring's Booking API. The corresponding unit tests have been updated to reflect this structural change. Feedback was provided to replace the array union operator (+) with direct array assignment to improve readability and prevent potential confusion regarding array merging behavior in PHP.
| $consignments = array_map( | ||
| static function (Consignment $c) use ($customerNumber): array { | ||
| $arr = $c->toArray(); | ||
| $arr['product'] = ['customerNumber' => $customerNumber] + (array) $arr['product']; |
There was a problem hiding this comment.
Using the array union operator (+) for merging arrays can be non-intuitive in PHP because the left-hand side takes precedence (unlike array_merge where the right-hand side overwrites). To improve readability and avoid potential confusion for future maintainers, it is more idiomatic to use direct array assignment.
$product = (array) $arr['product'];
$product['customerNumber'] = $customerNumber;
$arr['product'] = $product;
Summary
Bring's Booking API expects
customerNumberinsideconsignments[].product.customerNumber, but the SDK was emitting it at the request root. Bring's gateway reads from the product slot, finds nothing, and rejects every consignment withBOOK-INPUT-019 "Customer number must be provided". TheBookingRequestconstructor invariant (non-emptycustomerNumber) prevented a local failure but couldn't help the wire shape — every live booking would fail.This was masked because the existing
BookingApiTestonly validated the (incorrect) root placement against a mock client.Change
BookingRequest::toArray()no longer emitscustomerNumberat the root. Instead, it injects the field into each consignment's product object at serialization time.customerNumberonce onBookingRequest. The repetition Bring's schema demands per consignment is an implementation detail.consignments[0].product.customerNumberAND to assert it is NOT present at the root, locking in the correct shape against the live API.Confirmed against https://developer.bring.com/api/booking/ — the schema lists
customerNumberas a required property of the product object inside each consignment.Test plan
vendor/bin/phpunit— 198 tests, all greenvendor/bin/phpstan analyse— no errorsBookingApiTestnow asserts the field at the product level and explicitly forbids the root placementGenerated by Claude Code