status(), Payment::OPEN, true ) ) { return; } $manager = new PaymentManager( $payment ); if ( '' === $payment->invoice_id() || ! Settings::keys_present() ) { return; } // The deadline notification has often resolved the invoice already. if ( ! $manager->should_recheck() ) { return; } $manager->sync_status_from_api( 'cron' ); } public function add_interval( array $schedules ): array { $schedules[ self::INTERVAL ] = [ 'interval' => 10 * MINUTE_IN_SECONDS, 'display' => __( 'Every 10 minutes (Cryptoway Payment)', 'cryptoway-button' ), ]; return $schedules; } /** Keep the shared pass scheduled while the keys are set. */ public function maybe_schedule(): void { $enabled = Settings::keys_present(); if ( $enabled && ! wp_next_scheduled( self::HOOK ) ) { wp_schedule_event( time() + 60, self::INTERVAL, self::HOOK ); } if ( ! $enabled ) { self::unschedule( false ); } } /** * @param bool $deadlines Also drop the one-off deadline checks (deactivation, uninstall); * false when only the keys are missing. */ public static function unschedule( bool $deadlines = true ): void { wp_unschedule_hook( self::HOOK ); if ( $deadlines ) { // The deadline events carry a payment id; only wp_unschedule_hook() removes events regardless of arguments. wp_unschedule_hook( self::HOOK_DEADLINE ); wp_unschedule_hook( Checkout::HOOK_ACCESS_MAIL ); } } public function run(): void { $started = microtime( true ); if ( ! Settings::keys_present() ) { return; } // Notifications cannot be verified without the callback key: retry the fetch // here, once per pass, if the settings save could not get it. if ( '' === CallbackKey::stored() ) { $fetched = CallbackKey::refresh(); if ( ! $fetched['ok'] ) { Log::write( 'Scheduled check: could not get the callback key from Cryptoway: ' . $fetched['error'] ); } } // Only the payments that need a read count towards the batch: those skipped are never stamped, // and counting them would let a few dozen of them fill every batch for good. $ids = Payment::open_ids( self::SCAN ); $checked = 0; foreach ( $ids as $id ) { if ( $checked >= self::BATCH ) { break; } // Stop at the time budget, checked between payments: with the API hanging one read takes // up to a minute (20 s, retried twice), and a host limit would kill the pass halfway. if ( microtime( true ) - $started > self::TIME_BUDGET ) { Log::write( sprintf( 'Scheduled check stopped on time after %d payments. The next pass will take the rest.', $checked ) ); break; } $payment = Payment::get( $id ); if ( ! $payment || '' === $payment->invoice_id() ) { continue; } $manager = new PaymentManager( $payment ); if ( ! $manager->should_recheck() ) { continue; } $manager->sync_status_from_api( 'cron' ); ++$checked; } // The coin list is refreshed here, in the background, never while a visitor waits for a page; // after the payments, so an outage at Cryptoway cannot spend their time. if ( Coins::needs_refresh() && microtime( true ) - $started < self::TIME_BUDGET ) { Coins::refresh_available(); } } }