@aglyn/plugins-marketplace 1.0.0-beta.200 → 1.0.0-beta.202
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/package.json +14 -14
- package/src/lib/model/publisher-risk.d.ts +52 -0
- package/src/lib/model/publisher-risk.js +74 -0
- package/src/lib/model/publisher-risk.js.map +1 -0
- package/src/lib/server/billing-webhook.js +72 -4
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/checkout.js +21 -2
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/connect.js +15 -1
- package/src/lib/server/connect.js.map +1 -1
- package/src/lib/server/install-dataset-schema.js +3 -1
- package/src/lib/server/install-dataset-schema.js.map +1 -1
- package/src/lib/server/install-email-starter.js +3 -1
- package/src/lib/server/install-email-starter.js.map +1 -1
- package/src/lib/server/install-email-template.js +3 -1
- package/src/lib/server/install-email-template.js.map +1 -1
- package/src/lib/server/install-layout.js +3 -1
- package/src/lib/server/install-layout.js.map +1 -1
- package/src/lib/server/install-plugin.js +3 -1
- package/src/lib/server/install-plugin.js.map +1 -1
- package/src/lib/server/install-template.js +3 -1
- package/src/lib/server/install-template.js.map +1 -1
- package/src/lib/server/install-theme.js +3 -1
- package/src/lib/server/install-theme.js.map +1 -1
- package/src/lib/server/install.js +3 -1
- package/src/lib/server/install.js.map +1 -1
- package/src/lib/server/listing-screen.d.ts +83 -0
- package/src/lib/server/listing-screen.js +244 -0
- package/src/lib/server/listing-screen.js.map +1 -0
- package/src/lib/server/publish-dataset-schema.js +12 -0
- package/src/lib/server/publish-dataset-schema.js.map +1 -1
- package/src/lib/server/publish-email-starter.js +15 -1
- package/src/lib/server/publish-email-starter.js.map +1 -1
- package/src/lib/server/publish-email-template.js +15 -1
- package/src/lib/server/publish-email-template.js.map +1 -1
- package/src/lib/server/publish-layout.js +15 -1
- package/src/lib/server/publish-layout.js.map +1 -1
- package/src/lib/server/publish-plugin.js +41 -3
- package/src/lib/server/publish-plugin.js.map +1 -1
- package/src/lib/server/publish-template.js +15 -1
- package/src/lib/server/publish-template.js.map +1 -1
- package/src/lib/server/publish-theme.js +14 -1
- package/src/lib/server/publish-theme.js.map +1 -1
- package/src/lib/server/publish.js +15 -1
- package/src/lib/server/publish.js.map +1 -1
- package/src/lib/server/publisher-profile-save.js +36 -0
- package/src/lib/server/publisher-profile-save.js.map +1 -1
- package/src/lib/server/publisher-review-email.js +15 -5
- package/src/lib/server/publisher-review-email.js.map +1 -1
- package/src/lib/server/reviews.d.ts +8 -0
- package/src/lib/server/reviews.js +43 -3
- package/src/lib/server/reviews.js.map +1 -1
- package/src/lib/server/sale-risk.d.ts +105 -0
- package/src/lib/server/sale-risk.js +315 -0
- package/src/lib/server/sale-risk.js.map +1 -0
- package/src/lib/server/update-artifact.js +3 -1
- package/src/lib/server/update-artifact.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-marketplace",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.202",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,19 +25,19 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/aglyn-markdown-editor": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
40
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.202",
|
|
29
|
+
"@aglyn/aglyn-markdown-editor": "1.0.0-beta.202",
|
|
30
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.202",
|
|
31
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.202",
|
|
32
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.202",
|
|
33
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.202",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.202",
|
|
35
|
+
"@aglyn/shared-util-email": "1.0.0-beta.202",
|
|
36
|
+
"@aglyn/shared-util-http": "1.0.0-beta.202",
|
|
37
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.202",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.202",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.202",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.202",
|
|
41
41
|
"@swc/helpers": "0.5.23"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/** A publisher workspace younger than this many days is in the tier. */
|
|
18
|
+
export declare const YOUNG_PUBLISHER_DAYS = 30;
|
|
19
|
+
/** How long a young publisher's payouts sit in its account. */
|
|
20
|
+
export declare const YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS = 14;
|
|
21
|
+
/** A young publisher's single sale at or above this files a staff signal. */
|
|
22
|
+
export declare const YOUNG_PUBLISHER_REVIEW_SALE_CENTS = 10000;
|
|
23
|
+
/**
|
|
24
|
+
* Is a publisher workspace of this age (days) in the young tier? `null` — a
|
|
25
|
+
* creation date that cannot be read — is an existing customer, as the
|
|
26
|
+
* phishing screen reads it, and is not young.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isYoungPublisher(ageDays: number | null | undefined): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* The payout delay a publisher's account should carry: a number of days, or
|
|
31
|
+
* `null` for the account's standard (minimum) schedule.
|
|
32
|
+
*/
|
|
33
|
+
export declare function publisherPayoutDelayDays(ageDays: number | null | undefined): number | null;
|
|
34
|
+
/** One reason a sale looks like the publisher paying itself or cashing out cards. */
|
|
35
|
+
export type SaleRiskSignal = {
|
|
36
|
+
code: 'young-publisher-large-sale';
|
|
37
|
+
ageDays: number;
|
|
38
|
+
amountCents: number;
|
|
39
|
+
} | {
|
|
40
|
+
/** The buyer and the publisher share a member (a uid in both workspaces). */
|
|
41
|
+
code: 'shared-member';
|
|
42
|
+
uids: string[];
|
|
43
|
+
} | {
|
|
44
|
+
/**
|
|
45
|
+
* The card that paid was used by ANOTHER buyer workspace to buy from
|
|
46
|
+
* the same publisher — one card, several buyers, one seller.
|
|
47
|
+
*/
|
|
48
|
+
code: 'card-reused-across-buyers';
|
|
49
|
+
otherBuyerOrgIds: string[];
|
|
50
|
+
};
|
|
51
|
+
/** One line per signal, in the words staff read on the row. */
|
|
52
|
+
export declare function describeSaleRiskSignals(signals: readonly SaleRiskSignal[]): string[];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /*==========================================
|
|
17
|
+
* A YOUNG PUBLISHER'S RISK TIER (AGL-3365).
|
|
18
|
+
*
|
|
19
|
+
* The 9/26 actor signed up, paid for a plan on a card that never met 3-D
|
|
20
|
+
* Secure, and within days was sending lures. On the marketplace the same
|
|
21
|
+
* actor has one more move: publish a listing, buy it with stolen cards, and
|
|
22
|
+
* cash the sales out through the payouts the platform sends the publisher.
|
|
23
|
+
*
|
|
24
|
+
* Every marketplace sale is a destination charge — the funds settle on the
|
|
25
|
+
* platform and the publisher's share moves to its connected account at the
|
|
26
|
+
* charge — so the lever the platform keeps is the connected account's PAYOUT
|
|
27
|
+
* SCHEDULE: how long a share sits in the account before it leaves Stripe. A
|
|
28
|
+
* share still in the account can be reversed when a card turns out stolen
|
|
29
|
+
* (the refund and dispute doors already reverse it); a share already paid
|
|
30
|
+
* out to a bank cannot.
|
|
31
|
+
*
|
|
32
|
+
* So a publisher workspace in its first {@link YOUNG_PUBLISHER_DAYS} days has
|
|
33
|
+
* its payouts held {@link YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS} days, which
|
|
34
|
+
* outlasts the window in which issuers send early fraud warnings on most
|
|
35
|
+
* stolen-card charges, and a single sale of
|
|
36
|
+
* {@link YOUNG_PUBLISHER_REVIEW_SALE_CENTS} or more to one files a staff
|
|
37
|
+
* signal while the money can still be reversed. A publisher that ages out of
|
|
38
|
+
* the tier returns to the account's standard schedule on its next sale or its
|
|
39
|
+
* next visit to the payout settings.
|
|
40
|
+
*
|
|
41
|
+
* Age alone, as the phishing screen's tier is: payment history is what the
|
|
42
|
+
* actor bought.
|
|
43
|
+
*=========================================*/ /** A publisher workspace younger than this many days is in the tier. */ export const YOUNG_PUBLISHER_DAYS = 30;
|
|
44
|
+
/** How long a young publisher's payouts sit in its account. */ export const YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS = 14;
|
|
45
|
+
/** A young publisher's single sale at or above this files a staff signal. */ export const YOUNG_PUBLISHER_REVIEW_SALE_CENTS = 10000;
|
|
46
|
+
/**
|
|
47
|
+
* Is a publisher workspace of this age (days) in the young tier? `null` — a
|
|
48
|
+
* creation date that cannot be read — is an existing customer, as the
|
|
49
|
+
* phishing screen reads it, and is not young.
|
|
50
|
+
*/ export function isYoungPublisher(ageDays) {
|
|
51
|
+
return typeof ageDays === 'number' && Number.isFinite(ageDays) && ageDays < YOUNG_PUBLISHER_DAYS;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The payout delay a publisher's account should carry: a number of days, or
|
|
55
|
+
* `null` for the account's standard (minimum) schedule.
|
|
56
|
+
*/ export function publisherPayoutDelayDays(ageDays) {
|
|
57
|
+
return isYoungPublisher(ageDays) ? YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS : null;
|
|
58
|
+
}
|
|
59
|
+
/** One line per signal, in the words staff read on the row. */ export function describeSaleRiskSignals(signals) {
|
|
60
|
+
return signals.map((signal)=>{
|
|
61
|
+
switch(signal.code){
|
|
62
|
+
case 'young-publisher-large-sale':
|
|
63
|
+
return `The publisher's workspace is ${signal.ageDays} day(s) old and this one sale was ` + `$${(signal.amountCents / 100).toFixed(2)}.`;
|
|
64
|
+
case 'shared-member':
|
|
65
|
+
return `The buying and publishing workspaces share ${signal.uids.length} member(s): ` + `${signal.uids.join(', ')}.`;
|
|
66
|
+
case 'card-reused-across-buyers':
|
|
67
|
+
return 'The same card also bought from this publisher for other workspace(s): ' + `${signal.otherBuyerOrgIds.join(', ')}.`;
|
|
68
|
+
default:
|
|
69
|
+
return 'Sale risk signal.';
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=publisher-risk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/model/publisher-risk.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\n/*==========================================\n * A YOUNG PUBLISHER'S RISK TIER (AGL-3365).\n *\n * The 9/26 actor signed up, paid for a plan on a card that never met 3-D\n * Secure, and within days was sending lures. On the marketplace the same\n * actor has one more move: publish a listing, buy it with stolen cards, and\n * cash the sales out through the payouts the platform sends the publisher.\n *\n * Every marketplace sale is a destination charge — the funds settle on the\n * platform and the publisher's share moves to its connected account at the\n * charge — so the lever the platform keeps is the connected account's PAYOUT\n * SCHEDULE: how long a share sits in the account before it leaves Stripe. A\n * share still in the account can be reversed when a card turns out stolen\n * (the refund and dispute doors already reverse it); a share already paid\n * out to a bank cannot.\n *\n * So a publisher workspace in its first {@link YOUNG_PUBLISHER_DAYS} days has\n * its payouts held {@link YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS} days, which\n * outlasts the window in which issuers send early fraud warnings on most\n * stolen-card charges, and a single sale of\n * {@link YOUNG_PUBLISHER_REVIEW_SALE_CENTS} or more to one files a staff\n * signal while the money can still be reversed. A publisher that ages out of\n * the tier returns to the account's standard schedule on its next sale or its\n * next visit to the payout settings.\n *\n * Age alone, as the phishing screen's tier is: payment history is what the\n * actor bought.\n *=========================================*/\n\n/** A publisher workspace younger than this many days is in the tier. */\nexport const YOUNG_PUBLISHER_DAYS = 30\n\n/** How long a young publisher's payouts sit in its account. */\nexport const YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS = 14\n\n/** A young publisher's single sale at or above this files a staff signal. */\nexport const YOUNG_PUBLISHER_REVIEW_SALE_CENTS = 10_000\n\n/**\n * Is a publisher workspace of this age (days) in the young tier? `null` — a\n * creation date that cannot be read — is an existing customer, as the\n * phishing screen reads it, and is not young.\n */\nexport function isYoungPublisher(ageDays: number | null | undefined): boolean {\n return (\n typeof ageDays === 'number' &&\n Number.isFinite(ageDays) &&\n ageDays < YOUNG_PUBLISHER_DAYS\n )\n}\n\n/**\n * The payout delay a publisher's account should carry: a number of days, or\n * `null` for the account's standard (minimum) schedule.\n */\nexport function publisherPayoutDelayDays(\n ageDays: number | null | undefined,\n): number | null {\n return isYoungPublisher(ageDays) ? YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS : null\n}\n\n/** One reason a sale looks like the publisher paying itself or cashing out cards. */\nexport type SaleRiskSignal =\n | {\n code: 'young-publisher-large-sale'\n ageDays: number\n amountCents: number\n }\n | {\n /** The buyer and the publisher share a member (a uid in both workspaces). */\n code: 'shared-member'\n uids: string[]\n }\n | {\n /**\n * The card that paid was used by ANOTHER buyer workspace to buy from\n * the same publisher — one card, several buyers, one seller.\n */\n code: 'card-reused-across-buyers'\n otherBuyerOrgIds: string[]\n }\n\n/** One line per signal, in the words staff read on the row. */\nexport function describeSaleRiskSignals(\n signals: readonly SaleRiskSignal[],\n): string[] {\n return signals.map((signal) => {\n switch (signal.code) {\n case 'young-publisher-large-sale':\n return (\n `The publisher's workspace is ${signal.ageDays} day(s) old and this one sale was ` +\n `$${(signal.amountCents / 100).toFixed(2)}.`\n )\n case 'shared-member':\n return (\n `The buying and publishing workspaces share ${signal.uids.length} member(s): ` +\n `${signal.uids.join(', ')}.`\n )\n case 'card-reused-across-buyers':\n return (\n 'The same card also bought from this publisher for other workspace(s): ' +\n `${signal.otherBuyerOrgIds.join(', ')}.`\n )\n default:\n return 'Sale risk signal.'\n }\n })\n}\n"],"names":["YOUNG_PUBLISHER_DAYS","YOUNG_PUBLISHER_PAYOUT_DELAY_DAYS","YOUNG_PUBLISHER_REVIEW_SALE_CENTS","isYoungPublisher","ageDays","Number","isFinite","publisherPayoutDelayDays","describeSaleRiskSignals","signals","map","signal","code","amountCents","toFixed","uids","length","join","otherBuyerOrgIds"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;2CA2B2C,GAE3C,sEAAsE,GACtE,OAAO,MAAMA,uBAAuB,GAAE;AAEtC,6DAA6D,GAC7D,OAAO,MAAMC,oCAAoC,GAAE;AAEnD,2EAA2E,GAC3E,OAAO,MAAMC,oCAAoC,MAAM;AAEvD;;;;CAIC,GACD,OAAO,SAASC,iBAAiBC,OAAkC;IACjE,OACE,OAAOA,YAAY,YACnBC,OAAOC,QAAQ,CAACF,YAChBA,UAAUJ;AAEd;AAEA;;;CAGC,GACD,OAAO,SAASO,yBACdH,OAAkC;IAElC,OAAOD,iBAAiBC,WAAWH,oCAAoC;AACzE;AAuBA,6DAA6D,GAC7D,OAAO,SAASO,wBACdC,OAAkC;IAElC,OAAOA,QAAQC,GAAG,CAAC,CAACC;QAClB,OAAQA,OAAOC,IAAI;YACjB,KAAK;gBACH,OACE,CAAC,6BAA6B,EAAED,OAAOP,OAAO,CAAC,kCAAkC,CAAC,GAClF,CAAC,CAAC,EAAE,AAACO,CAAAA,OAAOE,WAAW,GAAG,GAAE,EAAGC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEhD,KAAK;gBACH,OACE,CAAC,2CAA2C,EAAEH,OAAOI,IAAI,CAACC,MAAM,CAAC,YAAY,CAAC,GAC9E,GAAGL,OAAOI,IAAI,CAACE,IAAI,CAAC,MAAM,CAAC,CAAC;YAEhC,KAAK;gBACH,OACE,2EACA,GAAGN,OAAOO,gBAAgB,CAACD,IAAI,CAAC,MAAM,CAAC,CAAC;YAE5C;gBACE,OAAO;QACX;IACF;AACF"}
|
|
@@ -23,7 +23,10 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
23
23
|
// the surviving half of "four server events ship to nothing" now that the
|
|
24
24
|
// Measurement Protocol credentials have landed (2026-08-17).
|
|
25
25
|
import { after } from "next/server";
|
|
26
|
-
import {
|
|
26
|
+
import { paymentRiskEventFrom } from "@aglyn/aglyn/app-utils/payment-risk";
|
|
27
|
+
import { recordPaymentRiskOnRecord } from "@aglyn/tenant-data-admin/server/payment-risk-record";
|
|
28
|
+
import { firebaseAdmin, notifyStaff, sendGa4Purchase, sendGa4Refund, clearConnectPayoutFailure, recordConnectPayoutFailure, syncConnectAccountStatus } from "@aglyn/tenant-data-admin";
|
|
29
|
+
import { screenMarketplaceSale } from "./sale-risk.js";
|
|
27
30
|
/** Stripe failures a redelivery can actually fix. */ function isTransientStripeStatus(status) {
|
|
28
31
|
return status === 429 || status >= 500;
|
|
29
32
|
}
|
|
@@ -707,6 +710,43 @@ async function reverseMarketplaceSellerShare(purchaseRef, cause) {
|
|
|
707
710
|
// Recorded and surfaced, never retried: Stripe runs its own retry schedule
|
|
708
711
|
// and a second transfer against an account that just refused one is how a
|
|
709
712
|
// duplicate lands.
|
|
713
|
+
// A CARD FRAUD SIGNAL ON A MARKETPLACE SALE (AGL-3365).
|
|
714
|
+
//
|
|
715
|
+
// An issuer's early fraud warning or a Radar review, landing on a charge
|
|
716
|
+
// that paid a publisher. The purchase carries the signal (the same
|
|
717
|
+
// `paymentRisk` field an order or booking carries), and the claim names the
|
|
718
|
+
// PUBLISHER's workspace, so the platform route counts it on the seller's
|
|
719
|
+
// ledger against the org that was paid — never the buyer's workspace,
|
|
720
|
+
// whose card it may have been. Nobody is notified per signal: the
|
|
721
|
+
// publisher cannot refund a charge the platform is merchant of record for,
|
|
722
|
+
// and staff hear through the seller pattern and the sale-risk row.
|
|
723
|
+
// Disputes keep their own section below, which claims them the same way.
|
|
724
|
+
if (type === 'radar.early_fraud_warning.created' || type === 'review.opened') {
|
|
725
|
+
var _purchase_get;
|
|
726
|
+
const risk = paymentRiskEventFrom(type, object);
|
|
727
|
+
if (!(risk == null ? void 0 : risk.paymentIntentId)) return;
|
|
728
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
729
|
+
const purchases = await firestore.collection('marketplacePurchases').where('paymentIntentId', '==', risk.paymentIntentId).limit(1).get();
|
|
730
|
+
if (purchases.empty) return;
|
|
731
|
+
const purchase = purchases.docs[0];
|
|
732
|
+
await recordPaymentRiskOnRecord({
|
|
733
|
+
ref: purchase.ref,
|
|
734
|
+
signal: risk.signal,
|
|
735
|
+
hostId: '',
|
|
736
|
+
subjectLabel: 'Marketplace sale',
|
|
737
|
+
link: '/admin/abuse-reports',
|
|
738
|
+
notificationType: 'marketplace.review'
|
|
739
|
+
}, {
|
|
740
|
+
firestore,
|
|
741
|
+
notify: async ()=>undefined
|
|
742
|
+
});
|
|
743
|
+
const sellerOrgId = String((_purchase_get = purchase.get('sellerOrgId')) != null ? _purchase_get : '');
|
|
744
|
+
return _extends({
|
|
745
|
+
claimed: true
|
|
746
|
+
}, sellerOrgId ? {
|
|
747
|
+
orgId: sellerOrgId
|
|
748
|
+
} : {});
|
|
749
|
+
}
|
|
710
750
|
if (type === 'payout.failed' || type === 'transfer.failed') {
|
|
711
751
|
var _ref1, _ref2, _ref3;
|
|
712
752
|
var _object_destination;
|
|
@@ -879,6 +919,29 @@ async function reverseMarketplaceSellerShare(purchaseRef, cause) {
|
|
|
879
919
|
stripeCustomerId: String((_ref1 = object == null ? void 0 : object.customer) != null ? _ref1 : '') || String(buyerUid)
|
|
880
920
|
}).catch(()=>undefined);
|
|
881
921
|
});
|
|
922
|
+
// The sale's fraud shape (AGL-3365): the publisher paying itself, one
|
|
923
|
+
// card buying for several workspaces, a brand-new publisher's large
|
|
924
|
+
// sale — and a young publisher's payouts held. Once per sale, after the
|
|
925
|
+
// response like the GA hit: it reads and files, and the sale it reads
|
|
926
|
+
// is already recorded. `screenMarketplaceSale` never throws.
|
|
927
|
+
if (!alreadyRecorded) {
|
|
928
|
+
after(()=>{
|
|
929
|
+
var _ref;
|
|
930
|
+
return screenMarketplaceSale({
|
|
931
|
+
purchaseRef,
|
|
932
|
+
sessionId: String(object.id),
|
|
933
|
+
buyerUid: String(buyerUid),
|
|
934
|
+
buyerOrgId: buyerOrgId ? String(buyerOrgId) : null,
|
|
935
|
+
sellerOrgId: String(sellerOrgId),
|
|
936
|
+
amountCents: grossCents - taxCents,
|
|
937
|
+
paymentIntentId: String((_ref = object == null ? void 0 : object.payment_intent) != null ? _ref : ''),
|
|
938
|
+
livemode: (event == null ? void 0 : event.livemode) === true
|
|
939
|
+
}, {
|
|
940
|
+
stripeKey: process.env.STRIPE_SECRET_KEY,
|
|
941
|
+
notify: notifyStaff
|
|
942
|
+
}).catch(()=>undefined);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
882
945
|
// A refund or a lost dispute that arrived before this document existed
|
|
883
946
|
// is applied NOW (AGL-2148) — revocation, GA and the transfer reversal,
|
|
884
947
|
// by the same function the refund door uses. Last on this path, and
|
|
@@ -1008,14 +1071,17 @@ async function reverseMarketplaceSellerShare(purchaseRef, cause) {
|
|
|
1008
1071
|
// purpose: a dispute carrying no payment intent cannot be joined to a
|
|
1009
1072
|
// purchase, which is a fault, not a routine miss.
|
|
1010
1073
|
let claimed = false;
|
|
1074
|
+
// The publisher the disputed sale paid (AGL-3365), for the seller ledger.
|
|
1075
|
+
let sellerOrgId = '';
|
|
1011
1076
|
if (disputeId && paymentIntentId) {
|
|
1012
1077
|
var _ref24;
|
|
1013
1078
|
const firestore = firebaseAdmin.app().firestore();
|
|
1014
1079
|
const purchases = await firestore.collection('marketplacePurchases').where('paymentIntentId', '==', paymentIntentId).limit(1).get();
|
|
1015
1080
|
if (!purchases.empty) {
|
|
1016
|
-
var _ref25;
|
|
1081
|
+
var _purchase_get1, _ref25;
|
|
1017
1082
|
claimed = true;
|
|
1018
1083
|
const purchase = purchases.docs[0];
|
|
1084
|
+
sellerOrgId = String((_purchase_get1 = purchase.get('sellerOrgId')) != null ? _purchase_get1 : '');
|
|
1019
1085
|
const status = String((_ref25 = object == null ? void 0 : object.status) != null ? _ref25 : '');
|
|
1020
1086
|
if (type === 'charge.dispute.created') {
|
|
1021
1087
|
await purchase.ref.set({
|
|
@@ -1065,9 +1131,11 @@ async function reverseMarketplaceSellerShare(purchaseRef, cause) {
|
|
|
1065
1131
|
claimed = await recordDisputeOrphanIfMarketplace(firestore, object, paymentIntentId);
|
|
1066
1132
|
}
|
|
1067
1133
|
}
|
|
1068
|
-
return {
|
|
1134
|
+
return _extends({
|
|
1069
1135
|
claimed
|
|
1070
|
-
}
|
|
1136
|
+
}, claimed && sellerOrgId ? {
|
|
1137
|
+
orgId: sellerOrgId
|
|
1138
|
+
} : {});
|
|
1071
1139
|
}
|
|
1072
1140
|
};
|
|
1073
1141
|
|