/api/user/connect/set-stripe-account-submitted (PATCH)
Account information like email addresses is generated with faker-js it is not real user information.
await global.api.user.connect.SetStripeAccountSubmitted.patch(req)Returns object (individual),object (company)
{
"stripeid": "acct_1LEC0tRnXb6DmWDd",
"object": "stripeAccount",
"accountid": "acct_a8f01ce377932f33",
"tokenUpdate": null,
"stripeObject": {
"id": "acct_1LEC0tRnXb6DmWDd",
"object": "account",
"business_profile": {
"mcc": "7535",
"name": null,
"product_description": null,
"support_address": null,
"support_email": null,
"support_phone": null,
"support_url": null,
"url": "https://www.an-example-website.com"
},
"business_type": "individual",
"capabilities": {
"card_payments": "pending",
"transfers": "pending"
},
"charges_enabled": false,
"company": {
"address": {
"city": "Berlin",
"country": "DE",
"line1": "123 Park Lane",
"line2": null,
"postal_code": "01067",
"state": null
},
"directors_provided": true,
"executives_provided": true,
"name": null,
"owners_provided": true,
"phone": "+14567890123",
"tax_id_provided": false,
"verification": {
"document": {
"back": null,
"details": null,
"details_code": null,
"front": null
}
}
},
"country": "DE",
"created": 1656075212,
"default_currency": "eur",
"details_submitted": true,
"email": null,
"external_accounts": {
"object": "list",
"data": [
{
"id": "ba_1LEC0wRnXb6DmWDdI9UqWQok",
"object": "bank_account",
"account": "acct_1LEC0tRnXb6DmWDd",
"account_holder_name": "Gerardo Roob",
"account_holder_type": "individual",
"account_type": null,
"available_payout_methods": [
"standard"
],
"bank_name": "STRIPE TEST BANK",
"country": "DE",
"currency": "eur",
"default_for_currency": true,
"fingerprint": "8JEgQB776Ig8OZcq",
"last4": "3000",
"metadata": {},
"routing_number": "110000000",
"status": "new"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/accounts/acct_1LEC0tRnXb6DmWDd/external_accounts"
},
"future_requirements": {
"alternatives": [],
"current_deadline": null,
"currently_due": [],
"disabled_reason": null,
"errors": [],
"eventually_due": [],
"past_due": [],
"pending_verification": []
},
"individual": {
"id": "person_1LEC10RnXb6DmWDdN3Neicsn",
"object": "person",
"account": "acct_1LEC0tRnXb6DmWDd",
"address": {
"city": "Berlin",
"country": "DE",
"line1": "123 Park Lane",
"line2": null,
"postal_code": "01067",
"state": null
},
"created": 1656075218,
"dob": {
"day": 1,
"month": 1,
"year": 1970
},
"email": "Gerardo.Roob8@hotmail.com",
"first_name": "Gerardo",
"future_requirements": {
"alternatives": [],
"currently_due": [],
"errors": [],
"eventually_due": [],
"past_due": [],
"pending_verification": []
},
"last_name": "Roob",
"metadata": {},
"phone": "+14567890123",
"relationship": {
"director": false,
"executive": false,
"owner": false,
"percent_ownership": null,
"representative": true,
"title": null
},
"requirements": {
"alternatives": [],
"currently_due": [],
"errors": [],
"eventually_due": [],
"past_due": [],
"pending_verification": [
"verification.additional_document",
"verification.document"
]
},
"verification": {
"additional_document": {
"back": null,
"details": null,
"details_code": null,
"front": null
},
"details": null,
"details_code": null,
"document": {
"back": null,
"details": null,
"details_code": null,
"front": null
},
"status": "pending"
}
},
"metadata": {},
"payouts_enabled": false,
"requirements": {
"alternatives": [],
"current_deadline": null,
"currently_due": [],
"disabled_reason": "requirements.pending_verification",
"errors": [],
"eventually_due": [],
"past_due": [],
"pending_verification": [
"individual.verification.additional_document",
"individual.verification.document"
]
},
"settings": {
"bacs_debit_payments": {},
"branding": {
"icon": null,
"logo": null,
"primary_color": null,
"secondary_color": null
},
"card_issuing": {
"tos_acceptance": {
"date": null,
"ip": null
}
},
"card_payments": {
"decline_on": {
"avs_failure": false,
"cvc_failure": false
},
"statement_descriptor_prefix": null,
"statement_descriptor_prefix_kana": null,
"statement_descriptor_prefix_kanji": null
},
"dashboard": {
"display_name": "An-example-website",
"timezone": "Etc/UTC"
},
"payments": {
"statement_descriptor": "WWW.AN-EXAMPLE-WEBSITE.COM",
"statement_descriptor_kana": null,
"statement_descriptor_kanji": null
},
"payouts": {
"debit_negative_balances": false,
"schedule": {
"delay_days": 7,
"interval": "daily"
},
"statement_descriptor": null
},
"sepa_debit_payments": {}
},
"tos_acceptance": {
"date": 1656075220,
"ip": "127.0.0.1",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0"
},
"type": "custom"
},
"requiresOwners": false,
"requiresDirectors": false,
"requiresExecutives": false,
"submittedAt": "2022-06-24T12:53:42.821Z",
"appid": "tests_1656075154",
"createdAt": "2022-06-24T12:53:33.636Z",
"updatedAt": "2022-06-24T12:53:42.821Z"
}
Exceptions
These exceptions are thrown (NodeJS) or returned as JSON (HTTP) if you provide incorrect data or do not meet the requirements:
Exception | Circumstances |
---|---|
invalid-account | ineligible accessing account |
invalid-company-owner | ineligible company owners not submitted |
invalid-director | ineligible company directors not submitted |
invalid-executive | ineligible company executives not submitted |
invalid-payment-details | ineligible Stripe company account missing payment details |
ineligible Stripe individual account missing payment details | |
invalid-person | ineligible company person missing information |
invalid-registration | ineligible Stripe company account missing information |
ineligible Stripe individual account missing information | |
invalid-stripe-account | ineligible Stripe company account is submitted |
ineligible Stripe individual account is submitted | |
invalid-stripeid | missing querystring stripeid |
invalid querystring stripeid |
NodeJS source (view on github)
const stripeCache = require('../../../../stripe-cache.js')
const connect = require('../../../../../index.js')
const dashboard = require('@layeredapps/dashboard')
module.exports = {
patch: async (req) => {
if (!req.query || !req.query.stripeid) {
throw new Error('invalid-stripeid')
}
const stripeAccount = await global.api.user.connect.StripeAccount.get(req)
if (stripeAccount.submittedAt ||
stripeAccount.accountid !== req.account.accountid) {
throw new Error('invalid-stripe-account')
}
if (!stripeAccount.stripeObject.external_accounts.data.length) {
throw new Error('invalid-payment-details')
}
if (stripeAccount.stripeObject.business_type === 'company') {
if (stripeAccount.requiresOwners && !stripeAccount.stripeObject.company.owners_provided) {
throw new Error('invalid-company-owner')
}
if (stripeAccount.requiresDirectors && !stripeAccount.stripeObject.company.directors_provided) {
throw new Error('invalid-company-director')
}
if (stripeAccount.requiresExecutives && !stripeAccount.stripeObject.company.executives_provided) {
throw new Error('invalid-company-executive')
}
req.query.all = true
const persons = await global.api.user.connect.Persons.get(req)
if (persons && persons.length) {
for (const person of persons) {
if (person.stripeObject.requirements.currently_due.length) {
throw new Error('invalid-person')
}
}
}
}
if (stripeAccount.stripeObject.requirements.currently_due.length) {
for (const field of stripeAccount.stripeObject.requirements.currently_due) {
if (field !== 'tos_acceptance.date' &&
field !== 'tos_acceptance.ip') {
throw new Error('invalid-registration')
}
}
}
const accountInfo = {
tos_acceptance: {
ip: req.ip,
user_agent: req.headers['user-agent'] || 'None',
date: Math.floor(new Date().getTime() / 1000)
}
}
const stripeAccountNow = await stripeCache.execute('accounts', 'update', req.query.stripeid, accountInfo, req.stripeKey)
await connect.Storage.StripeAccount.update({
stripeObject: stripeAccountNow,
submittedAt: new Date()
}, {
where: {
stripeid: req.query.stripeid,
appid: req.appid || global.appid
}
})
await dashboard.StorageCache.remove(req.query.stripeid)
return global.api.user.connect.StripeAccount.get(req)
}
}
Test source (view on github)
/* eslint-env mocha */
const assert = require('assert')
const TestHelper = require('../../../../../test-helper.js')
const DashboardTestHelper = require('@layeredapps/dashboard/test-helper.js')
const TestStripeAccounts = require('../../../../../test-stripe-accounts.js')
describe('/api/user/connect/set-stripe-account-submitted', function () {
before(TestHelper.disableMetrics)
after(TestHelper.enableMetrics)
let cachedResponses
async function bundledData (retryNumber) {
if (retryNumber > 0) {
cachedResponses = {}
}
if (cachedResponses && cachedResponses.finished) {
return
}
cachedResponses = {}
await DashboardTestHelper.setupBeforeEach()
await TestHelper.setupBeforeEach()
const user = await TestHelper.createUser()
// company issues
await TestHelper.createStripeAccount(user, {
country: 'DE',
business_type: 'company'
})
const user2 = await TestHelper.createUser()
let req = TestHelper.createRequest(`/api/user/connect/set-stripe-account-submitted?stripeid=${user.stripeAccount.stripeid}`)
req.account = user2.account
req.session = user2.session
try {
await req.patch()
} catch (error) {
cachedResponses.invalidAccount = error.message
}
// 1) missing payment details
req = TestHelper.createRequest(`/api/user/connect/set-stripe-account-submitted?stripeid=${user.stripeAccount.stripeid}`)
req.account = user.account
req.session = user.session
try {
await req.patch()
} catch (error) {
cachedResponses.companyMissingPaymentDetails = error.message
}
const companyBankingData = TestStripeAccounts.createBankingData(user.stripeAccount.stripeObject.business_type, user.profile, user.stripeAccount.stripeObject.country)
await TestHelper.createExternalAccount(user, companyBankingData)
await TestStripeAccounts.waitForAccountFieldToLeave(user, 'external_account')
// 2) missing submitted owners
try {
await req.patch()
} catch (error) {
cachedResponses.companyMissingOwners = error.message
}
await TestHelper.submitCompanyOwners(user)
// 3) missing submitted directors
try {
await req.patch()
} catch (error) {
cachedResponses.companyMissingDirectors = error.message
}
await TestHelper.submitCompanyDirectors(user)
// 4) missing submitted executives
try {
await req.patch()
} catch (error) {
cachedResponses.companyMissingExecutives = error.message
}
await TestHelper.submitCompanyExecutives(user)
// 5) missing person details
await TestHelper.createPerson(user, {
relationship_representative: 'true',
relationship_executive: user.stripeAccount.requiresExecutives ? 'true' : undefined,
relationship_title: 'SVP Testing',
relationship_percent_ownership: 0
})
req = TestHelper.createRequest(`/api/user/connect/set-stripe-account-submitted?stripeid=${user.stripeAccount.stripeid}`)
req.account = user.account
req.session = user.session
try {
await req.patch()
} catch (error) {
cachedResponses.invalidPerson = error.message
}
await TestStripeAccounts.waitForWebhook('person.created', (stripeEvent) => {
return stripeEvent.data.object.id === user.representative.personid
})
await TestStripeAccounts.waitForPersonField(user, 'representative', 'first_name')
const representativeData = TestStripeAccounts.createPersonData(TestHelper.nextIdentity(), user.stripeAccount.stripeObject.country, user.representative.stripeObject)
await TestHelper.updatePerson(user, user.representative, representativeData)
await TestStripeAccounts.waitForPersonField(user, 'representative', 'verification.document')
const representativeUploadData = TestStripeAccounts.createPersonUploadData(user.representative.stripeObject)
if (representativeUploadData && Object.keys(representativeUploadData).length) {
await TestHelper.updatePerson(user, user.representative, {}, representativeUploadData)
await TestStripeAccounts.waitForPersonFieldToLeave(user, 'representative', 'verification.document')
}
// 6) missing registration details
try {
await req.patch()
} catch (error) {
cachedResponses.companyMissingRegistrationDetails = error.message
}
await TestStripeAccounts.waitForAccountField(user, 'company.name')
const companyAccountData = TestStripeAccounts.createAccountData(user.profile, user.stripeAccount.stripeObject.country, user.stripeAccount.stripeObject)
await TestHelper.updateStripeAccount(user, companyAccountData)
// 7) submitted company
cachedResponses.submittedCompany = await req.patch()
// 8) company is already submitted
try {
await req.patch()
} catch (error) {
cachedResponses.companyAlreadySubmitted = error.message
}
// individual
await TestHelper.createStripeAccount(user, {
country: 'DE',
business_type: 'individual'
})
req = TestHelper.createRequest(`/api/user/connect/set-stripe-account-submitted?stripeid=${user.stripeAccount.stripeid}`)
req.account = user.account
req.session = user.session
// 1) missing banking details
try {
await req.patch()
} catch (error) {
cachedResponses.individualMissingPaymentDetails = error.message
}
const individualBankingData = TestStripeAccounts.createBankingData(user.stripeAccount.stripeObject.business_type, user.profile, user.stripeAccount.stripeObject.country)
await TestHelper.createExternalAccount(user, individualBankingData)
await TestStripeAccounts.waitForAccountFieldToLeave(user, 'external_account')
// 2) missing registration details
try {
await req.patch()
} catch (error) {
cachedResponses.individualMissingRegistrationDetails = error.message
}
const individualAccountData = TestStripeAccounts.createAccountData(user.profile, user.stripeAccount.stripeObject.country, user.stripeAccount.stripeObject)
await TestHelper.updateStripeAccount(user, individualAccountData)
// 3) submitted individual
req.filename = __filename
req.saveResponse = true
cachedResponses.submittedIndividual = await req.patch()
// 4) already submitted
try {
await req.patch()
} catch (error) {
cachedResponses.individualAlreadySubmitted = error.message
}
cachedResponses.finished = true
}
describe('exceptions', () => {
describe('invalid-stripeid', () => {
it('missing querystring stripeid', async function () {
await bundledData(this.test.currentRetry())
const user = await TestHelper.createUser()
const req = TestHelper.createRequest('/api/user/connect/set-stripe-account-submitted')
req.account = user.account
req.session = user.session
let errorMessage
try {
await req.patch()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-stripeid')
})
it('invalid querystring stripeid', async function () {
await bundledData(this.test.currentRetry())
const user = await TestHelper.createUser()
const req = TestHelper.createRequest('/api/user/connect/set-stripe-account-submitted?stripeid=invalid')
req.account = user.account
req.session = user.session
let errorMessage
try {
await req.patch()
} catch (error) {
errorMessage = error.message
}
assert.strictEqual(errorMessage, 'invalid-stripeid')
})
})
describe('invalid-stripe-account', () => {
it('ineligible Stripe company account is submitted', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.companyAlreadySubmitted
assert.strictEqual(errorMessage, 'invalid-stripe-account')
})
it('ineligible Stripe individual account is submitted', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.individualAlreadySubmitted
assert.strictEqual(errorMessage, 'invalid-stripe-account')
})
})
describe('invalid-account', () => {
it('ineligible accessing account', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.invalidAccount
assert.strictEqual(errorMessage, 'invalid-account')
})
})
describe('invalid-payment-details', () => {
it('ineligible Stripe company account missing payment details', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.companyMissingPaymentDetails
assert.strictEqual(errorMessage, 'invalid-payment-details')
})
it('ineligible Stripe individual account missing payment details', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.individualMissingPaymentDetails
assert.strictEqual(errorMessage, 'invalid-payment-details')
})
})
describe('invalid-registration', () => {
it('ineligible Stripe company account missing information', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.companyMissingRegistrationDetails
assert.strictEqual(errorMessage, 'invalid-registration')
})
it('ineligible Stripe individual account missing information', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.individualMissingRegistrationDetails
assert.strictEqual(errorMessage, 'invalid-registration')
})
})
describe('invalid-person', () => {
it('ineligible company person missing information', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.invalidPerson
assert.strictEqual(errorMessage, 'invalid-person')
})
})
describe('invalid-company-owner', () => {
it('ineligible company owners not submitted', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.companyMissingOwners
assert.strictEqual(errorMessage, 'invalid-company-owner')
})
})
describe('invalid-director', () => {
it('ineligible company directors not submitted', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.companyMissingDirectors
assert.strictEqual(errorMessage, 'invalid-company-director')
})
})
describe('invalid-executive', () => {
it('ineligible company executives not submitted', async function () {
await bundledData(this.test.currentRetry())
const errorMessage = cachedResponses.companyMissingExecutives
assert.strictEqual(errorMessage, 'invalid-company-executive')
})
})
})
describe('returns', () => {
it('object (individual)', async () => {
const stripeAccountNow = cachedResponses.submittedIndividual
assert.notStrictEqual(stripeAccountNow.submittedAt, undefined)
assert.notStrictEqual(stripeAccountNow.submittedAt, null)
})
it('object (company)', async () => {
const stripeAccountNow = cachedResponses.submittedCompany
assert.notStrictEqual(stripeAccountNow.submittedAt, undefined)
assert.notStrictEqual(stripeAccountNow.submittedAt, null)
})
})
})