Posts under category Google

I have a Flutter newsreader app that uses a WordPress JSON feed for its content. Im using Advanced Ads Wordpress plugin and its injecting the same ads that are on the web into the JSON Feed with the post content.

When my WebView loads it automatically spawns an external browser. This is without user interaction. The ads are loading fine in the webview. I had the same issue with youtube but applying the same solution to the google ads URL does not work.

import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; import 'data.dart'; // Post model import 'html.dart'; // For building HTML content import 'package:share_plus/share_plus.dart'; // Import share_plus class PostDetailScreen extends StatefulWidget{   final Post post;   final List<Post> recentPosts;   const PostDetailScreen({Key? key, required this.post, required this.recentPosts}) : super(key: key);   @override   PostDetailScreenState createState() => PostDetailScreenState(); } class PostDetailScreenState extends State<PostDetailScreen>{   late WebViewController _controller;   String htmlContent = '';   bool isLoading = true;   @override   void initState() {     super.initState();     loadContent();   }   Future<void> loadContent() async {     String encodedVisbyFont = await loadEncodedFont();     String encodedCanelaFont = await loadCanelaFont();     htmlContent = buildHtmlContent(         widget.post.imageUrl,         widget.post.title,         widget.post.content,         widget.post.author,         widget.post.modified,         encodedVisbyFont,         encodedCanelaFont,         widget.recentPosts.take(10).toList()     );     setState(() {       isLoading = false;     });   }   Future<void> updatePostContent(String postLink) async {     final selectedPost = widget.recentPosts.firstWhere(           (post) => post.link == postLink,       orElse: () => widget.post,     );     String updatedContent = buildHtmlContent(       selectedPost.imageUrl,       selectedPost.title,       selectedPost.content,       selectedPost.author,       selectedPost.modified,       await loadEncodedFont(),       await loadCanelaFont(),       widget.recentPosts.take(10).toList(),     );     setState(() {       htmlContent = updatedContent;     });     _controller.loadUrl(Uri.dataFromString(       htmlContent,       mimeType: 'text/html',       encoding: Encoding.getByName('utf-8'),     ).toString());   }   @override   Widget build(BuildContext context) {     final bool tablet = MediaQuery.of(context).size.width > 600;     final double titleFontSize = tablet ? 36.0 : 24.0;     final double iconSize = tablet ? 36.0 : 30.0;     final EdgeInsetsGeometry iconPadding = tablet ? const EdgeInsets.all(12.0) : const EdgeInsets.all(8.0);     final double appBarHeight = tablet ? 70.0 : 56.0;     return Scaffold(       appBar: PreferredSize(         preferredSize: Size.fromHeight(appBarHeight),         child: AppBar(           title: Text(             'FRENCHLY',             style: TextStyle(               fontFamily: 'Oswald',               fontSize: titleFontSize,               fontWeight: FontWeight.bold,               color: Colors.white,             ),           ),           backgroundColor: const Color(0xFF1D5986),           centerTitle: true,           iconTheme: IconThemeData(color: Colors.white, size: iconSize),           actions: <Widget>[             Padding(               padding: iconPadding,               child: IconButton(                 icon: Icon(Icons.share, color: Colors.white, size: iconSize),                 onPressed: () {                   Share.share('${widget.post.title}\n\n${widget.post.link}');                 },               ),             ),           ],         ),       ),       body: isLoading           ? const Center(child: CircularProgressIndicator())           : WebView(         initialUrl: Uri.dataFromString(           htmlContent,           mimeType: 'text/html',           encoding: Encoding.getByName('utf-8'),         ).toString(),         javascriptMode: JavascriptMode.unrestricted,         onWebViewCreated: (WebViewController controller) {           _controller = controller;         },         navigationDelegate: (NavigationRequest request) async {           if (request.url.startsWith('post://')) {             final postLink = Uri.decodeFull(request.url.substring(7));             updatePostContent(postLink);             return NavigationDecision.prevent;           } else if (request.url.contains('youtube.com') || request.url.contains('youtu.be')) {             // Load YouTube URLs in the WebView itself             return NavigationDecision.navigate;           } else if (await canLaunchUrl(Uri.parse(request.url))) {             await launchUrl(Uri.parse(request.url), mode: LaunchMode.externalApplication);             return NavigationDecision.prevent;           }           return NavigationDecision.navigate;         },       ),     );   } } 

