Beispiel #1
0
def web_automation(driver, merchant, amount):
    driver.get('https://duckduckgo.com/')

    WebDriverWait(driver, 10).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, 'search_form_input_homepage')))

    try:  # check if hypothetical bill has already been paid by seeing if remaining balance is $0.00
        driver.find_element_by_xpath("//*[contains(text(),'$0.00'").click()
        LOGGER.error('Example merchant balance is zero, will try again later.')
        return Result.skipped  # if $0.00 found on page, do not throw an error, but also don't retry for a while so we return Result.skipped
    except common.exceptions.NoSuchElementException:
        pass  # $0.00 not on page so continue

    driver.find_element_by_id('search_form_input_homepage').send_keys(
        merchant.usr)
    driver.find_element_by_id('search_form_input_homepage').send_keys(', ')
    driver.find_element_by_id('search_form_input_homepage').send_keys(
        cents_to_str(amount))  # 5 -> 0.05

    driver.find_element_by_id('search_button_homepage').click()

    try:
        WebDriverWait(driver, 10).until(
            expected_conditions.presence_of_element_located(
                (By.XPATH, "//*[contains(text(),'" + merchant.usr + ', ' +
                 cents_to_str(amount) + "')]")))
    except TimeoutException:
        return Result.unverified  # Purchase command was executed, yet we are unable to verify that it was successfully executed.
        # since debbit may have spent money but isn't sure, we log the error and stop any further payments for this merchant until the user intervenes

    return Result.success
Beispiel #2
0
def is_order_total_correct(driver, amount):
    elements_to_check = []

    try:
        elements_to_check.append(
            driver.find_element_by_id('subtotals-marketplace-spp-bottom').text)
    except common.exceptions.NoSuchElementException:
        pass

    try:
        elements_to_check.append(
            driver.find_element_by_class_name('grand-total-price').text)
    except common.exceptions.NoSuchElementException:
        pass

    expected_order_total = '$' + utils.cents_to_str(amount)
    for element in elements_to_check:
        if expected_order_total in element:
            return True

    LOGGER.error(
        'Unable to verify order total is correct, not purchasing. Could not find expected amount '
        + expected_order_total + ' in ' + str(elements_to_check))
    return False
Beispiel #3
0
def web_automation(driver, merchant: Merchant, amount):
    driver.get(
        'https://www.easypaymetrocard.com/vector/forte/cgi_bin/forteisapi.dll?ServiceName=ETCAccountWebSO&TemplateName=accounts/PrePaidPayment.html'
    )
    time.sleep(5)  # wait for page to load or redirect to login page
    logged_in = utils.is_logged_in(driver,
                                   timeout=90,
                                   logged_out_element=(By.ID, 'iPassword'),
                                   logged_in_element=(By.ID, 'securitycode'))

    if not logged_in:
        time.sleep(
            2
        )  # pause to let user watch what's happening - not necessary for real merchants

        try:  # some websites will have the username auto-filled in due to a previous login
            if hasattr(merchant, 'usr'):
                driver.find_element_by_id('username').send_keys(merchant.usr)
        except ElementNotInteractableException:
            pass
        try:  # only one of .usr and .accountnumber should exist
            if hasattr(merchant, 'accountnumber'):
                driver.find_element_by_id('iAccountNumber').send_keys(
                    merchant.accountnumber)
        except ElementNotInteractableException:
            pass

        time.sleep(
            2
        )  # pause to let user watch what's happening - not necessary for real merchants
        driver.find_element_by_id('iPassword').send_keys(merchant.psw)
        time.sleep(
            2
        )  # pause to let user watch what's happening - not necessary for real merchants
        driver.find_element_by_xpath("//input[@type='submit']").click()

        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.LINK_TEXT, 'One Time Payment')))

    driver.get(
        'https://www.easypaymetrocard.com/vector/forte/cgi_bin/forteisapi.dll?ServiceName=ETCAccountWebSO&TemplateName=accounts/PrePaidPayment.html'
    )

    assert merchant.card in [
        "Primary", "Secondary"
    ], "Only supports existing Primary or Secondary card"
    card_xpath = "//input[@value='{}']".format(merchant.card)
    driver.find_element_by_xpath(card_xpath).click()
    time.sleep(2)
    driver.find_element_by_id("securitycode").send_keys(merchant.cvv)
    time.sleep(2)
    driver.find_element_by_id("iAmount").send_keys(utils.cents_to_str(amount))
    time.sleep(2)
    driver.find_element_by_id("Address1").send_keys(merchant.address1)
    time.sleep(2)
    driver.find_element_by_id("Address2").send_keys(merchant.address2)
    time.sleep(2)
    driver.find_element_by_id("City").send_keys(merchant.city)
    time.sleep(2)
    driver.find_element_by_id("usStates").send_keys(merchant.state)
    time.sleep(2)
    driver.find_element_by_id("iZip").send_keys(merchant.zip)
    time.sleep(2)
    driver.find_element_by_xpath("//input[@type='submit']").click()

    try:
        WebDriverWait(driver, 30).until(
            expected_conditions.presence_of_element_located(
                (By.XPATH, "//*[contains(text(),'successfully processed')]")))
    except TimeoutException:
        return Result.unverified  # Purchase command was executed, yet we are unable to verify that it was successfully executed.
        # since debbit may have spent money but isn't sure, we log the error and stop any further payments for this merchant until the user intervenes

    time.sleep(
        5)  # sleep for a bit to show user that payment screen is reached
    return Result.success
Beispiel #4
0
def web_automation(driver, merchant, amount):
    driver.get('http://payments.xfinity.com/')

    logged_in = utils.is_logged_in(driver,
                                   timeout=90,
                                   logged_out_element=(By.ID, 'user'),
                                   logged_in_element=(By.ID, 'customAmount'))

    if not logged_in:
        driver.find_element_by_id('user').send_keys(merchant.usr)
        driver.find_element_by_id('passwd').send_keys(merchant.psw)
        driver.find_element_by_id('sign_in').click()

        try:  # first time run captcha
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'nucaptcha-answer')))
            LOGGER.info('captcha detected')
            input('''
Detected first time run captcha. Please follow these one-time steps. Future runs won't need this.

1. Open the Firefox window that debbit created.
2. Enter your user, pass, and the moving characters manually.
3. Click the "Sign In" button.
4. Click on this terminal window and hit "Enter" to continue running debbit.
''')
        except TimeoutException:
            pass

        WebDriverWait(driver, 90).until(
            expected_conditions.element_to_be_clickable(
                (By.ID, 'customAmount')))

    if driver.find_elements_by_id('no'):  # survey pop-up
        driver.find_element_by_id('no').click()

    cur_balance = driver.find_element_by_xpath(
        "//span[contains(text(), '$')]").text
    if utils.str_to_cents(cur_balance) == 0:
        LOGGER.error('xfinity balance is zero, will try again later.')
        return Result.skipped
    elif utils.str_to_cents(cur_balance) < amount:
        amount = utils.str_to_cents(cur_balance)

    driver.find_element_by_id('customAmount').send_keys(
        utils.cents_to_str(amount))
    driver.find_element_by_xpath("//span[contains(text(),'nding in " +
                                 merchant.card[-4:] + "')]").click()
    driver.find_element_by_xpath("//span[contains(text(),'nding in " +
                                 merchant.card[-4:] + "')]").click()
    driver.find_element_by_xpath(
        "//button[contains(text(),'Continue')]").click()
    driver.find_element_by_xpath(
        "//button[contains(text(),'Submit Payment')]").click()

    try:
        WebDriverWait(driver, 90).until(
            expected_conditions.presence_of_element_located(
                (By.XPATH,
                 "//*[contains(text(),'Your payment was successful')]")))
    except TimeoutException:
        return Result.unverified

    return Result.success
