googleads / googleads-mobile-flutter

A Flutter plugin for the Google Mobile Ads SDK
Apache License 2.0
339 stars 280 forks source link

Server side verification not passing user id #1090

Closed joukhar closed 4 months ago

joukhar commented 4 months ago

[REQUIRED] Step 1: Describe your environment

Plugin Version

5.1.0

[REQUIRED] Step 2: Describe the problem

here is my code any suggestions are welcome at keyword "HERE"


class AdsService extends GetxService {
  Future<AdsService> init({required String userId}) async {
    /* ------------------------------------------------------------------------------------------ */
    /* Init Google Ads                                                                            */
    /* ------------------------------------------------------------------------------------------ */
    initGoogleAds(userId: userId);

    return this;
  }

  Future<void> enableTestAds() async {
    MobileAds.instance.updateRequestConfiguration(
      RequestConfiguration(
        testDeviceIds: ['REDACTED'],
      ),
    );
  }

  Future<void> initGoogleAds({required String userId}) async {
    // Enable test ads in debug mode
    if (kDebugMode) await enableTestAds();

    // Authorization Request for IDFA use
    if (Platform.isIOS) await checkATT();

    // Finally, initialize
    await MobileAds.instance.initialize();

    // HERE set user id not working
    ServerSideVerificationOptions(userId: userId);
  }

  // For iOS14 IDFA access
  // Must be called when the app is in the state UIApplicationStateActive
  Future<void> checkATT() async {
    final params = ConsentRequestParameters();
    ConsentInformation.instance.requestConsentInfoUpdate(
      params,
      () async {
        // The consent information state was updated.
        // You are now ready to check if a form is available.
        if (await ConsentInformation.instance.isConsentFormAvailable()) {
          _loadConsentForm();
        }
      },
      (FormError error) {
        // Handle the error
        throw error;
      },
    );

    return;
  }

  void _loadConsentForm() {
    ConsentForm.loadConsentForm(
      (ConsentForm consentForm) async {
        var status = await ConsentInformation.instance.getConsentStatus();
        if (status == ConsentStatus.required) {
          consentForm.show(
            (FormError? formError) {
              // Handle dismissal by reloading form
              _loadConsentForm();
            },
          );
        }
      },
      (formError) {
        // Handle the error
        throw formError;
      },
    );
  }
}
malandr2 commented 4 months ago

Hi @joukhar, ServerSideVerificationOptions are applicable to Rewarded and RewardedInterstitial ads.

Apply it in a manner similar to this:

RewardedAd.load(
      adUnitId: adUnitId,
      request: AdRequest(),
      rewardedAdLoadCallback: RewardedAdLoadCallback( `...` ),
      serverSideVerificationOptions: ServerSideVerificationOptions(userId: userId),
);

Our Android and iOS docs offer more information on Server-side verification options.

EDIT: SEE https://github.com/googleads/googleads-mobile-flutter/issues/1090#issuecomment-2109092975 for the correct code to use. This code posted above is deprecated as of 2.0.0

joukhar commented 4 months ago

@malandr2

The parameter doesn't exist

The named parameter 'serverSideVerificationOptions' isn't defined.
Try correcting the name to an existing named parameter's name, or defining a named parameter with the name 'serverSideVerificationOptions'.
  /// Loads an rewarded ad.
  static Future<RewardedAd?> loadRewardedAd({
    required String adUnitId,
    VoidCallback? onAdFailedToShowFullScreenContent,
  }) async {
    Completer<RewardedAd?> adCompleter = Completer<RewardedAd?>(); // Create a Completer

    await RewardedAd.load(
        adUnitId: adUnitId,
        request: AdRequest(extras: {
          'user': UserUtils.getStoredUser().username,
        }),
        serverSideVerificationOptions: ServerSideVerificationOptions(userId: "userId"),
        rewardedAdLoadCallback: RewardedAdLoadCallback(
          // Called when an ad is successfully received.
          onAdLoaded: (ad) async {
            LoggerHelper.info('$ad loaded.');

            // Handle ad events
            ad.fullScreenContentCallback = FullScreenContentCallback(
              // Called when the ad showed the full screen content.
              onAdShowedFullScreenContent: (ad) {
                LoggerHelper.info('Rewarded Ad showed successfully');
              },
              // Called when an impression occurs on the ad.
              onAdImpression: (ad) => _onAdImpression(ad),
              // Called when the ad failed to show full screen content.
              onAdFailedToShowFullScreenContent: (ad, err) {
                LoggerHelper.info('Rewarded Ad Failed to show');
                AppHelper().errorSnackbar(
                  message: StringConstants.adFailedToShowMessage.tr,
                );

                // Dispose the ad here to free resources.
                ad.dispose();
                // throw error
                throw err;
              },
              // Called when the ad dismissed full screen content.
              onAdDismissedFullScreenContent: (ad) {
                LoggerHelper.info('Rewarded Ad Dismissed');
                // Dispose the ad here to free resources.
                ad.dispose();
              },
              // Called when a click is recorded for an ad.
              onAdClicked: (ad) {
                LoggerHelper.info('Rewarded Ad Clicked');
              },
            );

            // Keep a reference to the ad so you can show it later.
            adCompleter.complete(ad);
          },
          // Called when an ad request failed.
          onAdFailedToLoad: (LoadAdError error) {
            // Check if the error is one of the errors to be skipped
            if (error.code == AdmobAdErrorCode.ERROR_CODE_NETWORK_ERROR.code ||
                error.code == AdmobAdErrorCode.ERROR_CODE_NO_FILL.code) {
              LoggerHelper.warning('Skipping error: $error');
              // set default null
              adCompleter.complete(null);
            } else {
              AppHelper().errorSnackbar(
                message: StringConstants.adFailedToLoadMessage.tr,
              );
              LoggerHelper.error('Rewarded Ad failed to load: $error');

              // set default null
              adCompleter.complete(null);
              throw error;
            }
          },
        ));
    return adCompleter.future;
  }
joukhar commented 4 months ago

i found this in changelog:

2.0.0

malandr2 commented 4 months ago

Thanks for that, upon further review you can do this:

    RewardedInterstitialAd.load(
        adUnitId: _adUnitId,
        request: const AdRequest(),
        rewardedInterstitialAdLoadCallback:
        RewardedInterstitialAdLoadCallback(onAdLoaded: (ad) {
          // Set the SSV after the ad is loaded.
          ad.setServerSideOptions(ServerSideVerificationOptions(userId: "userId"));

          ad.fullScreenContentCallback = FullScreenContentCallback(
            // Called when the ad showed the full screen content.
              onAdShowedFullScreenContent: (ad) {},
              // Called when an impression occurs on the ad.
              onAdImpression: (ad) {},
              // Called when the ad failed to show full screen content.
              onAdFailedToShowFullScreenContent: (ad, err) {
                ad.dispose();
              },
              // Called when the ad dismissed full screen content.
              onAdDismissedFullScreenContent: (ad) {
                ad.dispose();
              },
              // Called when a click is recorded for an ad.
              onAdClicked: (ad) {});

          // Keep a reference to the ad so you can show it later.
          _rewardedInterstitialAd = ad;
        }, onAdFailedToLoad: (LoadAdError error) {
          // ignore: avoid_print
          print('RewardedInterstitialAd failed to load: $error');
        }));

I've updated my original comment to reference this.

joukhar commented 4 months ago

Thank you so much, There is no error and it seems its working. i will test it on production in closed testing but it will take time.

have a good day :heart: