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
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
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
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
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
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
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
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
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
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
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