Beispiel #5
0
def web_automation(driver, merchant, amount):
    driver.get('https://www.amazon.com/gp/product/B086KKT3RX')

    WebDriverWait(driver, 90).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, "gcui-asv-reload-buynow-button")))
    for i in range(300):
        if driver.find_element_by_id(
                "gcui-asv-reload-buynow-button"
        ).text == 'Buy Now':  # wait for 'Loading...' text to turn into 'Buy Now'
            break
        time.sleep(0.1)

    time.sleep(1 + random.random() *
               2)  # slow down automation randomly to help avoid bot detection
    driver.find_element_by_id('gcui-asv-reload-form-custom-amount').send_keys(
        utils.cents_to_str(amount))
    time.sleep(1 + random.random() *
               2)  # slow down automation randomly to help avoid bot detection
    driver.find_element_by_id("gcui-asv-reload-buynow-button").click()

    WebDriverWait(driver, 90).until(
        utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable(
                (By.ID, 'ap_email')),  # first time login
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//*[contains(text(),'" + merchant.usr +
                 "')]")),  # username found on login page
            # Already logged in
            expected_conditions.element_to_be_clickable(
                (By.XPATH,
                 "//*[contains(text(),'Order Summary')]")),  # Checkout page
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//*[contains(text(),'a payment method')]"
                 ))  # Another version of the checkout page
        ))

    if not driver.find_elements_by_xpath(
            "//*[contains(text(),'Order Summary')]"
    ) and not driver.find_elements_by_xpath(
            "//*[contains(text(),'a payment method')]"
    ):  # Not in checkout, so we did not auto login. Finish login flow.
        if driver.find_elements_by_xpath("//*[contains(text(),'" +
                                         merchant.usr + "')]"):
            driver.find_element_by_xpath(
                "//*[contains(text(),'" + merchant.usr + "')]").click(
                )  # click username in case we're on the Switch Accounts page
            WebDriverWait(driver, 30).until(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'signInSubmit')))
            time.sleep(1 + random.random() * 2)

        if driver.find_elements_by_id(
                'ap_email'
        ):  # if first run, fill in email. If subsequent run, nothing to fill in
            try:
                driver.find_element_by_id('ap_email').send_keys(merchant.usr)
                time.sleep(1 + random.random() * 2)
            except ElementNotInteractableException:  # Sometimes this field is prefilled with Firstname Lastname and does not accept input
                pass

        if driver.find_elements_by_id('continue'):  # a/b tested new UI flow
            driver.find_element_by_id('continue').click()
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'ap_password')))
            time.sleep(1 + random.random() * 2)

        if driver.find_elements_by_name('rememberMe'):
            time.sleep(1 + random.random() * 2)
            driver.find_element_by_name('rememberMe').click()

        driver.find_element_by_id('ap_password').send_keys(merchant.psw)
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id('signInSubmit').click()
        time.sleep(1 + random.random() * 2)

        handle_anti_automation_challenge(driver, merchant)

        try:  # Push Notification / Email MFA
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH,
                     "//*[contains(text(),'approve the notification')]")))
            if driver.find_elements_by_xpath(
                    "//*[contains(text(),'approve the notification')]"):
                LOGGER.info('\n')
                LOGGER.info(
                    'Please approve the Amazon login notification sent to your email or phone. Debbit will wait up to 3 minutes.'
                )
                for i in range(
                        180
                ):  # Wait for up to 3 minutes for user to approve login notification
                    if not driver.find_elements_by_xpath(
                            "//*[contains(text(),'approve the notification')]"
                    ):
                        break
                    time.sleep(1)
        except TimeoutException:
            pass

        try:  # OTP text message
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH,
                     "//*[contains(text(),'phone number ending in')]")))
            if driver.find_elements_by_id('auth-mfa-remember-device'):
                driver.find_element_by_id('auth-mfa-remember-device').click()

            sent_to_text = driver.find_element_by_xpath(
                "//*[contains(text(),'phone number ending in')]").text
            LOGGER.info(sent_to_text)
            LOGGER.info('Enter OTP here:')
            otp = input()

            driver.find_element_by_id('auth-mfa-otpcode').send_keys(otp)
            time.sleep(1 + random.random() * 2)
            driver.find_element_by_id('auth-signin-button').click()
            time.sleep(1 + random.random() * 2)
        except TimeoutException:
            pass

        try:  # OTP email validation
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//*[contains(text(),'One Time Pass')]")))
            otp_email = True
        except TimeoutException:
            otp_email = False

        try:
            driver.find_element_by_xpath(
                "//*[contains(text(),'one-time pass')]").click()
            time.sleep(1 + random.random() * 2)
            otp_email = True
        except common.exceptions.NoSuchElementException:
            pass

        if otp_email:
            if driver.find_elements_by_id('continue'):
                driver.find_element_by_id('continue').click()
                time.sleep(1 + random.random() * 2)

            handle_anti_automation_challenge(driver, merchant)

            try:  # User may have manually advanced to gift card screen or stopped at OTP input. Handle OTP input if on OTP screen.
                WebDriverWait(driver, 5).until(
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH, "//*[contains(text(),'Enter OTP')]")))
                sent_to_text = driver.find_element_by_xpath(
                    "//*[contains(text(),'@')]").text
                LOGGER.info(sent_to_text)
                LOGGER.info('Enter OTP here:')
                otp = input()

                elem = driver.find_element_by_xpath("//input")
                elem.send_keys(otp)
                time.sleep(1 + random.random() * 2)
                elem.send_keys(Keys.TAB)
                time.sleep(1 + random.random() * 2)
                elem.send_keys(Keys.ENTER)
                time.sleep(1 + random.random() * 2)
            except TimeoutException:
                pass

        try:
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//*[contains(text(),'Not now')]")))
            driver.find_element_by_xpath(
                "//*[contains(text(),'Not now')]").click()
            time.sleep(1 + random.random() * 2)
        except TimeoutException:  # add mobile number page
            pass

    # Now expecting to be on checkout page with debit card selection present
    WebDriverWait(driver, 30).until(
        utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable(
                (By.XPATH,
                 "//*[contains(text(),'Order Summary')]")),  # Checkout page
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//*[contains(text(),'a payment method')]"
                 ))  # Another version of the checkout page
        ))

    if driver.find_elements_by_id('payChangeButtonId'):  # expand list of cards
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id('payChangeButtonId').click()
        WebDriverWait(driver, 10).until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//span[contains(text(),'ending in " +
                 merchant.card[-4:] + "')]")))

    if driver.find_elements_by_id(
            'payment-change-link'
    ):  # expand list of cards if prior element did not exist
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id('payment-change-link').click()
        WebDriverWait(driver, 10).until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//span[contains(text(),'ending in " +
                 merchant.card[-4:] + "')]")))

    card_selected = False
    for element in driver.find_elements_by_xpath(
            "//span[contains(text(),'ending in " + merchant.card[-4:] + "')]"):
        try:  # Amazon has redundant non-clickable elements. This will try each one until one works.
            time.sleep(1 + random.random() * 2)
            element.click()
            card_selected = True
            break
        except WebDriverException:
            pass

    if not card_selected:
        raise Exception(
            'Unable to find or unable to click on card that has last 4 digits matching config file card.'
        )

    if driver.find_elements_by_id('orderSummaryPrimaryActionBtn'):
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id('orderSummaryPrimaryActionBtn').click(
        )  # Click "Use this payment method" button
    else:  # Find Continue button. There are also non clickable spans with 'Continue' in them so try all of them until one works.
        for element in driver.find_elements_by_xpath(
                "//span[contains(text(),'Continue')]"):
            try:
                time.sleep(1 + random.random() * 2)
                element.find_element_by_xpath('../..').click(
                )  # the grandparent element of the text is the clickable Continue button
                break
            except Exception:
                pass

    WebDriverWait(driver, 10).until(
        utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable(
                (By.ID, 'submitOrderButtonId')
            ),  # "Place your order" button showing, card ready to be used
            expected_conditions.element_to_be_clickable(
                (By.ID, 'placeYourOrder')
            ),  # Other checkout page "Place your order" button showing, card ready to be used
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//input[@placeholder='ending in " +
                 merchant.card[-4:] + "']"))  # Verify card flow
        ))

    if driver.find_elements_by_xpath("//input[@placeholder='ending in " +
                                     merchant.card[-4:] +
                                     "']"):  # Verify card flow
        elem = driver.find_element_by_xpath(
            "//input[@placeholder='ending in " + merchant.card[-4:] + "']")
        time.sleep(1 + random.random() * 2)
        elem.send_keys(merchant.card)
        time.sleep(1 + random.random() * 2)
        elem.send_keys(Keys.TAB)
        time.sleep(1 + random.random() * 2)
        elem.send_keys(Keys.ENTER)

        time.sleep(10 + random.random() * 2)
        if driver.find_elements_by_id('orderSummaryPrimaryActionBtn'):
            driver.find_element_by_id('orderSummaryPrimaryActionBtn').click(
            )  # Click "Use this payment method" button
        else:  # Find Continue text, the grandparent element of the text is the clickable Continue button
            driver.find_element_by_xpath(
                "//span[contains(text(),'Continue')]").find_element_by_xpath(
                    '../..').click()

        WebDriverWait(driver, 10).until(
            utils.AnyExpectedCondition(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'submitOrderButtonId')
                ),  # "Place your order" button showing, card ready to be used
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'placeYourOrder')
                ),  # Other checkout page "Place your order" button showing, card ready to be used
            ))

    time.sleep(1 + random.random() * 2)

    if not is_order_total_correct(driver, amount):
        return Result.unverified

    if driver.find_elements_by_id('submitOrderButtonId'):
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id(
            'submitOrderButtonId').click()  # Click "Place your order" button
    else:
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id('placeYourOrder').click(
        )  # Other checkout page click "Place your order" button

    try:
        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable((
                By.XPATH,
                "//*[contains(text(), 'your order has been placed') or contains(text(),'Order placed')]"
            )))
    except TimeoutException:
        LOGGER.error(
            'Clicked "Place your order" button, but unable to confirm if order was successful.'
        )
        return Result.unverified

    if driver.find_elements_by_xpath(
            "//*[contains(text(), 'your order has been placed') or contains(text(),'Order placed')]"
    ):
        return Result.success
    else:
        LOGGER.error(
            'Clicked "Place your order" button, but unable to confirm if order was successful.'
        )
        return Result.unverified
