@aglyn/plugins-marketplace 1.1.0 → 1.3.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.
Files changed (73) hide show
  1. package/README.md +1 -1
  2. package/package.json +14 -14
  3. package/src/lib/components/listing-content.component.js +11 -3
  4. package/src/lib/components/listing-content.component.js.map +1 -1
  5. package/src/lib/components/marketplace-pay-panel.component.d.ts +67 -0
  6. package/src/lib/components/marketplace-pay-panel.component.js +228 -0
  7. package/src/lib/components/marketplace-pay-panel.component.js.map +1 -0
  8. package/src/lib/components/publish-artifact-dialog.component.d.ts +1 -1
  9. package/src/lib/components/publish-artifact-dialog.component.js +144 -20
  10. package/src/lib/components/publish-artifact-dialog.component.js.map +1 -1
  11. package/src/lib/components/publish-starter-fields.component.d.ts +35 -0
  12. package/src/lib/components/publish-starter-fields.component.js +176 -0
  13. package/src/lib/components/publish-starter-fields.component.js.map +1 -0
  14. package/src/lib/components/starter-picker-shelf.component.d.ts +2 -2
  15. package/src/lib/components/starter-picker-shelf.component.js +28 -6
  16. package/src/lib/components/starter-picker-shelf.component.js.map +1 -1
  17. package/src/lib/hooks/use-marketplace-actions.d.ts +9 -3
  18. package/src/lib/hooks/use-marketplace-actions.js +47 -89
  19. package/src/lib/hooks/use-marketplace-actions.js.map +1 -1
  20. package/src/lib/hooks/use-marketplace-pay.d.ts +70 -0
  21. package/src/lib/hooks/use-marketplace-pay.js +359 -0
  22. package/src/lib/hooks/use-marketplace-pay.js.map +1 -0
  23. package/src/lib/hooks/use-starter-publish-facts.d.ts +25 -0
  24. package/src/lib/hooks/use-starter-publish-facts.js +116 -0
  25. package/src/lib/hooks/use-starter-publish-facts.js.map +1 -0
  26. package/src/lib/hooks/use-starter-purchase.d.ts +17 -22
  27. package/src/lib/hooks/use-starter-purchase.js +26 -73
  28. package/src/lib/hooks/use-starter-purchase.js.map +1 -1
  29. package/src/lib/model/marketplace-invoice.d.ts +52 -0
  30. package/src/lib/model/marketplace-invoice.js +75 -0
  31. package/src/lib/model/marketplace-invoice.js.map +1 -0
  32. package/src/lib/model/marketplace-pay.d.ts +42 -0
  33. package/src/lib/model/marketplace-pay.js +33 -0
  34. package/src/lib/model/marketplace-pay.js.map +1 -0
  35. package/src/lib/model/platform-seller.d.ts +37 -0
  36. package/src/lib/model/platform-seller.js +36 -0
  37. package/src/lib/model/platform-seller.js.map +1 -0
  38. package/src/lib/model/starter-publish.d.ts +159 -0
  39. package/src/lib/model/starter-publish.js +189 -0
  40. package/src/lib/model/starter-publish.js.map +1 -0
  41. package/src/lib/model/starter-query.d.ts +0 -8
  42. package/src/lib/model/starter-query.js +0 -10
  43. package/src/lib/model/starter-query.js.map +1 -1
  44. package/src/lib/server/billing-webhook.d.ts +6 -0
  45. package/src/lib/server/billing-webhook.js +269 -211
  46. package/src/lib/server/billing-webhook.js.map +1 -1
  47. package/src/lib/server/checkout.d.ts +2 -48
  48. package/src/lib/server/checkout.js +21 -241
  49. package/src/lib/server/checkout.js.map +1 -1
  50. package/src/lib/server/pay.d.ts +18 -0
  51. package/src/lib/server/pay.js +540 -0
  52. package/src/lib/server/pay.js.map +1 -0
  53. package/src/lib/server/platform-org.d.ts +23 -0
  54. package/src/lib/server/platform-org.js +27 -0
  55. package/src/lib/server/platform-org.js.map +1 -0
  56. package/src/lib/server/publish-preconditions.js +4 -1
  57. package/src/lib/server/publish-preconditions.js.map +1 -1
  58. package/src/lib/server/publish-starter.js +5 -2
  59. package/src/lib/server/publish-starter.js.map +1 -1
  60. package/src/lib/server/publisher-profile.d.ts +5 -0
  61. package/src/lib/server/publisher-profile.js +20 -1
  62. package/src/lib/server/publisher-profile.js.map +1 -1
  63. package/src/lib/server/publisher-standing.d.ts +26 -0
  64. package/src/lib/server/publisher-standing.js +66 -0
  65. package/src/lib/server/publisher-standing.js.map +1 -0
  66. package/src/lib/server/sale.d.ts +119 -0
  67. package/src/lib/server/sale.js +304 -0
  68. package/src/lib/server/sale.js.map +1 -0
  69. package/src/lib/server/starter-publish-facts.d.ts +36 -0
  70. package/src/lib/server/starter-publish-facts.js +135 -0
  71. package/src/lib/server/starter-publish-facts.js.map +1 -0
  72. package/src/lib/server.js +8 -0
  73. package/src/lib/server.js.map +1 -1
@@ -0,0 +1,540 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { firebaseAdmin, logOrgActivity, memberHasOrgPermission, readOrgBilling, resolveOrgMembership } from "@aglyn/tenant-data-admin";
18
+ import { invoiceCardAuthenticationParams } from "@aglyn/tenant-data-admin/server/stripe-card-authentication";
19
+ import { readBillingDetails } from "@aglyn/tenant-data-admin/server/stripe-payment-method";
20
+ import { claimAttempt, deriveStripeObjectKey } from "@aglyn/aglyn/server";
21
+ import { permissionRefusalText } from "@aglyn/aglyn/app-utils/org-permissions";
22
+ import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
23
+ import { invoicePaymentIntentId, isMarketplaceInvoice, marketplaceSaleMetadataParams } from "../model/marketplace-invoice.js";
24
+ import { recordMarketplaceInvoicePurchase } from "./billing-webhook.js";
25
+ import { MARKETPLACE_TAX_CODE, resolveSale } from "./sale.js";
26
+ function stripeCaller(secretKey) {
27
+ return async (method, path, body, idempotencyKey)=>{
28
+ const response = await fetch(`https://api.stripe.com/v1/${path}`, _extends({
29
+ method,
30
+ headers: _extends({
31
+ Authorization: `Bearer ${secretKey}`
32
+ }, body ? {
33
+ 'Content-Type': 'application/x-www-form-urlencoded'
34
+ } : {}, idempotencyKey ? {
35
+ 'Idempotency-Key': idempotencyKey
36
+ } : {})
37
+ }, body ? {
38
+ body: body.toString()
39
+ } : {}));
40
+ const payload = await response.json().catch(()=>null);
41
+ return {
42
+ ok: response.ok,
43
+ status: response.status,
44
+ payload
45
+ };
46
+ };
47
+ }
48
+ /** Why a workspace cannot be charged yet, and whether this person can fix it here. */ function prerequisiteRefusal(code, canManageBilling, sale) {
49
+ return {
50
+ status: 412,
51
+ body: {
52
+ error: code === 'card_required' ? 'Add a card in Billing first' : 'Add a billing address in Billing first: sales tax is calculated from it.',
53
+ code,
54
+ canManageBilling,
55
+ // The price stays in front of someone who cannot meet this themselves.
56
+ amountCents: sale.amountCents
57
+ }
58
+ };
59
+ }
60
+ /** The refusal for someone who may see the price but may not charge the workspace's card. */ const billingManageRefusal = ()=>({
61
+ error: permissionRefusalText('billing.manage'),
62
+ code: 'billing_manage_required'
63
+ });
64
+ /** What this person may do with the workspace's billing, as billing itself asks it. */ async function billingAccess(decoded, orgId) {
65
+ if (decoded['staff'] === true) return {
66
+ canManage: true,
67
+ canView: true
68
+ };
69
+ const actor = await resolveOrgMembership(decoded.uid, orgId);
70
+ const [canManage, canView] = await Promise.all([
71
+ memberHasOrgPermission(orgId, actor == null ? void 0 : actor.member, 'billing.manage'),
72
+ memberHasOrgPermission(orgId, actor == null ? void 0 : actor.member, 'billing.view')
73
+ ]);
74
+ return {
75
+ canManage,
76
+ canView
77
+ };
78
+ }
79
+ /** The Stripe Tax calculation a quote reads, or `null` when Stripe refused it. */ async function priceSale(stripe, customerId, listingId, sale) {
80
+ var _ref, _ref1;
81
+ var _calculation_payload, _calculation_payload1;
82
+ const calculation = await stripe('POST', 'tax/calculations', new URLSearchParams({
83
+ currency: 'usd',
84
+ customer: customerId,
85
+ 'line_items[0][amount]': String(sale.amountCents),
86
+ 'line_items[0][reference]': listingId.slice(0, 200),
87
+ 'line_items[0][tax_behavior]': 'exclusive',
88
+ 'line_items[0][tax_code]': MARKETPLACE_TAX_CODE
89
+ }));
90
+ if (!calculation.ok) {
91
+ var _ref2;
92
+ var _calculation_payload2;
93
+ // Measured: an address Stripe cannot place (a country and nothing else)
94
+ // answers a 400 on the `customer` param with no code. The invoice's own
95
+ // refusal, `customer_tax_location_invalid`, is the same fact.
96
+ const refused = (_calculation_payload2 = calculation.payload) == null ? void 0 : _calculation_payload2.error;
97
+ if ((refused == null ? void 0 : refused.code) === 'customer_tax_location_invalid' || (refused == null ? void 0 : refused.param) === 'customer') {
98
+ return 'no-tax-location';
99
+ }
100
+ console.error('Stripe tax calculation failed', (_ref2 = refused == null ? void 0 : refused.code) != null ? _ref2 : null);
101
+ return null;
102
+ }
103
+ return {
104
+ taxCents: Math.round(Number((_ref = (_calculation_payload = calculation.payload) == null ? void 0 : _calculation_payload.tax_amount_exclusive) != null ? _ref : 0)),
105
+ totalCents: Math.round(Number((_ref1 = (_calculation_payload1 = calculation.payload) == null ? void 0 : _calculation_payload1.amount_total) != null ? _ref1 : sale.amountCents))
106
+ };
107
+ }
108
+ /** Takes an attempt back: a draft is deleted, a finalized invoice is voided. */ async function withdrawInvoice(stripe, invoiceId, state) {
109
+ const answer = state === 'draft' ? await stripe('DELETE', `invoices/${encodeURIComponent(invoiceId)}`) : await stripe('POST', `invoices/${encodeURIComponent(invoiceId)}/void`);
110
+ if (!answer.ok) {
111
+ var _ref;
112
+ var _answer_payload_error, _answer_payload;
113
+ console.error('Marketplace invoice not withdrawn', invoiceId, (_ref = (_answer_payload = answer.payload) == null ? void 0 : (_answer_payload_error = _answer_payload.error) == null ? void 0 : _answer_payload_error.code) != null ? _ref : null);
114
+ }
115
+ }
116
+ /**
117
+ * Voids an earlier attempt at this listing that was left open at the bank's
118
+ * step, by a closed tab for instance. Left there it is a second way to pay for
119
+ * the same licence, sitting in Billing as money owed.
120
+ */ async function voidAbandonedAttempts(stripe, customerId, listingId, orgId) {
121
+ var _open_payload;
122
+ const open = await stripe('GET', `invoices?customer=${encodeURIComponent(customerId)}&status=open&limit=20`);
123
+ const invoices = Array.isArray((_open_payload = open.payload) == null ? void 0 : _open_payload.data) ? open.payload.data : [];
124
+ for (const invoice of invoices){
125
+ var _invoice_metadata, _invoice_metadata1;
126
+ if (isMarketplaceInvoice(invoice) && (invoice == null ? void 0 : (_invoice_metadata = invoice.metadata) == null ? void 0 : _invoice_metadata.listingId) === listingId && (invoice == null ? void 0 : (_invoice_metadata1 = invoice.metadata) == null ? void 0 : _invoice_metadata1.buyerOrgId) === orgId) {
127
+ await withdrawInvoice(stripe, String(invoice.id), 'open');
128
+ }
129
+ }
130
+ }
131
+ /** Records a paid invoice and answers the buyer. The money has moved: nothing here may undo that. */ async function completePurchase(invoice, actor, orgId, listingName) {
132
+ var _ref;
133
+ const invoiceId = String((_ref = invoice == null ? void 0 : invoice.id) != null ? _ref : '');
134
+ try {
135
+ await recordMarketplaceInvoicePurchase(invoice);
136
+ } catch (error) {
137
+ // The webhook writes the same document: this is a race lost, not a sale.
138
+ console.error('Marketplace purchase left for the webhook to record', invoiceId, error);
139
+ }
140
+ await logOrgActivity(orgId, {
141
+ uid: actor.uid,
142
+ email: actor.email
143
+ }, `Bought "${listingName}" from the marketplace`, {
144
+ type: 'org',
145
+ id: orgId
146
+ }).catch((error)=>console.error('Marketplace purchase not logged', error));
147
+ return {
148
+ paid: true,
149
+ invoiceId
150
+ };
151
+ }
152
+ const listingNameOf = (sale)=>{
153
+ var _sale_listing_displayName;
154
+ return String((_sale_listing_displayName = sale.listing['displayName']) != null ? _sale_listing_displayName : 'Marketplace component').slice(0, 120);
155
+ };
156
+ export const payHandler = async (req, res)=>{
157
+ var _ref, _ref1, _ref2, _req_headers_authorization;
158
+ var _req_body, _req_body1, _req_body2;
159
+ if (req.method !== 'POST') {
160
+ return res.status(405).json({
161
+ error: 'Method not allowed'
162
+ });
163
+ }
164
+ const secretKey = process.env['STRIPE_SECRET_KEY'];
165
+ if (!secretKey) {
166
+ return res.status(501).json({
167
+ error: 'Purchases are not configured (STRIPE_SECRET_KEY).'
168
+ });
169
+ }
170
+ const listingId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.listingId) != null ? _ref : '').trim();
171
+ const orgId = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.orgId) != null ? _ref1 : '').trim();
172
+ const action = String((_ref2 = (_req_body2 = req.body) == null ? void 0 : _req_body2.action) != null ? _ref2 : 'pay');
173
+ if (!listingId) return res.status(400).json({
174
+ error: 'Missing listingId'
175
+ });
176
+ if (!orgId) return res.status(400).json({
177
+ error: 'Missing orgId'
178
+ });
179
+ if (action !== 'quote' && action !== 'pay' && action !== 'settle') {
180
+ return res.status(400).json({
181
+ error: 'Unknown action'
182
+ });
183
+ }
184
+ const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
185
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
186
+ if (!idToken) return res.status(401).json({
187
+ error: 'Unauthenticated'
188
+ });
189
+ const stripe = stripeCaller(secretKey);
190
+ let claim = null;
191
+ try {
192
+ var _ref3, _req_headers_idempotencykey, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
193
+ var _req_body3, _created_payload, _req_body4, _finalized_payload, _paid_payload, _paid_payload_error, _paid_payload1, _paid_payload_error1, _paid_payload2, _paid_payload_error2, _paid_payload3, _paid_payload_error3, _paid_payload4;
194
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
195
+ // The org this licence is for is asked of the person's own membership,
196
+ // as the hosted checkout does (AGL-2331). It is the org whose card pays,
197
+ // so naming one you are not in is refused before anything is read.
198
+ const membership = await resolveOrgPermissions(decoded.uid, {
199
+ orgId,
200
+ hostId: null
201
+ });
202
+ if (membership.orgId !== orgId) {
203
+ return res.status(403).json({
204
+ error: 'Not a member of that organization'
205
+ });
206
+ }
207
+ const firestore = firebaseAdmin.app().firestore();
208
+ const buyerOrg = await firestore.collection('orgs').doc(orgId).get();
209
+ // The dispatcher reads a lock off a site, and this door names none.
210
+ if (buyerOrg.get('suspendedAt') != null) {
211
+ return res.status(403).json({
212
+ error: 'This organization is locked, so it cannot buy anything.'
213
+ });
214
+ }
215
+ const customerId = (await readOrgBilling(orgId)).stripeCustomerId;
216
+ if (action === 'settle') {
217
+ // Not a way round `pay`: an attempt is finished by someone who could start it.
218
+ if (!(await billingAccess(decoded, orgId)).canManage) {
219
+ return res.status(403).json(billingManageRefusal());
220
+ }
221
+ return await settle({
222
+ req,
223
+ res,
224
+ stripe,
225
+ decoded,
226
+ orgId,
227
+ listingId,
228
+ customerId
229
+ });
230
+ }
231
+ const resolved = await resolveSale({
232
+ firestore,
233
+ listingId,
234
+ buyer: {
235
+ uid: decoded.uid,
236
+ orgId,
237
+ permissions: membership.permissions
238
+ }
239
+ });
240
+ if ('refusal' in resolved) {
241
+ return res.status(resolved.refusal.status).json(resolved.refusal.body);
242
+ }
243
+ const { sale } = resolved;
244
+ // Paying moves money: the same check `billing/pay-invoice` makes, on top of
245
+ // the buyer permission the sale has just asked for.
246
+ if (action === 'pay' && !(await billingAccess(decoded, orgId)).canManage) {
247
+ return res.status(403).json(billingManageRefusal());
248
+ }
249
+ const billing = customerId ? await readBillingDetails(secretKey, customerId) : {
250
+ paymentMethodId: null,
251
+ method: null,
252
+ hasAddress: false
253
+ };
254
+ if (!billing) {
255
+ return res.status(502).json({
256
+ error: 'We could not read your billing details just now.'
257
+ });
258
+ }
259
+ const needed = !billing.paymentMethodId ? 'card_required' : !billing.hasAddress ? 'billing_address_required' : null;
260
+ if (needed || !customerId || !billing.paymentMethodId) {
261
+ const access = await billingAccess(decoded, orgId);
262
+ const refusal = prerequisiteRefusal(needed != null ? needed : 'card_required', access.canManage, sale);
263
+ return res.status(refusal.status).json(refusal.body);
264
+ }
265
+ if (action === 'quote') {
266
+ const priced = await priceSale(stripe, customerId, listingId, sale);
267
+ if (priced === 'no-tax-location') {
268
+ const access = await billingAccess(decoded, orgId);
269
+ const refusal = prerequisiteRefusal('billing_address_required', access.canManage, sale);
270
+ return res.status(refusal.status).json(refusal.body);
271
+ }
272
+ if (!priced) {
273
+ return res.status(502).json({
274
+ error: 'We could not price that just now.'
275
+ });
276
+ }
277
+ const access = await billingAccess(decoded, orgId);
278
+ return res.status(200).json({
279
+ quote: _extends({
280
+ listingName: listingNameOf(sale),
281
+ amountCents: sale.amountCents
282
+ }, priced, {
283
+ currency: 'usd',
284
+ // The card itself is billing's to show: someone who may buy but may
285
+ // not see billing is told "the card on file" and nothing about it.
286
+ card: access.canView ? billing.method : null,
287
+ // Whether `pay` would take this person's word for it.
288
+ canPay: access.canManage
289
+ })
290
+ });
291
+ }
292
+ // Point of no return (AGL-1697): every refusal above answered without
293
+ // burning the key. Required here: this door is new, so no older bundle
294
+ // needs the no-key path, and a money call with no key is the hole
295
+ // `claimAttempt` warns about.
296
+ const idempotencyKey = String((_ref3 = (_req_headers_idempotencykey = req.headers['idempotency-key']) != null ? _req_headers_idempotencykey : req.headers['Idempotency-Key']) != null ? _ref3 : '').trim().slice(0, 200);
297
+ if (!idempotencyKey) {
298
+ return res.status(400).json({
299
+ error: 'Missing Idempotency-Key'
300
+ });
301
+ }
302
+ const claimed = await claimAttempt(firestore, {
303
+ kind: 'marketplace-pay',
304
+ scopeId: `${listingId}:${orgId}:${decoded.uid}`,
305
+ orgId,
306
+ key: idempotencyKey,
307
+ busyMessage: 'This purchase is already being processed'
308
+ });
309
+ if ('replay' in claimed) {
310
+ return res.status(claimed.replay.status).json(claimed.replay.body);
311
+ }
312
+ claim = claimed.claim;
313
+ const objectKey = (object)=>deriveStripeObjectKey(claim.stripeKey, object);
314
+ await voidAbandonedAttempts(stripe, customerId, listingId, orgId);
315
+ const saleMetadata = marketplaceSaleMetadataParams({
316
+ listingId,
317
+ buyerUid: decoded.uid,
318
+ buyerOrgId: orgId,
319
+ sellerOrgId: sale.sellerOrgId,
320
+ feeCents: sale.feeCents,
321
+ transferCents: sale.transferCents,
322
+ gaClientId: String((_ref4 = (_req_body3 = req.body) == null ? void 0 : _req_body3.gaClientId) != null ? _ref4 : '')
323
+ });
324
+ const created = await stripe('POST', 'invoices', new URLSearchParams(_extends({
325
+ customer: customerId,
326
+ collection_method: 'charge_automatically',
327
+ // Charged here, now, once: no retry schedule and no dunning. Nobody
328
+ // is owed a marketplace sale that did not go through.
329
+ auto_advance: 'false',
330
+ // ONLY this sale. The account's default API version sweeps a
331
+ // customer's pending invoice items onto any new invoice, which would
332
+ // put the workspace's metered usage on a charge with a seller's
333
+ // transfer on it.
334
+ pending_invoice_items_behavior: 'exclude',
335
+ default_payment_method: billing.paymentMethodId,
336
+ // Tax and funds flow exactly as the hosted checkout's (AGL-1544): the
337
+ // platform's charge, tax on top, and a FIXED transfer of the seller's
338
+ // pre-tax share, because the tax must stay with the platform that
339
+ // owes it. No transfer at all when the platform's own org is selling.
340
+ 'automatic_tax[enabled]': 'true'
341
+ }, sale.platformSale ? {} : {
342
+ 'transfer_data[destination]': String(sale.accountId),
343
+ 'transfer_data[amount]': String(sale.transferCents)
344
+ }, invoiceCardAuthenticationParams('one-time'), saleMetadata)), objectKey('invoice'));
345
+ const invoiceId = String((_ref5 = (_created_payload = created.payload) == null ? void 0 : _created_payload.id) != null ? _ref5 : '');
346
+ if (!created.ok || !invoiceId) {
347
+ var _ref10;
348
+ var _created_payload_error, _created_payload1;
349
+ console.error('Marketplace invoice not created', (_ref10 = (_created_payload1 = created.payload) == null ? void 0 : (_created_payload_error = _created_payload1.error) == null ? void 0 : _created_payload_error.code) != null ? _ref10 : null);
350
+ await claim.release();
351
+ return res.status(502).json({
352
+ error: 'The payment could not start. Nothing has been charged.'
353
+ });
354
+ }
355
+ const item = await stripe('POST', 'invoiceitems', new URLSearchParams({
356
+ customer: customerId,
357
+ invoice: invoiceId,
358
+ amount: String(sale.amountCents),
359
+ currency: 'usd',
360
+ tax_behavior: 'exclusive',
361
+ tax_code: MARKETPLACE_TAX_CODE,
362
+ description: listingNameOf(sale)
363
+ }), objectKey('item'));
364
+ if (!item.ok) {
365
+ var _ref11;
366
+ var _item_payload_error, _item_payload;
367
+ console.error('Marketplace invoice item not created', (_ref11 = (_item_payload = item.payload) == null ? void 0 : (_item_payload_error = _item_payload.error) == null ? void 0 : _item_payload_error.code) != null ? _ref11 : null);
368
+ await withdrawInvoice(stripe, invoiceId, 'draft');
369
+ await claim.release();
370
+ return res.status(502).json({
371
+ error: 'The payment could not start. Nothing has been charged.'
372
+ });
373
+ }
374
+ const finalized = await stripe('POST', `invoices/${encodeURIComponent(invoiceId)}/finalize`, new URLSearchParams({
375
+ auto_advance: 'false'
376
+ }), objectKey('finalize'));
377
+ if (!finalized.ok) {
378
+ var _ref12;
379
+ var _finalized_payload_error, _finalized_payload1, _finalized_payload_error1, _finalized_payload2;
380
+ await withdrawInvoice(stripe, invoiceId, 'draft');
381
+ await claim.release();
382
+ if (((_finalized_payload1 = finalized.payload) == null ? void 0 : (_finalized_payload_error = _finalized_payload1.error) == null ? void 0 : _finalized_payload_error.code) === 'customer_tax_location_invalid') {
383
+ const access = await billingAccess(decoded, orgId);
384
+ const refusal = prerequisiteRefusal('billing_address_required', access.canManage, sale);
385
+ return res.status(refusal.status).json(refusal.body);
386
+ }
387
+ console.error('Marketplace invoice not finalized', (_ref12 = (_finalized_payload2 = finalized.payload) == null ? void 0 : (_finalized_payload_error1 = _finalized_payload2.error) == null ? void 0 : _finalized_payload_error1.code) != null ? _ref12 : null);
388
+ return res.status(502).json({
389
+ error: 'The payment could not start. Nothing has been charged.'
390
+ });
391
+ }
392
+ // The total the buyer agreed to. A listing repriced or an address changed
393
+ // since the quote is a different purchase, and they see it first.
394
+ const expected = Number((_req_body4 = req.body) == null ? void 0 : _req_body4.expectedTotalCents);
395
+ if (Number.isFinite(expected) && Math.round(Number((_ref6 = (_finalized_payload = finalized.payload) == null ? void 0 : _finalized_payload.amount_due) != null ? _ref6 : 0)) !== Math.round(expected)) {
396
+ await withdrawInvoice(stripe, invoiceId, 'open');
397
+ await claim.release();
398
+ return res.status(409).json({
399
+ error: 'The total changed. Check the new total and pay again.',
400
+ code: 'total_changed'
401
+ });
402
+ }
403
+ const intentId = invoicePaymentIntentId(finalized.payload);
404
+ if (!intentId) {
405
+ console.error('Marketplace invoice has no payment intent', invoiceId);
406
+ await withdrawInvoice(stripe, invoiceId, 'open');
407
+ await claim.release();
408
+ return res.status(502).json({
409
+ error: 'The payment could not start. Nothing has been charged.'
410
+ });
411
+ }
412
+ // WHAT THE REFUND DOOR READS (AGL-2148). Stripe does not copy an invoice's
413
+ // metadata to its PaymentIntent, and the charge inherits the intent's, so
414
+ // without this the charge says nothing about whose sale it is. Before the
415
+ // payment, because a charge never picks up a later change.
416
+ const stamped = await stripe('POST', `payment_intents/${encodeURIComponent(intentId)}`, new URLSearchParams(saleMetadata), objectKey('intent'));
417
+ if (!stamped.ok) {
418
+ var _ref13;
419
+ var _stamped_payload_error, _stamped_payload;
420
+ console.error('Marketplace payment not stamped', (_ref13 = (_stamped_payload = stamped.payload) == null ? void 0 : (_stamped_payload_error = _stamped_payload.error) == null ? void 0 : _stamped_payload_error.code) != null ? _ref13 : null);
421
+ await withdrawInvoice(stripe, invoiceId, 'open');
422
+ await claim.release();
423
+ return res.status(502).json({
424
+ error: 'The payment could not start. Nothing has been charged.'
425
+ });
426
+ }
427
+ const paid = await stripe('POST', `invoices/${encodeURIComponent(invoiceId)}/pay`, undefined, objectKey('pay'));
428
+ const actor = {
429
+ uid: decoded.uid,
430
+ email: decoded.email
431
+ };
432
+ if (paid.ok && ((_paid_payload = paid.payload) == null ? void 0 : _paid_payload.status) === 'paid') {
433
+ const payload = await completePurchase(paid.payload, actor, orgId, listingNameOf(sale));
434
+ await claim.record(200, payload);
435
+ return res.status(200).json(payload);
436
+ }
437
+ if (paid.ok) {
438
+ // Accepted and not yet settled: a bank debit, for one. `invoice.paid`
439
+ // records it when it clears.
440
+ const payload = {
441
+ submitted: true,
442
+ invoiceId
443
+ };
444
+ await claim.record(200, payload);
445
+ return res.status(200).json(payload);
446
+ }
447
+ // The bank wants the buyer. As `pay-invoice` found against a real test
448
+ // invoice, the secret is not in this error: it is on the invoice's intent,
449
+ // so it takes one read, made only for this code.
450
+ let intent = (_ref7 = (_paid_payload1 = paid.payload) == null ? void 0 : (_paid_payload_error = _paid_payload1.error) == null ? void 0 : _paid_payload_error.payment_intent) != null ? _ref7 : null;
451
+ if (!(intent == null ? void 0 : intent.client_secret) && ((_paid_payload2 = paid.payload) == null ? void 0 : (_paid_payload_error1 = _paid_payload2.error) == null ? void 0 : _paid_payload_error1.code) === 'invoice_payment_intent_requires_action') {
452
+ var _ref14;
453
+ var _expanded_payload;
454
+ const expanded = await stripe('GET', `invoices/${encodeURIComponent(invoiceId)}?expand[]=payment_intent`);
455
+ if (expanded.ok) intent = (_ref14 = (_expanded_payload = expanded.payload) == null ? void 0 : _expanded_payload.payment_intent) != null ? _ref14 : null;
456
+ }
457
+ if ((intent == null ? void 0 : intent.client_secret) && (intent == null ? void 0 : intent.status) === 'requires_action') {
458
+ const payload = {
459
+ requiresAction: true,
460
+ paymentClientSecret: intent.client_secret,
461
+ invoiceId
462
+ };
463
+ await claim.record(200, payload);
464
+ return res.status(200).json(payload);
465
+ }
466
+ // Anything but a decline leaves the charge unknown, so it is read rather
467
+ // than assumed: a void of a paid invoice is refused, and a buyer who was
468
+ // charged must not be told they were not.
469
+ if (paid.status !== 402) {
470
+ var _state_payload;
471
+ const state = await stripe('GET', `invoices/${encodeURIComponent(invoiceId)}`);
472
+ if (((_state_payload = state.payload) == null ? void 0 : _state_payload.status) === 'paid') {
473
+ const payload = await completePurchase(state.payload, actor, orgId, listingNameOf(sale));
474
+ await claim.record(200, payload);
475
+ return res.status(200).json(payload);
476
+ }
477
+ }
478
+ await withdrawInvoice(stripe, invoiceId, 'open');
479
+ console.error('Marketplace payment declined', orgId, (_ref8 = (_paid_payload3 = paid.payload) == null ? void 0 : (_paid_payload_error2 = _paid_payload3.error) == null ? void 0 : _paid_payload_error2.code) != null ? _ref8 : null);
480
+ const declined = {
481
+ // Stripe's own reason: the buyer needs to know whether to try another card.
482
+ error: (_ref9 = (_paid_payload4 = paid.payload) == null ? void 0 : (_paid_payload_error3 = _paid_payload4.error) == null ? void 0 : _paid_payload_error3.message) != null ? _ref9 : 'That payment did not go through. Nothing has been charged.',
483
+ declined: true
484
+ };
485
+ // Finished, and recorded as finished: a repeat of this attempt hears the
486
+ // same answer, and pressing Pay again is a new attempt with a new key.
487
+ await claim.record(402, declined);
488
+ return res.status(402).json(declined);
489
+ } catch (error) {
490
+ console.error(error);
491
+ await (claim == null ? void 0 : claim.release());
492
+ return res.status(500).json({
493
+ error: 'The payment failed. Check Billing before trying again.'
494
+ });
495
+ }
496
+ };
497
+ /**
498
+ * After the bank's step: whether that attempt paid.
499
+ *
500
+ * Only the person who started it, for the listing it was for, on this
501
+ * workspace's customer: the invoice id comes from the browser, and an id from
502
+ * elsewhere must not read as anything but unknown.
503
+ */ async function settle(input) {
504
+ var _ref, _ref1, _ref2;
505
+ var _req_body, _invoice_customer, _invoice_metadata, _invoice_metadata1, _invoice_payment_intent;
506
+ const { req, res, stripe, decoded, orgId, listingId, customerId } = input;
507
+ const invoiceId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.invoiceId) != null ? _ref : '').trim();
508
+ if (!invoiceId) return res.status(400).json({
509
+ error: 'Missing invoice'
510
+ });
511
+ const read = await stripe('GET', `invoices/${encodeURIComponent(invoiceId)}?expand[]=payment_intent`);
512
+ const invoice = read.payload;
513
+ const invoiceCustomer = typeof (invoice == null ? void 0 : invoice.customer) === 'string' ? invoice.customer : (_ref1 = invoice == null ? void 0 : (_invoice_customer = invoice.customer) == null ? void 0 : _invoice_customer.id) != null ? _ref1 : null;
514
+ if (!read.ok || !customerId || invoiceCustomer !== customerId || !isMarketplaceInvoice(invoice) || (invoice == null ? void 0 : (_invoice_metadata = invoice.metadata) == null ? void 0 : _invoice_metadata.listingId) !== listingId || (invoice == null ? void 0 : (_invoice_metadata1 = invoice.metadata) == null ? void 0 : _invoice_metadata1.buyerUid) !== decoded.uid) {
515
+ return res.status(404).json({
516
+ error: 'That payment is not on this workspace.'
517
+ });
518
+ }
519
+ if (invoice.status === 'paid') {
520
+ var _ref3;
521
+ var _invoice_lines_data_, _invoice_lines_data, _invoice_lines;
522
+ const name = String((_ref3 = invoice == null ? void 0 : (_invoice_lines = invoice.lines) == null ? void 0 : (_invoice_lines_data = _invoice_lines.data) == null ? void 0 : (_invoice_lines_data_ = _invoice_lines_data[0]) == null ? void 0 : _invoice_lines_data_.description) != null ? _ref3 : listingId);
523
+ return res.status(200).json(await completePurchase(invoice, decoded, orgId, name));
524
+ }
525
+ // Authenticated and still landing: never voided under the charge.
526
+ const intentStatus = String((_ref2 = invoice == null ? void 0 : (_invoice_payment_intent = invoice.payment_intent) == null ? void 0 : _invoice_payment_intent.status) != null ? _ref2 : '');
527
+ if (intentStatus === 'succeeded' || intentStatus === 'processing') {
528
+ return res.status(200).json({
529
+ submitted: true,
530
+ invoiceId
531
+ });
532
+ }
533
+ if (invoice.status === 'open') await withdrawInvoice(stripe, invoiceId, 'open');
534
+ return res.status(402).json({
535
+ error: 'That payment was not completed. Nothing has been charged.',
536
+ declined: true
537
+ });
538
+ }
539
+
540
+ //# sourceMappingURL=pay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/pay.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n firebaseAdmin,\n logOrgActivity,\n memberHasOrgPermission,\n readOrgBilling,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin'\nimport { invoiceCardAuthenticationParams } from '@aglyn/tenant-data-admin/server/stripe-card-authentication'\nimport {\n readBillingDetails,\n type BillingDetails,\n} from '@aglyn/tenant-data-admin/server/stripe-payment-method'\nimport {\n claimAttempt,\n deriveStripeObjectKey,\n type AttemptClaim,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport { permissionRefusalText } from '@aglyn/aglyn/app-utils/org-permissions'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport {\n invoicePaymentIntentId,\n isMarketplaceInvoice,\n marketplaceSaleMetadataParams,\n} from '../model/marketplace-invoice'\nimport { recordMarketplaceInvoicePurchase } from './billing-webhook'\nimport { MARKETPLACE_TAX_CODE, resolveSale, type MarketplaceSale } from './sale'\n\n/**\n * IN-APP PAYMENT FOR A MARKETPLACE LISTING (AGL-4049).\n *\n * The marketplace pays the way billing does: the workspace's card on file, in\n * the console, with the bank's own step when the bank asks for one. Nothing\n * redirects to a payment page.\n *\n * ## The shape of the charge, and what was measured\n *\n * An Invoice on the workspace's billing customer, driven in test mode before\n * this was built (see the PR):\n *\n * - `automatic_tax` and a FIXED `transfer_data[amount]` work together. On a\n * $10.00 sale to a Texas address the charge was $10.66, the 80% base and\n * 8.25% rate the filing describes (`MARKETPLACE_TAX_CODE`), and the seller's\n * transfer was exactly $8.00, leaving the tax and the fee on the platform.\n * That is the same split the hosted checkout makes (AGL-1544).\n * - The invoice's metadata does NOT reach its PaymentIntent or its charge.\n * The refund door reads `charge.metadata.type` during the window before\n * the purchase exists (AGL-2148), so the intent is stamped after finalize\n * and before paying, and the charge then carries it.\n * - A customer with no address cannot finalize an `automatic_tax` invoice\n * (`customer_tax_location_invalid`), so an address is a prerequisite, shown\n * before the click rather than met as an error after it.\n *\n * ## Three actions, one door\n *\n * - `quote`: every gate, then the total from a Stripe Tax calculation and the\n * card that would be charged. Nothing is created. The window that asks the\n * buyer to pay shows this, because a saved card charged without its total\n * in front of the buyer is not a purchase they agreed to.\n * - `pay` (the default): the Invoice, finalized and paid. Priced by the same\n * engine, and refused if the total moved from the one the buyer saw.\n * - `settle`: after the bank's step, whether that attempt paid.\n *\n * ## Who may do what\n *\n * Two questions, asked separately. The buyer permission (`hosts.create` for a\n * starter, `plugins.install` otherwise) decides who may see the price and get\n * a quote. Charging the workspace's saved card moves money, so `pay` also takes\n * `billing.manage`, asked exactly as `billing/pay-invoice` asks it, and `settle`\n * does too, so that finishing an attempt is never a way round it. The quote says\n * whether this person can pay, so the panel can say so before anyone presses\n * anything.\n *\n * ## The purchase is written here as well as by the webhook\n *\n * Stripe's answer to `pay` is authoritative, and the picker's very next\n * request creates a site from what was just bought. Waiting for `invoice.paid`\n * would race it. The webhook remains the record of every other way an invoice\n * gets paid, and both write the same document by the invoice id, so whichever\n * lands second changes nothing.\n *\n * ## Nothing is left owing\n *\n * A declined attempt voids its invoice: the buyer has been told \"no\", and an\n * open invoice in Billing would tell them they owe it. An attempt abandoned at\n * the bank's step is voided by `settle`, or by the next attempt at the same\n * listing if the tab was closed.\n *\n * lockdown: `marketplace/pay` carries `checkout` and `marketplace-installs`,\n * like the hosted checkout (AGL-1545), and the dispatcher answers both.\n */\n\ninterface StripeAnswer {\n ok: boolean\n status: number\n payload: any\n}\n\ntype StripeCaller = (\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: URLSearchParams,\n idempotencyKey?: string | null,\n) => Promise<StripeAnswer>\n\nfunction stripeCaller(secretKey: string): StripeCaller {\n return async (method, path, body, idempotencyKey) => {\n const response = await fetch(`https://api.stripe.com/v1/${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${secretKey}`,\n ...(body ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}),\n ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),\n },\n ...(body ? { body: body.toString() } : {}),\n })\n const payload = await response.json().catch(() => null)\n return { ok: response.ok, status: response.status, payload }\n }\n}\n\n/** Why a workspace cannot be charged yet, and whether this person can fix it here. */\nfunction prerequisiteRefusal(\n code: 'card_required' | 'billing_address_required',\n canManageBilling: boolean,\n sale: MarketplaceSale,\n): { status: number; body: Record<string, unknown> } {\n return {\n status: 412,\n body: {\n error:\n code === 'card_required'\n ? 'Add a card in Billing first'\n : 'Add a billing address in Billing first: sales tax is calculated from it.',\n code,\n canManageBilling,\n // The price stays in front of someone who cannot meet this themselves.\n amountCents: sale.amountCents,\n },\n }\n}\n\n/** The refusal for someone who may see the price but may not charge the workspace's card. */\nconst billingManageRefusal = () => ({\n error: permissionRefusalText('billing.manage'),\n code: 'billing_manage_required',\n})\n\n/** What this person may do with the workspace's billing, as billing itself asks it. */\nasync function billingAccess(\n decoded: { uid: string; [claim: string]: unknown },\n orgId: string,\n): Promise<{ canManage: boolean; canView: boolean }> {\n if (decoded['staff'] === true) return { canManage: true, canView: true }\n const actor = await resolveOrgMembership(decoded.uid, orgId)\n const [canManage, canView] = await Promise.all([\n memberHasOrgPermission(orgId, actor?.member, 'billing.manage'),\n memberHasOrgPermission(orgId, actor?.member, 'billing.view'),\n ])\n return { canManage, canView }\n}\n\n/** The Stripe Tax calculation a quote reads, or `null` when Stripe refused it. */\nasync function priceSale(\n stripe: StripeCaller,\n customerId: string,\n listingId: string,\n sale: MarketplaceSale,\n): Promise<{ taxCents: number; totalCents: number } | null | 'no-tax-location'> {\n const calculation = await stripe(\n 'POST',\n 'tax/calculations',\n new URLSearchParams({\n currency: 'usd',\n customer: customerId,\n 'line_items[0][amount]': String(sale.amountCents),\n 'line_items[0][reference]': listingId.slice(0, 200),\n 'line_items[0][tax_behavior]': 'exclusive',\n 'line_items[0][tax_code]': MARKETPLACE_TAX_CODE,\n }),\n )\n if (!calculation.ok) {\n // Measured: an address Stripe cannot place (a country and nothing else)\n // answers a 400 on the `customer` param with no code. The invoice's own\n // refusal, `customer_tax_location_invalid`, is the same fact.\n const refused = calculation.payload?.error\n if (refused?.code === 'customer_tax_location_invalid' || refused?.param === 'customer') {\n return 'no-tax-location'\n }\n console.error('Stripe tax calculation failed', refused?.code ?? null)\n return null\n }\n return {\n taxCents: Math.round(Number(calculation.payload?.tax_amount_exclusive ?? 0)),\n totalCents: Math.round(Number(calculation.payload?.amount_total ?? sale.amountCents)),\n }\n}\n\n/** Takes an attempt back: a draft is deleted, a finalized invoice is voided. */\nasync function withdrawInvoice(\n stripe: StripeCaller,\n invoiceId: string,\n state: 'draft' | 'open',\n): Promise<void> {\n const answer =\n state === 'draft'\n ? await stripe('DELETE', `invoices/${encodeURIComponent(invoiceId)}`)\n : await stripe('POST', `invoices/${encodeURIComponent(invoiceId)}/void`)\n if (!answer.ok) {\n console.error('Marketplace invoice not withdrawn', invoiceId, answer.payload?.error?.code ?? null)\n }\n}\n\n/**\n * Voids an earlier attempt at this listing that was left open at the bank's\n * step, by a closed tab for instance. Left there it is a second way to pay for\n * the same licence, sitting in Billing as money owed.\n */\nasync function voidAbandonedAttempts(\n stripe: StripeCaller,\n customerId: string,\n listingId: string,\n orgId: string,\n): Promise<void> {\n const open = await stripe(\n 'GET',\n `invoices?customer=${encodeURIComponent(customerId)}&status=open&limit=20`,\n )\n const invoices: any[] = Array.isArray(open.payload?.data) ? open.payload.data : []\n for (const invoice of invoices) {\n if (\n isMarketplaceInvoice(invoice) &&\n invoice?.metadata?.listingId === listingId &&\n invoice?.metadata?.buyerOrgId === orgId\n ) {\n await withdrawInvoice(stripe, String(invoice.id), 'open')\n }\n }\n}\n\n/** Records a paid invoice and answers the buyer. The money has moved: nothing here may undo that. */\nasync function completePurchase(\n invoice: any,\n actor: { uid: string; email?: string },\n orgId: string,\n listingName: string,\n): Promise<{ paid: true; invoiceId: string }> {\n const invoiceId = String(invoice?.id ?? '')\n try {\n await recordMarketplaceInvoicePurchase(invoice)\n } catch (error) {\n // The webhook writes the same document: this is a race lost, not a sale.\n console.error('Marketplace purchase left for the webhook to record', invoiceId, error)\n }\n await logOrgActivity(\n orgId,\n { uid: actor.uid, email: actor.email },\n `Bought \"${listingName}\" from the marketplace`,\n { type: 'org', id: orgId },\n ).catch((error: unknown) => console.error('Marketplace purchase not logged', error))\n return { paid: true, invoiceId }\n}\n\nconst listingNameOf = (sale: MarketplaceSale) =>\n String(sale.listing['displayName'] ?? 'Marketplace component').slice(0, 120)\n\nexport const payHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const secretKey = process.env['STRIPE_SECRET_KEY']\n if (!secretKey) {\n return res\n .status(501)\n .json({ error: 'Purchases are not configured (STRIPE_SECRET_KEY).' })\n }\n const listingId = String(req.body?.listingId ?? '').trim()\n const orgId = String(req.body?.orgId ?? '').trim()\n const action = String(req.body?.action ?? 'pay')\n if (!listingId) return res.status(400).json({ error: 'Missing listingId' })\n if (!orgId) return res.status(400).json({ error: 'Missing orgId' })\n if (action !== 'quote' && action !== 'pay' && action !== 'settle') {\n return res.status(400).json({ error: 'Unknown action' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n\n const stripe = stripeCaller(secretKey)\n let claim: AttemptClaim | null = null\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n // The org this licence is for is asked of the person's own membership,\n // as the hosted checkout does (AGL-2331). It is the org whose card pays,\n // so naming one you are not in is refused before anything is read.\n const membership = await resolveOrgPermissions(decoded.uid, { orgId, hostId: null })\n if (membership.orgId !== orgId) {\n return res.status(403).json({ error: 'Not a member of that organization' })\n }\n const firestore = firebaseAdmin.app().firestore()\n const buyerOrg = await firestore.collection('orgs').doc(orgId).get()\n // The dispatcher reads a lock off a site, and this door names none.\n if (buyerOrg.get('suspendedAt') != null) {\n return res.status(403).json({ error: 'This organization is locked, so it cannot buy anything.' })\n }\n const customerId = (await readOrgBilling(orgId)).stripeCustomerId as string | undefined\n\n if (action === 'settle') {\n // Not a way round `pay`: an attempt is finished by someone who could start it.\n if (!(await billingAccess(decoded, orgId)).canManage) {\n return res.status(403).json(billingManageRefusal())\n }\n return await settle({ req, res, stripe, decoded, orgId, listingId, customerId })\n }\n\n const resolved = await resolveSale({\n firestore,\n listingId,\n buyer: { uid: decoded.uid, orgId, permissions: membership.permissions },\n })\n if ('refusal' in resolved) {\n return res.status(resolved.refusal.status).json(resolved.refusal.body)\n }\n const { sale } = resolved\n\n // Paying moves money: the same check `billing/pay-invoice` makes, on top of\n // the buyer permission the sale has just asked for.\n if (action === 'pay' && !(await billingAccess(decoded, orgId)).canManage) {\n return res.status(403).json(billingManageRefusal())\n }\n\n const billing: BillingDetails | null = customerId\n ? await readBillingDetails(secretKey, customerId)\n : { paymentMethodId: null, method: null, hasAddress: false }\n if (!billing) {\n return res.status(502).json({ error: 'We could not read your billing details just now.' })\n }\n const needed = !billing.paymentMethodId\n ? 'card_required'\n : !billing.hasAddress\n ? 'billing_address_required'\n : null\n if (needed || !customerId || !billing.paymentMethodId) {\n const access = await billingAccess(decoded, orgId)\n const refusal = prerequisiteRefusal(needed ?? 'card_required', access.canManage, sale)\n return res.status(refusal.status).json(refusal.body)\n }\n\n if (action === 'quote') {\n const priced = await priceSale(stripe, customerId, listingId, sale)\n if (priced === 'no-tax-location') {\n const access = await billingAccess(decoded, orgId)\n const refusal = prerequisiteRefusal('billing_address_required', access.canManage, sale)\n return res.status(refusal.status).json(refusal.body)\n }\n if (!priced) {\n return res.status(502).json({ error: 'We could not price that just now.' })\n }\n const access = await billingAccess(decoded, orgId)\n return res.status(200).json({\n quote: {\n listingName: listingNameOf(sale),\n amountCents: sale.amountCents,\n ...priced,\n currency: 'usd',\n // The card itself is billing's to show: someone who may buy but may\n // not see billing is told \"the card on file\" and nothing about it.\n card: access.canView ? billing.method : null,\n // Whether `pay` would take this person's word for it.\n canPay: access.canManage,\n },\n })\n }\n\n // Point of no return (AGL-1697): every refusal above answered without\n // burning the key. Required here: this door is new, so no older bundle\n // needs the no-key path, and a money call with no key is the hole\n // `claimAttempt` warns about.\n const idempotencyKey = String(\n req.headers['idempotency-key'] ?? req.headers['Idempotency-Key'] ?? '',\n )\n .trim()\n .slice(0, 200)\n if (!idempotencyKey) {\n return res.status(400).json({ error: 'Missing Idempotency-Key' })\n }\n const claimed = await claimAttempt(firestore, {\n kind: 'marketplace-pay',\n scopeId: `${listingId}:${orgId}:${decoded.uid}`,\n orgId,\n key: idempotencyKey,\n busyMessage: 'This purchase is already being processed',\n })\n if ('replay' in claimed) {\n return res.status(claimed.replay.status).json(claimed.replay.body)\n }\n claim = claimed.claim\n const objectKey = (object: string) =>\n deriveStripeObjectKey((claim as AttemptClaim).stripeKey, object)\n\n await voidAbandonedAttempts(stripe, customerId, listingId, orgId)\n\n const saleMetadata = marketplaceSaleMetadataParams({\n listingId,\n buyerUid: decoded.uid,\n buyerOrgId: orgId,\n sellerOrgId: sale.sellerOrgId,\n feeCents: sale.feeCents,\n transferCents: sale.transferCents,\n gaClientId: String(req.body?.gaClientId ?? ''),\n })\n const created = await stripe(\n 'POST',\n 'invoices',\n new URLSearchParams({\n customer: customerId,\n collection_method: 'charge_automatically',\n // Charged here, now, once: no retry schedule and no dunning. Nobody\n // is owed a marketplace sale that did not go through.\n auto_advance: 'false',\n // ONLY this sale. The account's default API version sweeps a\n // customer's pending invoice items onto any new invoice, which would\n // put the workspace's metered usage on a charge with a seller's\n // transfer on it.\n pending_invoice_items_behavior: 'exclude',\n default_payment_method: billing.paymentMethodId,\n // Tax and funds flow exactly as the hosted checkout's (AGL-1544): the\n // platform's charge, tax on top, and a FIXED transfer of the seller's\n // pre-tax share, because the tax must stay with the platform that\n // owes it. No transfer at all when the platform's own org is selling.\n 'automatic_tax[enabled]': 'true',\n ...(sale.platformSale\n ? {}\n : {\n 'transfer_data[destination]': String(sale.accountId),\n 'transfer_data[amount]': String(sale.transferCents),\n }),\n // 3-D Secure, from the one seam every card payment shares (AGL-3360).\n ...invoiceCardAuthenticationParams('one-time'),\n ...saleMetadata,\n }),\n objectKey('invoice'),\n )\n const invoiceId = String(created.payload?.id ?? '')\n if (!created.ok || !invoiceId) {\n console.error('Marketplace invoice not created', created.payload?.error?.code ?? null)\n await claim.release()\n return res.status(502).json({ error: 'The payment could not start. Nothing has been charged.' })\n }\n const item = await stripe(\n 'POST',\n 'invoiceitems',\n new URLSearchParams({\n customer: customerId,\n invoice: invoiceId,\n amount: String(sale.amountCents),\n currency: 'usd',\n tax_behavior: 'exclusive',\n tax_code: MARKETPLACE_TAX_CODE,\n description: listingNameOf(sale),\n }),\n objectKey('item'),\n )\n if (!item.ok) {\n console.error('Marketplace invoice item not created', item.payload?.error?.code ?? null)\n await withdrawInvoice(stripe, invoiceId, 'draft')\n await claim.release()\n return res.status(502).json({ error: 'The payment could not start. Nothing has been charged.' })\n }\n const finalized = await stripe(\n 'POST',\n `invoices/${encodeURIComponent(invoiceId)}/finalize`,\n new URLSearchParams({ auto_advance: 'false' }),\n objectKey('finalize'),\n )\n if (!finalized.ok) {\n await withdrawInvoice(stripe, invoiceId, 'draft')\n await claim.release()\n if (finalized.payload?.error?.code === 'customer_tax_location_invalid') {\n const access = await billingAccess(decoded, orgId)\n const refusal = prerequisiteRefusal('billing_address_required', access.canManage, sale)\n return res.status(refusal.status).json(refusal.body)\n }\n console.error('Marketplace invoice not finalized', finalized.payload?.error?.code ?? null)\n return res.status(502).json({ error: 'The payment could not start. Nothing has been charged.' })\n }\n // The total the buyer agreed to. A listing repriced or an address changed\n // since the quote is a different purchase, and they see it first.\n const expected = Number(req.body?.expectedTotalCents)\n if (\n Number.isFinite(expected) &&\n Math.round(Number(finalized.payload?.amount_due ?? 0)) !== Math.round(expected)\n ) {\n await withdrawInvoice(stripe, invoiceId, 'open')\n await claim.release()\n return res.status(409).json({\n error: 'The total changed. Check the new total and pay again.',\n code: 'total_changed',\n })\n }\n const intentId = invoicePaymentIntentId(finalized.payload)\n if (!intentId) {\n console.error('Marketplace invoice has no payment intent', invoiceId)\n await withdrawInvoice(stripe, invoiceId, 'open')\n await claim.release()\n return res.status(502).json({ error: 'The payment could not start. Nothing has been charged.' })\n }\n // WHAT THE REFUND DOOR READS (AGL-2148). Stripe does not copy an invoice's\n // metadata to its PaymentIntent, and the charge inherits the intent's, so\n // without this the charge says nothing about whose sale it is. Before the\n // payment, because a charge never picks up a later change.\n const stamped = await stripe(\n 'POST',\n `payment_intents/${encodeURIComponent(intentId)}`,\n new URLSearchParams(saleMetadata),\n objectKey('intent'),\n )\n if (!stamped.ok) {\n console.error('Marketplace payment not stamped', stamped.payload?.error?.code ?? null)\n await withdrawInvoice(stripe, invoiceId, 'open')\n await claim.release()\n return res.status(502).json({ error: 'The payment could not start. Nothing has been charged.' })\n }\n\n const paid = await stripe(\n 'POST',\n `invoices/${encodeURIComponent(invoiceId)}/pay`,\n undefined,\n objectKey('pay'),\n )\n const actor = { uid: decoded.uid, email: decoded.email }\n if (paid.ok && paid.payload?.status === 'paid') {\n const payload = await completePurchase(paid.payload, actor, orgId, listingNameOf(sale))\n await claim.record(200, payload)\n return res.status(200).json(payload)\n }\n if (paid.ok) {\n // Accepted and not yet settled: a bank debit, for one. `invoice.paid`\n // records it when it clears.\n const payload = { submitted: true, invoiceId }\n await claim.record(200, payload)\n return res.status(200).json(payload)\n }\n // The bank wants the buyer. As `pay-invoice` found against a real test\n // invoice, the secret is not in this error: it is on the invoice's intent,\n // so it takes one read, made only for this code.\n let intent = paid.payload?.error?.payment_intent ?? null\n if (\n !intent?.client_secret &&\n paid.payload?.error?.code === 'invoice_payment_intent_requires_action'\n ) {\n const expanded = await stripe(\n 'GET',\n `invoices/${encodeURIComponent(invoiceId)}?expand[]=payment_intent`,\n )\n if (expanded.ok) intent = expanded.payload?.payment_intent ?? null\n }\n if (intent?.client_secret && intent?.status === 'requires_action') {\n const payload = {\n requiresAction: true,\n paymentClientSecret: intent.client_secret,\n invoiceId,\n }\n await claim.record(200, payload)\n return res.status(200).json(payload)\n }\n // Anything but a decline leaves the charge unknown, so it is read rather\n // than assumed: a void of a paid invoice is refused, and a buyer who was\n // charged must not be told they were not.\n if (paid.status !== 402) {\n const state = await stripe('GET', `invoices/${encodeURIComponent(invoiceId)}`)\n if (state.payload?.status === 'paid') {\n const payload = await completePurchase(state.payload, actor, orgId, listingNameOf(sale))\n await claim.record(200, payload)\n return res.status(200).json(payload)\n }\n }\n await withdrawInvoice(stripe, invoiceId, 'open')\n console.error('Marketplace payment declined', orgId, paid.payload?.error?.code ?? null)\n const declined = {\n // Stripe's own reason: the buyer needs to know whether to try another card.\n error:\n paid.payload?.error?.message ??\n 'That payment did not go through. Nothing has been charged.',\n declined: true,\n }\n // Finished, and recorded as finished: a repeat of this attempt hears the\n // same answer, and pressing Pay again is a new attempt with a new key.\n await claim.record(402, declined)\n return res.status(402).json(declined)\n } catch (error) {\n console.error(error)\n await claim?.release()\n return res.status(500).json({ error: 'The payment failed. Check Billing before trying again.' })\n }\n}\n\n/**\n * After the bank's step: whether that attempt paid.\n *\n * Only the person who started it, for the listing it was for, on this\n * workspace's customer: the invoice id comes from the browser, and an id from\n * elsewhere must not read as anything but unknown.\n */\nasync function settle(input: {\n req: Parameters<PluginApiHandler>[0]\n res: Parameters<PluginApiHandler>[1]\n stripe: StripeCaller\n decoded: { uid: string; email?: string }\n orgId: string\n listingId: string\n customerId: string | undefined\n}) {\n const { req, res, stripe, decoded, orgId, listingId, customerId } = input\n const invoiceId = String(req.body?.invoiceId ?? '').trim()\n if (!invoiceId) return res.status(400).json({ error: 'Missing invoice' })\n const read = await stripe(\n 'GET',\n `invoices/${encodeURIComponent(invoiceId)}?expand[]=payment_intent`,\n )\n const invoice = read.payload\n const invoiceCustomer =\n typeof invoice?.customer === 'string' ? invoice.customer : (invoice?.customer?.id ?? null)\n if (\n !read.ok ||\n !customerId ||\n invoiceCustomer !== customerId ||\n !isMarketplaceInvoice(invoice) ||\n invoice?.metadata?.listingId !== listingId ||\n invoice?.metadata?.buyerUid !== decoded.uid\n ) {\n return res.status(404).json({ error: 'That payment is not on this workspace.' })\n }\n if (invoice.status === 'paid') {\n const name = String(invoice?.lines?.data?.[0]?.description ?? listingId)\n return res.status(200).json(await completePurchase(invoice, decoded, orgId, name))\n }\n // Authenticated and still landing: never voided under the charge.\n const intentStatus = String(invoice?.payment_intent?.status ?? '')\n if (intentStatus === 'succeeded' || intentStatus === 'processing') {\n return res.status(200).json({ submitted: true, invoiceId })\n }\n if (invoice.status === 'open') await withdrawInvoice(stripe, invoiceId, 'open')\n return res.status(402).json({\n error: 'That payment was not completed. Nothing has been charged.',\n declined: true,\n })\n}\n"],"names":["firebaseAdmin","logOrgActivity","memberHasOrgPermission","readOrgBilling","resolveOrgMembership","invoiceCardAuthenticationParams","readBillingDetails","claimAttempt","deriveStripeObjectKey","permissionRefusalText","resolveOrgPermissions","invoicePaymentIntentId","isMarketplaceInvoice","marketplaceSaleMetadataParams","recordMarketplaceInvoicePurchase","MARKETPLACE_TAX_CODE","resolveSale","stripeCaller","secretKey","method","path","body","idempotencyKey","response","fetch","headers","Authorization","toString","payload","json","catch","ok","status","prerequisiteRefusal","code","canManageBilling","sale","error","amountCents","billingManageRefusal","billingAccess","decoded","orgId","canManage","canView","actor","uid","Promise","all","member","priceSale","stripe","customerId","listingId","calculation","URLSearchParams","currency","customer","String","slice","refused","param","console","taxCents","Math","round","Number","tax_amount_exclusive","totalCents","amount_total","withdrawInvoice","invoiceId","state","answer","encodeURIComponent","voidAbandonedAttempts","open","invoices","Array","isArray","data","invoice","metadata","buyerOrgId","id","completePurchase","listingName","email","type","paid","listingNameOf","listing","payHandler","req","res","process","env","trim","action","authorization","idToken","startsWith","length","undefined","claim","created","finalized","app","auth","verifyIdToken","membership","hostId","firestore","buyerOrg","collection","doc","get","stripeCustomerId","settle","resolved","buyer","permissions","refusal","billing","paymentMethodId","hasAddress","needed","access","priced","quote","card","canPay","claimed","kind","scopeId","key","busyMessage","replay","objectKey","object","stripeKey","saleMetadata","buyerUid","sellerOrgId","feeCents","transferCents","gaClientId","collection_method","auto_advance","pending_invoice_items_behavior","default_payment_method","platformSale","accountId","release","item","amount","tax_behavior","tax_code","description","expected","expectedTotalCents","isFinite","amount_due","intentId","stamped","record","submitted","intent","payment_intent","client_secret","expanded","requiresAction","paymentClientSecret","declined","message","input","read","invoiceCustomer","name","lines","intentStatus"],"mappings":"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"}