Testing Custom Account Verification

    A walk-through of testing different verification states for Connect Custom accounts using your test API key.

    This document assumes you’re familiar with Custom accounts, how to update accounts, and identity verification.

    You should test your verification flows to make sure they can handle changes in account state (e.g., when charges are enabled or disabled). Account states generally change after requirements are fulfilled or when processing or time thresholds are reached. The sections below walk you through these changes and how to test your verification flows.

    Testing initial requirements

    Let’s start by creating a new Custom account in test mode, adding a bank account, and showing that the account holder accepted the Stripe Services Agreement. Explicit Services Agreement acceptance is required for making payouts. For this example, the business_type is set to company to illustrate a more complex scenario.

    curl https://api.stripe.com/v1/accounts \
      -u {PLATFORM_SECRET_KEY}: \
      -d type=custom \
      -d country=US \
      -d business_type=company \
      -d requested_capabilities[]=card_payments \
      -d external_account[object]=bank_account \
      -d external_account[country]=US \
      -d external_account[currency]=usd \
      -d external_account[routing_number]=110000000 \
      -d external_account[account_number]=000123456789 \
      -d tos_acceptance[date]=1547923073 \
      -d tos_acceptance[ip]="172.18.80.19"

    At this point, the account is created but charges and payouts are still disabled. In the response, check the requirements[currently_due] array to determine what information needs to be collected:

    "requirements": {
        ...
        "currently_due": [
            "business_profile.mcc",
            "business_profile.url",
            "company.address.city",
            "company.address.line1",
            "company.address.postal_code",
            "company.address.state",
            "company.tax_id_number",
            "company.name",
            "company.phone",
            "external_account",
            "tos_acceptance.date",
            "tos_acceptance.ip"
            "relationship.account_opener",
            "relationship.owner",
            "settings.charge.statement_descriptor",
        ],
        ...
      }

    Then, collect Account object information and update the account:

    curl https://api.stripe.com/v1/accounts/acct_xxx \
      -H "Stripe-Version: 2018-11-08; accounts_upgrade=true" \
      -u {PLATFORM_SECRET_KEY}: \
      -d business_profile[mcc]=5045 \
      -d business_profile[url]=https://cookies.com \
      -d company[address][city]=Schenectady \
      -d company[address][line1]="123 State St" \
      -d company[address][postal_code]=12345 \
      -d company[address][state]=NY \
      -d company[tax_id]=000000000 \
      -d company[name]="The Best Cookie Co" \
      -d company[phone]=12345678 \
      -d external_account[object]=bank_account \
      -d external_account[account_number]=000123456789 \
      -d external_account[routing_number]=110000000 \
      -d external_account[country]=US \
      -d external_account[currency]=usd \
      -d tos_acceptance[date]=1539356786 \
      -d tos_acceptance[ip]="127.0.0.1" \

    If you check requirements[currently_due] after updating the account, only the relationship requirements are left:

    "requirements": {
        ...
        "currently_due": [
            "relationship.account_opener",
            "relationship.owner",
        ],
        ...
    }

    Now you need to use the Persons API to collect information on these individuals. For this example, the person providing account information is Jenny Rosen, so they’re the account_opener. When creating a Person object for Jenny Rosen, you can specify both relationships:

    curl https://api.stripe.com/v1/accounts/acct_xxx/persons \
      -H "Stripe-Version: 2018-11-08; accounts_upgrade=true" \
      -u {PLATFORM_SECRET_KEY}: \
      -d first_name=Jenny \
      -d last_name=Rosen \
      -d relationship[account_opener]=true \
      -d relationship[title]=CEO

    When you create a Person, the response includes a requirements hash. This hash lists the required verification information for that person.

    
    "requirements": {
        ...
        "currently_due": [
            "address.city",
            "address.line1",
            "address.postal_code",
            "address.state",
            "dob.day",
            "dob.month",
            "dob.year",
            "id_number",
            "phone",
            "email",
        ],
        ...
    }

    If you check the Account object at this point, the required verification information is also displayed in the requirements[currently_due] array:

    "requirements": {
        ...
        "currently_due": [
            "person.person_xxx.address.city",
            "person.person_xxx.address.line1",
            "person.person_xxx.address.postal_code",
            "person.person_xxx.address.state",
            "person.person_xxx.dob.day",
            "person.person_xxx.dob.month",
            "person.person_xxx.dob.year",
            "person.person_xxx.id_number",
            "person.person_xxx.phone",
            "person.person_xxx.email",
            "relationship.owner",
        ],
        ...
    }

    Because a Person object (person_xxx in the example below) exists for Jenny Rosen already, you can update it with the required information:

    curl https://api.stripe.com/v1/accounts/acct_xxx/persons/person_xxx \
      -H "Stripe-Version: 2018-11-08; accounts_upgrade=true" \
      -u {PLATFORM_SECRET_KEY}: \
      -d address[city]=Schenectady \
      -d address[line1]="123 State St" \
      -d address[postal_code]=12345 \
      -d address[state]=NY \
      -d dob[day]=10 \
      -d dob[month]=11 \
      -d dob[year]=1980 \
      -d id_number=000000000 \
      -d phone=2015550123 \
      -d email="jenny@cookies.com"

    With the account_opener information collected, only the owner remains. For this example, Kathleen Banks is the sole owner of The Best Cookie Co.

    curl https://api.stripe.com/v1/accounts/acct_xxx/persons \
      -H "Stripe-Version: 2018-11-08; accounts_upgrade=true" \
      -u {PLATFORM_SECRET_KEY}: \
      -d first_name=Kathleen \
      -d last_name=Banks \
      -d email="kathleen@cookies.com" \
      -d relationship[owner]=true \
      -d relationship[percent_ownership]=100

    At this stage, all required information is collected and charges are enabled for the account. You should make sure you received an account.updated webhook and that charges_enabled=true and requirements[currently_due]=nil.

    Testing thresholds

    Whether you use upfront or incremental onboarding, Stripe might request more information about connected accounts as different thresholds are reached. Sometimes these thresholds are triggered by verification failures or OFAC checks. Other times they’re triggered by a processing or time component. For example, more information might be required after $1,500 in charges or 30 days after an account is created (whichever comes first). To find out what information is required and by when, you can check the requirements[eventually_due] array and the requirements[current_deadline] timestamp.

    In some cases, if the new information isn’t collected by a certain date, charges and payouts might be disabled until the information is collected. You can trigger these scenarios so that you can test these thresholds, and then collect the required information.

    Triggering thresholds

    You can create a charge with the verification token (tok_visa_triggerVerification) to trigger a generic verification threshold. This doesn’t block charges or payouts, but it does trigger the request for additional information. If you’re listening to the account.updated webhook, you can check:

    • requirements[currently_due] to find out what information is needed.
    • requirements[current_deadline] to find out when the information needs to be collected by.

    If the information isn’t collected by the current_deadline, charges and payouts might be disabled. To test scenarios like this, see the blocking charges and payouts sections below.

    You can also trigger more specific verification thresholds, like when there’s an identity mismatch or when an OFAC threshold is reached. Testing these thresholds is beneficial because they often happen when verification fails.

    Testing blocked charges

    You can block charges by creating a test charge with the charge block token (tok_visa_triggerChargeBlock). After doing this, you should receive an account.updated webhook that shows:

    • charges_enabled=false.
    • The required information in the requirements[currently_due] array.
    • An empty requirements[eventually_due] array.

    You can then update the account with the new information. This triggers another webhook that now indicates that charges are enabled, and that the requirements[currently_due] and requirements[eventually_due] arrays are both empty.

    Testing blocked payouts

    You can block payouts by creating a test charge with the block transfer token (tok_visa_triggerTransferBlock). After doing this, you should receive an account.updated webhook that shows:

    • payouts_enabled=false.
    • The required information in the requirements[currently_due] array.
    • An empty requirements[eventually_due] array.

    You can then update the account with the new information. This triggers another webhook that now indicates that payouts are enabled, and that the requirements[currently_due] and requirements[eventually_due] arrays are both empty.

    Was this page helpful?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    Questions?

    We're always happy to help with code or other questions you might have. Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    On this page