Beispiel #6
0
def web_automation(driver, merchant, amount):
    driver.get('https://www.optimum.net/pay-bill/payment-options/')

    WebDriverWait(driver, 90).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, 'loginPageUsername')))

    driver.find_element_by_id('loginPageUsername').send_keys(merchant.usr)
    driver.find_element_by_id('loginPagePassword').send_keys(merchant.psw)
    driver.find_element_by_xpath(
        "//button[contains(text(),'Sign in to Optimum.net')]").click()

    WebDriverWait(driver, 90).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, 'otherAmountInput')))

    cur_balance = driver.find_element_by_xpath(
        "//span[@class='payment--radio--bold ng-binding']").text
    LOGGER.info('Current Optimum balance is ' + cur_balance)

    if str_to_cents(cur_balance) < 100:
        LOGGER.error(
            'Optimum account balance is less than minimum $1 payment, will try again later.'
        )
        return Result.skipped
    elif str_to_cents(cur_balance) < amount:
        LOGGER.info('Adjusting spend to ' + str_to_cents(cur_balance) +
                    ' cents since current balance is less than ' + amount +
                    ' cents')
        amount = str_to_cents(cur_balance)

    driver.find_element_by_id('otherAmountInput').send_keys(
        cents_to_str(amount))  # Enter the amount
    driver.find_element_by_xpath(
        "//span[contains(text(),'Other amount')]/preceding-sibling::div"
    ).click()  # Select the radio button
    driver.find_element_by_xpath(
        "//div[contains(text(),'Payment Method')]/following-sibling::div"
    ).click()  # Open the selector dropdown box
    try:
        driver.find_element_by_xpath(
            "//span[contains(text(),'" + merchant.card +
            "')]").click()  # Select the payment method
    except common.exceptions.NoSuchElementException:
        LOGGER.error('Failed to find payment method with name ' +
                     merchant.card)
        return Result.failed

    button_str = driver.find_element_by_id('otpSubmit').get_attribute('value')
    expect_str = "Pay $" + cents_to_str(amount) + " now with " + merchant.card

    if button_str == expect_str:
        driver.find_element_by_id('otpSubmit').click()
        LOGGER.info('Submitting purchase: ' + button_str)
    else:
        LOGGER.error('Failed to find valid pay button.')
        LOGGER.info('Detected: ' + button_str)
        LOGGER.info('Expected: ' + expect_str)
        return Result.failed

    # Check if the payment succeeded.
    try:
        WebDriverWait(driver, 90).until(
            expected_conditions.presence_of_element_located(
                (By.XPATH, "//*[contains(text(),'Confirmation Number:')]")))
        LOGGER.info("Successful payment: " + driver.find_element_by_xpath(
            "//*[contains(text(),'Confirmation Number:')]").text)
        return Result.success
    except TimeoutException:
        # Check if there was an error, if so log the error.
        try:
            WebDriverWait(driver, 5).until(
                expected_conditions.presence_of_element_located(
                    (By.XPATH, "//*[contains(text(),'unable')]")))
            LOGGER.error("Failed payment: " + driver.find_element_by_xpath(
                "//*[contains(text(),'unable')]").text)
        except TimeoutException:
            return Result.unverified
        return Result.unverified
Beispiel #7
0
def web_automation(driver, merchant, amount):
    driver.get('https://www.amazon.com/asv/reload/order')

    logged_in = utils.is_logged_in(
        driver,
        timeout=30,
        logged_out_element=(
            By.XPATH, "//button[contains(text(),'Sign In to Continue')]"),
        logged_in_element=(By.XPATH, "//button[contains(text(),'Reload $')]"))

    if not logged_in:
        try:
            driver.find_element_by_xpath(
                "//button[contains(text(),'Sign In to Continue')]").click()
        except ElementClickInterceptedException:  # spinner blocking button
            time.sleep(3)
            driver.find_element_by_xpath(
                "//button[contains(text(),'Sign In to Continue')]").click()

        driver.find_element_by_id('ap_email').send_keys(merchant.usr)

        try:  # a/b tested new UI flow
            driver.find_element_by_id(
                'continue').click()  # if not exists, exception is raised
        except common.exceptions.NoSuchElementException:  # a/b tested old UI flow
            pass

        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.ID, 'ap_password')))
        driver.find_element_by_id('ap_password').send_keys(merchant.psw)
        driver.find_element_by_id('signInSubmit').click()

        try:  # OTP email validation
            WebDriverWait(driver, 3).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//*[contains(text(),'One Time Password')]")))
            otp_flow = True
        except TimeoutException:
            otp_flow = False

        try:
            driver.find_element_by_xpath(
                "//*[contains(text(),'one-time pass')]").click()
            otp_flow = True
        except common.exceptions.NoSuchElementException:
            pass

        if otp_flow:
            driver.find_element_by_id('continue').click()

            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//input")))
            sent_to_text = driver.find_element_by_xpath(
                "//*[contains(text(),'@')]").text
            LOGGER.info(sent_to_text)
            LOGGER.info('Enter OTP here:')
            otp = input()

            elem = driver.find_element_by_xpath("//input")
            elem.send_keys(otp)
            elem.send_keys(Keys.TAB)
            elem.send_keys(Keys.ENTER)

    WebDriverWait(driver, 30).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, 'asv-manual-reload-amount')))
    driver.find_element_by_id('asv-manual-reload-amount').send_keys(
        utils.cents_to_str(amount))
    driver.find_element_by_xpath("//span[contains(text(),'ending in " +
                                 merchant.card[-4:] + "')]").click()
    driver.find_element_by_xpath("//button[contains(text(),'Reload $" +
                                 utils.cents_to_str(amount) + "')]").click()

    time.sleep(10)  # give page a chance to load
    if 'thank-you' not in driver.current_url:
        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//input[@placeholder='ending in " +
                 merchant.card[-4:] + "']")))
        elem = driver.find_element_by_xpath(
            "//input[@placeholder='ending in " + merchant.card[-4:] + "']")
        elem.send_keys(merchant.card)
        elem.send_keys(Keys.TAB)
        elem.send_keys(Keys.ENTER)
        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//button[contains(text(),'Reload $" +
                 utils.cents_to_str(amount) + "')]")))
        time.sleep(1)
        driver.find_element_by_xpath("//button[contains(text(),'Reload $" +
                                     utils.cents_to_str(amount) +
                                     "')]").click()
        time.sleep(10)  # give page a chance to load

    if 'thank-you' not in driver.current_url:
        return Result.unverified

    return Result.success
Beispiel #8
0
def web_automation(driver, merchant, amount):
    driver.get(
        'https://jakehilborn.github.io/debbit/example-merchant/login.html')

    logged_in = utils.is_logged_in(driver,
                                   timeout=90,
                                   logged_out_element=(By.ID, 'password'),
                                   logged_in_element=(By.ID, 'submit-payment'))

    if not logged_in:
        time.sleep(
            2
        )  # pause to let user watch what's happening - not necessary for real merchants

        try:  # some websites will have the username auto-filled in due to a previous login
            driver.find_element_by_id('username').send_keys(merchant.usr)
        except ElementNotInteractableException:
            pass

        time.sleep(
            2
        )  # pause to let user watch what's happening - not necessary for real merchants
        driver.find_element_by_id('password').send_keys(merchant.usr)
        time.sleep(
            2
        )  # pause to let user watch what's happening - not necessary for real merchants
        driver.find_element_by_id('login').click()
        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.ID, 'submit-payment')))

    cur_balance = driver.find_element_by_xpath(
        "//span[contains(text(), '$')]").text
    if utils.str_to_cents(cur_balance) == 0:
        LOGGER.error('example_merchant balance is zero, will try again later.')
        return Result.skipped
    elif utils.str_to_cents(cur_balance) < amount:
        amount = utils.str_to_cents(cur_balance)

    time.sleep(
        2
    )  # pause to let user watch what's happening - not necessary for real merchants
    driver.find_element_by_xpath("//*[contains(text(), 'card ending in " +
                                 merchant.card + "')]").click()
    time.sleep(
        2
    )  # pause to let user watch what's happening - not necessary for real merchants
    driver.find_element_by_id('amount').send_keys(utils.cents_to_str(amount))
    time.sleep(
        2
    )  # pause to let user watch what's happening - not necessary for real merchants
    driver.find_element_by_id('submit-payment').click()

    try:
        WebDriverWait(driver, 30).until(
            expected_conditions.presence_of_element_located(
                (By.XPATH, "//*[contains(text(),'Thank you!')]")))
    except TimeoutException:
        return Result.unverified  # Purchase command was executed, yet we are unable to verify that it was successfully executed.
        # since debbit may have spent money but isn't sure, we log the error and stop any further payments for this merchant until the user intervenes

    time.sleep(
        5)  # sleep for a bit to show user that payment screen is reached
    return Result.success
