403]); if(isset($_GET['courierapp_print_orders'])){courierapp_connect_print_page();return;} $result=null; if(isset($_POST['courierapp_component_test'])){check_admin_referer('courierapp_component_test');$result=courierapp_connect_component_test();} $hosted=null;if(isset($_POST['courierapp_hosted_test'])){check_admin_referer('courierapp_hosted_test');$hosted=courierapp_connect_hosted_test();} $live=null;if(isset($_POST['courierapp_live_rates'])){check_admin_referer('courierapp_live_rates');$live=courierapp_connect_live_rates_test();} echo '

CourierAPP Connect

Έκδοση '.esc_html(COURIERAPP_CONNECT_VERSION).' — σύνδεση καταστήματος και αποστολές.

'; $connection=courierapp_connect_connection(); echo '

Κατάσταση σύνδεσης

'; $info=['WooCommerce'=>defined('WC_VERSION')?WC_VERSION:'not loaded','PHP'=>PHP_VERSION,'Visibility'=>courierapp_connect_pilot_mode()?'Admin only':'Public checkout','API mode'=>courierapp_connect_connection_mode(),'API configuration'=>$connection['key']?'Configured server-side':'Not configured','Scheduling'=>function_exists('as_enqueue_async_action')?'Action Scheduler available':'Not available','Carrier methods'=>'CourierAPP registered; existing shipping zones preserved']; foreach($info as $label=>$value)echo ''; echo '
'.esc_html($label).''.esc_html($value).'

Ο παρακάτω έλεγχος χρησιμοποιεί το πραγματικό WooCommerce με ενδεικτικές απαντήσεις HTTP μόνο εντός του ελέγχου. Δεν είναι hosted API E2E, δεν αποθηκεύει παραγγελία, δεν κάνει πληρωμή, δεν δημιουργεί ετικέτα και δεν στέλνει email.

'; wp_nonce_field('courierapp_component_test');submit_button('Έλεγχος WooCommerce components','secondary','courierapp_component_test');echo '
'; if($result){echo '

Αποτέλεσμα component test

';} echo '

Hosted sandbox E2E

Πραγματικό WordPress → hosted CourierAPP sandbox → άκυρο δοκιμαστικό PDF. Δεν αποθηκεύεται WooCommerce παραγγελία και δεν εκτελείται πραγματική μεταφορά ή πληρωμή.

'; if(courierapp_connect_connection_mode()==='sandbox'){echo '
';wp_nonce_field('courierapp_hosted_test');submit_button('Έλεγχος hosted sandbox E2E','secondary','courierapp_hosted_test');echo '
';} else echo '

Η τρέχουσα σύνδεση είναι live. Ο έλεγχος sandbox απαιτεί ξεχωριστή δοκιμαστική σύνδεση· για αυτή τη σύνδεση χρησιμοποίησε τον έλεγχο πραγματικών τιμών παρακάτω.

'; if($hosted){echo '

Αποτέλεσμα hosted sandbox E2E

';} echo '

Πραγματικές τιμές — έλεγχος σύνδεσης

Παράδειγμα: Ηράκλειο 71601 → Βρυξέλλες 1000, 1 kg, 30 × 20 × 10 cm. Γίνεται μόνο άντληση τιμών, χωρίς παραγγελία, χρέωση ή φορτωτική.

';wp_nonce_field('courierapp_live_rates');submit_button('Έλεγχος πραγματικών τιμών','secondary','courierapp_live_rates');echo '
'; if($live){if(is_wp_error($live))echo '

'.esc_html($live->get_error_message()).'

';else{echo '

Τρέχουσες τιμές με ΦΠΑ

';foreach($live['quotes'] as $q)echo '';echo '
ΥπηρεσίαΤιμή EURΕνδεικτικές εργάσιμες
'.esc_html($q['service_name']).''.esc_html(number_format($q['total_cents']/100,2)).''.esc_html($q['estimated_business_days']??'—').'

Οι τιμές ισχύουν κατά τον έλεγχο και επαληθεύονται ξανά πριν από πληρωμή/αποστολή.