I've set up an OAuth2 web project as well as a Service Account. Both IDs have been added with domain-wide delegation with the adwords scope in my Google Workspace.

I have a Google Ads Manager account with a Developer Key (it's in test mode, if that matters?). The email I'm trying to authenticate with for either method is an Admin in the domain and on the relevant Ads Manager and Ads accounts.

No matter what I do, I get the following error.

{     "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https:\/\/developers.google.com\/identity\/sign-in\/web\/devconsole-project.",     "code": 16,     "status": "UNAUTHENTICATED",     "details": [         {             "@type": "type.googleapis.com\/google.ads.googleads.v16.errors.GoogleAdsFailure",             "errors": [                 {                     "errorCode": {                         "authenticationError": "OAUTH_TOKEN_HEADER_INVALID"                     },                     "message": "Oauth token HTTP header is malformed."                 }             ],             "requestId": "9QCN5RO9mRkmS1ULtDAKMA"         }     ] } 

If I try to use the service account email as the impersonateEmail parameter, I get a NOT_ADS_USER. I've invited it to the Ads Manager account, but am not sure how to accept that invite. I think it would result in the same OAUTH_TOKEN_HEADER_INVALID, anyways.

This is within a Laravel project.

$oauth2 = (new OAuth2TokenBuilder())                 ->withClientId(config('google.client_id'))                 ->withClientSecret(config('google.client_secret'))                 ->withRefreshToken(config('googleads.refresh_token'))                 ->build(); 
$oauth2 = (new OAuth2TokenBuilder())                 ->withJsonKeyFilePath(realpath(config('google.service.file')))                 ->withScopes('https://www.googleapis.com/auth/adwords')                 ->withImpersonatedEmail(config('googleads.impersonated_email'))                 ->build(); 

What's strange is that, using either OAuth2Token instance, I can run fetchAuthToken(), and I see that the object gets its access_token. I can also see in Google\ApiCore\CredentialsWrapper::getAuthorizationHeaderCallback that it is added as the authorization Bearer token.

Here's how my GoogleAdsClient is built:

$this->client = (new GoogleAdsClientBuilder())                 ->withOAuth2Credential($oauth2)                 ->withDeveloperToken(config('google.developer_key'))                 ->usingGapicV2Source(true)                 ->build(); 

And here's the request that fails with the OAUTH_TOKEN_HEADER_INVALID:

$requestArgs = [             // Set the language resource using the provided language ID.             'language' => $this->getLanguageConstant(),             'customer_id' => $this->getCustomerId(),             // Add the resource name of each location ID to the request. - currently an empty array             'geo_target_constants' => $this->getGeoTargetConstants(),             // Set the network. To restrict to only Google Search, change the parameter below to             'keyword_plan_network' => KeywordPlanNetwork::GOOGLE_SEARCH,         ] + $requestOptionalArgs;         $response = $ideaClient->generateKeywordIdeas(             new GenerateKeywordIdeasRequest($requestArgs)         ); 

I've been going in circles on this for over a day. Thank you in advance!

I have a problem about writing correct Google Ads API query to get statistics by country.

I need to get click, cost, impression and conversion statistics by country with Google Ads API. How should i update my query; does someone have any idea?

SELECT metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions  FROM geographic_view  WHERE country = 'TR' 

I configured the purchase tag in google tag manager

I also configured the remarketing tags:

add_to_cart purchase view_item view_item_list

I want to use max performance

Should I use the conversion label of my purchase conversion action present in Google Ads for all my Google Tag Manager remarketing tags?

Should I create a conversion action in Google Ads for each remarketing tag and add a different conversion label for each remarketing tag in Google Tag Manager?

Or should I leave the field empty for each conversion label of each of my remarketing tags in google tag manager?

Please enlighten me

THANKS

I don't know how to fill out my conversion labels of my remarketing tags in my google tag manager to advertise google ads

As a frontend developer I've been tasked with the following. The customer is using Google Ads to get traffic to their website A. They can successfully track campaigns in their Google Ads dashboard. Now they want to be able to do the same for website B, which is on the same domain. And they ask me to do it.

Do I need to do anything as frontend developer to make customer able to track UTM campaigns on website B? E.g. do I need to send Google Analytics events, do need to configure something on frontend side?

I found it very hard to find any info on how it actually works (there is a ton of very basic articles on what UTM parameters are, how to generate them and rubbish like that, but nothing about how Google knows when someone landed on a URL with UTM parameters). If anything, I am under impression that it should just work out of the box.