def web_automation(driver, merchant, amount):
    driver.get('https://www.amazon.com/asv/reload/order')

    logged_in = utils.is_logged_in(
        driver,
        timeout=30,
        logged_out_element=(
            By.XPATH, "//button[contains(text(),'Sign In to Continue')]"),
        logged_in_element=(By.XPATH, "//button[starts-with(text(),'Reload')]"))

    time.sleep(1 + random.random() *
               2)  # slow down automation randomly to help avoid bot detection
    if not logged_in:
        try:
            driver.find_element_by_xpath(
                "//button[contains(text(),'Sign In to Continue')]").click()
            time.sleep(1 + random.random() * 2)
        except ElementClickInterceptedException:  # spinner blocking button
            time.sleep(3)
            driver.find_element_by_xpath(
                "//button[contains(text(),'Sign In to Continue')]").click()
            time.sleep(1 + random.random() * 2)

        WebDriverWait(driver, 30).until(
            utils.AnyExpectedCondition(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'ap_email')),  # first time login
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//*[contains(text(),'" + merchant.usr +
                     "')]"))  # username found on page
            ))

        if driver.find_elements_by_xpath("//*[contains(text(),'" +
                                         merchant.usr + "')]"):
            driver.find_element_by_xpath(
                "//*[contains(text(),'" + merchant.usr + "')]").click(
                )  # click username in case we're on the Switch Accounts page
            WebDriverWait(driver, 30).until(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'signInSubmit')))
            time.sleep(1 + random.random() * 2)

        if driver.find_elements_by_id(
                'ap_email'
        ):  # if first run, fill in email. If subsequent run, nothing to fill in
            driver.find_element_by_id('ap_email').send_keys(merchant.usr)
            time.sleep(1 + random.random() * 2)

        if driver.find_elements_by_id('continue'):  # a/b tested new UI flow
            driver.find_element_by_id('continue').click()
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.ID, 'ap_password')))
            time.sleep(1 + random.random() * 2)

        driver.find_element_by_id('ap_password').send_keys(merchant.psw)
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_id('signInSubmit').click()
        time.sleep(1 + random.random() * 2)

        handle_anti_automation_challenge(driver, merchant)

        try:  # OTP text message
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH,
                     "//*[contains(text(),'phone number ending in')]")))
            if driver.find_elements_by_id('auth-mfa-remember-device'):
                driver.find_element_by_id('auth-mfa-remember-device').click()

            sent_to_text = driver.find_element_by_xpath(
                "//*[contains(text(),'phone number ending in')]").text
            LOGGER.info(sent_to_text)
            LOGGER.info('Enter OTP here:')
            otp = input()

            driver.find_element_by_id('auth-mfa-otpcode').send_keys(otp)
            time.sleep(1 + random.random() * 2)
            driver.find_element_by_id('auth-signin-button').click()
            time.sleep(1 + random.random() * 2)
        except TimeoutException:
            pass

        try:  # OTP email validation
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//*[contains(text(),'One Time Pass')]")))
            otp_email = True
        except TimeoutException:
            otp_email = False

        try:
            driver.find_element_by_xpath(
                "//*[contains(text(),'one-time pass')]").click()
            time.sleep(1 + random.random() * 2)
            otp_email = True
        except common.exceptions.NoSuchElementException:
            pass

        if otp_email:
            if driver.find_elements_by_id('continue'):
                driver.find_element_by_id('continue').click()
                time.sleep(1 + random.random() * 2)

            handle_anti_automation_challenge(driver, merchant)

            try:  # User may have manually advanced to gift card screen or stopped at OTP input. Handle OTP input if on OTP screen.
                WebDriverWait(driver, 5).until(
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH, "//*[contains(text(),'Enter OTP')]")))
                sent_to_text = driver.find_element_by_xpath(
                    "//*[contains(text(),'@')]").text
                LOGGER.info(sent_to_text)
                LOGGER.info('Enter OTP here:')
                otp = input()

                elem = driver.find_element_by_xpath("//input")
                elem.send_keys(otp)
                time.sleep(1 + random.random() * 2)
                elem.send_keys(Keys.TAB)
                time.sleep(1 + random.random() * 2)
                elem.send_keys(Keys.ENTER)
                time.sleep(1 + random.random() * 2)
            except TimeoutException:
                pass

        try:
            WebDriverWait(driver, 5).until(
                expected_conditions.element_to_be_clickable(
                    (By.XPATH, "//*[contains(text(),'Not now')]")))
            driver.find_element_by_xpath(
                "//*[contains(text(),'Not now')]").click()
            time.sleep(1 + random.random() * 2)
        except TimeoutException:  # add mobile number page
            pass

    WebDriverWait(driver, 30).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, 'asv-manual-reload-amount')))
    driver.find_element_by_id('asv-manual-reload-amount').send_keys(
        utils.cents_to_str(amount))
    time.sleep(1 + random.random() * 2)

    for element in driver.find_elements_by_xpath(
            "//span[contains(text(),'ending in " + merchant.card[-4:] + "')]"):
        try:  # Amazon has redundant non-clickable elements. This will try each one until one works.
            element.click()
            time.sleep(1 + random.random() * 2)
            break
        except WebDriverException:
            pass

    driver.find_element_by_xpath(
        "//button[starts-with(text(),'Reload') and contains(text(),'" +
        utils.cents_to_str(amount) + "')]").click()
    time.sleep(1 + random.random() * 2)

    time.sleep(10)  # give page a chance to load
    if 'thank-you' not in driver.current_url:
        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//input[@placeholder='ending in " +
                 merchant.card[-4:] + "']")))
        elem = driver.find_element_by_xpath(
            "//input[@placeholder='ending in " + merchant.card[-4:] + "']")
        time.sleep(1 + random.random() * 2)
        elem.send_keys(merchant.card)
        time.sleep(1 + random.random() * 2)
        elem.send_keys(Keys.TAB)
        time.sleep(1 + random.random() * 2)
        elem.send_keys(Keys.ENTER)
        WebDriverWait(driver, 30).until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//button[contains(text(),'Reload $" +
                 utils.cents_to_str(amount) + "')]")))
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_xpath(
            "//button[starts-with(text(),'Reload') and contains(text(),'" +
            utils.cents_to_str(amount) + "')]").click()
        time.sleep(10)  # give page a chance to load

    if 'thank-you' not in driver.current_url:
        return Result.unverified

    return Result.success
Beispiel #10
0
def web_automation(driver, merchant, amount):
    driver.get('https://www.att.com/my/#/passthrough/overview')

    # Wait until login screen, promotion pop-up, or account dashboard shows.
    WebDriverWait(driver, 120).until(
        utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable(
                (By.NAME, "password")),  # logged out
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//*[contains(@id,'ancel')]"
                 )),  # mfa flow identified by cancel button
            expected_conditions.element_to_be_clickable(
                (By.XPATH,
                 "//img[contains(@src,'btnNoThanks')]")),  # logged in
            expected_conditions.element_to_be_clickable(
                (By.XPATH,
                 "//button[contains(text(),'Make a payment')]"))  # logged in
        ))

    handle_mfa_code_flow(driver)

    time.sleep(
        1 + random.random() * 2
    )  # AT&T is using bot detection software, slow down the automation a bit to help avoid detection
    if driver.find_elements_by_name(
            'password'):  # password field found, need to log in
        try:
            driver.find_element_by_id('userID').send_keys(merchant.usr)
            time.sleep(1 + random.random() * 2)
        except common.exceptions.NoSuchElementException:
            pass

        driver.find_element_by_name('password').send_keys(merchant.psw)
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_xpath(
            "//button[contains(text(),'Sign in')]").click()

        try:
            # Wait for potential promotions screen, regular account overview, or OTP flow
            WebDriverWait(driver, 120).until(
                utils.AnyExpectedCondition(
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH, "//img[contains(@src,'btnNoThanks')]")),
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH,
                         "//button[contains(text(),'Make a payment')]")),
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH, "//*[contains(@id,'ancel')]"
                         ))  # mfa flow identified by cancel button
                ))
        except TimeoutException:
            pass  # Try continuing to the makePayment page just in case log in worked, but timeout failed

        time.sleep(1 + random.random() * 2)
        handle_mfa_code_flow(driver)

    driver.get("https://www.att.com/my/#/makePayment")

    WebDriverWait(driver, 20).until(
        expected_conditions.element_to_be_clickable(
            (By.ID, "MAP_Amount_TextField")))
    time.sleep(1 + random.random() * 2)

    cur_balance_html_text = driver.find_element_by_xpath(
        "//span[contains(text(), 'Balance due')]").text
    cur_balance = cur_balance_html_text.split('$')[1]
    if utils.str_to_cents(cur_balance) == 0:
        LOGGER.warning('AT&T balance is zero, will try again later.')
        return Result.skipped
    elif utils.str_to_cents(cur_balance) < amount:
        amount = utils.str_to_cents(cur_balance)

    elem = driver.find_element_by_id('MAP_Amount_TextField')
    elem.clear()
    time.sleep(1 + random.random() * 2)
    elem.send_keys(utils.cents_to_str(amount))
    time.sleep(1 + random.random() * 2)
    if driver.find_elements_by_xpath(
            "//*[contains(text(),'Debit or credit card')]"):
        driver.find_element_by_xpath(
            "//*[contains(text(),'Debit or credit card')]").click()
        time.sleep(1 + random.random() * 2)
    driver.find_element_by_xpath("//input[@value='" + merchant.card +
                                 "']").click()
    time.sleep(1 + random.random() * 2)
    driver.find_element_by_xpath("//*[contains(text(),'Pay $" +
                                 utils.cents_to_str(amount) + "')]").click()
    time.sleep(1 + random.random() * 2)

    try:
        WebDriverWait(driver, 120).until(
            utils.AnyExpectedCondition(
                expected_conditions.presence_of_element_located(
                    (By.XPATH, "//*[contains(text(),'We got your $" +
                     utils.cents_to_str(amount) + " payment')]")),
                expected_conditions.presence_of_element_located((
                    By.XPATH,
                    "//*[contains(text(),'multiple payments for the same amount')]"
                ))))

        if driver.find_elements_by_xpath(
                "//*[contains(text(),'multiple payments for the same amount')]"
        ):
            LOGGER.info(
                "Duplicate payment amount not accepted within 24 hours. Trying again later. Please use a larger range between amount_min and amount_max in config.txt for att_bill_pay to avoid duplicate payment amount scenarios."
            )
            return Result.skipped
        elif driver.find_elements_by_xpath("//*[text()='We got your $ + " +
                                           utils.cents_to_str(amount) +
                                           " payment']"):
            return Result.success
        else:
            return Result.unverified

    except (KeyboardInterrupt, SystemExit):
        raise
    except Exception:
        return Result.unverified  # Purchase command was executed, yet we are unable to verify that it was successfully executed.
        # since debbit may have spent money but isn't sure, we log the error and stop any further payments for this merchant until the user intervenes

    return Result.success
