Skip to content

Instantly share code, notes, and snippets.

@gtdse
Created May 17, 2025 02:10
Show Gist options
  • Save gtdse/392545726dc49aeb8c67c4c932bae35f to your computer and use it in GitHub Desktop.
Save gtdse/392545726dc49aeb8c67c4c932bae35f to your computer and use it in GitHub Desktop.
Full automation for mss merchant creation
import datetime
import random
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
def extract_application_id_from_url(url_string):
"""
Extracts the application ID from a URL string.
The ID is expected to be between 'application/' and '/submit-with'.
Args:
url_string (str): The URL to process.
Returns:
str or None: The extracted application ID, or None if not found.
"""
application_id = None
try:
start_delimiter = "application/"
end_delimiter = "/submit-with"
start_index = url_string.find(start_delimiter)
if start_index != -1:
# Adjust start_index to be after the delimiter
actual_start_index = start_index + len(start_delimiter)
end_index = url_string.find(end_delimiter, actual_start_index)
if end_index != -1:
application_id = url_string[actual_start_index:end_index]
return application_id
except Exception as e:
print(f"Error with string find method: {e}")
application_id = None # Ensure it's None if an error occurs
def affirm_full_onboarding_flow_final_steps(url):
"""
Full onboarding flow: initial form, dynamic fields, submit, choose package,
handle prohibited items, fill store details, fill business information,
fill owner information, fill bank details, interact with submit application page,
and finally submit the application.
"""
print(f"--- Starting Playwright FULL ONBOARDING FLOW (FINAL STEPS including Submit Application) for {url} ---")
# Screenshot paths
screenshot_path_intake_form = "intake_form.png"
screenshot_path_after_offered_packages = "offered_packages.png"
screenshot_path_selected_package = "selected_package_card.png"
screenshot_path_prohibited_items_checked = "prohibited_items_checked.png"
screenshot_path_store_details_filled = "store_details_filled.png"
screenshot_path_business_info_filled = "business_info_filled.png"
screenshot_path_owner_info_filled = "owner_info_filled.png"
screenshot_path_bank_details_filled = "bank_details_filled.png"
screenshot_path_terms_checked = "application_terms_checked.png"
screenshot_path_flow_completed = "affirm_application_submitted.png"
screenshot_path_error = "affirm_onboarding_error.png"
# 1. Generate dynamic data
current_date_obj = datetime.datetime.now()
current_date_str = current_date_obj.strftime("%m.%d.%y")
random_id_str = f".{random.randint(100, 999):03}"
date_id_for_email = current_date_str + random_id_str
date_id_for_website_raw = current_date_str + random_id_str
date_id_for_website_modified = date_id_for_website_raw.replace(".", "-")
business_email_value = f"mae+msstest{date_id_for_email}@affirm.com"
business_website_value = f"www.maeinternaltest{date_id_for_website_modified}.com"
country_value = "United States"
platform_value = "Magento 2"
annual_volume_value = "<$2M"
average_order_value_value = "$100-$150"
industry_value = "Accessories"
sub_industry_value = "Optical"
capture_to_shipment_value = "< 7 days"
business_type_value = "LLC"
legal_name_value = f"MSS INTERNAL TEST {date_id_for_email}"
tax_id_ein_value = "99-9999999"
business_street1_value = "650 California Ave"
business_city_value = "San Francisco"
business_state_value = "California"
business_postal_code_value = "94108"
owner_first_name_value = "MAE"
owner_last_name_value = "Tester"
owner_dob_value = "01/01/1990"
owner_phone_value = "888-484-4282"
owner_ssn_value = "000-99-1111"
# Values for "Bank Details" page
routing_number_value = "072403004"
account_number_value = "856667"
print(f"Generated Business Email: {business_email_value}")
print(f"Generated Business Website: {business_website_value}")
print(f"Generated Legal Name: {legal_name_value}")
with sync_playwright() as p:
try:
browser = p.chromium.launch(headless=False, slow_mo=100) # Consider making headless configurable
page = browser.new_page()
page.set_viewport_size({"width": 1280, "height": 1080})
print(f"Navigating to {url}...")
page.goto(url, timeout=60000)
print("Page loaded. Current URL:", page.url)
# --- Initial 7 Fields (Onboarding) ---
print("--- Filling initial 7 fields on Onboarding page ---")
page.locator('input[name="business_email"]').fill(business_email_value)
page.locator('input[name="business_website"]').fill(business_website_value)
page.locator('select[name="business_country"]').select_option(label=country_value)
page.locator('select[name="platform"]').select_option(label=platform_value)
page.locator('select[name="annual_volume"]').wait_for(state="visible", timeout=30000)
page.locator('select[name="annual_volume"]').select_option(label=annual_volume_value)
page.locator('select[name="average_order_value"]').wait_for(state="visible", timeout=15000)
page.locator('select[name="average_order_value"]').select_option(label=average_order_value_value)
checkbox1_input_id = "marketing-disclosure-checkbox"
label1_locator = page.locator(f'label[for="{checkbox1_input_id}"]')
label1_locator.first.wait_for(state="visible", timeout=15000)
label1_locator.first.click(timeout=10000)
page.wait_for_timeout(200) # Consider replacing with explicit waits if possible
print(f"Marketing Disclosure Checkbox checked: {page.locator(f'input#{checkbox1_input_id}').is_checked()}")
print("--- All 7 initial fields processed ---")
page.screenshot(path=screenshot_path_intake_form, full_page=True)
print(f"Screenshot {screenshot_path_intake_form} saved successfully!")
# --- First Submit ("Continue") -> Leads to Pricing/Package Page ---
print("--- Submitting initial form (to Pricing/Package page) ---")
page.locator('[data-testid="step-buttons--nextbutton"] button:has-text("Continue")').click(timeout=30000)
print("Initial form 'Continue' button clicked.")
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Pricing/Package page loaded. Current URL: {page.url}")
page.locator('[data-testid="fp-cards-grid-item"]').first.wait_for(state="visible", timeout=15000)
page.screenshot(path=screenshot_path_after_offered_packages, full_page=True)
print(f"Screenshot {screenshot_path_after_offered_packages} saved successfully!")
# --- Choose Package -> Leads to Prohibited Items Page ---
print("--- Choosing the first financing package ---")
first_package_card_locator = page.locator('[data-testid="fp-cards-grid-item"] > div').first
first_package_card_locator.screenshot(path=screenshot_path_selected_package)
first_package_card_locator.locator('button:has-text("Choose this package")').click(timeout=15000)
print("'Choose this package' button clicked.")
# Prohibited Items Page
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Prohibited Items page loaded. Current URL: {page.url}")
page.locator('label[for="pbp-disclosure-checkbox"]').first.wait_for(state="visible", timeout=15000)
print("--- Interacting with 'Prohibited Items' page ---")
checkbox2_input_id = "pbp-disclosure-checkbox"
page.locator(f'label[for="{checkbox2_input_id}"]').first.click(timeout=10000)
page.wait_for_timeout(200) # Consider replacing
print(f"Prohibited items checkbox checked: {page.locator(f'input#{checkbox2_input_id}').is_checked()}")
page.screenshot(path=screenshot_path_prohibited_items_checked, full_page=True)
print(f"Screenshot {screenshot_path_prohibited_items_checked} saved successfully!")
page.locator('[data-testid="step-buttons--nextbutton"] button:has-text("Continue")').click(timeout=30000)
print("'Continue' button on Prohibited Items page clicked.")
# Store Details Page
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Store Details page loaded. Current URL: {page.url}")
page.locator('select[name="industry"]').wait_for(state="visible", timeout=15000)
print("--- Interacting with 'Store details' page ---")
page.locator('select[name="industry"]').select_option(label=industry_value)
page.wait_for_timeout(500) # Consider replacing
page.locator('select[name="sub_industry"]').select_option(label=sub_industry_value)
page.wait_for_timeout(500) # Consider replacing
page.locator('select[name="capture_to_shipment"]').select_option(label=capture_to_shipment_value)
print("Store details fields filled.")
page.screenshot(path=screenshot_path_store_details_filled, full_page=True)
print(f"Screenshot {screenshot_path_store_details_filled} saved successfully!")
page.locator('[data-testid="step-buttons--nextbutton"] button:has-text("Continue")').click(timeout=30000)
print("'Continue' button on Store Details page clicked.")
# Business Information Page
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Business Information page loaded. Current URL: {page.url}")
page.locator('select[name="business_type"]').wait_for(state="visible", timeout=15000)
print("--- Interacting with 'Business Information' page ---")
page.locator('select[name="business_type"]').select_option(label=business_type_value)
page.locator('input[name="legal_name"]').fill(legal_name_value)
page.locator('label[for="dba-checkbox"]').click()
page.wait_for_timeout(200) # Consider replacing
print(f"DBA Checkbox clicked. Is checked: {page.locator('input#dba-checkbox').is_checked()}")
page.locator('input[name="tax_id_ein"]').fill(tax_id_ein_value)
page.locator('input[name="business_street1"]').fill(business_street1_value)
page.locator('input[name="business_city"]').fill(business_city_value)
page.locator('select[name="business_state"]').select_option(label=business_state_value)
page.locator('input[name="business_postal_code"]').fill(business_postal_code_value)
print("Business information fields filled.")
page.screenshot(path=screenshot_path_business_info_filled, full_page=True)
print(f"Screenshot {screenshot_path_business_info_filled} saved successfully!")
page.locator('[data-testid="step-buttons--nextbutton"] button:has-text("Continue")').click(timeout=30000)
print("'Continue' button on Business Information page clicked.")
# Owner Information Page
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Owner Information page loaded. Current URL: {page.url}")
page.locator('input[name="business_owner_first_name"]').wait_for(state="visible", timeout=15000)
print("--- Interacting with 'Owner Information' page ---")
page.locator('input[name="business_owner_first_name"]').fill(owner_first_name_value)
page.locator('input[name="business_owner_last_name"]').fill(owner_last_name_value)
page.locator('input[name="business_owner_dob"]').fill(owner_dob_value)
page.locator('input[name="business_owner_phone_number"]').fill(owner_phone_value)
page.locator('input[name="business_owner_ssn"]').fill(owner_ssn_value)
print("Owner information fields filled.")
page.screenshot(path=screenshot_path_owner_info_filled, full_page=True)
print(f"Screenshot {screenshot_path_owner_info_filled} saved successfully!")
page.locator('[data-testid="step-buttons--nextbutton"] button:has-text("Continue")').click(timeout=30000)
print("'Continue' button on Owner Information page clicked.")
# Bank Details Page
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Bank Details page loaded. Current URL: {page.url}")
page.locator('input[name="settlement_bank_routing_number"]').wait_for(state="visible", timeout=15000)
print("Bank Details page elements are visible.")
print("--- Interacting with 'Bank Details' page ---")
page.locator('input[name="settlement_bank_routing_number"]').fill(routing_number_value)
print(f"Filled Routing Number: {routing_number_value}")
page.locator('input[name="settlement_bank_account_number"]').fill(account_number_value)
print(f"Filled Account Number: {account_number_value}")
print("Bank details fields filled.")
page.screenshot(path=screenshot_path_bank_details_filled, full_page=True)
print(f"Screenshot {screenshot_path_bank_details_filled} saved successfully!")
bank_details_continue_button = page.locator('[data-testid="step-buttons--nextbutton"] button:has-text("Continue")')
bank_details_continue_button.wait_for(state="visible", timeout=20000)
print("Attempting to click 'Continue' button on Bank Details page...")
bank_details_continue_button.click(timeout=30000)
print("'Continue' button on Bank Details page clicked.")
# Submit your application Page
page.wait_for_load_state("domcontentloaded", timeout=60000)
print(f"Landed on page after Bank Details. Current URL: {page.url}. Expecting 'Submit your application' page.")
print("--- Waiting for 'Submit your application' page elements ---")
submit_app_content_locator = page.locator('div#ps-group-embedded-nas10a0kg-onboarding')
submit_app_content_locator.wait_for(state="visible", timeout=30000)
print("'Submit your application' page content (PactSafe agreement block) is visible.")
page.wait_for_timeout(3000)
print("--- Interacting with 'Submit your application' page (PactSafe Terms) ---")
terms_label_locator = page.locator(
'div#ps-group-embedded-nas10a0kg-onboarding label.ps-contract-label:has-text("I confirm that I am an authorized representative")'
)
terms_label_locator.wait_for(state="visible", timeout=15000)
print("PactSafe terms and conditions label is visible.")
terms_label_locator.click()
print("Clicked on the PactSafe terms and conditions label.")
page.wait_for_timeout(200) # Small pause for UI to update if needed
checkbox_input_locator = page.locator(
'div#ps-group-embedded-nas10a0kg-onboarding input.ps-contract-target[type="checkbox"]'
)
if checkbox_input_locator.count() > 0:
if checkbox_input_locator.is_checked():
print("PactSafe Terms checkbox is confirmed checked.")
else:
print("WARNING: PactSafe Terms checkbox IS NOT checked after clicking label. Attempting direct input click...")
checkbox_input_locator.click()
page.wait_for_timeout(200)
if checkbox_input_locator.is_checked():
print("PactSafe Terms checkbox is NOW checked after direct input click.")
else:
print("ERROR: PactSafe Terms checkbox STILL NOT checked after direct input click.")
else:
print("ERROR: Could not find the PactSafe Terms checkbox input to verify its state.")
page.screenshot(path=screenshot_path_terms_checked, full_page=True)
print(f"Screenshot {screenshot_path_terms_checked} saved successfully!")
print("--- 'Submit your application' page interaction complete (terms checkbox handled) ---")
# --- Submitting the application ---
print("--- Submitting the application ---")
page.wait_for_timeout(1000)
final_submit_button_locator = page.locator('button:text-is("Submit")')
final_submit_button_locator.wait_for(state="visible", timeout=15000)
print("Final 'Submit' button is visible")
print("Setting up to intercept submission API call and clicking 'Submit' button...")
extracted_info_from_payload = {}
with page.expect_request(
lambda request: "/api/v1/merchants/onboarding/application/" in request.url and \
request.url.endswith("/submit-with-fp") and \
request.method == "POST",
timeout=0
) as request_info: # This still fails inexplicably at times. added additional timeouts
# throughout to try and mitigate.
page.wait_for_timeout(1000)
final_submit_button_locator.click()
captured_request = request_info.value
print(f"Successfully intercepted POST request to: {captured_request.url}")
try:
payload = captured_request.post_data_json
if payload:
keys = payload.keys()
print(f"Keys in the POST data: {keys}")
print("Payload successfully parsed as JSON.")
object_to_extract = "financing_package"
field_to_get = "financing_package"
if payload:
extracted_info_from_payload['finacing program uuid'] = payload.get(object_to_extract, {}).get(field_to_get, "NESTED_NOT_FOUND")
else:
print("Payload was None after JSON parsing attempt.")
print("--- Information Extracted from POST Payload: ---")
if extracted_info_from_payload:
for key, value in extracted_info_from_payload.items():
print(f" {key}: {value}")
print(f"Merchant ARI: {extract_application_id_from_url(captured_request.url)}")
else:
print(" No specific data was configured for extraction, or payload was empty/None.")
print("-------------------------------------------------")
except Exception as e:
print(f"Could not process POST data as JSON or extract keys: {e}")
raw_payload = captured_request.post_data
print(f"Raw POST data (if available): {raw_payload}")
extracted_info_from_payload['error_during_payload_processing'] = str(e)
if raw_payload:
extracted_info_from_payload['raw_payload_on_error_for_debug'] = raw_payload
print("Waiting for response after final submission...")
page.wait_for_load_state("domcontentloaded", timeout=60000)
page.wait_for_load_state("domcontentloaded", timeout=60000) # there are multiple stages to this spinner
heading_locator = page.get_by_role("heading",name="We’re reviewing your application",level=2)
heading_locator.wait_for(state="visible", timeout=120000)
print(f"Application submitted. Current URL: {page.url}")
page.screenshot(path=screenshot_path_flow_completed, full_page=True)
print(f"Screenshot {screenshot_path_flow_completed} saved successfully!")
print("--- Application submission process finished ---")
except PlaywrightTimeoutError as te:
print(f"ERROR: A Playwright timeout occurred: {te}")
current_screenshot_path = screenshot_path_error
if 'page' in locals() and not page.is_closed():
page.screenshot(path=current_screenshot_path, full_page=True)
print(f"Timeout error screenshot taken: {current_screenshot_path}")
except Exception as e:
error_message = f"ERROR: An unexpected error occurred: {e} (Type: {type(e).__name__})"
print(error_message)
if 'page' in locals() and not page.is_closed():
page.screenshot(path="affirm_onboarding_generic_error.png", full_page=True)
print(f"Generic error screenshot taken.")
finally:
if 'browser' in locals() and browser.is_connected():
print("Closing browser...")
browser.close()
print(f"--- Playwright FULL ONBOARDING FLOW (FINAL STEPS including Submit Application) finished ---")
target_url = "https://www.affirm.com/dashboard/onboarding"
if __name__ == "__main__":
affirm_full_onboarding_flow_final_steps(target_url)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment