googleads / google-ads-python

Google Ads API Client Library for Python
Apache License 2.0
526 stars 480 forks source link

Can't create billing setup #889

Closed acedinh001 closed 3 weeks ago

acedinh001 commented 1 month ago

I use create billing setup file in example and it return this error.

Request made: ClientCustomerId: 9582317986, Host: googleads.googleapis.com, Method: /google.ads.googleads.v17.services.BillingSetupService/MutateBillingSetup, RequestId: IEXsvG376itn2_wE0yEs4A, IsFault: True, FaultMessage: A start time in the future cannot be used because there is currently no active billing setup for this customer. Request with ID "IEXsvG376itn2_wE0yEs4A" failed with status "INVALID_ARGUMENT" and includes the following errors:
Error with message "A start time in the future cannot be used because there is currently no active billing setup for this customer.". On field: operation On field: create On field: start_time

acedinh001 commented 1 month ago
#!/usr/bin/env python
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This example creates a billing setup for a customer.

A billing setup is a link between a payments account and a customer. The new
billing setup can either reuse an existing payments account, or create a new
payments account with a given payments profile. Billing setups are applicable
for clients on monthly invoicing only. See here for details about applying for
monthly invoicing: https://support.google.com/google-ads/answer/2375377.
In the case of consolidated billing, a payments account is linked to the
manager account and is linked to a customer account via a billing setup.
"""

import argparse
from datetime import datetime, timedelta
import sys
import time
from uuid import uuid4

from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException

def main(
    client, customer_id, payments_account_id=None, payments_profile_id=None
):
    """The main method that creates all necessary entities for the example.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        payments_account_id: payments account ID to attach to the new billing
            setup. If provided it must be formatted as "1234-5678-9012-3456".
        payments_profile_id: payments profile ID to attach to a new payments
            account and to the new billing setup. If provided it must be
            formatted as "1234-5678-9012".
    """
    billing_setup = create_billing_setup(
        client, customer_id, payments_account_id, payments_profile_id
    )
    print(billing_setup)
    set_billing_setup_date_times(client, customer_id, billing_setup)
    billing_setup_operation = client.get_type("BillingSetupOperation")
    client.copy_from(billing_setup_operation.create, billing_setup)
    billing_setup_service = client.get_service("BillingSetupService")
    response = billing_setup_service.mutate_billing_setup(
        customer_id=customer_id, operation=billing_setup_operation
    )
    # print(
    #     "Added new billing setup with resource name "
    #     f"{response.result.resource_name}"
    # )

def create_billing_setup(
    client, customer_id, payments_account_id=None, payments_profile_id=None
):
    """Creates and returns a new billing setup instance.

    The new billing setup will have its payment details populated. One of the
    payments_account_id or payments_profile_id must be provided.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        payments_account_id: payments account ID to attach to the new billing
            setup. If provided it must be formatted as "1234-5678-9012-3456".
        payments_profile_id: payments profile ID to attach to a new payments
            account and to the new billing setup. If provided it must be
            formatted as "1234-5678-9012".

    Returns:
        A newly created BillingSetup instance.
    """
    billing_setup = client.get_type("BillingSetup")

    # Sets the appropriate payments account field.
    if payments_account_id != None:
        # If a payments account ID has been provided, set the payments_account
        # field to the full resource name of the given payments account ID.
        # You can list available payments accounts via the
        # PaymentsAccountService's ListPaymentsAccounts method.
        billing_setup.payments_account = client.get_service(
            "BillingSetupService"
        ).payments_account_path(customer_id, payments_account_id)
    elif payments_profile_id != None:
        # Otherwise, create a new payments account by setting the
        # payments_account_info field
        # See https://support.google.com/google-ads/answer/7268503
        # for more information about payments profiles.
        billing_setup.payments_account_info.payments_account_name = (
            f"Payments Account #{uuid4()}"
        )
        billing_setup.payments_account_info.payments_profile_id = (
            payments_profile_id
        )

    return billing_setup

def set_billing_setup_date_times(client, customer_id, billing_setup):
    """Sets the starting and ending date times for the new billing setup.

    Queries the customer's account to see if there are any approved billing
    setups. If there are any, the new billing setup starting date time is set to
    one day after the last. If not, the billing setup is set to start
    immediately. The ending date is set to one day after the starting date time.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        billing_setup: the billing setup whose starting and ending date times
            will be set.
    """
    # The query to search existing approved billing setups in the end date time
    # descending order. See get_billing_setup.py for a more detailed example of
    # how to retrieve billing setups.
    query = """
      SELECT
        billing_setup.end_date_time
      FROM billing_setup
      WHERE billing_setup.status = APPROVED
      ORDER BY billing_setup.end_date_time DESC
      LIMIT 1"""

    ga_service = client.get_service("GoogleAdsService")
    stream = ga_service.search_stream(customer_id=customer_id, query=query)
    # Coercing the response iterator to a list causes the stream to be fully
    # consumed so that we can easily access the last row in the request.
    batches = list(stream)
    # Checks if any results were included in the response.
    if batches:
        # Retrieves the ending_date_time of the last BillingSetup.
        last_batch = batches[0]
        last_row = last_batch.results[0]
        last_ending_date_time = last_row.billing_setup.end_date_time

        if not last_ending_date_time:
            # A null ending date time indicates that the current billing setup
            # is set to run indefinitely. Billing setups cannot overlap, so
            # throw an exception in this case.
            raise Exception(
                "Cannot set starting and ending date times for the new billing "
                "setup; the latest existing billing setup is set to run "
                "indefinitely."
            )

        try:
            # BillingSetup.end_date_time is a string that can be in the format
            # %Y-%m-%d or %Y-%m-%d %H:%M:%S. This checks for the first format.
            end_date_time_obj = datetime.strptime(
                last_ending_date_time, "%Y-%m-%d"
            )
        except ValueError:
            # If a ValueError is raised then the end_date_time string is in the
            # second format that includes hours, minutes and seconds.
            end_date_time_obj = datetime.strptime(
                last_ending_date_time, "%Y-%m-%d %H:%M:%S"
            )

        # Sets the new billing setup start date to one day after the end date.
        start_date = end_date_time_obj + timedelta(days=1)
    else:
        # If there are no BillingSetup objects to retrieve, the only acceptable
        # start date time is today.
        start_date = datetime.now() + timedelta(days=1)
    print("Start date: %s" % start_date)
    time.sleep(5)
    billing_setup.start_date_time = start_date.strftime("%Y-%m-%d %H:%M:%S")
    billing_setup.end_date_time = (start_date + timedelta(days=1)).strftime(
        "%Y-%m-%d %H:%M:%S"
    )

def create_billing_setup(
    client, customer_id, payments_account_id=None, payments_profile_id=None
):
    """Creates and returns a new billing setup instance.

    The new billing setup will have its payment details populated. One of the
    payments_account_id or payments_profile_id must be provided.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        payments_account_id: payments account ID to attach to the new billing
            setup. If provided it must be formatted as "1234-5678-9012-3456".
        payments_profile_id: payments profile ID to attach to a new payments
            account and to the new billing setup. If provided it must be
            formatted as "1234-5678-9012".

    Returns:
        A newly created BillingSetup instance.
    """
    billing_setup = client.get_type("BillingSetup")

    # Sets the appropriate payments account field.
    if payments_account_id != None:
        # If a payments account ID has been provided, set the payments_account
        # field to the full resource name of the given payments account ID.
        # You can list available payments accounts via the
        # PaymentsAccountService's ListPaymentsAccounts method.
        billing_setup.payments_account = client.get_service(
            "BillingSetupService"
        ).payments_account_path(customer_id, payments_account_id)
    elif payments_profile_id != None:
        # Otherwise, create a new payments account by setting the
        # payments_account_info field
        # See https://support.google.com/google-ads/answer/7268503
        # for more information about payments profiles.
        billing_setup.payments_account_info.payments_account_name = (
            f"Payments Account #{uuid4()}"
        )
        billing_setup.payments_account_info.payments_profile_id = (
            payments_profile_id
        )

    return billing_setup

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=("Creates a billing setup for a given customer.")
    )
    # The following argument(s) should be provided to run the example.
    parser.add_argument(
        "-c",
        "--customer_id",
        type=str,
        required=True,
        help="The Google Ads customer ID.",
    )
    # Creates a mutually exclusive argument group to ensure that only one of the
    # following two arguments are given, otherwise it will raise an error.
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "-a",
        "--payments_account_id",
        type=str,
        help="Either a payments account ID or a payments profile ID must be "
        "provided for the example to run successfully. "
        "See: https://developers.google.com/google-ads/api/docs/billing/billing-setups#creating_new_billing_setups. "
        "Provide an existing payments account ID to link to the new "
        "billing setup. Must be formatted as '1234-5678-9012-3456'.",
    )
    group.add_argument(
        "-p",
        "--payments_profile_id",
        type=str,
        help="Either a payments account ID or a payments profile ID must be "
        "provided for the example to run successfully. "
        "See: https://developers.google.com/google-ads/api/docs/billing/billing-setups#creating_new_billing_setups. "
        "Provide an existing payments profile ID to link to a new payments "
        "account and the new billing setup. Must be formatted as: "
        "'1234-5678-9012-3456'.",
    )
    args = parser.parse_args()

    # GoogleAdsClient will read the google-ads.yaml configuration file in the
    # home directory if none is specified.
    googleads_client = GoogleAdsClient.load_from_storage(version="v17")

    try:
        main(
            googleads_client,
            args.customer_id,
            args.payments_account_id,
            args.payments_profile_id,
        )
    except GoogleAdsException as ex:
        print(
            f'Request with ID "{ex.request_id}" failed with status '
            f'"{ex.error.code().name}" and includes the following errors:'
        )
        for error in ex.failure.errors:
            print(f'\tError with message "{error.message}".')
            if error.location:
                for field_path_element in error.location.field_path_elements:
                    print(f"\t\tOn field: {field_path_element.field_name}")
        sys.exit(1)

this is all my code

siddharth-ethinos commented 1 month ago

Issue with Billing Setup via API

File Name : AddBillingSetup.php

We are encountering an issue when setting up billing for a customer account using the Google Ads API. Specifically, the following error occurs:

ApiException was thrown with message '{ "message": "Request contains an invalid argument.", "code": 3, "status": "INVALID_ARGUMENT", "details": [ { "@type": "type.googleapis.com\/google.ads.googleads.v16.errors.GoogleAdsFailure", "errors": [ { "errorCode": { "billingSetupError": "FUTURE_START_TIME_PROHIBITED" }, "message": "A start time in the future cannot be used because there is currently no active billing setup for this customer.", "location": { "fieldPathElements": [ { "fieldName": "operation" }, { "fieldName": "create" }, { "fieldName": "start_time" } ] } } ], "requestId": "1Z_7PyJ9cNH0rJKVvEdGuQ" } ] }'.

he above error occurs only when we attempt to create the billing setup through the API. However, the issue does not occur if we log in to the Google Ads account manually, select the relevant customer ID, and click on the Billing section. After doing this, the API request works correctly.

This behavior forces us to manually log in and click the Billing option every time we create a new account, which is not feasible as we need to create and set up billing for over 500 accounts using the API.

My server timezone is UTC and my account setup timezone is IST is it affect any on API call

acedinh001 commented 1 month ago

he above error occurs only when we attempt to create the billing setup through the API. However, the issue does not occur if we log in to the Google Ads account manually, select the relevant customer ID, and click on the Billing section. After doing this, the API request works correctly.

This behavior forces us to manually log in and click the Billing option every time we create a new account, which is not feasible as we need to create and set up billing for over 500 accounts using the API.

My server timezone is UTC and my account setup timezone is IST is it affect any on API call

yess i face the same issue, do you have solution to solve this problem

siddharthundare commented 1 month ago

No I didn't get solution already raised query with the Google Team

https://groups.google.com/g/adwords-api/c/zYrg-DwEApc

acedinh001 commented 1 month ago

No I didn't get solution already raised query with the Google Team

https://groups.google.com/g/adwords-api/c/zYrg-DwEApc

hjc, it's very headache

siddharthundare commented 1 month ago

Please check link they suggest to add NOW insted of startdatetime https://groups.google.com/g/adwords-api/c/zYrg-DwEApc?pli=1

acedinh001 commented 1 month ago

Please check link they suggest to add NOW insted of startdatetime https://groups.google.com/g/adwords-api/c/zYrg-DwEApc?pli=1

can you give me all the code you wrote to run add billing setup? i don't know how to modify my code because i not good at api

BenRKarl commented 3 weeks ago

@siddharthundare @acedinh001 thanks for working together on this, if you have further questions or issues please continue with the API support thread here: https://groups.google.com/g/adwords-api/c/zYrg-DwEApc