$v)$raw.=$k.'='.$v.'&'; if(!preg_match('/^[a-f0-9]{32}$/D',$p['sign']) || !hash_equals(md5($raw.'key='.$key),$p['sign']))throw new InvalidArgumentException('Invalid signature'); if($p['callback_version']!=='2' || !preg_match('/^cn_[a-f0-9]{32}$/D',$p['event_id']) || !preg_match('/^[1-9][0-9]{0,9}$/D',$p['event_seq']) || (int)$p['event_seq']>2147483647 || !in_array($p['status'],['expired','confirming','confirmed'],true))throw new InvalidArgumentException('Invalid event'); foreach(['mch_id','pt_order','sh_order','money','pay_money','ptype','network','is_testnet','asset','address','decimals','expected_amount_raw'] as $f)if(!isset($expected[$f]) || (string)$expected[$f]!==$p[$f])throw new InvalidArgumentException('Order identity mismatch'); if(!in_array($p['payment_detected'],['0','1'],true) || $p['confirmed']!==($p['status']==='confirmed'?'1':'0') || ($p['status']!=='expired' && $p['payment_detected']!=='1'))throw new InvalidArgumentException('Invalid phase'); // This sample accepts exact-amount orders only. Manual differences require a merchant review policy. if($p['payment_detected']==='1' && ($p['amount_raw']!==$p['expected_amount_raw'] || $p['tx_hash']==='' || $p['event_index']===''))throw new InvalidArgumentException('Unexpected transfer'); if($p['payment_detected']==='0' && ($p['tx_hash']!=='' || $p['amount_raw']!==''))throw new InvalidArgumentException('Unexpected unpaid transfer'); $state=$previous+['status'=>'awaiting','event_seq'=>0,'events'=>[]];$digest=hash('sha256',json_encode($signed,JSON_UNESCAPED_UNICODE)); $seen=$state['events'][$p['event_id']]??null; if($seen!==null && $seen!==$digest)throw new InvalidArgumentException('Conflicting event replay'); if($seen!==null)return ['state'=>$state,'should_fulfil'=>false]; if((int)$p['event_seq']===(int)$state['event_seq'])throw new InvalidArgumentException('Conflicting sequence'); $state['events'][$p['event_id']]=$digest; if((int)$p['event_seq']<(int)$state['event_seq'])return ['state'=>$state,'should_fulfil'=>false]; if($state['status']==='confirmed' && $p['status']!=='confirmed')throw new InvalidArgumentException('Confirmed payment cannot regress'); $fulfil=$p['status']==='confirmed' && $state['status']!=='confirmed'; $state['status']=$p['status'];$state['event_seq']=(int)$p['event_seq']; return ['state'=>$state,'should_fulfil'=>$fulfil]; }