context(); $saved=$context['writer']->readNativeScope($context,$requestId); if(($saved['status']??null)==='native_scope_saved'){return $saved;} if(($saved['status']??null)!=='native_scope_missing'){throw new RuntimeException('scope_storage_unavailable');} if(!function_exists('wc_get_orders') || !function_exists('orderguard_phone_hashes_for_lookup')){throw new RuntimeException('scope_source_unavailable');} $normal=strtolower(trim($email));$emailHash=hash('sha256',$normal); $wpdb->last_error=''; // Match WooCommerce's eraser: billing email OR the account owning that email. $user=get_user_by('email',$normal);$customerId=$user instanceof WP_User?(int)$user->ID:0; if($wpdb->last_error!==''){throw new RuntimeException('scope_source_unavailable');} $customers=array($normal);if($customerId>0){$customers[]=$customerId;} $orders=wc_get_orders(array('type'=>'shop_order','customer'=>$customers,'status'=>array_keys(wc_get_order_statuses()),'limit'=>26,'orderby'=>'ID','order'=>'ASC','return'=>'objects','paginate'=>false)); if($wpdb->last_error!=='' || !is_array($orders) || !array_is_list($orders)){throw new RuntimeException('scope_source_unavailable');} if(count($orders)>25){throw new RuntimeException('scope_capacity');} $rows=self::observeOrders($orders,$normal,$customerId); $saved=$context['writer']->saveNativeScope($context,$requestId,$rows); if(($saved['status']??null)!=='native_scope_saved'){ if(($saved['reason']??null)==='native_scope_invalid'){throw new RuntimeException('scope_source_invalid');} if(($saved['reason']??null)==='native_scope_capacity'){throw new RuntimeException('scope_capacity');} throw new RuntimeException('scope_storage_unavailable'); } return $saved; }catch(Throwable $e){ $recorded=false; try{$attention=(new OG_Owned_WordPress_Context($wpdb,false))->attentionContext();$r=$attention['writer']->recordNativeAttention($attention,$requestId,'native_scope_incomplete');$recorded=($r['status']??null)==='native_attention_recorded';}catch(Throwable $ignored){} $allowed=array('scope_storage_unavailable','scope_source_unavailable','scope_capacity','scope_source_mismatch','scope_source_invalid'); $reason=in_array($e->getMessage(),$allowed,true)?$e->getMessage():'scope_source_unavailable'; return new WP_Error('owned_scope_unavailable',__('OrderGuard could not retain the complete observed order set. Erasure remains incomplete.','orderguard'),array('reason'=>$reason,'attention_recorded'=>$recorded,'scope_complete'=>false,'erasure_complete'=>false)); } } /** Pure observation conversion; membership is order-linked, not identity ownership. */ public static function observeOrders(array $orders,string $normal,int $customerId): array { $emailHash=hash('sha256',$normal); $rows=array(); foreach($orders as $order){ if(!$order instanceof WC_Order || $order->get_type()!=='shop_order'){throw new RuntimeException('scope_source_mismatch');} $billingEmail=strtolower(trim($order->get_billing_email('edit'))); if($billingEmail!==$normal && !($customerId>0 && $order->get_customer_id('edit')===$customerId)){throw new RuntimeException('scope_source_mismatch');} $id=$order->get_id();$key=$order->get_order_key('edit');$phone=$order->get_billing_phone('edit');$country=$order->get_billing_country('edit'); if(!is_int($id) || $id<1 || !is_string($key) || $key==='' || strlen($key)>128 || !is_string($phone) || strlen($phone)>128 || !is_string($country) || strlen($country)>2){throw new RuntimeException('scope_source_invalid');} $aliases=orderguard_phone_hashes_for_lookup($phone,$country); if(!is_array($aliases) || !array_is_list($aliases) || count($aliases)>7){throw new RuntimeException('scope_source_invalid');} $emails=array($emailHash);if($billingEmail!==''){$emails[]=hash('sha256',$billingEmail);} $hashes=array_values(array_unique(array_merge($emails,$aliases)));sort($hashes,SORT_STRING); // Index roles into the canonical hash vector: no ownership inferred from order contact. $phoneIndexes=array();foreach($aliases as $alias){$phoneIndexes[]=array_search($alias,$hashes,true);} $phoneIndexes=array_values(array_unique($phoneIndexes));sort($phoneIndexes,SORT_NUMERIC); $roles=array('request_email'=>array_search($emailHash,$hashes,true),'billing_email'=>$billingEmail===''?null:array_search(hash('sha256',$billingEmail),$hashes,true),'billing_phone'=>$phoneIndexes); $date=$order->get_date_created('edit'); $rows[]=array('ref'=>(string)$id,'order_key_hash'=>hash('sha256',$key),'observed_created_seconds'=>$date?$date->getTimestamp():null,'identity_hashes'=>$hashes,'source_roles'=>$roles); } return $rows; } }