| @@ -248,60 +248,12 @@ | ||
| 248 | 248 | $payload = self::add_payment_link( $payload, $order ); |
| 249 | 249 | return self::add_receipt_link( $payload, $order ); |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | - public function sync_metadata( array $payload, int $order_id, string $source, bool $partial, int $sequence ): array { | |
| 253 | - return array( | |
| 254 | - 'order_id' => $order_id, | |
| 255 | - 'source' => $source, | |
| 256 | - 'partial' => $partial, | |
| 257 | - 'sequence' => $sequence, | |
| 258 | - // UNIFIED (#423 step 1): orders hash through THE canonical | |
| 259 | - // Revision::compute like every other collection — identity-strip, | |
| 260 | - // recursive key-sort, excluded volatile fields. Every order site | |
| 261 | - // (pull, stream, skeleton, snapshot, sync-index, push check) | |
| 262 | - // funnels through here or revision_for, so all move atomically. | |
| 263 | - 'revision' => self::canonical_revision( $payload ), | |
| 264 | - 'generated_at_gmt' => gmdate( 'c' ), | |
| 265 | - ); | |
| 266 | - } | |
| 267 | - | |
| 268 | 252 | /* |
| 269 | - * --------------------------------------------------------------------------- | |
| 270 | - * ORDER REVISION RECIPES — a VERSIONED list, newest first. | |
| 271 | - * | |
| 272 | - * Every entry below is a complete hashing recipe for an order payload, and each | |
| 273 | - * one corresponds to a wire shape this plugin has shipped. A deployed client | |
| 274 | - * stores whatever `currentRevision` it was handed at the time, so the write | |
| 275 | - * path's grace comparer (Write_Controller::revision_matches_with_grace) must be | |
| 276 | - * able to recognise ALL of them. NONE of these may be deleted or altered while | |
| 277 | - * the `woocommerce_pos_sync_legacy_revision_grace` option still exists. | |
| 278 | - * | |
| 279 | - * canonical_revision() | |
| 280 | - * CURRENT. Identity-stripped (order meta + item meta), image.id normalized | |
| 281 | - * to int, `tax_ids` and `links` excluded. Defined so that the augmented v2 | |
| 282 | - * document and a BARE wc/v3 re-read of the same order hash identically — | |
| 283 | - * which is what lets the pull, proxy and write-ack lanes all serve the | |
| 284 | - * augmented shape while the write path keeps hashing the bare one. | |
| 285 | - * | |
| 286 | - * pre_item_uuid_canonical_revision() | |
| 287 | - * The shape shipped between the read lanes gaining `tax_ids`/links and the | |
| 288 | - * read-time item-uuid stamping + image.id cast: item uuids stripped, but | |
| 289 | - * image.id left as wc/v3's string and `tax_ids` left in the hash. | |
| 290 | - * | |
| 291 | - * pre_augmentation_canonical_revision() | |
| 292 | - * The shape shipped before ANY v2 read augmentation: order identity meta | |
| 293 | - * stripped, nothing else. Item uuids, image.id and `tax_ids` all hashed | |
| 294 | - * as they arrive. | |
| 295 | - * | |
| 296 | - * legacy_revision() | |
| 297 | - * PRE-CUTOVER (#423 step 2): raw wp_json_encode with no ksort and no | |
| 298 | - * excluded-field list. Its comparer branch reserializes the order through | |
| 299 | - * serialize_order() rather than hashing a bare re-read. | |
| 300 | - * | |
| 301 | - * Retirement (#423 step 4) drops the option and the three non-canonical | |
| 302 | - * recipes together, not one at a time. | |
| 303 | - * --------------------------------------------------------------------------- | |
| 253 | + * canonical_revision() is THE single order revision recipe. | |
| 254 | + * The pre-1.10.0 versioned recipe list and grace comparer were retired per | |
| 255 | + * docs/adr/0033 (free#1745). | |
| 304 | 256 | */ |
| 305 | 257 | |
| 306 | 258 | /** THE canonical order revision: identity-stripped, then Revision::compute. */ |
| 307 | 259 | public static function canonical_revision( array $payload ): string { |
| @@ -310,9 +262,12 @@ | ||
| 310 | 262 | // revisions agree with a bare wc/v3 read of the same order. |
| 311 | 263 | unset( $payload['tax_ids'], $payload['_rxdb_digest'] ); |
| 312 | 264 | |
| 313 | 265 | // HPOS removes these internal fields only after the restored order's save |
| 314 | - // hooks run. Exclude them so that save and the completed restore hash alike. | |
| 266 | + // hooks run. The exclusion predates compute-at-pull (ADR 0033) and is frozen | |
| 267 | + // with the 1.10.x recipe: legacy journal rows stored hashes computed with it, | |
| 268 | + // and every read-time hash site (pull fallback, CAS re-read, proxy stamp) | |
| 269 | + // must keep matching them and each other across restore states. | |
| 315 | 270 | if ( isset( $payload['meta_data'] ) && is_array( $payload['meta_data'] ) ) { |
| 316 | 271 | $payload['meta_data'] = array_values( |
| 317 | 272 | array_filter( |
| 318 | 273 | $payload['meta_data'], |
| @@ -326,18 +281,8 @@ | ||
| 326 | 281 | |
| 327 | 282 | return Revision::compute( self::strip_item_identity_meta( self::strip_identity_meta( $payload ) ) ); |
| 328 | 283 | } |
| 329 | 284 | |
| 330 | - /** The canonical recipe used before v2 read augmentations were added. */ | |
| 331 | - public static function pre_augmentation_canonical_revision( array $payload ): string { | |
| 332 | - return Revision::compute( self::strip_identity_meta( $payload ) ); | |
| 333 | - } | |
| 334 | - | |
| 335 | - /** The pre-augmentation canonical recipe before read-time item UUID stamping. */ | |
| 336 | - public static function pre_item_uuid_canonical_revision( array $payload ): string { | |
| 337 | - return Revision::compute( self::strip_item_identity_meta( self::strip_identity_meta( $payload ), false ) ); | |
| 338 | - } | |
| 339 | - | |
| 340 | 285 | /** |
| 341 | 286 | * Canonicalize items in a COPY of the payload before hashing, so revision |
| 342 | 287 | * sources hashing the BARE wc/v3 form and lanes serving the augmented form |
| 343 | 288 | * agree on identical state: |
| @@ -348,9 +293,9 @@ | ||
| 348 | 293 | * would make the first post-stamp edit a false 409. |
| 349 | 294 | * - Normalize line_items[].image.id to an int — the augmented read lanes |
| 350 | 295 | * serve it typed (v1 parity) while bare wc/v3 serves a string. |
| 351 | 296 | */ |
| 352 | - private static function strip_item_identity_meta( array $payload, bool $normalize_image_ids = true ): array { | |
| 297 | + private static function strip_item_identity_meta( array $payload ): array { | |
| 353 | 298 | // coupon_lines joined the uuid-stamped set with the rest (the client pairs |
| 354 | 299 | // coupons by uuid too); their identity meta must be hash-invisible for the |
| 355 | 300 | // same reason as every other line type — the augmented document and a bare |
| 356 | 301 | // wc/v3 re-read of the same order must hash identically. For payloads from |
| @@ -362,9 +307,9 @@ | ||
| 362 | 307 | foreach ( $payload[ $items_key ] as $index => $item ) { |
| 363 | 308 | if ( ! is_array( $item ) ) { |
| 364 | 309 | continue; |
| 365 | 310 | } |
| 366 | - if ( $normalize_image_ids && 'line_items' === $items_key && isset( $item['image']['id'] ) ) { | |
| 311 | + if ( 'line_items' === $items_key && isset( $item['image']['id'] ) ) { | |
| 367 | 312 | $payload[ $items_key ][ $index ]['image']['id'] = (int) $item['image']['id']; |
| 368 | 313 | } |
| 369 | 314 | if ( ! isset( $item['meta_data'] ) || ! is_array( $item['meta_data'] ) ) { |
| 370 | 315 | continue; |
| @@ -381,31 +326,8 @@ | ||
| 381 | 326 | } |
| 382 | 327 | } |
| 383 | 328 | |
| 384 | 329 | return $payload; |
| 385 | - } | |
| 386 | - | |
| 387 | - /** | |
| 388 | - * The PRE-CUTOVER byte recipe (no ksort, volatiles included) — kept ONLY | |
| 389 | - * for the write path's grace comparer (#423 step 2), so a client whose | |
| 390 | - * stored baseRevision predates the cutover still drains. Deleted at | |
| 391 | - * retirement (step 4) along with the grace option. | |
| 392 | - */ | |
| 393 | - public static function legacy_revision( array $payload ): string { | |
| 394 | - // Pre-cutover payloads never contained the read-time `links` augmentation. | |
| 395 | - // The write path's grace comparer reserializes the CURRENT order (links now | |
| 396 | - // injected) and compares against a hash the client computed BEFORE this | |
| 397 | - // deployment — hashing links here would reject every unchanged pre-upgrade | |
| 398 | - // order with a false 409. | |
| 399 | - unset( $payload['links'], $payload['tax_ids'], $payload['_rxdb_digest'] ); | |
| 400 | - $payload = self::strip_item_identity_meta( $payload ); | |
| 401 | - foreach ( $payload['line_items'] ?? array() as $index => $line_item ) { | |
| 402 | - if ( isset( $line_item['image']['id'] ) ) { | |
| 403 | - $payload['line_items'][ $index ]['image']['id'] = (string) $line_item['image']['id']; | |
| 404 | - } | |
| 405 | - } | |
| 406 | - $source = wp_json_encode( self::strip_identity_meta( $payload ) ); | |
| 407 | - return 'sha256:' . hash( 'sha256', false === $source ? '' : $source ); | |
| 408 | 330 | } |
| 409 | 331 | |
| 410 | 332 | /** |
| 411 | 333 | * Drop `_woocommerce_pos_uuid` from a COPY of the payload before hashing the |