@@ -262,7 +262,7 @@ public function maybe_skip_postcode_validation( $valid, $postcode, $country ) {
262
262
return $ valid ;
263
263
}
264
264
265
- // We padded the string with `0` in the ` get_normalized_postal_code` method.
265
+ // We padded the string with `0` in the express_checkout_button_helper's get_normalized_postal_code method.
266
266
// It's a flimsy check, but better than nothing.
267
267
// Plus, this check is only made for the scenarios outlined in the `tokenized_cart_store_api_address_normalization` method.
268
268
if ( substr ( $ postcode , - 1 ) === '0 ' ) {
@@ -285,15 +285,142 @@ private function transform_ece_address_state_data( $address ) {
285
285
return $ address ;
286
286
}
287
287
288
- // states from Apple Pay or Google Pay might be in long format, we need their short format.
288
+ // Due to a bug in Apple Pay, the "Region" part of a Hong Kong address is delivered in
289
+ // `shipping_postcode`, so we need some special case handling for that. According to
290
+ // our sources at Apple Pay people will sometimes use the district or even sub-district
291
+ // for this value. As such we check against all regions, districts, and sub-districts
292
+ // with both English and Mandarin spelling.
293
+ //
294
+ // @reykjalin: The check here is quite elaborate in an attempt to make sure this doesn't break once
295
+ // Apple Pay fixes the bug that causes address values to be in the wrong place. Because of that the
296
+ // algorithm becomes:
297
+ // 1. Use the supplied state if it's valid (in case Apple Pay bug is fixed)
298
+ // 2. Use the value supplied in the postcode if it's a valid HK region (equivalent to a WC state).
299
+ // 3. Fall back to the value supplied in the state. This will likely cause a validation error, in
300
+ // which case a merchant can reach out to us so we can either: 1) add whatever the customer used
301
+ // as a state to our list of valid states; or 2) let them know the customer must spell the state
302
+ // in some way that matches our list of valid states.
303
+ //
304
+ // @reykjalin: This HK specific sanitazation *should be removed* once Apple Pay fix
305
+ // the address bug. More info on that in pc4etw-bY-p2.
306
+ if ( Country_Code::HONG_KONG === $ country ) {
307
+ include_once WCPAY_ABSPATH . 'includes/constants/class-express-checkout-hong-kong-states.php ' ;
308
+
309
+ $ state = $ address ['state ' ] ?? '' ;
310
+ if ( ! empty ( $ state ) && ! \WCPay \Constants \Express_Checkout_Hong_Kong_States::is_valid_state ( strtolower ( $ state ) ) ) {
311
+ $ postcode = $ address ['postcode ' ] ?? '' ;
312
+ if ( ! empty ( $ postcode ) && \WCPay \Constants \Express_Checkout_Hong_Kong_States::is_valid_state ( strtolower ( $ postcode ) ) ) {
313
+ $ address ['state ' ] = $ postcode ;
314
+ }
315
+ }
316
+ }
317
+
318
+ // States from Apple Pay or Google Pay are in long format, we need their short format.
289
319
$ state = $ address ['state ' ] ?? '' ;
290
320
if ( ! empty ( $ state ) ) {
291
- $ address ['state ' ] = $ this ->express_checkout_button_helper -> get_normalized_state ( $ state , $ country );
321
+ $ address ['state ' ] = $ this ->get_normalized_state ( $ state , $ country );
292
322
}
293
323
294
324
return $ address ;
295
325
}
296
326
327
+ /**
328
+ * Gets the normalized state/county field because in some
329
+ * cases, the state/county field is formatted differently from
330
+ * what WC is expecting and throws an error. An example
331
+ * for Ireland, the county dropdown in Chrome shows "Co. Clare" format.
332
+ *
333
+ * @param string $state Full state name or an already normalized abbreviation.
334
+ * @param string $country Two-letter country code.
335
+ *
336
+ * @return string Normalized state abbreviation.
337
+ */
338
+ private function get_normalized_state ( $ state , $ country ) {
339
+ // If it's empty or already normalized, skip.
340
+ if ( ! $ state || $ this ->is_normalized_state ( $ state , $ country ) ) {
341
+ return $ state ;
342
+ }
343
+
344
+ // Try to match state from the Express Checkout API list of states.
345
+ $ state = $ this ->get_normalized_state_from_ece_states ( $ state , $ country );
346
+
347
+ // If it's normalized, return.
348
+ if ( $ this ->is_normalized_state ( $ state , $ country ) ) {
349
+ return $ state ;
350
+ }
351
+
352
+ // If the above doesn't work, fallback to matching against the list of translated
353
+ // states from WooCommerce.
354
+ return $ this ->get_normalized_state_from_wc_states ( $ state , $ country );
355
+ }
356
+
357
+ /**
358
+ * Checks if given state is normalized.
359
+ *
360
+ * @param string $state State.
361
+ * @param string $country Two-letter country code.
362
+ *
363
+ * @return bool Whether state is normalized or not.
364
+ */
365
+ private function is_normalized_state ( $ state , $ country ) {
366
+ $ wc_states = WC ()->countries ->get_states ( $ country );
367
+ return is_array ( $ wc_states ) && array_key_exists ( $ state , $ wc_states );
368
+ }
369
+
370
+ /**
371
+ * Get normalized state from Express Checkout API dropdown list of states.
372
+ *
373
+ * @param string $state Full state name or state code.
374
+ * @param string $country Two-letter country code.
375
+ *
376
+ * @return string Normalized state or original state input value.
377
+ */
378
+ private function get_normalized_state_from_ece_states ( $ state , $ country ) {
379
+ // Include Express Checkout Element API State list for compatibility with WC countries/states.
380
+ include_once WCPAY_ABSPATH . 'includes/constants/class-express-checkout-element-states.php ' ;
381
+ $ pr_states = \WCPay \Constants \Express_Checkout_Element_States::STATES ;
382
+
383
+ if ( ! isset ( $ pr_states [ $ country ] ) ) {
384
+ return $ state ;
385
+ }
386
+
387
+ foreach ( $ pr_states [ $ country ] as $ wc_state_abbr => $ pr_state ) {
388
+ $ sanitized_state_string = $ this ->express_checkout_button_helper ->sanitize_string ( $ state );
389
+ // Checks if input state matches with Express Checkout state code (0), name (1) or localName (2).
390
+ if (
391
+ ( ! empty ( $ pr_state [0 ] ) && $ sanitized_state_string === $ this ->express_checkout_button_helper ->sanitize_string ( $ pr_state [0 ] ) ) ||
392
+ ( ! empty ( $ pr_state [1 ] ) && $ sanitized_state_string === $ this ->express_checkout_button_helper ->sanitize_string ( $ pr_state [1 ] ) ) ||
393
+ ( ! empty ( $ pr_state [2 ] ) && $ sanitized_state_string === $ this ->express_checkout_button_helper ->sanitize_string ( $ pr_state [2 ] ) )
394
+ ) {
395
+ return $ wc_state_abbr ;
396
+ }
397
+ }
398
+
399
+ return $ state ;
400
+ }
401
+
402
+ /**
403
+ * Get normalized state from WooCommerce list of translated states.
404
+ *
405
+ * @param string $state Full state name or state code.
406
+ * @param string $country Two-letter country code.
407
+ *
408
+ * @return string Normalized state or original state input value.
409
+ */
410
+ private function get_normalized_state_from_wc_states ( $ state , $ country ) {
411
+ $ wc_states = WC ()->countries ->get_states ( $ country );
412
+
413
+ if ( is_array ( $ wc_states ) ) {
414
+ foreach ( $ wc_states as $ wc_state_abbr => $ wc_state_value ) {
415
+ if ( preg_match ( '/ ' . preg_quote ( $ wc_state_value , '/ ' ) . '/i ' , $ state ) ) {
416
+ return $ wc_state_abbr ;
417
+ }
418
+ }
419
+ }
420
+
421
+ return $ state ;
422
+ }
423
+
297
424
/**
298
425
* Transform a Google Pay/Apple Pay postcode address data fields into values that are valid for WooCommerce.
299
426
*
@@ -310,12 +437,36 @@ private function transform_ece_address_postcode_data( $address ) {
310
437
// Normalizes postal code in case of redacted data from Apple Pay or Google Pay.
311
438
$ postcode = $ address ['postcode ' ] ?? '' ;
312
439
if ( ! empty ( $ postcode ) ) {
313
- $ address ['postcode ' ] = $ this ->express_checkout_button_helper -> get_normalized_postal_code ( $ postcode , $ country );
440
+ $ address ['postcode ' ] = $ this ->get_normalized_postal_code ( $ postcode , $ country );
314
441
}
315
442
316
443
return $ address ;
317
444
}
318
445
446
+ /**
447
+ * Normalizes postal code in case of redacted data from Apple Pay.
448
+ *
449
+ * @param string $postcode Postal code.
450
+ * @param string $country Country.
451
+ */
452
+ private function get_normalized_postal_code ( $ postcode , $ country ) {
453
+ /**
454
+ * Currently, Apple Pay truncates the UK and Canadian postal codes to the first 4 and 3 characters respectively
455
+ * when passing it back from the shippingcontactselected object. This causes WC to invalidate
456
+ * the postal code and not calculate shipping zones correctly.
457
+ */
458
+ if ( Country_Code::UNITED_KINGDOM === $ country ) {
459
+ // Replaces a redacted string with something like N1C0000.
460
+ return str_pad ( preg_replace ( '/\s+/ ' , '' , $ postcode ), 7 , '0 ' );
461
+ }
462
+ if ( Country_Code::CANADA === $ country ) {
463
+ // Replaces a redacted string with something like H3B000.
464
+ return str_pad ( preg_replace ( '/\s+/ ' , '' , $ postcode ), 6 , '0 ' );
465
+ }
466
+
467
+ return $ postcode ;
468
+ }
469
+
319
470
/**
320
471
* Modify country locale settings to handle express checkout address requirements.
321
472
*
0 commit comments