Beispiel #11
0
def web_automation(driver, merchant, amount):
    driver.get('https://www.att.com/my/#/passthrough/overview')

    # Wait until login screen, promotion pop-up, or account dashboard shows.
    WebDriverWait(driver, 120).until(utils.AnyExpectedCondition(
        expected_conditions.element_to_be_clickable((By.ID, "password")),
        expected_conditions.element_to_be_clickable((By.XPATH, "//img[contains(@src,'btnNoThanks')]")),
        expected_conditions.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Make a payment')]")),
    ))

    if not driver.find_elements_by_id('password'):  # password field found, need to log in
        try:
            driver.find_element_by_id('userName').send_keys(merchant.usr)
        except common.exceptions.ElementNotInteractableException:
            pass
        try:
            driver.find_element_by_xpath("//a[@value='" + merchant.usr + "']").click()
        except common.exceptions.NoSuchElementException:
            pass

        driver.find_element_by_id('password').send_keys(merchant.psw)
        driver.find_element_by_xpath("//button[contains(@id,'loginButton')]").click()

        # Wait for potential promotions screen, regular account overview, or OTP flow
        WebDriverWait(driver, 30).until(utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable((By.XPATH, "//img[contains(@src,'btnNoThanks')]")),
            expected_conditions.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Make a payment')]")),
            expected_conditions.element_to_be_clickable((By.NAME, "Send code"))
        ))

        try:  # OTP text validation
            driver.find_element_by_name("Send code").click()
            WebDriverWait(driver, 20).until(expected_conditions.element_to_be_clickable((By.ID, "verificationCodeInput")))
            sent_to_text = driver.find_element_by_xpath("//*[contains(text(),'We sent it to')]").text.strip()
            LOGGER.info(sent_to_text)
            LOGGER.info('Enter OTP here: ')
            otp = input()

            elem = driver.find_element_by_id("verificationCodeInput")
            elem.send_keys(otp)
            elem.send_keys(Keys.ENTER)
        except common.exceptions.NoSuchElementException:
            pass

        # Wait for potential promotions screen or regular account overview
        WebDriverWait(driver, 30).until(utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable((By.XPATH, "//img[contains(@src,'btnNoThanks')]")),
            expected_conditions.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Make a payment')]"))
        ))

        try:  # Dismiss promotions screen if it appeared
            driver.find_element_by_xpath("//*[contains(@src,'btnNoThanks')]").click()
            WebDriverWait(driver, 20).until(expected_conditions.element_to_be_clickable((By.XPATH, "//*[contains(text(),'Make a payment')]")))
        except common.exceptions.NoSuchElementException:
            pass

    driver.get("https://www.att.com/my/#/makePayment")

    # Enter amount and select payment card
    WebDriverWait(driver, 20).until(expected_conditions.element_to_be_clickable((By.ID, "pmtAmount0")))
    elem = driver.find_element_by_id('pmtAmount0')
    elem.clear()
    elem.send_keys(utils.cents_to_str(amount))
    elem = driver.find_element_by_id("paymentMethod0")
    before_first_payment_card = "Select Payment Method"
    after_last_payment_card = "New checking / savings account"
    while elem.get_attribute("value").lower() != before_first_payment_card.lower():
        elem.send_keys(Keys.UP)
    while elem.get_attribute("value").lower() != merchant.card.lower() and elem.get_attribute("value").lower() != after_last_payment_card.lower():
        elem.send_keys(Keys.DOWN)
    if elem.get_attribute("value").lower() == after_last_payment_card.lower():
        raise Exception("Payment method " + merchant.card + " not found in list of saved payment methods")

    # Continue
    elem.send_keys(Keys.ENTER)
    try:
        WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.XPATH, "//html/body/div[contains(@class,'modalwrapper active')]//p[contains(text(),'paying more than the amount due')]")))
        driver.find_element_by_xpath("//html/body/div[contains(@class,'modalwrapper active')]//button[text()='OK']").click()
    except TimeoutException:
        pass

    # Submit
    WebDriverWait(driver, 20).until(expected_conditions.element_to_be_clickable((By.XPATH, "//button[text()='Submit']")))
    WebDriverWait(driver, 20).until(expected_conditions.invisibility_of_element_located((By.ID, "loaderOverlay")))
    time.sleep(2)
    driver.find_element_by_xpath("//button[text()='Submit']").click()

    try:
        WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.XPATH, "//*[contains(text(),'Thank you for your payment')]")))
        WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.XPATH, "//*[text()='$" + utils.cents_to_str(amount) + "']")))
    except TimeoutException:
        return Result.unverified  # Purchase command was executed, yet we are unable to verify that it was successfully executed.
        # since debbit may have spent money but isn't sure, we log the error and stop any further payments for this merchant until the user intervenes

    return Result.success
Beispiel #12
0
def web_automation(driver, merchant, amount):
    driver.get('https://www.att.com/my/#/passthrough/overview')

    # Wait until login screen, promotion pop-up, or account dashboard shows.
    WebDriverWait(driver, 120).until(
        utils.AnyExpectedCondition(
            expected_conditions.element_to_be_clickable(
                (By.NAME, "password")),  # logged out
            expected_conditions.element_to_be_clickable(
                (By.XPATH, "//*[contains(@id,'ancel')]"
                 )),  # mfa flow identified by cancel button
            expected_conditions.element_to_be_clickable(
                (By.XPATH,
                 "//img[contains(@src,'btnNoThanks')]")),  # logged in
            expected_conditions.element_to_be_clickable(
                (By.XPATH,
                 "//button[contains(text(),'Make a payment')]"))  # logged in
        ))

    handle_mfa_code_flow(driver)

    time.sleep(
        1 + random.random() * 2
    )  # AT&T is using bot detection software, slow down the automation a bit to help avoid detection
    if driver.find_elements_by_name(
            'password'):  # password field found, need to log in
        try:
            driver.find_element_by_id('userID').send_keys(merchant.usr)
            time.sleep(1 + random.random() * 2)
        except common.exceptions.NoSuchElementException:
            pass

        driver.find_element_by_name('password').send_keys(merchant.psw)
        time.sleep(1 + random.random() * 2)
        driver.find_element_by_xpath(
            "//button[contains(text(),'Sign in')]").click()

        try:
            # Wait for potential promotions screen, regular account overview, or OTP flow
            WebDriverWait(driver, 120).until(
                utils.AnyExpectedCondition(
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH, "//img[contains(@src,'btnNoThanks')]")),
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH,
                         "//button[contains(text(),'Make a payment')]")),
                    expected_conditions.element_to_be_clickable(
                        (By.XPATH, "//*[contains(@id,'ancel')]"
                         ))  # mfa flow identified by cancel button
                ))
        except TimeoutException:
            pass  # Try continuing to the makePayment page just in case log in worked, but timeout failed

        time.sleep(1 + random.random() * 2)
        handle_mfa_code_flow(driver)

    driver.get("https://www.att.com/my/#/makePayment")

    # Enter amount and select payment card
    WebDriverWait(driver, 20).until(
        expected_conditions.element_to_be_clickable((By.ID, "pmtAmount0")))
    time.sleep(1 + random.random() * 2)
    elem = driver.find_element_by_id('pmtAmount0')
    elem.clear()
    time.sleep(1 + random.random() * 2)
    elem.send_keys(utils.cents_to_str(amount))
    time.sleep(1 + random.random() * 2)
    elem = driver.find_element_by_id("paymentMethod0")
    before_first_payment_card = "Select Payment Method"
    after_last_payment_card = "New checking / savings account"
    while elem.get_attribute(
            "value").lower() != before_first_payment_card.lower():
        elem.send_keys(Keys.UP)
        time.sleep(1 + random.random())
    while elem.get_attribute(
            "value").lower() != merchant.card.lower() and elem.get_attribute(
                "value").lower() != after_last_payment_card.lower():
        elem.send_keys(Keys.DOWN)
        time.sleep(1 + random.random())
    if elem.get_attribute("value").lower() == after_last_payment_card.lower():
        raise Exception("Payment method " + merchant.card +
                        " not found in list of saved payment methods")

    # Continue
    elem.send_keys(Keys.ENTER)
    time.sleep(1 + random.random() * 2)
    try:
        WebDriverWait(driver, 20).until(
            expected_conditions.presence_of_element_located((
                By.XPATH,
                "//html/body/div[contains(@class,'modalwrapper active')]//p[contains(text(),'paying more than the amount due')]"
            )))
        driver.find_element_by_xpath(
            "//html/body/div[contains(@class,'modalwrapper active')]//button[text()='OK']"
        ).click()
        time.sleep(1 + random.random() * 2)
    except TimeoutException:
        pass

    # Submit
    WebDriverWait(driver, 20).until(
        expected_conditions.element_to_be_clickable(
            (By.XPATH, "//button[text()='Submit']")))
    WebDriverWait(driver, 20).until(
        expected_conditions.invisibility_of_element_located(
            (By.ID, "loaderOverlay")))
    time.sleep(2 + random.random())
    driver.find_element_by_xpath("//button[text()='Submit']").click()

    try:
        WebDriverWait(driver, 120).until(
            utils.AnyExpectedCondition(
                expected_conditions.presence_of_element_located(
                    (By.XPATH,
                     "//*[contains(text(),'Thank you for your payment')]")),
                expected_conditions.presence_of_element_located(
                    (By.XPATH,
                     "//*[contains(text(),'payment was unsuccessful')]"))))

        if driver.find_elements_by_xpath(
                "//*[contains(text(),'multiple payments')]"):
            return Result.skipped  # att does not allow payments of the same dollar amount within 24 hours, skip this purchase and try again 24 hours later
        elif driver.find_elements_by_xpath("//*[text()='$" +
                                           utils.cents_to_str(amount) + "']"):
            return Result.success
        else:
            return Result.unverified

    except (KeyboardInterrupt, SystemExit):
        raise
    except Exception:
        return Result.unverified  # Purchase command was executed, yet we are unable to verify that it was successfully executed.
        # since debbit may have spent money but isn't sure, we log the error and stop any further payments for this merchant until the user intervenes

    return Result.success