get( 'pp_shop_orders', '*', [ 'id' => $order_id ] ); if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val ) $this -> $key = $val; } if ( $hash ) { $result = $mdb -> get( 'pp_shop_orders', '*', [ 'hash' => $hash ] ); if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val ) $this -> $key = $val; } if ( $przelewy24_hash ) { $result = $mdb -> get( 'pp_shop_orders', '*', [ 'przelewy24_hash' => $przelewy24_hash ] ); if ( \S::is_array_fix( $result ) ) foreach ( $result as $key => $val ) $this -> $key = $val; } $this -> products = $mdb -> select( 'pp_shop_order_products', '*', [ 'order_id' => $order_id ] ); $this -> statuses = $mdb -> select( 'pp_shop_order_statuses', '*', [ 'order_id' => $order_id, 'ORDER' => [ 'id' => 'DESC' ] ] ); } static public function notes_save( int $order_id, $notes ) { global $mdb; return $mdb -> update( 'pp_shop_orders', [ 'notes' => $notes ], [ 'id' => $order_id ] ); } public static function order_statuses() { global $mdb; $repository = new \Domain\ShopStatus\ShopStatusRepository( $mdb ); return $repository -> allStatuses(); } public function update_aplio_order_status_date( $date ) { global $mdb; return $mdb -> update( 'pp_shop_orders', [ 'apilo_order_status_date' => $date ], [ 'id' => $this -> id ] ); } // set_as_unpaid public function set_as_unpaid() { global $mdb; $mdb -> update( 'pp_shop_orders', [ 'paid' => 0 ], [ 'id' => $this -> id ] ); return true; } // set_as_paid public function set_as_paid( $send_email = false ) { global $mdb, $config; $integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb ); // apilo $apilo_settings = $integrationsRepository -> getSettings( 'apilo' ); if ( $apilo_settings['enabled'] and $apilo_settings['access-token'] and $apilo_settings['sync_orders'] ) { // put data to file if ( $config['debug']['apilo'] ) { file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', date( 'Y-m-d H:i:s' ) . " --- SET AS PAID\n\n", FILE_APPEND ); file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', print_r( $this, true ) . "\n\n", FILE_APPEND ); } if ( $this -> apilo_order_id and !$this -> sync_apilo_payment() ) { self::queue_apilo_sync( (int)$this -> id, true, null, 'payment_sync_failed' ); } } $mdb -> update( 'pp_shop_orders', [ 'paid' => 1 ], [ 'id' => $this -> id ] ); $this -> update_status( 1, $send_email ); $dir_logs = $_SERVER['DOCUMENT_ROOT'] . '/logs/'; if ( !is_dir( $dir_logs ) ) mkdir( $dir_logs, 0777, true ); // $file = $dir_logs . date( 'Y-m-d' ) . '-order-set-as-paid.txt'; // $content = date( 'Y-m-d H:i:s' ) . ' | ' . $this -> id . ' | ' . $this -> number . ' | ' . $this -> summary . ' | ' . $this -> client_email . "\n"; // $content .= print_r( $apilo_response, true ) . "\n\n"; // file_put_contents( $file, $content, FILE_APPEND ); return true; } // zmiana statusu zamówienia public function update_status( int $status, int $email_send ) { global $mdb, $config; $integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb ); if ( $this -> status == $status ) return false; if ( $mdb -> update( 'pp_shop_orders', [ 'status' => $status ], [ 'id' => $this -> id ] ) ) { $mdb -> insert( 'pp_shop_order_statuses', [ 'order_id' => $this -> id, 'status_id' => $status, 'mail' => $email_send ] ); if ( $email_send ) { $this -> status = $status; $response['email'] = $this -> send_status_change_email(); } $response['result'] = true; // apilo $apilo_settings = $integrationsRepository -> getSettings( 'apilo' ); if ( $apilo_settings['enabled'] and $apilo_settings['access-token'] and $apilo_settings['sync_orders'] ) { // put data to file if ( $config['debug']['apilo'] ) { file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', date( 'Y-m-d H:i:s' ) . " --- UPDATE STATUS\n\n", FILE_APPEND ); file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', print_r( $this, true ) . "\n\n", FILE_APPEND ); } if ( $this -> apilo_order_id and !$this -> sync_apilo_status( (int)$status ) ) { self::queue_apilo_sync( (int)$this -> id, false, (int)$status, 'status_sync_failed' ); } } return $response; } return false; } // ponowne wysłanie maila o złożonym zamówieniu public function order_resend_confirmation_email() { global $settings; $order = \front\factory\ShopOrder::order_details( $this -> id ); $coupon = (int)$order['coupon_id'] ? new \shop\Coupon( (int)$order['coupon_id'] ) : null; $mail_order = \Tpl::view( 'shop-order/mail-summary', [ 'settings' => $settings, 'order' => $order, 'coupon' => $coupon, ] ); $settings[ 'ssl' ] ? $base = 'https' : $base = 'http'; $regex = "-(]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i"; $mail_order = preg_replace( $regex, "$1" . $base . "://" . $_SERVER[ 'SERVER_NAME' ] . "$2$4", $mail_order ); $regex = "-(]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i"; $mail_order = preg_replace( $regex, "$1" . $base . "://" . $_SERVER[ 'SERVER_NAME' ] . "$2$4", $mail_order ); \S::send_email( $this -> client_email, \S::lang( 'potwierdzenie-zamowienia-ze-sklepu' ) . ' ' . $settings[ 'firm_name' ], $mail_order ); \S::send_email( $settings[ 'contact_email' ], 'Nowe zamówienie / ' . $settings[ 'firm_name' ] . ' / ' . $order['number'] . ' - ' . $order['client_surname'] . ' ' . $order['client_name'], $mail_order ); return true; } // wysłanie maila o zmianie statusu public function send_status_change_email() { if ( !$this -> client_email ) return false; $order_statuses = self::order_statuses(); $firm_name = ( new \Domain\Settings\SettingsRepository( $mdb ) )->getSingleValue( 'firm_name' ); switch( $this -> status ): case 0: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało złożone' ); break; case 1: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało opłacone' ); break; case 2: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - płatność za zamówienie [NUMER] została odrzucona' ); break; case 3: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - płatność za zamówienie [NUMER] jest sprawdzania ręcznie' ); break; case 4: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało przyjęte do realizacji' ); break; case 5: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało wysłane' ); break; case 6: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało zrealizowane' ); break; case 7: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało przygotowane go wysłania' ); break; case 8: $subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało anulowane' ); break; endswitch; $email = new \Email( 0 ); $email -> load_by_name( '#sklep-zmiana-statusu-zamowienia' ); $email -> text = str_replace( '[NUMER_ZAMOWIENIA]', $this -> number, $email -> text ); $email -> text = str_replace( '[DATA_ZAMOWIENIA]', date( 'Y/m/d', strtotime( $this -> date_order ) ), $email -> text ); $email -> text = str_replace( '[STATUS]', $order_statuses[ $this -> status ], $email -> text ); return $email -> send( $this -> client_email, $subject, true ); } // wyliczanie wartości zamówienia podczas edycji zamówienia static public function calculate_order_summary_by_admin( int $order_id ) { global $mdb; $rows = $mdb -> select( 'pp_shop_order_products', '*', [ 'order_id' => $order_id ] ); if ( \S::is_array_fix( $rows ) ) foreach ( $rows as $row ) { if ( $row['price_brutto_promo'] ) $summary += $row['price_brutto_promo'] * $row['quantity']; else $summary += $row['price_brutto'] * $row['quantity']; } return $summary + $mdb -> get( 'pp_shop_orders', 'transport_cost', [ 'id' => $order_id ] ); } // ADMIN - usunięcie zamówienia static public function order_delete( int $order_id ) { global $mdb; return $mdb -> delete( 'pp_shop_orders', [ 'id' => $order_id ] ); } // ADMIN - zmiana zamówienia static public function order_save_by_admin( int $order_id, string $client_name, string $client_surname, string $client_street, string $client_postal_code, string $client_city, string $client_email, $firm_name = '', $firm_street = '', $firm_postal_code = '', $firm_city = '', $firm_nip = '', int $transport_id, string $inpost_paczkomat, int $payment_method_id ) { global $mdb, $user; $mdb -> update( 'pp_shop_orders', [ 'client_name' => $client_name, 'client_surname' => $client_surname, 'client_street' => $client_street, 'client_postal_code' => $client_postal_code, 'client_city' => $client_city, 'client_email' => $client_email, 'firm_name' => $firm_name ? $firm_name : null, 'firm_street' => $firm_street ? $firm_street : null, 'firm_postal_code' => $firm_postal_code ? $firm_postal_code : null, 'firm_city' => $firm_city ? $firm_city : null, 'firm_nip' => $firm_nip ? $firm_nip : null, 'transport_id' => $transport_id, 'transport' => $mdb -> get( 'pp_shop_transports', 'name_visible', [ 'id' => $transport_id ] ), 'transport_cost' => $mdb -> get( 'pp_shop_transports', 'cost', [ 'id' => $transport_id ] ), 'transport_description' => $mdb -> get( 'pp_shop_transports', 'description', [ 'id' => $transport_id ] ), 'inpost_paczkomat' => $inpost_paczkomat, 'payment_method_id' => $payment_method_id, 'payment_method' => $mdb -> get( 'pp_shop_payment_methods', 'name', [ 'id' => $payment_method_id ] ), ], [ 'id' => $order_id ] ); $mdb -> update( 'pp_shop_orders', [ 'summary' => \shop\Order::calculate_order_summary_by_admin( $order_id ) ], [ 'id' => $order_id ] ); \Log::save_log( 'Zamówienie zmienione przez administratora | ID: ' . $order_id, $user['id'] ); return true; } public function offsetExists( $offset ) { return isset( $this -> $offset ); } public function offsetGet( $offset ) { return $this -> $offset; } public function offsetSet( $offset, $value ) { $this -> $offset = $value; } public function offsetUnset( $offset ) { unset( $this -> $offset ); } private function sync_apilo_payment(): bool { global $config, $mdb; $integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb ); if ( !(int)$this -> apilo_order_id ) return true; $payment_type = (int)\front\factory\ShopPaymentMethod::get_apilo_payment_method_id( (int)$this -> payment_method_id ); if ( $payment_type <= 0 ) $payment_type = 1; $payment_date = new \DateTime( $this -> date_order ); $access_token = $integrationsRepository -> apiloGetAccessToken(); $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $this -> apilo_order_id . '/payment/' ); curl_setopt( $ch, CURLOPT_POST, 1 ); curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( [ 'amount' => str_replace( ',', '.', $this -> summary ), 'paymentDate' => $payment_date -> format('Y-m-d\TH:i:s\Z'), 'type' => $payment_type ] ) ); curl_setopt( $ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer " . $access_token, "Accept: application/json", "Content-Type: application/json" ] ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 5 ); curl_setopt( $ch, CURLOPT_TIMEOUT, 15 ); $apilo_response = curl_exec( $ch ); $http_code = (int)curl_getinfo( $ch, CURLINFO_HTTP_CODE ); $curl_error = curl_errno( $ch ) ? curl_error( $ch ) : ''; curl_close( $ch ); if ( $config['debug']['apilo'] ) { self::append_apilo_log( "PAYMENT RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r( $apilo_response, true ) . "\n" ); } if ( $curl_error !== '' ) return false; if ( $http_code < 200 or $http_code >= 300 ) return false; return true; } private function sync_apilo_status( int $status ): bool { global $config, $mdb; $integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb ); if ( !(int)$this -> apilo_order_id ) return true; $access_token = $integrationsRepository -> apiloGetAccessToken(); $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $this -> apilo_order_id . '/status/' ); curl_setopt( $ch, CURLOPT_POST, 1 ); curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "PUT"); curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( [ 'id' => $this -> apilo_order_id, 'status' => (int)\front\factory\ShopStatuses::get_apilo_status_id( $status ) ] ) ); curl_setopt( $ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer " . $access_token, "Accept: application/json", "Content-Type: application/json" ] ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 5 ); curl_setopt( $ch, CURLOPT_TIMEOUT, 15 ); $apilo_result = curl_exec( $ch ); $http_code = (int)curl_getinfo( $ch, CURLINFO_HTTP_CODE ); $curl_error = curl_errno( $ch ) ? curl_error( $ch ) : ''; curl_close( $ch ); if ( $config['debug']['apilo'] ) { self::append_apilo_log( "STATUS RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r( $apilo_result, true ) . "\n" ); } if ( $curl_error !== '' ) return false; if ( $http_code < 200 or $http_code >= 300 ) return false; return true; } public static function process_apilo_sync_queue( int $limit = 10 ): int { $queue = self::load_apilo_sync_queue(); if ( !\S::is_array_fix( $queue ) ) return 0; $processed = 0; foreach ( $queue as $key => $task ) { if ( $processed >= $limit ) break; $order_id = (int)( $task['order_id'] ?? 0 ); if ( $order_id <= 0 ) { unset( $queue[$key] ); continue; } $order = new self( $order_id ); if ( !(int)$order -> id ) { unset( $queue[$key] ); continue; } $error = ''; $sync_failed = false; $payment_pending = !empty( $task['payment'] ) and (int)$order -> paid === 1; if ( $payment_pending and (int)$order -> apilo_order_id ) { if ( !$order -> sync_apilo_payment() ) { $sync_failed = true; $error = 'payment_sync_failed'; } } $status_pending = isset( $task['status'] ) and $task['status'] !== null and $task['status'] !== ''; if ( !$sync_failed and $status_pending and (int)$order -> apilo_order_id ) { if ( !$order -> sync_apilo_status( (int)$task['status'] ) ) { $sync_failed = true; $error = 'status_sync_failed'; } } if ( $sync_failed ) { $task['attempts'] = (int)( $task['attempts'] ?? 0 ) + 1; $task['last_error'] = $error; $task['updated_at'] = date( 'Y-m-d H:i:s' ); $queue[$key] = $task; } else { unset( $queue[$key] ); } $processed++; } self::save_apilo_sync_queue( $queue ); return $processed; } private static function queue_apilo_sync( int $order_id, bool $payment, ?int $status, string $error ): void { if ( $order_id <= 0 ) return; $queue = self::load_apilo_sync_queue(); $key = (string)$order_id; $row = is_array( $queue[$key] ?? null ) ? $queue[$key] : []; $row['order_id'] = $order_id; $row['payment'] = !empty( $row['payment'] ) || $payment ? 1 : 0; if ( $status !== null ) $row['status'] = $status; $row['attempts'] = (int)( $row['attempts'] ?? 0 ) + 1; $row['last_error'] = $error; $row['updated_at'] = date( 'Y-m-d H:i:s' ); $queue[$key] = $row; self::save_apilo_sync_queue( $queue ); } private static function apilo_sync_queue_path(): string { return dirname( __DIR__, 2 ) . self::APILO_SYNC_QUEUE_FILE; } private static function load_apilo_sync_queue(): array { $path = self::apilo_sync_queue_path(); if ( !file_exists( $path ) ) return []; $content = file_get_contents( $path ); if ( !$content ) return []; $decoded = json_decode( $content, true ); if ( !is_array( $decoded ) ) return []; return $decoded; } private static function save_apilo_sync_queue( array $queue ): void { $path = self::apilo_sync_queue_path(); $dir = dirname( $path ); if ( !is_dir( $dir ) ) mkdir( $dir, 0777, true ); file_put_contents( $path, json_encode( $queue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ), LOCK_EX ); } private static function append_apilo_log( string $message ): void { $base = isset( $_SERVER['DOCUMENT_ROOT'] ) && $_SERVER['DOCUMENT_ROOT'] ? rtrim( $_SERVER['DOCUMENT_ROOT'], '/\\' ) : dirname( __DIR__, 2 ); $dir = $base . '/logs'; if ( !is_dir( $dir ) ) mkdir( $dir, 0777, true ); file_put_contents( $dir . '/apilo.txt', date( 'Y-m-d H:i:s' ) . ' --- ' . $message . "\n\n", FILE_APPEND ); } }