';}} echo '
'; } function courierapp_connect_live_rates_test(){ if(!current_user_can('manage_woocommerce')||courierapp_connect_connection_mode()!=='live')return new WP_Error('courierapp_live_required','Απαιτείται live σύνδεση CourierAPP, επιτρέπεται και κλειδί μόνο για τιμές.'); return courierapp_connect_request('/v1/quotes',['origin'=>['country'=>'GR','postal'=>'71601','city'=>'Heraklion'],'destination'=>['country'=>'BE','postal'=>'1000','city'=>'Brussels'],'packages'=>[['weightKg'=>1,'lengthCm'=>30,'widthCm'=>20,'heightCm'=>10]],'declaredValueEur'=>80]); } function courierapp_connect_hosted_test(){ $checks=[];$owner=get_current_user_id(); $verify=function($ok,$name)use(&$checks){$checks[]=['name'=>$name,'passed'=>(bool)$ok];if(!$ok)throw new RuntimeException('Sandbox check failed');}; try{ if(!courierapp_connect_pilot_mode())throw new RuntimeException('Pilot required'); $cap=courierapp_connect_request('/v1/capabilities');$verify(!is_wp_error($cap)&&($cap['mode']??'')==='sandbox','WordPress authenticates to hosted sandbox'); if(!class_exists('COURIERAPP_Connect_Shipping'))WC()->shipping(); $product=new WC_Product_Simple();$product->set_weight(wc_get_weight(1,courierapp_compat_get_option('woocommerce_weight_unit','kg'),'kg')); $method=new class(0) extends COURIERAPP_Connect_Shipping{public $fixture=[];public function get_option($key,$empty_value=null){return $this->fixture[$key]??$empty_value;}public function fixture_rates(){return $this->rates;}}; $method->fixture=['sender_postal'=>'71601','sender_city'=>'Heraklion','box_length'=>30,'box_width'=>20,'box_height'=>10,'max_items'=>2,'max_weight'=>5]; $package=['destination'=>['country'=>'BE','postcode'=>'1000','city'=>'Brussels'],'contents'=>[['data'=>$product,'quantity'=>1,'line_total'=>50,'line_tax'=>0]]]; wp_set_current_user(0);$method->calculate_shipping($package);$verify(count($method->fixture_rates())===0,'Anonymous customers still excluded from pilot quotes'); wp_set_current_user($owner);$method->calculate_shipping($package);$rates=$method->fixture_rates();$verify(count($rates)===2,'Real WooCommerce shipping method receives two hosted quotes'); $rate=reset($rates);$meta=$rate->get_meta_data();$verify(($meta['_courierapp_mode']??'')==='sandbox'&&!empty($meta['_courierapp_quote_id']),'Hosted quote identity attached to WooCommerce rate'); $gross=(float)$rate->get_cost()+array_sum($rate->get_taxes());$verify(abs($gross-39.9)<0.015,'WooCommerce retains cheapest gross sandbox quote of 39.90 EUR'); $date=new DateTimeImmutable('+2 days',new DateTimeZone('Europe/Athens'));while((int)$date->format('N')>5)$date=$date->modify('+1 day'); $run=wp_generate_uuid4();$request=$meta['_courierapp_request']; $command=['quote_id'=>$meta['_courierapp_quote_id'],'external_order_id'=>'WP-SANDBOX-'.$run,'parcel_reference'=>'1','payment'=>['status'=>'paid','reference'=>'SANDBOX-NO-REAL-PAYMENT'], 'sender'=>array_merge($request['origin'],['name'=>'Sandbox shop','address'=>'Fixture address 1','phone'=>'+302810000000','email'=>'sandbox-shop@example.invalid']), 'recipient'=>array_merge($request['destination'],['name'=>'Sandbox recipient','address'=>'Fixture address 2','phone'=>'+32123456789','email'=>'sandbox-buyer@example.invalid']), 'pickup'=>['date'=>$date->format('Y-m-d'),'window'=>'10:00-16:30'],'content_description'=>'Sandbox books fixture','customs_exempt'=>true,'terms'=>['version'=>'sandbox-test-only','accepted_at'=>gmdate('c',time()-60)]]; $key='wpdiag:'.$run.':parcel-1';$receipt=courierapp_connect_request('/v1/shipments',$command,$key);$verify(!is_wp_error($receipt)&&!empty($receipt['id'])&&empty($receipt['order_id']),'Sandbox receipt created with no production CourierAPP order'); $again=courierapp_connect_request('/v1/shipments',$command,$key);$verify(!is_wp_error($again)&&$again['id']===$receipt['id'],'Identical PHP retry returns same hosted receipt'); $label=courierapp_connect_request('/v1/shipments/'.$receipt['id'].'/label');$bytes=!is_wp_error($label)?base64_decode($label['content_base64']??'',true):false; $verify(!is_wp_error($label)&&($label['valid_for_shipping']??true)===false&&$bytes!==false&&str_starts_with($bytes,'%PDF-')&&str_contains($bytes,'NOT VALID FOR SHIPPING'),'Hosted PDF generated and explicitly invalid for real shipping'); $tracking=courierapp_connect_request('/v1/shipments/'.$receipt['id'].'/tracking');$verify(!is_wp_error($tracking)&&($tracking['tracking']['physical_scan']??true)===false&&($tracking['pickup']['status']??'')==='not_booked','No pickup booked or physical scan asserted'); }catch(Throwable $e){if(!$checks||end($checks)['passed'])$checks[]=['name'=>'Hosted test unavailable; no real commerce operation executed','passed'=>false];} finally{wp_set_current_user($owner);} return $checks; } function courierapp_connect_component_test(){ $checks=[];$owner=get_current_user_id();$base=fn()=> 'https://fixture.invalid';$key=fn()=> 'fixture-only-not-a-credential';$calls=0;$mode=courierapp_connect_connection_mode(); $remote=function($pre,$args,$url)use(&$calls,$mode){ if(!str_starts_with($url,'https://fixture.invalid/'))return $pre; $calls++; if($url!=='https://fixture.invalid/v1/quotes'||$args['method']!=='POST')return new WP_Error('fixture_guard','Unexpected fixture operation'); return ['headers'=>[],'body'=>wp_json_encode(['quotes'=>[['quote_id'=>'11111111-1111-4111-8111-111111111111','mode'=>$mode,'service_name'=>'TEST CourierAPP Express','currency'=>'EUR','total_cents'=>1240,'expires_at'=>gmdate('c',time()+900)]]]),'response'=>['code'=>200,'message'=>'OK'],'cookies'=>[]]; }; try{ if(!class_exists('COURIERAPP_Connect_Shipping'))WC()->shipping(); $checks[]=['name'=>'Real WooCommerce shipping class loaded','passed'=>class_exists('COURIERAPP_Connect_Shipping')]; add_filter('courierapp_connect_api_base',$base);add_filter('courierapp_connect_api_key',$key);add_filter('pre_http_request',$remote,10,3); $product=new WC_Product_Simple();$product->set_weight(wc_get_weight(1,courierapp_compat_get_option('woocommerce_weight_unit','kg'),'kg')); $settings=['sender_postal'=>'71601','sender_city'=>'Heraklion','box_length'=>30,'box_width'=>20,'box_height'=>10,'max_items'=>2,'max_weight'=>5]; $method=new class(0) extends COURIERAPP_Connect_Shipping{public $fixture=[];public function get_option($key,$empty_value=null){return $this->fixture[$key]??$empty_value;}public function fixture_rates(){return $this->rates;}}; $method->fixture=$settings; $package=['destination'=>['country'=>'BE','postcode'=>'1000','city'=>'Brussels'],'contents'=>[['data'=>$product,'quantity'=>1,'line_total'=>50,'line_tax'=>0]]]; wp_set_current_user(0);$method->calculate_shipping($package); $checks[]=['name'=>'Anonymous customers cannot request pilot quotes','passed'=>count($method->fixture_rates())===0&&$calls===0]; wp_set_current_user($owner);$method->calculate_shipping($package);$rates=$method->fixture_rates(); $checks[]=['name'=>'Administrator receives one fixture rate through real WooCommerce','passed'=>count($rates)===1&&$calls===1]; $rate=count($rates)===1?reset($rates):null;$total=$rate?(float)$rate->get_cost()+array_sum($rate->get_taxes()):0; $checks[]=['name'=>'Gross shipping amount remains 12.40 EUR after WooCommerce taxes','passed'=>abs($total-12.4)<0.015]; $before=$calls;$product->set_weight('');$method->calculate_shipping($package); $checks[]=['name'=>'Missing weight fails closed without an HTTP request','passed'=>$calls===$before]; $product->set_weight(wc_get_weight(1,courierapp_compat_get_option('woocommerce_weight_unit','kg'),'kg'));$package['contents'][0]['quantity']=3;$method->calculate_shipping($package); $checks[]=['name'=>'Packing quantity overflow fails closed','passed'=>$calls===$before]; }catch(Throwable $e){$checks[]=['name'=>'Component test exception (no customer operation executed)','passed'=>false];} finally{wp_set_current_user($owner);remove_filter('courierapp_connect_api_base',$base);remove_filter('courierapp_connect_api_key',$key);remove_filter('pre_http_request',$remote,10);} return $checks; }