@volter/twin-stripe 0.1.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -27
- package/client/dashboard-api.ts +286 -0
- package/client/stripe-mirror.css +272 -159
- package/client/stripe-mirror.tsx +1384 -541
- package/dist/client/dashboard-api.d.ts +107 -0
- package/dist/client/dashboard-api.js +238 -0
- package/dist/client/dashboard-api.ts +286 -0
- package/dist/client/stripe-mirror.bundle.js +236 -0
- package/dist/client/stripe-mirror.css +275 -0
- package/dist/client/stripe-mirror.d.ts +134 -0
- package/dist/client/stripe-mirror.js +823 -0
- package/dist/client/stripe-mirror.tsx +1534 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +39 -0
- package/dist/src/generated/events.gen.json +1 -0
- package/dist/src/generated/surface.gen.json +1 -0
- package/dist/src/generated/ui.gen.json +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +75 -0
- package/dist/src/manifest.d.ts +2 -0
- package/dist/src/manifest.js +1070 -0
- package/dist/src/screens/checkout.d.ts +31 -0
- package/dist/src/screens/checkout.js +255 -0
- package/dist/src/screens/connect-oauth.d.ts +27 -0
- package/dist/src/screens/connect-oauth.js +414 -0
- package/dist/src/screens/connect-settings.d.ts +22 -0
- package/dist/src/screens/connect-settings.js +103 -0
- package/dist/src/screens/consent-skin.d.ts +4 -0
- package/dist/src/screens/consent-skin.js +18 -0
- package/dist/src/screens/financial-connections.d.ts +5 -0
- package/dist/src/screens/financial-connections.js +90 -0
- package/dist/src/screens/identity.d.ts +5 -0
- package/dist/src/screens/identity.js +86 -0
- package/dist/src/screens/industries.d.ts +1 -0
- package/dist/src/screens/industries.js +267 -0
- package/dist/src/screens/onboarding.d.ts +13 -0
- package/dist/src/screens/onboarding.js +225 -0
- package/dist/src/screens/portal.d.ts +5 -0
- package/dist/src/screens/portal.js +216 -0
- package/dist/src/screens/public-details.d.ts +5 -0
- package/dist/src/screens/public-details.js +90 -0
- package/dist/src/semantics/after-payment.d.ts +22 -0
- package/dist/src/semantics/after-payment.js +99 -0
- package/dist/src/semantics/apps-secrets.d.ts +2 -0
- package/dist/src/semantics/apps-secrets.js +54 -0
- package/dist/src/semantics/balance.d.ts +11 -0
- package/dist/src/semantics/balance.js +195 -0
- package/dist/src/semantics/billing.d.ts +2 -0
- package/dist/src/semantics/billing.js +220 -0
- package/dist/src/semantics/charges.d.ts +28 -0
- package/dist/src/semantics/charges.js +209 -0
- package/dist/src/semantics/checkout.d.ts +15 -0
- package/dist/src/semantics/checkout.js +316 -0
- package/dist/src/semantics/connect.d.ts +5 -0
- package/dist/src/semantics/connect.js +493 -0
- package/dist/src/semantics/coupons.d.ts +6 -0
- package/dist/src/semantics/coupons.js +92 -0
- package/dist/src/semantics/credit-notes.d.ts +2 -0
- package/dist/src/semantics/credit-notes.js +172 -0
- package/dist/src/semantics/customers.d.ts +6 -0
- package/dist/src/semantics/customers.js +429 -0
- package/dist/src/semantics/disputes.d.ts +2 -0
- package/dist/src/semantics/disputes.js +51 -0
- package/dist/src/semantics/entitlements.d.ts +2 -0
- package/dist/src/semantics/entitlements.js +95 -0
- package/dist/src/semantics/ephemeral-keys.d.ts +2 -0
- package/dist/src/semantics/ephemeral-keys.js +34 -0
- package/dist/src/semantics/files.d.ts +2 -0
- package/dist/src/semantics/files.js +125 -0
- package/dist/src/semantics/invoices.d.ts +18 -0
- package/dist/src/semantics/invoices.js +545 -0
- package/dist/src/semantics/issuing.d.ts +13 -0
- package/dist/src/semantics/issuing.js +575 -0
- package/dist/src/semantics/ledger.d.ts +59 -0
- package/dist/src/semantics/ledger.js +200 -0
- package/dist/src/semantics/payment-intents.d.ts +18 -0
- package/dist/src/semantics/payment-intents.js +404 -0
- package/dist/src/semantics/payment-links.d.ts +2 -0
- package/dist/src/semantics/payment-links.js +133 -0
- package/dist/src/semantics/payment-methods.d.ts +20 -0
- package/dist/src/semantics/payment-methods.js +140 -0
- package/dist/src/semantics/plans.d.ts +5 -0
- package/dist/src/semantics/plans.js +121 -0
- package/dist/src/semantics/platform.d.ts +9 -0
- package/dist/src/semantics/platform.js +206 -0
- package/dist/src/semantics/products.d.ts +2 -0
- package/dist/src/semantics/products.js +140 -0
- package/dist/src/semantics/radar.d.ts +2 -0
- package/dist/src/semantics/radar.js +83 -0
- package/dist/src/semantics/refunds.d.ts +9 -0
- package/dist/src/semantics/refunds.js +195 -0
- package/dist/src/semantics/renewals.d.ts +47 -0
- package/dist/src/semantics/renewals.js +251 -0
- package/dist/src/semantics/setup-intents.d.ts +2 -0
- package/dist/src/semantics/setup-intents.js +84 -0
- package/dist/src/semantics/shared.d.ts +82 -0
- package/dist/src/semantics/shared.js +203 -0
- package/dist/src/semantics/subscription-schedules.d.ts +2 -0
- package/dist/src/semantics/subscription-schedules.js +119 -0
- package/dist/src/semantics/subscriptions.d.ts +11 -0
- package/dist/src/semantics/subscriptions.js +605 -0
- package/dist/src/semantics/tax.d.ts +2 -0
- package/dist/src/semantics/tax.js +197 -0
- package/dist/src/semantics/terminal.d.ts +5 -0
- package/dist/src/semantics/terminal.js +182 -0
- package/dist/src/semantics/test-cards.d.ts +4 -0
- package/dist/src/semantics/test-cards.js +7 -0
- package/dist/src/semantics/test-clocks.d.ts +6 -0
- package/dist/src/semantics/test-clocks.js +73 -0
- package/dist/src/semantics/tokens.d.ts +4 -0
- package/dist/src/semantics/tokens.js +44 -0
- package/dist/src/semantics/transfers.d.ts +2 -0
- package/dist/src/semantics/transfers.js +154 -0
- package/dist/src/semantics/treasury.d.ts +2 -0
- package/dist/src/semantics/treasury.js +377 -0
- package/dist/src/semantics/webhook-endpoints.d.ts +3 -0
- package/dist/src/semantics/webhook-endpoints.js +85 -0
- package/dist/src/stripe-budget.d.ts +55 -0
- package/dist/src/stripe-budget.js +155 -0
- package/dist/src/stripe-capabilities.d.ts +3 -0
- package/dist/src/stripe-capabilities.js +5695 -0
- package/dist/src/stripe-conformance.d.ts +43 -0
- package/dist/src/stripe-conformance.js +105 -0
- package/dist/src/stripe-connector.d.ts +161 -0
- package/dist/src/stripe-connector.js +414 -0
- package/dist/src/stripe-emit.d.ts +2 -0
- package/dist/src/stripe-emit.js +145 -0
- package/dist/src/stripe-events.d.ts +93 -0
- package/dist/src/stripe-events.js +392 -0
- package/dist/src/stripe-js.d.ts +4 -0
- package/dist/src/stripe-js.js +70 -0
- package/dist/src/stripe-mirror-ui.d.ts +15 -0
- package/dist/src/stripe-mirror-ui.js +87 -0
- package/dist/src/stripe-params.d.ts +3 -0
- package/dist/src/stripe-params.js +43 -0
- package/dist/src/stripe-perform-harness.d.ts +9 -0
- package/dist/src/stripe-perform-harness.js +26 -0
- package/dist/src/stripe-server.d.ts +33 -0
- package/dist/src/stripe-server.js +393 -0
- package/dist/src/stripe-shared.d.ts +109 -0
- package/dist/src/stripe-shared.js +276 -0
- package/dist/src/stripe-twin.d.ts +155 -0
- package/dist/src/stripe-twin.js +1232 -0
- package/dist/src/stripe-ui-conformance.d.ts +5 -0
- package/dist/src/stripe-ui-conformance.js +79 -0
- package/dist/src/stripe-ui-structure.d.ts +3 -0
- package/dist/src/stripe-ui-structure.js +168 -0
- package/dist/src/stripe-version.d.ts +12 -0
- package/dist/src/stripe-version.js +287 -0
- package/dist/test-fixtures/stripe-known-deviations.json +110 -0
- package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +14 -0
- package/dist/test-fixtures/stripe-openapi-operations.json +4717 -0
- package/dist/test-fixtures/stripe-schemas.SOURCE.md +35 -0
- package/dist/test-fixtures/stripe-schemas.json +3813 -0
- package/package.json +18 -10
- package/src/cli.ts +7 -7
- package/src/generated/events.gen.json +1 -0
- package/src/generated/surface.gen.json +1 -0
- package/src/generated/ui.gen.json +1 -0
- package/src/index.ts +34 -10
- package/src/manifest.ts +1102 -0
- package/src/screens/checkout.tsx +267 -0
- package/src/screens/connect-oauth.tsx +400 -0
- package/src/screens/connect-settings.tsx +121 -0
- package/src/screens/consent-skin.ts +20 -0
- package/src/screens/financial-connections.tsx +101 -0
- package/src/screens/identity.tsx +96 -0
- package/src/screens/industries.ts +267 -0
- package/src/screens/onboarding.tsx +243 -0
- package/src/screens/portal.tsx +220 -0
- package/src/screens/public-details.tsx +105 -0
- package/src/semantics/after-payment.ts +118 -0
- package/src/semantics/apps-secrets.ts +58 -0
- package/src/semantics/balance.ts +209 -0
- package/src/semantics/billing.ts +216 -0
- package/src/semantics/charges.ts +220 -0
- package/src/semantics/checkout.ts +310 -0
- package/src/semantics/connect.ts +487 -0
- package/src/semantics/coupons.ts +97 -0
- package/src/semantics/credit-notes.ts +168 -0
- package/src/semantics/customers.ts +432 -0
- package/src/semantics/disputes.ts +62 -0
- package/src/semantics/entitlements.ts +94 -0
- package/src/semantics/ephemeral-keys.ts +34 -0
- package/src/semantics/files.ts +143 -0
- package/src/semantics/invoices.ts +545 -0
- package/src/semantics/issuing.ts +590 -0
- package/src/semantics/ledger.ts +253 -0
- package/src/semantics/payment-intents.ts +420 -0
- package/src/semantics/payment-links.ts +148 -0
- package/src/semantics/payment-methods.ts +145 -0
- package/src/semantics/plans.ts +131 -0
- package/src/semantics/platform.ts +220 -0
- package/src/semantics/products.ts +154 -0
- package/src/semantics/radar.ts +85 -0
- package/src/semantics/refunds.ts +218 -0
- package/src/semantics/renewals.ts +274 -0
- package/src/semantics/setup-intents.ts +87 -0
- package/src/semantics/shared.ts +226 -0
- package/src/semantics/subscription-schedules.ts +129 -0
- package/src/semantics/subscriptions.ts +610 -0
- package/src/semantics/tax.ts +220 -0
- package/src/semantics/terminal.ts +195 -0
- package/src/semantics/test-cards.ts +7 -0
- package/src/semantics/test-clocks.ts +77 -0
- package/src/semantics/tokens.ts +52 -0
- package/src/semantics/transfers.ts +174 -0
- package/src/semantics/treasury.ts +383 -0
- package/src/semantics/webhook-endpoints.ts +87 -0
- package/src/stripe-budget.ts +4 -4
- package/src/stripe-capabilities.ts +2258 -380
- package/src/stripe-conformance.ts +19 -7
- package/src/stripe-connector.ts +68 -40
- package/src/stripe-emit.ts +15 -8
- package/src/stripe-events.ts +102 -40
- package/src/stripe-js.ts +70 -0
- package/src/stripe-mirror-ui.ts +28 -298
- package/src/stripe-params.ts +44 -0
- package/src/stripe-perform-harness.ts +29 -0
- package/src/stripe-server.ts +318 -38
- package/src/stripe-shared.ts +297 -0
- package/src/stripe-twin.ts +434 -5325
- package/src/stripe-ui-conformance.ts +70 -107
- package/src/stripe-ui-structure.ts +124 -348
- package/src/stripe-version.ts +281 -0
- package/test-fixtures/stripe-known-deviations.json +8 -8
- package/test-fixtures/stripe-openapi-operations.json +1188 -2855
- package/test-fixtures/stripe-schemas.json +85 -12
- package/src/stripe-form.ts +0 -35
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// STRIPE FINANCIAL CONNECTIONS — a hosted flow (docs/contributing/architecture.md, "Screens"): an application
|
|
2
|
+
// creates a Financial Connections Session for an account holder and the permissions it wants, and hands its
|
|
3
|
+
// client_secret to Stripe.js, whose authentication flow lets the holder pick their bank and the accounts to share;
|
|
4
|
+
// the linked accounts are then on the session and readable through the Accounts API, with balances and
|
|
5
|
+
// transactions when the permissions asked for them (docs.stripe.com/financial-connections/fundamentals,
|
|
6
|
+
// docs.stripe.com/financial-connections/other-data-powered-products). In test mode the bank is Stripe's test
|
|
7
|
+
// institution (docs.stripe.com/financial-connections/testing). Linking is the holder's move, not an API call.
|
|
8
|
+
// Authored from @volter/world-ui's consent piece under Stripe's skin; nothing of Stripe's page is copied.
|
|
9
|
+
//
|
|
10
|
+
// The flow is opened with the session's client_secret, never its id: the session's own documentation says "The
|
|
11
|
+
// session's client_secret can be used to launch the flow using Stripe.js", and stripe.collectFinancialConnectionsAccounts
|
|
12
|
+
// takes `clientSecret`, "The client_secret of the Financial Connections Session"
|
|
13
|
+
// (docs.stripe.com/js/financial_connections/collect_financial_connections_accounts).
|
|
14
|
+
// Where the documentation stops and the twin decides: the page is served at
|
|
15
|
+
// js.stripe.com/v3/financial-connections/{client_secret}, the twin's address for the "on-page modal UI" Stripe.js
|
|
16
|
+
// loads (the twin's stripe.js navigates there, ../stripe-js.ts); an id or an unknown secret finds no flow;
|
|
17
|
+
// the test institution holds a checking account (•••• 6789) and a savings account (•••• 4321); a linked account's
|
|
18
|
+
// balance and its three transactions are the twin's test data.
|
|
19
|
+
import { semanticsContext, type SemanticsContext } from '@volter/world-core';
|
|
20
|
+
import { Consent, CONSENT_CSS, flowPage } from '@volter/world-ui';
|
|
21
|
+
import surface from '../generated/surface.gen.json' with { type: 'json' };
|
|
22
|
+
import { manifest } from '../manifest.ts';
|
|
23
|
+
import { created, type Row } from '../semantics/shared.ts';
|
|
24
|
+
import { formOf, seeOther, STRIPE_CONSENT_SKIN } from './consent-skin.ts';
|
|
25
|
+
|
|
26
|
+
const FCS = 'financial_connections.session';
|
|
27
|
+
const FCA = 'financial_connections.account';
|
|
28
|
+
const FCT = 'financial_connections.transaction';
|
|
29
|
+
const OPERATION = (surface.operations as Array<{ id: string; method: string; path: string; class: string }>).find((o) => o.id === 'GetFinancialConnectionsSessionsSession')!;
|
|
30
|
+
const css = [CONSENT_CSS, STRIPE_CONSENT_SKIN];
|
|
31
|
+
|
|
32
|
+
const TEST_ACCOUNTS = [
|
|
33
|
+
{ last4: '6789', subcategory: 'checking', display_name: 'Checking', balance: 125_000 },
|
|
34
|
+
{ last4: '4321', subcategory: 'savings', display_name: 'Savings', balance: 980_000 },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const message = (status: number, text: string): Response => flowPage({ title: 'Link your account', status, css, body: <main className="consent"><h1 className="consent-heading">{text}</h1></main> });
|
|
38
|
+
const permissionsOf = (session: Row): string[] => (Array.isArray(session.permissions) ? session.permissions.map(String) : []);
|
|
39
|
+
|
|
40
|
+
function page(secret: string, session: Row): Response {
|
|
41
|
+
const says: Record<string, string> = { balances: 'Account balances', ownership: 'Account ownership details', payment_method: 'Account and routing numbers', transactions: 'Transactions' };
|
|
42
|
+
return flowPage({
|
|
43
|
+
title: 'Link your account',
|
|
44
|
+
css,
|
|
45
|
+
body: (
|
|
46
|
+
<Consent
|
|
47
|
+
heading="Link your account"
|
|
48
|
+
app={{ name: 'Test Institution' }}
|
|
49
|
+
request={`Test mode · ${TEST_ACCOUNTS.map((a) => `${a.display_name} •••• ${a.last4}`).join(', ')}`}
|
|
50
|
+
permissions={permissionsOf(session).map((p) => ({ title: says[p] ?? p, detail: 'Shared with the business through Stripe' }))}
|
|
51
|
+
action={`/v3/financial-connections/${secret}`}
|
|
52
|
+
fields={{}}
|
|
53
|
+
deny={{ name: 'answer', value: 'cancel', label: 'Cancel' }}
|
|
54
|
+
allow={{ name: 'answer', value: 'link', label: 'Link accounts' }}
|
|
55
|
+
note="Stripe will share the data above with the business."
|
|
56
|
+
/>
|
|
57
|
+
),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The holder links the test institution's accounts to the session. */
|
|
62
|
+
async function link(ctx: SemanticsContext, id: string, session: Row): Promise<void> {
|
|
63
|
+
const permissions = permissionsOf(session);
|
|
64
|
+
const now = Number(ctx.now());
|
|
65
|
+
for (const a of TEST_ACCOUNTS) {
|
|
66
|
+
const account = await created(ctx, FCA, { session: id }, {
|
|
67
|
+
livemode: false, status: 'active', account_holder: session.account_holder ?? null, institution_name: 'StripeBank', display_name: a.display_name,
|
|
68
|
+
last4: a.last4, category: 'cash', subcategory: a.subcategory, supported_payment_method_types: ['us_bank_account', 'link'], permissions,
|
|
69
|
+
balance: permissions.includes('balances') ? { as_of: now, type: 'cash', cash: { available: { usd: a.balance } }, current: { usd: a.balance } } : null,
|
|
70
|
+
balance_refresh: permissions.includes('balances') ? { status: 'succeeded', last_attempted_at: now, next_refresh_available_at: null } : null,
|
|
71
|
+
transaction_refresh: permissions.includes('transactions') ? { id: `fctxnref_${a.last4}`, status: 'succeeded', last_attempted_at: now, next_refresh_available_at: null } : null,
|
|
72
|
+
ownership: null, ownership_refresh: null, subscriptions: [],
|
|
73
|
+
});
|
|
74
|
+
if (!permissions.includes('transactions')) continue;
|
|
75
|
+
for (const [i, t] of [{ amount: -4_250, description: 'COFFEE ROASTERS' }, { amount: -12_000, description: 'CITY UTILITIES' }, { amount: 250_000, description: 'PAYROLL DEPOSIT' }].entries()) {
|
|
76
|
+
await created(ctx, FCT, { account: account.id }, {
|
|
77
|
+
livemode: false, amount: t.amount, currency: 'usd', description: t.description, status: 'posted',
|
|
78
|
+
status_transitions: { posted_at: now - (3 - i) * 86_400, void_at: null }, transacted_at: now - (3 - i) * 86_400, transaction_refresh: `fctxnref_${a.last4}`, updated: now,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The authentication flow Stripe.js opens, or undefined for any other request. */
|
|
85
|
+
export function stripeFinancialConnectionsFlow(scope: { root?: string; clock?: () => string }): (request: Request) => Promise<Response | undefined> {
|
|
86
|
+
return async (request) => {
|
|
87
|
+
const m = /^\/v3\/financial-connections\/([A-Za-z0-9_]+)\/?$/.exec(new URL(request.url).pathname);
|
|
88
|
+
if (!m || (request.method !== 'GET' && request.method !== 'POST')) return undefined;
|
|
89
|
+
const secret = m[1]!;
|
|
90
|
+
const form = request.method === 'POST' ? await formOf(request.clone()) : {};
|
|
91
|
+
const ctx = await semanticsContext(manifest, request, OPERATION, scope);
|
|
92
|
+
const session = ctx.rows(FCS).find((s) => s.client_secret === secret);
|
|
93
|
+
if (!session) return message(404, 'This link has expired.');
|
|
94
|
+
const id = String(session.id);
|
|
95
|
+
if (ctx.rows(FCA).some((a) => a.session === id)) return message(200, 'Your accounts are linked.');
|
|
96
|
+
if (request.method === 'GET') return page(secret, session);
|
|
97
|
+
if (form.answer === 'link') await link(ctx, id, session);
|
|
98
|
+
const back = typeof session.return_url === 'string' && session.return_url ? session.return_url : undefined;
|
|
99
|
+
return back ? seeOther(back) : message(200, form.answer === 'link' ? 'Your accounts are linked.' : 'No accounts were linked.');
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// STRIPE IDENTITY — a hosted flow (docs/contributing/architecture.md, "Screens"): an application creates a
|
|
2
|
+
// VerificationSession and sends the person to its `url` at verify.stripe.com/start/{session}; they submit their
|
|
3
|
+
// ID there, the session is processing and then verified, with a VerificationReport of what was checked, or it
|
|
4
|
+
// asks for input again with the reason in last_error; the person returns to return_url
|
|
5
|
+
// (docs.stripe.com/identity/how-sessions-work, docs.stripe.com/identity/verification-sessions). In test mode the
|
|
6
|
+
// page offers the outcome instead of a camera (the twin's decision: docs.stripe.com/identity/testing, once cited
|
|
7
|
+
// for it, no longer answers). Submitting is the person's move
|
|
8
|
+
// (the session machine's external actor) and the check Stripe's (its vendor moves). Authored from
|
|
9
|
+
// @volter/world-ui's consent piece under Stripe's skin; nothing of Stripe's page is copied.
|
|
10
|
+
//
|
|
11
|
+
// Where the documentation stops and the twin decides: test mode's person is Jenny Rosen with a driving licence,
|
|
12
|
+
// the name Stripe's test reports carry; a failed check is document_unverified_other.
|
|
13
|
+
import { semanticsContext, type SemanticsContext } from '@volter/world-core';
|
|
14
|
+
import { Consent, CONSENT_CSS, flowPage } from '@volter/world-ui';
|
|
15
|
+
import surface from '../generated/surface.gen.json' with { type: 'json' };
|
|
16
|
+
import { manifest } from '../manifest.ts';
|
|
17
|
+
import { created, type Row } from '../semantics/shared.ts';
|
|
18
|
+
import { formOf, seeOther, STRIPE_CONSENT_SKIN } from './consent-skin.ts';
|
|
19
|
+
|
|
20
|
+
const VS = 'identity.verification_session';
|
|
21
|
+
const OPERATION = (surface.operations as Array<{ id: string; method: string; path: string; class: string }>).find((o) => o.id === 'GetIdentityVerificationSessionsSession')!;
|
|
22
|
+
const css = [CONSENT_CSS, STRIPE_CONSENT_SKIN];
|
|
23
|
+
|
|
24
|
+
const message = (status: number, text: string): Response => flowPage({ title: 'Stripe Identity', status, css, body: <main className="consent"><h1 className="consent-heading">{text}</h1></main> });
|
|
25
|
+
|
|
26
|
+
function page(id: string, session: Row): Response {
|
|
27
|
+
const kind = session.type === 'id_number' ? 'ID number' : 'Government-issued photo ID';
|
|
28
|
+
return flowPage({
|
|
29
|
+
title: 'Verify your identity',
|
|
30
|
+
css,
|
|
31
|
+
body: (
|
|
32
|
+
<Consent
|
|
33
|
+
heading="Verify your identity"
|
|
34
|
+
app={{ name: 'Stripe Identity' }}
|
|
35
|
+
request="Test mode: choose how the verification ends"
|
|
36
|
+
permissions={[
|
|
37
|
+
{ title: kind, detail: 'Test data: Jenny Rosen, driving licence' },
|
|
38
|
+
...(((session.options as Row | undefined)?.document as Row | undefined)?.require_matching_selfie === true ? [{ title: 'Selfie', detail: 'Matched to the ID photo' }] : []),
|
|
39
|
+
]}
|
|
40
|
+
action={`/start/${id}`}
|
|
41
|
+
fields={{}}
|
|
42
|
+
deny={{ name: 'outcome', value: 'unverified', label: 'Fail verification' }}
|
|
43
|
+
allow={{ name: 'outcome', value: 'verified', label: 'Verify' }}
|
|
44
|
+
note="Stripe uses this data only to verify your identity."
|
|
45
|
+
/>
|
|
46
|
+
),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The person submits; Stripe checks and writes the report, and the session is verified or asks again. */
|
|
51
|
+
async function submit(ctx: SemanticsContext, id: string, session: Row, verified: boolean): Promise<void> {
|
|
52
|
+
if (ctx.legal(VS, 'status', OPERATION.id, String(session.status), 'processing', id, 'external')) return;
|
|
53
|
+
// Stripe's check decides the processing session at once: the second move of the one write below
|
|
54
|
+
ctx.legal(VS, 'status', OPERATION.id, 'processing', verified ? 'verified' : 'requires_input', id, 'vendor');
|
|
55
|
+
const type = session.type === 'id_number' ? 'id_number' : 'document';
|
|
56
|
+
const error = verified ? null : { code: 'document_unverified_other', reason: 'The document could not be verified.' };
|
|
57
|
+
// a document check holds its images: files "Array of File ids containing images for this document" (the served
|
|
58
|
+
// spec's document report). Where the documentation stops and the twin decides: the licence's front and back, two
|
|
59
|
+
// identity_document files, as the published report's two
|
|
60
|
+
const images: string[] = [];
|
|
61
|
+
if (type === 'document') for (const side of ['front', 'back']) {
|
|
62
|
+
const f = await created(ctx, 'file', { purpose: 'identity_document' }, { filename: `driving_license_${side}.jpg`, size: 0, type: 'jpg', title: null, expires_at: null, url: null, links: { object: 'list', data: [], has_more: false, url: '' } });
|
|
63
|
+
images.push(String(f.id));
|
|
64
|
+
}
|
|
65
|
+
// the report records the options the session was checked with (the session's own, as its object answers them)
|
|
66
|
+
const sessionOptions = (session.options as Row | undefined) ?? {};
|
|
67
|
+
const report = await created(ctx, 'identity.verification_report', { verification_session: id, type }, {
|
|
68
|
+
livemode: false, client_reference_id: session.client_reference_id ?? null,
|
|
69
|
+
document: type === 'document' ? { status: verified ? 'verified' : 'unverified', type: 'driving_license', first_name: 'Jenny', last_name: 'Rosen', address: null, dob: null, error, files: images } : null,
|
|
70
|
+
id_number: type === 'id_number' ? { status: verified ? 'verified' : 'unverified', id_number_type: 'us_ssn', first_name: 'Jenny', last_name: 'Rosen', dob: null, error } : null,
|
|
71
|
+
selfie: null, email: null, phone: null,
|
|
72
|
+
options: type === 'document' ? { document: (sessionOptions.document as Row | undefined) ?? {} } : { id_number: (sessionOptions.id_number as Row | undefined) ?? {} },
|
|
73
|
+
});
|
|
74
|
+
// the url "can only be used once" (the served spec): spent by the submission, as the verification sessions page's
|
|
75
|
+
// examples answer a verified and a requires_input session with url null
|
|
76
|
+
await ctx.write(VS, id, verified
|
|
77
|
+
? { status: 'verified', last_verification_report: report.id, last_error: null, url: null, client_secret: null, verified_outputs: { first_name: 'Jenny', last_name: 'Rosen' } }
|
|
78
|
+
: { status: 'requires_input', last_verification_report: report.id, last_error: error, url: null }, verified ? 'verification_session.verified' : 'verification_session.requires_input');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** verify.stripe.com's pages, or undefined for any other request. */
|
|
82
|
+
export function stripeIdentityFlow(scope: { root?: string; clock?: () => string }): (request: Request) => Promise<Response | undefined> {
|
|
83
|
+
return async (request) => {
|
|
84
|
+
const m = /^\/start\/(vs_[A-Za-z0-9_]+)\/?$/.exec(new URL(request.url).pathname);
|
|
85
|
+
if (!m || (request.method !== 'GET' && request.method !== 'POST')) return undefined;
|
|
86
|
+
const id = m[1]!;
|
|
87
|
+
const form = request.method === 'POST' ? await formOf(request.clone()) : {};
|
|
88
|
+
const ctx = await semanticsContext(manifest, request, OPERATION, scope);
|
|
89
|
+
const session = ctx.get(VS, id);
|
|
90
|
+
if (!session) return message(404, 'This verification link is invalid.');
|
|
91
|
+
if (session.status !== 'requires_input') return message(200, session.status === 'verified' ? 'You are verified.' : 'This verification has ended.');
|
|
92
|
+
if (request.method === 'GET') return page(id, session);
|
|
93
|
+
await submit(ctx, id, session, form.outcome === 'verified');
|
|
94
|
+
return typeof session.return_url === 'string' && session.return_url ? seeOther(session.return_url) : message(200, form.outcome === 'verified' ? 'You are verified.' : 'We could not verify your identity.');
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// The industries the twin's hosted onboarding offers, as merchant category codes. What Stripe documents: "Generally,
|
|
2
|
+
// the industry listed in the Stripe Dashboard for the connected account determines its MCC. For connected accounts ...
|
|
3
|
+
// that use Stripe-hosted onboarding, the MCC is set during onboarding", and its "Merchant category code list" of
|
|
4
|
+
// supported MCCs, of which "You must contact Stripe to use a restricted MCC" (docs.stripe.com/connect/setting-mcc; the
|
|
5
|
+
// page's table, read 2026-09-27, has 292 entries: 3 prohibited, 33 restricted, one of those also internal only).
|
|
6
|
+
// Where the documentation stops and the twin decides: Stripe does not publish the industry picker its onboarding shows,
|
|
7
|
+
// so the twin offers that list's supported codes as the industries, leaving out the prohibited and restricted ones (a
|
|
8
|
+
// person cannot choose a code Stripe must first be asked for), 256 in the page's order with its names; and the page does
|
|
9
|
+
// not group them, so neither does the twin.
|
|
10
|
+
export const INDUSTRIES: ReadonlyArray<readonly [mcc: string, name: string]> = [
|
|
11
|
+
['7623', "A/C, Refrigeration Repair"],
|
|
12
|
+
['8931', "Accounting/Bookkeeping Services"],
|
|
13
|
+
['7311', "Advertising Services"],
|
|
14
|
+
['0763', "Agricultural Cooperative"],
|
|
15
|
+
['4582', "Airports, Flying Fields"],
|
|
16
|
+
['4119', "Ambulance Services"],
|
|
17
|
+
['5937', "Antique Reproductions"],
|
|
18
|
+
['5932', "Antique Shops"],
|
|
19
|
+
['7998', "Aquariums"],
|
|
20
|
+
['8911', "Architectural/Surveying Services"],
|
|
21
|
+
['5971', "Art Dealers and Galleries"],
|
|
22
|
+
['5970', "Artists Supply and Craft Shops"],
|
|
23
|
+
['7531', "Auto Body Repair Shops"],
|
|
24
|
+
['7535', "Auto Paint Shops"],
|
|
25
|
+
['7538', "Auto Service Shops"],
|
|
26
|
+
['5531', "Auto and Home Supply Stores"],
|
|
27
|
+
['5542', "Automated Fuel Dispensers"],
|
|
28
|
+
['8675', "Automobile Associations"],
|
|
29
|
+
['5533', "Automotive Parts and Accessories Stores"],
|
|
30
|
+
['5532', "Automotive Tire Stores"],
|
|
31
|
+
['5462', "Bakeries"],
|
|
32
|
+
['7929', "Bands, Orchestras"],
|
|
33
|
+
['7230', "Barber and Beauty Shops"],
|
|
34
|
+
['5940', "Bicycle Shops"],
|
|
35
|
+
['7932', "Billiard/Pool Establishments"],
|
|
36
|
+
['5551', "Boat Dealers"],
|
|
37
|
+
['4457', "Boat Rentals and Leases"],
|
|
38
|
+
['5942', "Book Stores"],
|
|
39
|
+
['5192', "Books, Periodicals, and Newspapers"],
|
|
40
|
+
['7933', "Bowling Alleys"],
|
|
41
|
+
['4131', "Bus Lines"],
|
|
42
|
+
['8244', "Business/Secretarial Schools"],
|
|
43
|
+
['7278', "Buying/Shopping Services"],
|
|
44
|
+
['4899', "Cable, Satellite, and Other Pay Television and Radio"],
|
|
45
|
+
['5946', "Camera and Photographic Supply Stores"],
|
|
46
|
+
['5441', "Candy, Nut, and Confectionery Stores"],
|
|
47
|
+
['7512', "Car Rental Agencies"],
|
|
48
|
+
['7542', "Car Washes"],
|
|
49
|
+
['5511', "Car and Truck Dealers (New & Used) Sales, Service, Repairs Parts and Leasing"],
|
|
50
|
+
['5521', "Car and Truck Dealers (Used Only) Sales, Service, Repairs Parts and Leasing"],
|
|
51
|
+
['1750', "Carpentry Services"],
|
|
52
|
+
['7217', "Carpet/Upholstery Cleaning"],
|
|
53
|
+
['5811', "Caterers"],
|
|
54
|
+
['5169', "Chemicals and Allied Products (Not Elsewhere Classified)"],
|
|
55
|
+
['8351', "Child Care Services"],
|
|
56
|
+
['5641', "Childrens and Infants Wear Stores"],
|
|
57
|
+
['8049', "Chiropodists, Podiatrists"],
|
|
58
|
+
['8041', "Chiropractors"],
|
|
59
|
+
['8641', "Civic, Social, Fraternal Associations"],
|
|
60
|
+
['7349', "Cleaning and Maintenance"],
|
|
61
|
+
['7296', "Clothing Rental"],
|
|
62
|
+
['8220', "Colleges, Universities"],
|
|
63
|
+
['5046', "Commercial Equipment (Not Elsewhere Classified)"],
|
|
64
|
+
['5139', "Commercial Footwear"],
|
|
65
|
+
['7333', "Commercial Photography, Art and Graphics"],
|
|
66
|
+
['4111', "Commuter Transport, Ferries"],
|
|
67
|
+
['4816', "Computer Network Services"],
|
|
68
|
+
['7372', "Computer Programming"],
|
|
69
|
+
['7379', "Computer Repair"],
|
|
70
|
+
['5734', "Computer Software Stores"],
|
|
71
|
+
['5045', "Computers, Peripherals, and Software"],
|
|
72
|
+
['1771', "Concrete Work Services"],
|
|
73
|
+
['5039', "Construction Materials (Not Elsewhere Classified)"],
|
|
74
|
+
['7392', "Consulting, Public Relations"],
|
|
75
|
+
['8241', "Correspondence Schools"],
|
|
76
|
+
['5977', "Cosmetic Stores"],
|
|
77
|
+
['7277', "Counseling Services"],
|
|
78
|
+
['7997', "Country Clubs"],
|
|
79
|
+
['4215', "Courier Services"],
|
|
80
|
+
['7321', "Credit Reporting Agencies"],
|
|
81
|
+
['5451', "Dairy Products Stores"],
|
|
82
|
+
['7911', "Dance Hall, Studios, Schools"],
|
|
83
|
+
['8021', "Dentists, Orthodontists"],
|
|
84
|
+
['5311', "Department Stores"],
|
|
85
|
+
['7393', "Detective Agencies"],
|
|
86
|
+
['5815', "Digital Goods Media – Books, Movies, Music"],
|
|
87
|
+
['5817', "Digital Goods – Applications (Excludes Games)"],
|
|
88
|
+
['5816', "Digital Goods – Games"],
|
|
89
|
+
['5818', "Digital Goods – Large Digital Goods Merchant"],
|
|
90
|
+
['5964', "Direct Marketing - Catalog Merchant"],
|
|
91
|
+
['5965', "Direct Marketing - Combination Catalog and Retail Merchant"],
|
|
92
|
+
['5960', "Direct Marketing - Insurance Services"],
|
|
93
|
+
['5969', "Direct Marketing - Other"],
|
|
94
|
+
['5968', "Direct Marketing - Subscription"],
|
|
95
|
+
['5310', "Discount Stores"],
|
|
96
|
+
['8011', "Doctors"],
|
|
97
|
+
['5714', "Drapery, Window Covering, and Upholstery Stores"],
|
|
98
|
+
['5813', "Drinking Places"],
|
|
99
|
+
['7216', "Dry Cleaners"],
|
|
100
|
+
['5099', "Durable Goods (Not Elsewhere Classified)"],
|
|
101
|
+
['5309', "Duty Free Stores"],
|
|
102
|
+
['5812', "Eating Places, Restaurants"],
|
|
103
|
+
['8299', "Educational Services"],
|
|
104
|
+
['5997', "Electric Razor Stores"],
|
|
105
|
+
['5552', "Electric Vehicle Charging"],
|
|
106
|
+
['5065', "Electrical Parts and Equipment"],
|
|
107
|
+
['1731', "Electrical Services"],
|
|
108
|
+
['7622', "Electronics Repair Shops"],
|
|
109
|
+
['5732', "Electronics Stores"],
|
|
110
|
+
['8211', "Elementary, Secondary Schools"],
|
|
111
|
+
['9702', "Emergency Services (GCAS) (Visa use only)"],
|
|
112
|
+
['7361', "Employment/Temp Agencies"],
|
|
113
|
+
['7394', "Equipment Rental"],
|
|
114
|
+
['7342', "Exterminating Services"],
|
|
115
|
+
['5651', "Family Clothing Stores"],
|
|
116
|
+
['5814', "Fast Food Restaurants"],
|
|
117
|
+
['5718', "Fireplace, Fireplace Screens, and Accessories Stores"],
|
|
118
|
+
['5713', "Floor Covering Stores"],
|
|
119
|
+
['5992', "Florists"],
|
|
120
|
+
['5193', "Florists Supplies, Nursery Stock, and Flowers"],
|
|
121
|
+
['5422', "Freezer and Locker Meat Provisioners"],
|
|
122
|
+
['5983', "Fuel Dealers (Non Automotive)"],
|
|
123
|
+
['7261', "Funeral Services, Crematories"],
|
|
124
|
+
['7641', "Furniture Repair, Refinishing"],
|
|
125
|
+
['5712', "Furniture, Home Furnishings, and Equipment Stores, Except Appliances"],
|
|
126
|
+
['5681', "Furriers and Fur Shops"],
|
|
127
|
+
['1520', "General Services"],
|
|
128
|
+
['5947', "Gift, Card, Novelty, and Souvenir Shops"],
|
|
129
|
+
['5231', "Glass, Paint, and Wallpaper Stores"],
|
|
130
|
+
['5950', "Glassware, Crystal Stores"],
|
|
131
|
+
['7992', "Golf Courses - Public"],
|
|
132
|
+
['9406', "Government-Owned Lotteries (Non-US region)"],
|
|
133
|
+
['5411', "Grocery Stores, Supermarkets"],
|
|
134
|
+
['5251', "Hardware Stores"],
|
|
135
|
+
['5072', "Hardware, Equipment, and Supplies"],
|
|
136
|
+
['7298', "Health and Beauty Spas"],
|
|
137
|
+
['5975', "Hearing Aids Sales and Supplies"],
|
|
138
|
+
['1711', "Heating, Plumbing, A/C"],
|
|
139
|
+
['5945', "Hobby, Toy, and Game Shops"],
|
|
140
|
+
['5200', "Home Supply Warehouse Stores"],
|
|
141
|
+
['8062', "Hospitals"],
|
|
142
|
+
['7011', "Hotels, Motels, and Resorts"],
|
|
143
|
+
['5722', "Household Appliance Stores"],
|
|
144
|
+
['5085', "Industrial Supplies (Not Elsewhere Classified)"],
|
|
145
|
+
['7375', "Information Retrieval Services"],
|
|
146
|
+
['9950', "Intra-Company Purchases"],
|
|
147
|
+
['5944', "Jewelry Stores, Watches, Clocks, and Silverware Stores"],
|
|
148
|
+
['0780', "Landscaping Services"],
|
|
149
|
+
['7211', "Laundries"],
|
|
150
|
+
['7210', "Laundry, Cleaning Services"],
|
|
151
|
+
['8111', "Legal Services, Attorneys"],
|
|
152
|
+
['5948', "Luggage and Leather Goods Stores"],
|
|
153
|
+
['5211', "Lumber, Building Materials Stores"],
|
|
154
|
+
['4468', "Marinas, Service and Supplies"],
|
|
155
|
+
['1740', "Masonry, Stonework, and Plaster"],
|
|
156
|
+
['7297', "Massage Parlors"],
|
|
157
|
+
['8099', "Medical Services"],
|
|
158
|
+
['8071', "Medical and Dental Labs"],
|
|
159
|
+
['5047', "Medical, Dental, Ophthalmic, and Hospital Equipment and Supplies"],
|
|
160
|
+
['8699', "Membership Organizations"],
|
|
161
|
+
['5611', "Mens and Boys Clothing and Accessories Stores"],
|
|
162
|
+
['5691', "Mens, Womens Clothing Stores"],
|
|
163
|
+
['5051', "Metal Service Centers"],
|
|
164
|
+
['5699', "Miscellaneous Apparel and Accessory Shops"],
|
|
165
|
+
['5599', "Miscellaneous Auto Dealers"],
|
|
166
|
+
['7399', "Miscellaneous Business Services"],
|
|
167
|
+
['5499', "Miscellaneous Food Stores - Convenience Stores and Specialty Markets"],
|
|
168
|
+
['5399', "Miscellaneous General Merchandise"],
|
|
169
|
+
['7299', "Miscellaneous General Services"],
|
|
170
|
+
['5719', "Miscellaneous Home Furnishing Specialty Stores"],
|
|
171
|
+
['2741', "Miscellaneous Publishing and Printing"],
|
|
172
|
+
['7999', "Miscellaneous Recreation Services"],
|
|
173
|
+
['7699', "Miscellaneous Repair Shops"],
|
|
174
|
+
['5999', "Miscellaneous Specialty Retail"],
|
|
175
|
+
['5271', "Mobile Home Dealers"],
|
|
176
|
+
['7832', "Motion Picture Theaters"],
|
|
177
|
+
['4214', "Motor Freight Carriers and Trucking - Local and Long Distance, Moving and Storage Companies, and Local Delivery Services"],
|
|
178
|
+
['5592', "Motor Homes Dealers"],
|
|
179
|
+
['5013', "Motor Vehicle Supplies and New Parts"],
|
|
180
|
+
['5571', "Motorcycle Shops and Dealers"],
|
|
181
|
+
['5561', "Motorcycle Shops, Dealers"],
|
|
182
|
+
['5733', "Music Stores-Musical Instruments, Pianos, and Sheet Music"],
|
|
183
|
+
['5994', "News Dealers and Newsstands"],
|
|
184
|
+
['5199', "Nondurable Goods (Not Elsewhere Classified)"],
|
|
185
|
+
['5261', "Nurseries, Lawn and Garden Supply Stores"],
|
|
186
|
+
['8050', "Nursing/Personal Care"],
|
|
187
|
+
['5021', "Office and Commercial Furniture"],
|
|
188
|
+
['8043', "Opticians, Eyeglasses"],
|
|
189
|
+
['8042', "Optometrists, Ophthalmologist"],
|
|
190
|
+
['5976', "Orthopedic Goods - Prosthetic Devices"],
|
|
191
|
+
['8031', "Osteopaths"],
|
|
192
|
+
['5921', "Package Stores-Beer, Wine, and Liquor"],
|
|
193
|
+
['5198', "Paints, Varnishes, and Supplies"],
|
|
194
|
+
['7523', "Parking Lots, Garages"],
|
|
195
|
+
['4112', "Passenger Railways"],
|
|
196
|
+
['5933', "Pawn Shops"],
|
|
197
|
+
['5995', "Pet Shops, Pet Food, and Supplies"],
|
|
198
|
+
['5172', "Petroleum and Petroleum Products"],
|
|
199
|
+
['7395', "Photo Developing"],
|
|
200
|
+
['7221', "Photographic Studios"],
|
|
201
|
+
['5044', "Photographic, Photocopy, Microfilm Equipment, and Supplies"],
|
|
202
|
+
['7829', "Picture/Video Production"],
|
|
203
|
+
['5131', "Piece Goods, Notions, and Other Dry Goods"],
|
|
204
|
+
['5074', "Plumbing, Heating Equipment, and Supplies"],
|
|
205
|
+
['5094', "Precious Stones and Metals, Watches and Jewelry"],
|
|
206
|
+
['8999', "Professional Services"],
|
|
207
|
+
['4225', "Public Warehousing and Storage - Farm Products, Refrigerated Goods, Household Goods, and Storage"],
|
|
208
|
+
['7338', "Quick Copy, Repro, and Blueprint"],
|
|
209
|
+
['4011', "Railroads"],
|
|
210
|
+
['6513', "Real Estate Agents and Managers - Rentals"],
|
|
211
|
+
['5735', "Record Stores"],
|
|
212
|
+
['7519', "Recreational Vehicle Rentals"],
|
|
213
|
+
['5973', "Religious Goods Stores"],
|
|
214
|
+
['1761', "Roofing/Siding, Sheet Metal"],
|
|
215
|
+
['7339', "Secretarial Support Services"],
|
|
216
|
+
['5541', "Service Stations"],
|
|
217
|
+
['5949', "Sewing, Needlework, Fabric, and Piece Goods Stores"],
|
|
218
|
+
['7251', "Shoe Repair/Hat Cleaning"],
|
|
219
|
+
['5661', "Shoe Stores"],
|
|
220
|
+
['7629', "Small Appliance Repair"],
|
|
221
|
+
['5598', "Snowmobile Dealers"],
|
|
222
|
+
['1799', "Special Trade Services"],
|
|
223
|
+
['2842', "Specialty Cleaning"],
|
|
224
|
+
['5941', "Sporting Goods Stores"],
|
|
225
|
+
['7032', "Sporting/Recreation Camps"],
|
|
226
|
+
['7941', "Sports Clubs/Fields"],
|
|
227
|
+
['5655', "Sports and Riding Apparel Stores"],
|
|
228
|
+
['5972', "Stamp and Coin Stores"],
|
|
229
|
+
['5943', "Stationery Stores, Office, and School Supply Stores"],
|
|
230
|
+
['5111', "Stationery, Office Supplies, Printing and Writing Paper"],
|
|
231
|
+
['5996', "Swimming Pools Sales"],
|
|
232
|
+
['4723', "TUI Travel - Germany"],
|
|
233
|
+
['5697', "Tailors, Alterations"],
|
|
234
|
+
['7276', "Tax Preparation Services"],
|
|
235
|
+
['4121', "Taxicabs/Limousines"],
|
|
236
|
+
['4812', "Telecommunication Equipment and Telephone Sales"],
|
|
237
|
+
['4821', "Telegraph Services"],
|
|
238
|
+
['5998', "Tent and Awning Shops"],
|
|
239
|
+
['8734', "Testing Laboratories"],
|
|
240
|
+
['7922', "Theatrical Ticket Agencies"],
|
|
241
|
+
['7534', "Tire Retreading and Repair"],
|
|
242
|
+
['4784', "Tolls/Bridge Fees"],
|
|
243
|
+
['7991', "Tourist Attractions and Exhibits"],
|
|
244
|
+
['7549', "Towing Services"],
|
|
245
|
+
['7033', "Trailer Parks, Campgrounds"],
|
|
246
|
+
['4789', "Transportation Services (Not Elsewhere Classified)"],
|
|
247
|
+
['4722', "Travel Agencies, Tour Operators"],
|
|
248
|
+
['7513', "Truck/Utility Trailer Rentals"],
|
|
249
|
+
['2791', "Typesetting, Plate Making, and Related Services"],
|
|
250
|
+
['5978', "Typewriter Stores"],
|
|
251
|
+
['5137', "Uniforms, Commercial Clothing"],
|
|
252
|
+
['5931', "Used Merchandise and Secondhand Stores"],
|
|
253
|
+
['4900', "Utilities"],
|
|
254
|
+
['5331', "Variety Stores"],
|
|
255
|
+
['0742', "Veterinary Services"],
|
|
256
|
+
['7993', "Video Amusement Game Supplies"],
|
|
257
|
+
['7994', "Video Game Arcades"],
|
|
258
|
+
['7841', "Video Tape Rental Stores"],
|
|
259
|
+
['8249', "Vocational/Trade Schools"],
|
|
260
|
+
['7631', "Watch/Jewelry Repair"],
|
|
261
|
+
['7692', "Welding Repair"],
|
|
262
|
+
['5300', "Wholesale Clubs"],
|
|
263
|
+
['5698', "Wig and Toupee Stores"],
|
|
264
|
+
['5631', "Womens Accessory and Specialty Shops"],
|
|
265
|
+
['5621', "Womens Ready-To-Wear Stores"],
|
|
266
|
+
['5935', "Wrecking and Salvage Yards"],
|
|
267
|
+
];
|