@volter/twin-stripe 0.1.2 → 2.0.0
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 +64 -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 +73 -0
- package/dist/src/manifest.d.ts +2 -0
- package/dist/src/manifest.js +1065 -0
- package/dist/src/screens/checkout.d.ts +31 -0
- package/dist/src/screens/checkout.js +241 -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 +214 -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 +93 -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 +201 -0
- package/dist/src/semantics/checkout.d.ts +15 -0
- package/dist/src/semantics/checkout.js +303 -0
- package/dist/src/semantics/connect.d.ts +5 -0
- package/dist/src/semantics/connect.js +476 -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 +541 -0
- package/dist/src/semantics/issuing.d.ts +13 -0
- package/dist/src/semantics/issuing.js +570 -0
- package/dist/src/semantics/ledger.d.ts +54 -0
- package/dist/src/semantics/ledger.js +181 -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 +138 -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 +78 -0
- package/dist/src/semantics/shared.js +192 -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-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 +5052 -0
- package/dist/src/stripe-conformance.d.ts +41 -0
- package/dist/src/stripe-conformance.js +96 -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 +388 -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 +326 -0
- package/dist/src/stripe-shared.d.ts +106 -0
- package/dist/src/stripe-shared.js +273 -0
- package/dist/src/stripe-twin.d.ts +155 -0
- package/dist/src/stripe-twin.js +1226 -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 +10 -0
- package/dist/src/stripe-version.js +285 -0
- package/dist/test-fixtures/stripe-known-deviations.json +105 -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 +3740 -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 +31 -9
- package/src/manifest.ts +1097 -0
- package/src/screens/checkout.tsx +252 -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 +218 -0
- package/src/screens/public-details.tsx +105 -0
- package/src/semantics/after-payment.ts +113 -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 +211 -0
- package/src/semantics/checkout.ts +297 -0
- package/src/semantics/connect.ts +471 -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 +541 -0
- package/src/semantics/issuing.ts +585 -0
- package/src/semantics/ledger.ts +216 -0
- package/src/semantics/payment-intents.ts +420 -0
- package/src/semantics/payment-links.ts +148 -0
- package/src/semantics/payment-methods.ts +143 -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 +215 -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-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 +1456 -222
- package/src/stripe-conformance.ts +6 -5
- package/src/stripe-connector.ts +68 -40
- package/src/stripe-emit.ts +14 -7
- package/src/stripe-events.ts +94 -36
- 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 +263 -38
- package/src/stripe-shared.ts +294 -0
- package/src/stripe-twin.ts +429 -5325
- package/src/stripe-ui-conformance.ts +70 -107
- package/src/stripe-ui-structure.ts +124 -348
- package/src/stripe-version.ts +278 -0
- package/test-fixtures/stripe-known-deviations.json +2 -7
- package/test-fixtures/stripe-openapi-operations.json +1188 -2855
- package/src/stripe-form.ts +0 -35
|
@@ -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 = [
|
|
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
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Row } from '../semantics/shared.js';
|
|
2
|
+
type Form = Record<string, string>;
|
|
3
|
+
type Kind = 'company' | 'individual';
|
|
4
|
+
/** The set an account onboards against: the two US sets the twin models, or none. */
|
|
5
|
+
export declare function kindOf(account: Row): Kind | undefined;
|
|
6
|
+
/** What an account of this kind still owes, given the form's answers (none: all of it). */
|
|
7
|
+
export declare function currentlyDue(kind: Kind, v?: Form): string[];
|
|
8
|
+
/** connect.stripe.com's onboarding pages, or undefined for any other request. */
|
|
9
|
+
export declare function stripeOnboardingFlow(scope: {
|
|
10
|
+
root?: string;
|
|
11
|
+
clock?: () => string;
|
|
12
|
+
}): (request: Request) => Promise<Response | undefined>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// STRIPE CONNECT HOSTED ONBOARDING — a hosted flow (docs/contributing/architecture.md, "Screens"): a platform
|
|
3
|
+
// creates an Account Link for a connected account and sends the account's owner to its `url`. The link is a
|
|
4
|
+
// single-use entry point: visiting it takes the owner to their onboarding session, and visiting it again, or after it
|
|
5
|
+
// expires, sends them to refresh_url, where the platform makes a new one (docs.stripe.com/connect/hosted-onboarding).
|
|
6
|
+
// At the session's own address they give Stripe what it requires, and Stripe sends them to return_url. Submitting is
|
|
7
|
+
// the owner's move, not an API call. Authored from @volter/world-ui's payment piece; nothing of Stripe's page is copied.
|
|
8
|
+
//
|
|
9
|
+
// The page asks for exactly what Stripe requires before a US Express account with card_payments and transfers can
|
|
10
|
+
// take card payments (docs.stripe.com/connect/required-verification-information; the set is the one Stripe's
|
|
11
|
+
// requirements endpoint answers, docs.stripe.com/_endpoint/get-requirements-for-setups with apiVersion=v1,
|
|
12
|
+
// platformCountry=US, accountCountry=US, dashboardType=express, tosType=full, capabilities card_payments and transfers):
|
|
13
|
+
// thirty requirements for a company (the business profile, the statement descriptor, the company with its tax id,
|
|
14
|
+
// phone and address, its owners, its representative with their title and address, the agreement and the payout bank
|
|
15
|
+
// account) and eighteen for an individual. A submit that leaves any out is refused with what is missing. The account's
|
|
16
|
+
// requirements.currently_due lists what is still missing, and charges and payouts are enabled, and its requested
|
|
17
|
+
// capabilities become active (Stripe's move on the capability machine), only once none is. The agreement is the
|
|
18
|
+
// page's own checkbox, recorded as tos_acceptance with the request's address and the time.
|
|
19
|
+
//
|
|
20
|
+
// Where the documentation stops and the twin decides: only these two US sets are modelled; an account elsewhere, or
|
|
21
|
+
// of another business type, is told its requirements are not modelled. Test mode verifies at once whatever is
|
|
22
|
+
// submitted (docs.stripe.com/connect/testing lists the values that pass: date of birth 1901-01-01, SSN last four
|
|
23
|
+
// 0000, tax id 000000000, routing 110000000, account 000123456789); the industries offered are Stripe's supported
|
|
24
|
+
// merchant category codes as its list gives them (./industries.ts), and an industry the page does not offer leaves
|
|
25
|
+
// business_profile.mcc due; the states are the fifty and the District of Columbia; a request with no forwarded address
|
|
26
|
+
// records 127.0.0.1. An update link (account_update) shows the same page.
|
|
27
|
+
import { semanticsContext } from '@volter/world-core';
|
|
28
|
+
import { CONSENT_CSS, flowPage, Payment, PAYMENT_CSS } from '@volter/world-ui';
|
|
29
|
+
import surface from '../generated/surface.gen.json' with { type: 'json' };
|
|
30
|
+
import { manifest } from "../manifest.js";
|
|
31
|
+
import { created, syncExternals } from "../semantics/shared.js";
|
|
32
|
+
import { formOf, seeOther, STRIPE_CONSENT_SKIN } from "./consent-skin.js";
|
|
33
|
+
import { INDUSTRIES as MCCS } from "./industries.js";
|
|
34
|
+
const LINK = 'account_link';
|
|
35
|
+
const OPERATION = surface.operations.find((o) => o.id === 'PostAccountsAccount');
|
|
36
|
+
const gone = () => flowPage({ title: 'Stripe', status: 404, css: [CONSENT_CSS, STRIPE_CONSENT_SKIN], body: _jsx("main", { className: "consent", children: _jsx("h1", { className: "consent-heading", children: "This link is invalid." }) }) });
|
|
37
|
+
/** A link that can still be used: not used and not past its expiry. */
|
|
38
|
+
const usable = (ctx, link) => link.used !== true && Number(link.expires_at) > Number(ctx.now());
|
|
39
|
+
// the field asks, with no industry chosen until the person chooses one (an unanswered field leaves the MCC due)
|
|
40
|
+
const INDUSTRIES = [{ value: '', label: 'Select your industry' }, ...MCCS.map(([value, label]) => ({ value, label }))];
|
|
41
|
+
const OFFERED = new Set(MCCS.map(([mcc]) => mcc));
|
|
42
|
+
// the fifty states and the District of Columbia, by their USPS codes (the address's `state`, ISO 3166-2 without its
|
|
43
|
+
// country, docs.stripe.com/api/accounts/object)
|
|
44
|
+
const STATES = [
|
|
45
|
+
['AL', 'Alabama'], ['AK', 'Alaska'], ['AZ', 'Arizona'], ['AR', 'Arkansas'], ['CA', 'California'], ['CO', 'Colorado'], ['CT', 'Connecticut'],
|
|
46
|
+
['DE', 'Delaware'], ['DC', 'District of Columbia'], ['FL', 'Florida'], ['GA', 'Georgia'], ['HI', 'Hawaii'], ['ID', 'Idaho'], ['IL', 'Illinois'],
|
|
47
|
+
['IN', 'Indiana'], ['IA', 'Iowa'], ['KS', 'Kansas'], ['KY', 'Kentucky'], ['LA', 'Louisiana'], ['ME', 'Maine'], ['MD', 'Maryland'],
|
|
48
|
+
['MA', 'Massachusetts'], ['MI', 'Michigan'], ['MN', 'Minnesota'], ['MS', 'Mississippi'], ['MO', 'Missouri'], ['MT', 'Montana'], ['NE', 'Nebraska'],
|
|
49
|
+
['NV', 'Nevada'], ['NH', 'New Hampshire'], ['NJ', 'New Jersey'], ['NM', 'New Mexico'], ['NY', 'New York'], ['NC', 'North Carolina'],
|
|
50
|
+
['ND', 'North Dakota'], ['OH', 'Ohio'], ['OK', 'Oklahoma'], ['OR', 'Oregon'], ['PA', 'Pennsylvania'], ['RI', 'Rhode Island'],
|
|
51
|
+
['SC', 'South Carolina'], ['SD', 'South Dakota'], ['TN', 'Tennessee'], ['TX', 'Texas'], ['UT', 'Utah'], ['VT', 'Vermont'], ['VA', 'Virginia'],
|
|
52
|
+
['WA', 'Washington'], ['WV', 'West Virginia'], ['WI', 'Wisconsin'], ['WY', 'Wyoming'],
|
|
53
|
+
].map(([value, label]) => ({ value, label }));
|
|
54
|
+
const has = (v, k) => (v[k] ?? '').trim() !== '';
|
|
55
|
+
const dobPart = (v, i) => /^\d{4}-\d{2}-\d{2}$/.test(v.dob ?? '') && Number((v.dob ?? '').split('-')[i]) > 0;
|
|
56
|
+
/** Each requirement Stripe names, the form's answer that meets it, and which of the two sets it belongs to. */
|
|
57
|
+
const REQUIREMENTS = [
|
|
58
|
+
['business_profile.mcc', (v) => OFFERED.has((v.mcc ?? '').trim()), ['company', 'individual']],
|
|
59
|
+
['business_profile.url', (v) => has(v, 'url'), ['company', 'individual']],
|
|
60
|
+
['settings.payments.statement_descriptor', (v) => has(v, 'statement_descriptor'), ['company', 'individual']],
|
|
61
|
+
['company.name', (v) => has(v, 'company_name'), ['company']],
|
|
62
|
+
['company.tax_id', (v) => has(v, 'company_tax_id'), ['company']],
|
|
63
|
+
['company.phone', (v) => has(v, 'company_phone'), ['company']],
|
|
64
|
+
['company.address.line1', (v) => has(v, 'company_line1'), ['company']],
|
|
65
|
+
['company.address.city', (v) => has(v, 'company_city'), ['company']],
|
|
66
|
+
['company.address.state', (v) => has(v, 'company_state'), ['company']],
|
|
67
|
+
['company.address.postal_code', (v) => has(v, 'company_postal_code'), ['company']],
|
|
68
|
+
['company.owners_provided', (v) => v.owners_provided === 'provided', ['company']],
|
|
69
|
+
['owners.first_name', (v) => has(v, 'owner_first_name'), ['company']],
|
|
70
|
+
['owners.last_name', (v) => has(v, 'owner_last_name'), ['company']],
|
|
71
|
+
['owners.email', (v) => has(v, 'owner_email'), ['company']],
|
|
72
|
+
...['first_name', 'last_name', 'email', 'phone'].flatMap((f) => [[`representative.${f}`, (v) => has(v, f), ['company']], [`individual.${f}`, (v) => has(v, f), ['individual']]]),
|
|
73
|
+
...['day', 'month', 'year'].flatMap((f, i) => [[`representative.dob.${f}`, (v) => dobPart(v, 2 - i), ['company']], [`individual.dob.${f}`, (v) => dobPart(v, 2 - i), ['individual']]]),
|
|
74
|
+
...['line1', 'city', 'state', 'postal_code'].flatMap((f) => [[`representative.address.${f}`, (v) => has(v, f), ['company']], [`individual.address.${f}`, (v) => has(v, f), ['individual']]]),
|
|
75
|
+
['representative.relationship.title', (v) => has(v, 'title'), ['company']],
|
|
76
|
+
['representative.ssn_last_4', (v) => /^\d{4}$/.test(v.ssn_last_4 ?? ''), ['company']],
|
|
77
|
+
['individual.ssn_last_4', (v) => /^\d{4}$/.test(v.ssn_last_4 ?? ''), ['individual']],
|
|
78
|
+
['tos_acceptance.date', (v) => v.tos === 'accepted', ['company', 'individual']],
|
|
79
|
+
['tos_acceptance.ip', (v) => v.tos === 'accepted', ['company', 'individual']],
|
|
80
|
+
['external_account', (v) => has(v, 'routing_number') && has(v, 'account_number'), ['company', 'individual']],
|
|
81
|
+
];
|
|
82
|
+
/** The set an account onboards against: the two US sets the twin models, or none. */
|
|
83
|
+
export function kindOf(account) {
|
|
84
|
+
const type = account.business_type ?? 'individual';
|
|
85
|
+
return (account.country ?? 'US') === 'US' && (type === 'company' || type === 'individual') ? type : undefined;
|
|
86
|
+
}
|
|
87
|
+
/** What an account of this kind still owes, given the form's answers (none: all of it). */
|
|
88
|
+
export function currentlyDue(kind, v = {}) {
|
|
89
|
+
return REQUIREMENTS.filter(([, met, kinds]) => kinds.includes(kind) && !met(v)).map(([req]) => req);
|
|
90
|
+
}
|
|
91
|
+
const field = (id, label, v, extra = {}) => ({ id, label, value: v[id] ?? '', ...extra });
|
|
92
|
+
const addressFields = (prefix, v) => [
|
|
93
|
+
field(`${prefix}line1`, 'Address', v, { autoComplete: 'address-line1' }), field(`${prefix}city`, 'City', v),
|
|
94
|
+
field(`${prefix}state`, 'State', v, { options: STATES, value: v[`${prefix}state`] ?? 'OR' }), field(`${prefix}postal_code`, 'ZIP', v, { autoComplete: 'postal-code' }),
|
|
95
|
+
];
|
|
96
|
+
/** The industry the field shows: what the form sent, else what the account already holds, when the page offers it. */
|
|
97
|
+
const industryOf = (mcc) => (typeof mcc === 'string' && OFFERED.has(mcc.trim()) ? mcc.trim() : '');
|
|
98
|
+
function page(ctx, sessionUrl, link, kind, values = {}, error) {
|
|
99
|
+
const v = values;
|
|
100
|
+
const business = [
|
|
101
|
+
field('mcc', 'Industry', v, { options: INDUSTRIES, value: industryOf(v.mcc ?? ctx.get('account', String(link.account))?.business_profile?.mcc) }),
|
|
102
|
+
field('url', 'Business website', v, { placeholder: 'https://' }),
|
|
103
|
+
field('statement_descriptor', 'Statement descriptor (what customers see on their card statements)', v),
|
|
104
|
+
];
|
|
105
|
+
const company = [
|
|
106
|
+
field('company_name', 'Legal business name', v), field('company_tax_id', 'Employer Identification Number (EIN)', v), field('company_phone', 'Business phone', v, { type: 'tel' }),
|
|
107
|
+
...addressFields('company_', v),
|
|
108
|
+
];
|
|
109
|
+
const owners = [
|
|
110
|
+
field('owner_first_name', 'Owner first name', v), field('owner_last_name', 'Owner last name', v), field('owner_email', 'Owner email', v, { type: 'email' }),
|
|
111
|
+
field('owners_provided', 'All owners of 25% or more are listed', v, { type: 'checkbox', value: 'provided' }),
|
|
112
|
+
];
|
|
113
|
+
const person = [
|
|
114
|
+
field('first_name', 'First name', v, { autoComplete: 'given-name' }), field('last_name', 'Last name', v, { autoComplete: 'family-name' }),
|
|
115
|
+
field('email', 'Email', v, { type: 'email' }), field('phone', 'Phone', v, { type: 'tel' }), field('dob', 'Date of birth', v, { type: 'date' }),
|
|
116
|
+
...(kind === 'company' ? [field('title', 'Job title', v)] : []),
|
|
117
|
+
...addressFields('', v), field('ssn_last_4', 'Last 4 digits of SSN', v),
|
|
118
|
+
];
|
|
119
|
+
const payout = [field('routing_number', 'Routing number', v), field('account_number', 'Account number', v)];
|
|
120
|
+
const agreement = [field('tos', 'I agree to the Stripe Connected Account Agreement', v, { type: 'checkbox', value: 'accepted' })];
|
|
121
|
+
const sections = kind === 'company'
|
|
122
|
+
? [{ heading: 'Business details', fields: business }, { heading: 'Company', fields: company }, { heading: 'Owners', fields: owners }, { heading: 'Representative', fields: person }, { heading: 'Payout account', fields: payout }, { heading: 'Agreement', fields: agreement }]
|
|
123
|
+
: [{ heading: 'Business details', fields: business }, { heading: 'About you', fields: person }, { heading: 'Payout account', fields: payout }, { heading: 'Agreement', fields: agreement }];
|
|
124
|
+
return flowPage({
|
|
125
|
+
title: 'Stripe · Account onboarding',
|
|
126
|
+
css: [PAYMENT_CSS],
|
|
127
|
+
...(error ? { status: 400 } : {}),
|
|
128
|
+
body: (_jsx(Payment, { merchant: `Verify your ${kind === 'company' ? 'company' : 'business'} with Stripe · ${String(link.account)} · test mode`, lines: [], action: sessionUrl, sections: sections, submit: { label: 'Submit' }, back: { href: String(link.return_url), label: '← Return to platform' }, ...(error ? { error } : {}) })),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** An account whose requirements the twin does not model: outside the US, or of another business type. */
|
|
132
|
+
function unmodelled(account) {
|
|
133
|
+
return flowPage({ title: 'Stripe', status: 501, css: [CONSENT_CSS, STRIPE_CONSENT_SKIN], body: _jsx("main", { className: "consent", children: _jsx("h1", { className: "consent-heading", children: `Onboarding requirements for a ${String(account.business_type ?? 'business')} account in ${String(account.country ?? 'this country')} are not modelled by this twin.` }) }) });
|
|
134
|
+
}
|
|
135
|
+
/** A submit that leaves something out is refused on the page with what is missing, the owner's answers kept. */
|
|
136
|
+
function incomplete(ctx, sessionUrl, link, kind, v, missing) {
|
|
137
|
+
return page(ctx, sessionUrl, link, kind, v, `Still required: ${missing.join(', ')}.`);
|
|
138
|
+
}
|
|
139
|
+
/** The owner submits everything: it is stored on the account, nothing is due, and charges, payouts and the requested
|
|
140
|
+
* capabilities are enabled. */
|
|
141
|
+
async function submit(ctx, link, kind, v, ip) {
|
|
142
|
+
const id = String(link.account);
|
|
143
|
+
const account = ctx.get('account', id);
|
|
144
|
+
if (!account)
|
|
145
|
+
return;
|
|
146
|
+
const caps = { ...(account.capabilities ?? {}) };
|
|
147
|
+
for (const [cap, status] of Object.entries(caps)) {
|
|
148
|
+
if (status !== 'active' && !ctx.legal('capability', 'status', OPERATION.id, String(status), 'active', cap, 'vendor'))
|
|
149
|
+
caps[cap] = 'active';
|
|
150
|
+
}
|
|
151
|
+
const due = currentlyDue(kind, v);
|
|
152
|
+
const requirements = { ...(account.requirements ?? {}), currently_due: due, eventually_due: due, past_due: [], disabled_reason: null, current_deadline: null };
|
|
153
|
+
const addressOf = (p) => ({ line1: v[`${p}line1`], line2: null, city: v[`${p}city`], state: v[`${p}state`], postal_code: v[`${p}postal_code`], country: 'US' });
|
|
154
|
+
const [year, month, day] = (v.dob ?? '').split('-').map(Number);
|
|
155
|
+
const person = { first_name: v.first_name, last_name: v.last_name, email: v.email, phone: v.phone, dob: { day, month, year }, address: addressOf(''), ssn_last_4_provided: true, id_number_provided: false };
|
|
156
|
+
const company = kind === 'company';
|
|
157
|
+
const settings = account.settings ?? {};
|
|
158
|
+
await ctx.write('account', id, {
|
|
159
|
+
details_submitted: true, charges_enabled: due.length === 0, payouts_enabled: due.length === 0, capabilities: caps, requirements,
|
|
160
|
+
business_type: kind,
|
|
161
|
+
business_profile: { ...(account.business_profile ?? {}), name: company ? v.company_name : `${String(v.first_name)} ${String(v.last_name)}`, mcc: (v.mcc ?? '').trim(), url: v.url },
|
|
162
|
+
settings: { ...settings, payments: { ...(settings.payments ?? {}), statement_descriptor: v.statement_descriptor } },
|
|
163
|
+
...(company
|
|
164
|
+
? { company: { name: v.company_name, phone: v.company_phone, address: addressOf('company_'), tax_id_provided: true, owners_provided: true, directors_provided: false, executives_provided: false } }
|
|
165
|
+
: { individual: { ...person, id: `person_${id}` } }),
|
|
166
|
+
tos_acceptance: { date: Number(ctx.now()), ip, user_agent: null },
|
|
167
|
+
}, 'account.updated');
|
|
168
|
+
if (company) {
|
|
169
|
+
await created(ctx, 'person', { account: id }, { ...person, relationship: { representative: true, executive: true, director: false, owner: false, percent_ownership: null, title: v.title }, metadata: {} });
|
|
170
|
+
await created(ctx, 'person', { account: id }, { first_name: v.owner_first_name, last_name: v.owner_last_name, email: v.owner_email, relationship: { owner: true, representative: false, executive: false, director: false, percent_ownership: null, title: null }, metadata: {} });
|
|
171
|
+
}
|
|
172
|
+
const digits = (v.account_number ?? '').replace(/\D/g, '');
|
|
173
|
+
await created(ctx, 'external_account', { account: id }, {
|
|
174
|
+
object: 'bank_account', account_holder_name: company ? v.company_name : `${String(v.first_name)} ${String(v.last_name)}`, account_holder_type: company ? 'company' : 'individual', bank_name: 'STRIPE TEST BANK', country: 'US', currency: 'usd',
|
|
175
|
+
default_for_currency: true, fingerprint: `twin_ext_${digits.slice(-4)}`, last4: digits.slice(-4), routing_number: v.routing_number, status: 'new', metadata: {},
|
|
176
|
+
});
|
|
177
|
+
await syncExternals(ctx, id);
|
|
178
|
+
}
|
|
179
|
+
/** Where an onboarding session lives, on connect.stripe.com. */
|
|
180
|
+
const SESSIONS = 'https://connect.stripe.com/setup/s/';
|
|
181
|
+
/** Visiting an Account Link consumes it and sends the owner to their onboarding session; a used or expired one sends
|
|
182
|
+
* them to refresh_url for a fresh link. */
|
|
183
|
+
async function visit(ctx, id) {
|
|
184
|
+
const link = ctx.get(LINK, id);
|
|
185
|
+
if (!link)
|
|
186
|
+
return gone();
|
|
187
|
+
if (!usable(ctx, link))
|
|
188
|
+
return seeOther(String(link.refresh_url));
|
|
189
|
+
const session = `onbs_${id.replace(/^acctlink_/, '')}`;
|
|
190
|
+
await ctx.write(LINK, id, { used: true, _session: session }, 'account_link.used');
|
|
191
|
+
return seeOther(`${SESSIONS}${session}`);
|
|
192
|
+
}
|
|
193
|
+
/** connect.stripe.com's onboarding pages, or undefined for any other request. */
|
|
194
|
+
export function stripeOnboardingFlow(scope) {
|
|
195
|
+
return async (request) => {
|
|
196
|
+
const path = new URL(request.url).pathname;
|
|
197
|
+
const entry = /^\/setup\/(acctlink_[A-Za-z0-9_]+)\/?$/.exec(path);
|
|
198
|
+
const at = /^\/setup\/s\/(onbs_[A-Za-z0-9_]+)\/?$/.exec(path);
|
|
199
|
+
if (entry && request.method === 'GET')
|
|
200
|
+
return visit(await semanticsContext(manifest, request, OPERATION, scope), entry[1]);
|
|
201
|
+
if (!at || (request.method !== 'GET' && request.method !== 'POST'))
|
|
202
|
+
return undefined;
|
|
203
|
+
const sid = at[1];
|
|
204
|
+
const form = request.method === 'POST' ? await formOf(request.clone()) : {};
|
|
205
|
+
const ctx = await semanticsContext(manifest, request, OPERATION, scope);
|
|
206
|
+
// the session the visited link opened, until it is submitted
|
|
207
|
+
const stored = ctx.rowsRaw(LINK).find((l) => l._session === sid && l._submitted !== true);
|
|
208
|
+
const link = stored ? ctx.get(LINK, String(stored.id)) : undefined;
|
|
209
|
+
if (!stored || !link)
|
|
210
|
+
return gone();
|
|
211
|
+
const sessionUrl = `${SESSIONS}${sid}`;
|
|
212
|
+
const account = ctx.get('account', String(link.account)) ?? {};
|
|
213
|
+
const kind = kindOf(account);
|
|
214
|
+
if (!kind)
|
|
215
|
+
return unmodelled(account);
|
|
216
|
+
if (request.method === 'GET')
|
|
217
|
+
return page(ctx, sessionUrl, link, kind);
|
|
218
|
+
const missing = currentlyDue(kind, form);
|
|
219
|
+
if (missing.length)
|
|
220
|
+
return incomplete(ctx, sessionUrl, link, kind, form, missing);
|
|
221
|
+
await ctx.write(LINK, String(stored.id), { _submitted: true }, 'account_link.submitted');
|
|
222
|
+
await submit(ctx, link, kind, form, request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || '127.0.0.1');
|
|
223
|
+
return seeOther(String(link.return_url));
|
|
224
|
+
};
|
|
225
|
+
}
|