@aglyn/plugins-commerce 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 +13 -13
- package/src/lib/components/console/gift-cards-card.component.js +34 -2
- package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +12 -0
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/model/commerce-gift-cards.d.ts +28 -0
- package/src/lib/model/commerce-gift-cards.js +39 -1
- package/src/lib/model/commerce-gift-cards.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +7 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/server/billing-webhook.js +132 -14
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cart-checkout.js +49 -10
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/catalog.js +18 -3
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout.js +26 -6
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/download.js +12 -1
- package/src/lib/server/download.js.map +1 -1
- package/src/lib/server/draft-order.js +7 -3
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/feed.js +12 -2
- package/src/lib/server/feed.js.map +1 -1
- package/src/lib/server/gift-card-risk.d.ts +70 -0
- package/src/lib/server/gift-card-risk.js +123 -0
- package/src/lib/server/gift-card-risk.js.map +1 -0
- package/src/lib/server/gift-cards.js +41 -3
- package/src/lib/server/gift-cards.js.map +1 -1
- package/src/lib/server/membership-admin-password.js +8 -2
- package/src/lib/server/membership-admin-password.js.map +1 -1
- package/src/lib/server/membership-recover.js +4 -1
- package/src/lib/server/membership-recover.js.map +1 -1
- package/src/lib/server/paid-media-delivery.d.ts +26 -4
- package/src/lib/server/paid-media-delivery.js +36 -9
- package/src/lib/server/paid-media-delivery.js.map +1 -1
- package/src/lib/server/pos-order.js +3 -1
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/process-abandoned.js +6 -2
- package/src/lib/server/process-abandoned.js.map +1 -1
- package/src/lib/server/process-restock.js +6 -2
- package/src/lib/server/process-restock.js.map +1 -1
- package/src/lib/server/recurring-charges.d.ts +44 -0
- package/src/lib/server/recurring-charges.js +65 -0
- package/src/lib/server/recurring-charges.js.map +1 -0
- package/src/lib/server/refund.js +24 -0
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/reserve.js +17 -6
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/stream.js +12 -1
- package/src/lib/server/stream.js.map +1 -1
- package/src/lib/server/subscription-portal.js +13 -5
- package/src/lib/server/subscription-portal.js.map +1 -1
- package/src/lib/server/supplier-outbox.js +22 -7
- package/src/lib/server/supplier-outbox.js.map +1 -1
- package/src/lib/server.js +15 -3
- package/src/lib/server.js.map +1 -1
|
@@ -25,6 +25,9 @@ import { recordContactRefund } from "./contact-refund.js";
|
|
|
25
25
|
// specs in this library mock `@aglyn/tenant-data-admin` wholesale, and a
|
|
26
26
|
// permissive stub would turn a reversal that never happened green.
|
|
27
27
|
import { reverseEmailAttributedRevenue } from "@aglyn/tenant-data-admin/server/email-revenue-attribution";
|
|
28
|
+
import { paymentRiskEventFrom } from "@aglyn/aglyn/app-utils/payment-risk";
|
|
29
|
+
import { recordPaymentRiskOnRecord } from "@aglyn/tenant-data-admin/server/payment-risk-record";
|
|
30
|
+
import { applyGiftCardRiskToOrder, notifyGiftCardHold } from "./gift-card-risk.js";
|
|
28
31
|
import { mintDownloadToken, tokenSigningSecret } from "./download.js";
|
|
29
32
|
import { alertLowStockCrossing } from "./low-stock.js";
|
|
30
33
|
import { decrementVariantStock } from "./reserve-stock.js";
|
|
@@ -303,6 +306,26 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
303
306
|
evidenceDueByMs: dueBy * 1000
|
|
304
307
|
} : {});
|
|
305
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Apply a gift-card risk action to the cards an order issued (AGL-3363). The
|
|
311
|
+
* order doc id is the Checkout Session id every issued card carries as its
|
|
312
|
+
* `orderId`. Never throws.
|
|
313
|
+
*/ async function applyOrderGiftCardRisk(snapshot, action) {
|
|
314
|
+
try {
|
|
315
|
+
var _snapshot_ref_parent, _snapshot_ref;
|
|
316
|
+
const hostRef = (_snapshot_ref = snapshot.ref) == null ? void 0 : (_snapshot_ref_parent = _snapshot_ref.parent) == null ? void 0 : _snapshot_ref_parent.parent;
|
|
317
|
+
if (!hostRef) return 0;
|
|
318
|
+
return await applyGiftCardRiskToOrder({
|
|
319
|
+
firestore: firebaseAdmin.app().firestore(),
|
|
320
|
+
hostRef,
|
|
321
|
+
orderId: snapshot.id,
|
|
322
|
+
action
|
|
323
|
+
});
|
|
324
|
+
} catch (error) {
|
|
325
|
+
console.error('[gift-card-risk] skipped', snapshot.id, error);
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
306
329
|
/**
|
|
307
330
|
* `charge.dispute.created`: FLAG the order, reverse nothing (AGL-1787).
|
|
308
331
|
*
|
|
@@ -2591,7 +2614,10 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
2591
2614
|
fromName: (await brandFor(hostId)).fromName,
|
|
2592
2615
|
sendingIdentity: await hostSendingIdentity(String(hostId)),
|
|
2593
2616
|
audience: 'tenant',
|
|
2594
|
-
context: 'reservation confirmation'
|
|
2617
|
+
context: 'reservation confirmation',
|
|
2618
|
+
// Owed to the recipient by their own booking: the phishing
|
|
2619
|
+
// screen's soft rules never hold it (AGL-3356).
|
|
2620
|
+
owedFor: 'booking'
|
|
2595
2621
|
}));
|
|
2596
2622
|
// Cost meter (AGL-1438). Transactional: the guest has paid, and a
|
|
2597
2623
|
// confirmation a quota refused reads as a failed reservation.
|
|
@@ -2868,7 +2894,10 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
2868
2894
|
fromName: (await brandFor(hostId)).fromName,
|
|
2869
2895
|
sendingIdentity: await hostSendingIdentity(String(hostId)),
|
|
2870
2896
|
audience: 'tenant',
|
|
2871
|
-
context: 'cart receipt'
|
|
2897
|
+
context: 'cart receipt',
|
|
2898
|
+
// Owed to the recipient by their own order: the phishing
|
|
2899
|
+
// screen's soft rules never hold it (AGL-3356).
|
|
2900
|
+
owedFor: 'order'
|
|
2872
2901
|
}));
|
|
2873
2902
|
// Cost meter (AGL-1438). Transactional: a dropped receipt looks to
|
|
2874
2903
|
// the buyer like an order that did not go through.
|
|
@@ -3076,6 +3105,9 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3076
3105
|
balanceCents: line.unitAmountCents,
|
|
3077
3106
|
recipientEmail: (_ref53 = object == null ? void 0 : (_object_customer_details12 = object.customer_details) == null ? void 0 : _object_customer_details12.email) != null ? _ref53 : null,
|
|
3078
3107
|
orderId: String(object.id),
|
|
3108
|
+
// Which line bought it, so a refund that withdraws that line
|
|
3109
|
+
// voids this card and no other (AGL-3363).
|
|
3110
|
+
productId: line.productId,
|
|
3079
3111
|
// What the console's Gift cards search asks (AGL-3321).
|
|
3080
3112
|
searchTokens: giftCardSearchTokens(code, (_ref54 = object == null ? void 0 : (_object_customer_details13 = object.customer_details) == null ? void 0 : _object_customer_details13.email) != null ? _ref54 : null),
|
|
3081
3113
|
createdAtMs: Date.now()
|
|
@@ -3123,7 +3155,10 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3123
3155
|
fromName: (await brandFor(hostId)).fromName,
|
|
3124
3156
|
sendingIdentity: await hostSendingIdentity(String(hostId)),
|
|
3125
3157
|
audience: 'tenant',
|
|
3126
|
-
context: 'gift card'
|
|
3158
|
+
context: 'gift card',
|
|
3159
|
+
// Owed to the recipient by their own order: the phishing
|
|
3160
|
+
// screen's soft rules never hold it (AGL-3356).
|
|
3161
|
+
owedFor: 'order'
|
|
3127
3162
|
}));
|
|
3128
3163
|
// Cost meter (AGL-1438). Transactional: this email IS the
|
|
3129
3164
|
// purchased goods.
|
|
@@ -3698,7 +3733,10 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3698
3733
|
fromName: (await brandFor(hostId)).fromName,
|
|
3699
3734
|
sendingIdentity: await hostSendingIdentity(String(hostId)),
|
|
3700
3735
|
audience: 'tenant',
|
|
3701
|
-
context: 'dropship supplier notice'
|
|
3736
|
+
context: 'dropship supplier notice',
|
|
3737
|
+
// Owed to the recipient by their own order: the phishing
|
|
3738
|
+
// screen's soft rules never hold it (AGL-3356).
|
|
3739
|
+
owedFor: 'order'
|
|
3702
3740
|
});
|
|
3703
3741
|
// Cost meter (AGL-1438). Transactional: without it the order is
|
|
3704
3742
|
// never fulfilled.
|
|
@@ -3813,7 +3851,10 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3813
3851
|
fromName: (await brandFor(hostId)).fromName,
|
|
3814
3852
|
sendingIdentity: await hostSendingIdentity(String(hostId)),
|
|
3815
3853
|
audience: 'tenant',
|
|
3816
|
-
context: 'receipt'
|
|
3854
|
+
context: 'receipt',
|
|
3855
|
+
// Owed to the recipient by their own order: the phishing
|
|
3856
|
+
// screen's soft rules never hold it (AGL-3356).
|
|
3857
|
+
owedFor: 'order'
|
|
3817
3858
|
}));
|
|
3818
3859
|
// Cost meter (AGL-1438). Transactional, as the cart receipt above.
|
|
3819
3860
|
await meterHostEmail(String(hostId));
|
|
@@ -3857,7 +3898,10 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3857
3898
|
fromName: (await brandFor(hostId)).fromName,
|
|
3858
3899
|
sendingIdentity: await hostSendingIdentity(String(hostId)),
|
|
3859
3900
|
audience: 'tenant',
|
|
3860
|
-
context: 'seller order notice'
|
|
3901
|
+
context: 'seller order notice',
|
|
3902
|
+
// Owed to the recipient by their own order: the phishing
|
|
3903
|
+
// screen's soft rules never hold it (AGL-3356).
|
|
3904
|
+
owedFor: 'order'
|
|
3861
3905
|
}));
|
|
3862
3906
|
// Cost meter (AGL-1438). Transactional: the seller learns about
|
|
3863
3907
|
// the order here.
|
|
@@ -3867,6 +3911,57 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3867
3911
|
}
|
|
3868
3912
|
}
|
|
3869
3913
|
}
|
|
3914
|
+
// Fraud signals on a store's sale (AGL-3360): an issuer's early fraud
|
|
3915
|
+
// warning and a Radar review. The merchant decides what to do for their
|
|
3916
|
+
// own customer, so the signal lands on the ORDER — a timeline line and the
|
|
3917
|
+
// shared `paymentRisk` chip — and the site's managers are told once.
|
|
3918
|
+
// Nothing is refunded or canceled. Found by the same payment-intent join a
|
|
3919
|
+
// dispute uses; a signal on anything else is not commerce's and stays
|
|
3920
|
+
// silent here (the platform route and the other plugins answer it).
|
|
3921
|
+
// Disputes keep their own branch below, which has always told the
|
|
3922
|
+
// merchant; both report the site so the seller's fraud ledger can name it.
|
|
3923
|
+
const risk = paymentRiskEventFrom(type, object);
|
|
3924
|
+
if (risk && risk.signal.kind !== 'dispute') {
|
|
3925
|
+
var _ref83, _snapshot_data;
|
|
3926
|
+
var _snapshot_ref_parent_parent;
|
|
3927
|
+
const lookup = risk.paymentIntentId ? await findOrderForDispute(risk.paymentIntentId) : {
|
|
3928
|
+
kind: 'not-ours'
|
|
3929
|
+
};
|
|
3930
|
+
if (lookup.kind !== 'order') return;
|
|
3931
|
+
const snapshot = lookup.snapshot;
|
|
3932
|
+
const hostId = String((_ref83 = (_snapshot_ref_parent_parent = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent.id) != null ? _ref83 : '');
|
|
3933
|
+
const order = CommerceModel.liftLegacyOrder((_snapshot_data = snapshot.data()) != null ? _snapshot_data : {});
|
|
3934
|
+
await recordPaymentRiskOnRecord({
|
|
3935
|
+
ref: snapshot.ref,
|
|
3936
|
+
signal: risk.signal,
|
|
3937
|
+
hostId,
|
|
3938
|
+
subjectLabel: `Order ${CommerceModel.formatOrderNumber(order, snapshot.id)}`,
|
|
3939
|
+
link: `/${hostId}/orders`,
|
|
3940
|
+
notificationType: 'content.order',
|
|
3941
|
+
extraUpdate: (data)=>({
|
|
3942
|
+
timeline: CommerceModel.appendOrderEvent(CommerceModel.liftLegacyOrder(data), risk.signal.kind === 'early-fraud-warning' ? 'fraud-warning' : 'payment-review', risk.signal.kind === 'early-fraud-warning' ? 'The card issuer reported this payment as possibly fraudulent' + (risk.signal.detail ? ` (${risk.signal.detail.replace(/_/g, ' ')})` : '') : 'Stripe Radar is holding this payment for review', risk.signal.atMs)
|
|
3943
|
+
})
|
|
3944
|
+
}, {
|
|
3945
|
+
firestore: firebaseAdmin.app().firestore(),
|
|
3946
|
+
notify: notifyHostManagers
|
|
3947
|
+
});
|
|
3948
|
+
// The cards this order bought are frozen until the merchant releases them
|
|
3949
|
+
// (AGL-3363): a warning or review is the moment a stolen-card gift card
|
|
3950
|
+
// is still unspent.
|
|
3951
|
+
await notifyGiftCardHold({
|
|
3952
|
+
hostId,
|
|
3953
|
+
orderLabel: `Order ${CommerceModel.formatOrderNumber(order, snapshot.id)}`,
|
|
3954
|
+
cards: await applyOrderGiftCardRisk(snapshot, {
|
|
3955
|
+
kind: 'freeze',
|
|
3956
|
+
reason: risk.signal.kind
|
|
3957
|
+
}),
|
|
3958
|
+
reason: risk.signal.kind
|
|
3959
|
+
}, notifyHostManagers);
|
|
3960
|
+
return {
|
|
3961
|
+
claimed: true,
|
|
3962
|
+
hostId
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3870
3965
|
// Card disputes against a merchant's store (AGL-1787).
|
|
3871
3966
|
//
|
|
3872
3967
|
// Commerce subscribed no `charge.dispute.*` event at all, so a shopper
|
|
@@ -3892,7 +3987,8 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3892
3987
|
// finds). AGL-1554 will add its own `closed` branch for the marketplace
|
|
3893
3988
|
// side; the two coexist because each self-selects by finding its own record.
|
|
3894
3989
|
if (type === 'charge.dispute.created' || type === 'charge.dispute.closed') {
|
|
3895
|
-
var _dispute_payment_intent;
|
|
3990
|
+
var _dispute_payment_intent, _ref84;
|
|
3991
|
+
var _snapshot_ref_parent_parent1;
|
|
3896
3992
|
const dispute = object != null ? object : {};
|
|
3897
3993
|
const paymentIntentId = String((_dispute_payment_intent = dispute.payment_intent) != null ? _dispute_payment_intent : '');
|
|
3898
3994
|
const lookup = paymentIntentId ? await findOrderForDispute(paymentIntentId) : {
|
|
@@ -3917,10 +4013,20 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3917
4013
|
const claimed = lookup.kind === 'order' || lookup.kind === 'unresolved';
|
|
3918
4014
|
const snapshot = lookup.kind === 'order' ? lookup.snapshot : null;
|
|
3919
4015
|
if (snapshot) {
|
|
3920
|
-
var
|
|
3921
|
-
var
|
|
3922
|
-
const hostId = String((
|
|
4016
|
+
var _ref85;
|
|
4017
|
+
var _snapshot_ref_parent_parent2;
|
|
4018
|
+
const hostId = String((_ref85 = (_snapshot_ref_parent_parent2 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent2.id) != null ? _ref85 : '');
|
|
3923
4019
|
if (type === 'charge.dispute.created') {
|
|
4020
|
+
// A chargeback's gift cards are frozen while it is open (AGL-3363).
|
|
4021
|
+
await notifyGiftCardHold({
|
|
4022
|
+
hostId,
|
|
4023
|
+
orderLabel: `Order ${snapshot.id}`,
|
|
4024
|
+
cards: await applyOrderGiftCardRisk(snapshot, {
|
|
4025
|
+
kind: 'freeze',
|
|
4026
|
+
reason: 'dispute'
|
|
4027
|
+
}),
|
|
4028
|
+
reason: 'dispute'
|
|
4029
|
+
}, notifyHostManagers);
|
|
3924
4030
|
const { opened, record } = await recordDisputeOpened(snapshot, dispute);
|
|
3925
4031
|
if (opened && hostId) {
|
|
3926
4032
|
// Time-critical — Stripe's evidence window is days, not weeks — so
|
|
@@ -3936,7 +4042,17 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3936
4042
|
});
|
|
3937
4043
|
}
|
|
3938
4044
|
} else {
|
|
3939
|
-
var
|
|
4045
|
+
var _ref86, _ref87;
|
|
4046
|
+
// Lost: the money went back, so the cards it bought are voided. Won
|
|
4047
|
+
// (or closed on a warning): the dispute's freeze lifts; a freeze a
|
|
4048
|
+
// fraud warning or review put on stays for the merchant (AGL-3363).
|
|
4049
|
+
await applyOrderGiftCardRisk(snapshot, String((_ref86 = dispute == null ? void 0 : dispute.status) != null ? _ref86 : '') === 'lost' ? {
|
|
4050
|
+
kind: 'void',
|
|
4051
|
+
reason: 'dispute-lost'
|
|
4052
|
+
} : {
|
|
4053
|
+
kind: 'release',
|
|
4054
|
+
reason: 'dispute'
|
|
4055
|
+
});
|
|
3940
4056
|
const settled = await recordDisputeClosed(snapshot, dispute);
|
|
3941
4057
|
if (settled.recorded && hostId) {
|
|
3942
4058
|
await notifyHostManagers(hostId, {
|
|
@@ -3994,7 +4110,7 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
3994
4110
|
// transiently-failed reversal gets its retry — and gated inside on
|
|
3995
4111
|
// the order's own `reversedTransferCents` marker, so it runs at most
|
|
3996
4112
|
// once per dispute however many times Stripe delivers.
|
|
3997
|
-
if (String((
|
|
4113
|
+
if (String((_ref87 = dispute == null ? void 0 : dispute.status) != null ? _ref87 : '') === 'lost') {
|
|
3998
4114
|
const pulledBackCents = await reverseSellerShare(snapshot.ref, dispute);
|
|
3999
4115
|
// The merchant-experience half of the AGL-1794 decision, and not an
|
|
4000
4116
|
// optional extra: the reversal takes money out of the connected
|
|
@@ -4021,9 +4137,11 @@ async function findOrderForDispute(paymentIntentId) {
|
|
|
4021
4137
|
}
|
|
4022
4138
|
}
|
|
4023
4139
|
}
|
|
4024
|
-
return {
|
|
4140
|
+
return _extends({
|
|
4025
4141
|
claimed
|
|
4026
|
-
}
|
|
4142
|
+
}, snapshot ? {
|
|
4143
|
+
hostId: String((_ref84 = (_snapshot_ref_parent_parent1 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent1.id) != null ? _ref84 : '')
|
|
4144
|
+
} : {});
|
|
4027
4145
|
}
|
|
4028
4146
|
};
|
|
4029
4147
|
|