Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 99 additions & 2 deletions apps/api/src/__tests__/Account.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,15 +435,16 @@ describe('AccountModule', () => {
mockDb.Aggregate = jest
.fn()
.mockResolvedValueOnce([{ id: 'acct_z_connected' }])
.mockResolvedValueOnce([{ account: 'acct_z_connected' }]);
.mockResolvedValueOnce([{ account: 'acct_z_connected' }])
.mockResolvedValueOnce([]);
mockDb.Get = jest.fn().mockResolvedValue(matched);

const result = await accountModule.SearchAccounts(
'acct_z_platform',
'kurniawan.anto7@gmail.com'
);

expect(mockDb.Aggregate).toHaveBeenCalledTimes(2);
expect(mockDb.Aggregate).toHaveBeenCalledTimes(3);
expect(mockDb.Aggregate).toHaveBeenNthCalledWith(
1,
'Accounts',
Expand All @@ -454,6 +455,11 @@ describe('AccountModule', () => {
'Persons',
expect.any(Array)
);
expect(mockDb.Aggregate).toHaveBeenNthCalledWith(
3,
'VerificationSessions',
expect.any(Array)
);
expect(result.data).toEqual([matched]);
expect(result.has_more).toBe(false);
expect(result.url).toBe('/v1/accounts/search');
Expand Down Expand Up @@ -484,5 +490,96 @@ describe('AccountModule', () => {
expect(result.data).toHaveLength(1);
expect(result.has_more).toBe(true);
});

it('should return the related account for a verification session id', async () => {
const matched = {
id: 'acct_z_connected',
created: 1700000000,
platform_account: 'acct_z_platform',
} as Account;

mockDb.Aggregate = jest
.fn()
.mockResolvedValueOnce([])
.mockResolvedValueOnce([])
.mockResolvedValueOnce([{ related_account: 'acct_z_connected' }]);
mockDb.Get = jest.fn().mockResolvedValue(matched);

const result = await accountModule.SearchAccounts(
'acct_z_platform',
'21d342ef-6572-47ee-b305-39b233c0d2a2'
);

expect(mockDb.Aggregate).toHaveBeenNthCalledWith(
3,
'VerificationSessions',
[
{
$match: {
platform_account: 'acct_z_platform',
$or: [
{
id: {
$regex: '21d342ef-6572-47ee-b305-39b233c0d2a2',
$options: 'i',
},
},
{
provider_session_id: {
$regex: '21d342ef-6572-47ee-b305-39b233c0d2a2',
$options: 'i',
},
},
],
},
},
{ $project: { related_account: 1 } },
{ $limit: 11 },
]
);
expect(result.data).toEqual([matched]);
expect(result.has_more).toBe(false);
});

it('should return the related account for a Zoneless verification session id', async () => {
const matched = {
id: 'acct_z_connected',
created: 1700000000,
platform_account: 'acct_z_platform',
} as Account;

mockDb.Aggregate = jest
.fn()
.mockResolvedValueOnce([])
.mockResolvedValueOnce([])
.mockResolvedValueOnce([{ related_account: 'acct_z_connected' }]);
mockDb.Get = jest.fn().mockResolvedValue(matched);

const result = await accountModule.SearchAccounts(
'acct_z_platform',
'vs_z_abc123'
);

expect(mockDb.Aggregate).toHaveBeenNthCalledWith(
3,
'VerificationSessions',
expect.arrayContaining([
expect.objectContaining({
$match: expect.objectContaining({
$or: [
{ id: { $regex: 'vs_z_abc123', $options: 'i' } },
{
provider_session_id: {
$regex: 'vs_z_abc123',
$options: 'i',
},
},
],
}),
}),
])
);
expect(result.data).toEqual([matched]);
});
});
});
31 changes: 27 additions & 4 deletions apps/api/src/modules/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,12 @@ export class AccountModule {
}

/**
* Search connected accounts by email, name, or account id.
* Zoneless extension — not part of Stripe's Accounts API.
* Search connected accounts by email, name, account id, or verification
* session id. Zoneless extension — not part of Stripe's Accounts API.
*
* Matches account email / business name / display name / id, plus person
* email / first name / last name / full name under the same platform.
* Matches account email / business name / display name / id, person
* email / first name / last name / full name, and verification sessions
* (`vs_z...` or Didit `provider_session_id`) under the same platform.
*/
async SearchAccounts(
platformAccountId: string,
Expand Down Expand Up @@ -504,6 +505,28 @@ export class AccountModule {
}
}

if (accountIds.size <= limit) {
const sessionHits = await this.db.Aggregate<{ related_account: string }>(
'VerificationSessions',
[
{
$match: {
platform_account: platformAccountId,
$or: [{ id: regex }, { provider_session_id: regex }],
},
},
{ $project: { related_account: 1 } },
{ $limit: limit + 1 },
]
);

for (const hit of sessionHits) {
if (hit.related_account && hit.related_account !== platformAccountId) {
accountIds.add(hit.related_account);
}
}
}

const orderedIds = [...accountIds];
const hasMore = orderedIds.length > limit;
const pageIds = orderedIds.slice(0, limit);
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/app/data/services/account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ export class AccountService {
}

/**
* Search connected accounts by email, name, or account id.
* Search connected accounts by email, name, account id, or
* verification session id (`vs_z...` or Didit session id).
*/
async SearchConnectedAccounts(
query: string,
Expand Down
Loading