false, 'error' => __( 'Project ID and Secret key are not both saved yet.', 'cryptoway-button' ), ]; } $fetched = self::fetch( ApiClient::from_settings() ); if ( ! $fetched['ok'] ) { return [ 'ok' => false, 'error' => $fetched['error'] ]; } if ( self::stored() !== $fetched['key'] ) { Settings::set( self::SETTING, $fetched['key'] ); } return [ 'ok' => true, 'error' => '' ]; } /** * Ask Cryptoway for the callback key with the given client, without storing it (the * settings screen fetches it for keys that are about to be saved). * * @return array{ok: bool, key: string, error: string} */ public static function fetch( ApiClient $api ): array { $profile = $api->get_profile(); if ( is_wp_error( $profile ) ) { return [ 'ok' => false, 'key' => '', 'error' => $profile->get_error_message() ]; } $key = self::read( $profile ); if ( '' === $key ) { return [ 'ok' => false, 'key' => '', 'error' => __( 'the answer does not contain a callback key', 'cryptoway-button' ) ]; } return [ 'ok' => true, 'key' => $key, 'error' => '' ]; } /** * The key for an incoming webhook, fetched first when none is stored, at most once * per WEBHOOK_RETRY_GAP. */ public static function for_webhook(): string { $key = self::stored(); if ( '' !== $key ) { return $key; } $last = (int) get_option( self::OPTION_LAST_ATTEMPT, 0 ); if ( $last > 0 && time() - $last < self::WEBHOOK_RETRY_GAP ) { return ''; } // Written before the request, so a burst of notifications starts one fetch. update_option( self::OPTION_LAST_ATTEMPT, time(), false ); $result = self::refresh(); if ( ! $result['ok'] ) { Log::write( 'Webhook: could not get the callback key from Cryptoway: ' . $result['error'] ); } return self::stored(); } /** Pull the key out of the profile response. */ private static function read( $profile ): string { if ( ! is_array( $profile ) ) { return ''; } // Some endpoints wrap the payload; look one level down as well. $candidates = [ $profile ]; foreach ( [ 'data', 'result', 'user', 'project' ] as $wrapper ) { if ( isset( $profile[ $wrapper ] ) && is_array( $profile[ $wrapper ] ) ) { $candidates[] = $profile[ $wrapper ]; } } foreach ( $candidates as $candidate ) { foreach ( self::FIELDS as $field ) { if ( isset( $candidate[ $field ] ) && is_string( $candidate[ $field ] ) && '' !== trim( $candidate[ $field ] ) ) { return trim( $candidate[ $field ] ); } } } return ''; } }