order-management 0.0.95 → 0.0.96
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/.medusa/server/src/admin/index.js +212 -212
- package/.medusa/server/src/admin/index.mjs +212 -212
- package/.medusa/server/src/api/middlewares/block-refund-until-return-received.js +141 -42
- package/.medusa/server/src/api/middlewares.js +11 -5
- package/.medusa/server/src/helpers/refund-receive-guard.js +102 -1
- package/README.md +1 -2
- package/package.json +1 -1
|
@@ -3,27 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.blockRefundUntilReturnReceived = blockRefundUntilReturnReceived;
|
|
4
4
|
const utils_1 = require("@medusajs/framework/utils");
|
|
5
5
|
const refund_receive_guard_1 = require("../../helpers/refund-receive-guard");
|
|
6
|
+
function getKnex(req) {
|
|
7
|
+
return req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
8
|
+
}
|
|
6
9
|
function paymentIdFromRequest(req) {
|
|
7
10
|
const raw = req.params?.id;
|
|
8
|
-
const
|
|
9
|
-
|
|
11
|
+
const fromParams = typeof raw === "string" ? raw.trim() : "";
|
|
12
|
+
if (fromParams) {
|
|
13
|
+
return fromParams;
|
|
14
|
+
}
|
|
15
|
+
// Wildcard matchers (/admin/payments/*/refund) do not populate :id
|
|
16
|
+
const path = (typeof req.originalUrl === "string" && req.originalUrl) ||
|
|
17
|
+
(typeof req.url === "string" && req.url) ||
|
|
18
|
+
(typeof req.path === "string" && req.path) ||
|
|
19
|
+
"";
|
|
20
|
+
const match = path.match(/\/admin\/payments\/([^/?#]+)\/refund/i);
|
|
21
|
+
const fromPath = match?.[1]?.trim();
|
|
22
|
+
return fromPath || undefined;
|
|
10
23
|
}
|
|
11
24
|
async function resolveOrderIdForPayment(paymentId, req) {
|
|
12
25
|
const paymentModule = req.scope.resolve(utils_1.Modules.PAYMENT);
|
|
13
|
-
const payments = await paymentModule.listPayments({ id: [paymentId] }, {
|
|
26
|
+
const payments = await paymentModule.listPayments({ id: [paymentId] }, { take: 1 });
|
|
14
27
|
const paymentList = Array.isArray(payments) ? payments : [];
|
|
15
28
|
const paymentRow = paymentList[0];
|
|
16
29
|
if (!paymentRow) {
|
|
17
30
|
return null;
|
|
18
31
|
}
|
|
19
|
-
|
|
20
|
-
let collectionId = paymentRow.payment_collection_id?.trim() ||
|
|
21
|
-
sessionObj?.payment_collection_id?.trim() ||
|
|
22
|
-
null;
|
|
32
|
+
let collectionId = paymentRow.payment_collection_id?.trim() || null;
|
|
23
33
|
if (!collectionId && typeof paymentRow.payment_session_id === "string") {
|
|
24
34
|
const sessionId = paymentRow.payment_session_id.trim();
|
|
25
35
|
if (sessionId) {
|
|
26
|
-
const sessions = await paymentModule
|
|
36
|
+
const sessions = await paymentModule
|
|
37
|
+
.listPaymentSessions({ id: [sessionId] })
|
|
38
|
+
.catch(() => []);
|
|
27
39
|
const sessionList = Array.isArray(sessions) ? sessions : [];
|
|
28
40
|
const session = sessionList[0];
|
|
29
41
|
if (session?.payment_collection_id) {
|
|
@@ -34,34 +46,114 @@ async function resolveOrderIdForPayment(paymentId, req) {
|
|
|
34
46
|
if (!collectionId) {
|
|
35
47
|
return null;
|
|
36
48
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
try {
|
|
50
|
+
const knex = getKnex(req);
|
|
51
|
+
const rows = await knex("order_payment_collection")
|
|
52
|
+
.select("order_id")
|
|
53
|
+
.where("payment_collection_id", collectionId)
|
|
54
|
+
.whereNull("deleted_at")
|
|
55
|
+
.limit(1);
|
|
56
|
+
const orderId = rows?.[0]?.order_id;
|
|
57
|
+
if (typeof orderId === "string" && orderId.trim()) {
|
|
58
|
+
return orderId.trim();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Fall through
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
async function loadRmaForOrder(orderId, req) {
|
|
67
|
+
const knex = getKnex(req);
|
|
68
|
+
const returnRows = (await knex("return")
|
|
69
|
+
.select(["id", "status", "exchange_id"])
|
|
70
|
+
.where("order_id", orderId)
|
|
71
|
+
.whereNull("deleted_at"));
|
|
72
|
+
const returns = returnRows
|
|
73
|
+
.filter((row) => typeof row.id === "string")
|
|
74
|
+
.map((row) => ({
|
|
75
|
+
id: row.id,
|
|
76
|
+
status: row.status,
|
|
77
|
+
exchange_id: row.exchange_id ?? null,
|
|
78
|
+
}));
|
|
79
|
+
const exchangeRows = (await knex("order_exchange")
|
|
80
|
+
.select(["id", "canceled_at", "return_id"])
|
|
81
|
+
.where("order_id", orderId)
|
|
82
|
+
.whereNull("deleted_at"));
|
|
83
|
+
const exchanges = exchangeRows
|
|
84
|
+
.filter((row) => typeof row.id === "string")
|
|
85
|
+
.map((row) => {
|
|
86
|
+
const returnId = row.return_id ?? null;
|
|
87
|
+
const linked = (returnId ? returns.find((r) => r.id === returnId) : null) ?? null;
|
|
88
|
+
return {
|
|
89
|
+
id: row.id,
|
|
90
|
+
canceled_at: row.canceled_at ?? null,
|
|
91
|
+
return_id: returnId,
|
|
92
|
+
return: linked,
|
|
93
|
+
};
|
|
42
94
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
95
|
+
return { returns, exchanges };
|
|
96
|
+
}
|
|
97
|
+
async function loadWalletSettleEvidence(orderId, req) {
|
|
98
|
+
const knex = getKnex(req);
|
|
99
|
+
let orderTransactions = [];
|
|
100
|
+
try {
|
|
101
|
+
const txRows = (await knex("order_transaction")
|
|
102
|
+
.select(["reference", "reference_id"])
|
|
103
|
+
.where("order_id", orderId)
|
|
104
|
+
.whereNull("deleted_at"));
|
|
105
|
+
orderTransactions = txRows
|
|
106
|
+
.filter((row) => (row.reference ?? "").trim() === "refund")
|
|
107
|
+
.map((row) => ({
|
|
108
|
+
reference: row.reference,
|
|
109
|
+
reference_id: row.reference_id,
|
|
110
|
+
}));
|
|
57
111
|
}
|
|
58
|
-
|
|
112
|
+
catch {
|
|
113
|
+
orderTransactions = [];
|
|
114
|
+
}
|
|
115
|
+
const paymentRefunds = [];
|
|
116
|
+
try {
|
|
117
|
+
const result = await knex.raw(`
|
|
118
|
+
SELECT r.created_by, r.note, r.metadata
|
|
119
|
+
FROM refund r
|
|
120
|
+
INNER JOIN payment p ON p.id = r.payment_id AND p.deleted_at IS NULL
|
|
121
|
+
INNER JOIN order_payment_collection opc
|
|
122
|
+
ON opc.payment_collection_id = p.payment_collection_id
|
|
123
|
+
AND opc.deleted_at IS NULL
|
|
124
|
+
WHERE opc.order_id = ?
|
|
125
|
+
AND r.deleted_at IS NULL
|
|
126
|
+
`, [orderId]);
|
|
127
|
+
const rows = (result && typeof result === "object" && "rows" in result
|
|
128
|
+
? result.rows
|
|
129
|
+
: Array.isArray(result)
|
|
130
|
+
? result
|
|
131
|
+
: []);
|
|
132
|
+
for (const row of rows) {
|
|
133
|
+
paymentRefunds.push({
|
|
134
|
+
created_by: row.created_by,
|
|
135
|
+
note: row.note,
|
|
136
|
+
metadata: row.metadata ?? null,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Soft-fail: still enforce return/exchange checks
|
|
142
|
+
}
|
|
143
|
+
return { orderTransactions, paymentRefunds };
|
|
59
144
|
}
|
|
60
145
|
/**
|
|
61
|
-
* Block Medusa core POST /admin/payments/:id/refund while
|
|
62
|
-
*
|
|
146
|
+
* Block Medusa core POST /admin/payments/:id/refund while return/exchange is
|
|
147
|
+
* unreceived, or when the order was already settled to the customer wallet.
|
|
63
148
|
*/
|
|
64
149
|
async function blockRefundUntilReturnReceived(req, res, next) {
|
|
150
|
+
// Dual matchers can both hit; only evaluate once per request.
|
|
151
|
+
const checkedKey = "__om_refund_rma_checked";
|
|
152
|
+
const reqFlags = req;
|
|
153
|
+
if (reqFlags[checkedKey]) {
|
|
154
|
+
return next();
|
|
155
|
+
}
|
|
156
|
+
reqFlags[checkedKey] = true;
|
|
65
157
|
const paymentId = paymentIdFromRequest(req);
|
|
66
158
|
if (!paymentId) {
|
|
67
159
|
return next();
|
|
@@ -69,29 +161,36 @@ async function blockRefundUntilReturnReceived(req, res, next) {
|
|
|
69
161
|
try {
|
|
70
162
|
const orderId = await resolveOrderIdForPayment(paymentId, req);
|
|
71
163
|
if (!orderId) {
|
|
72
|
-
|
|
164
|
+
res.status(400).json({
|
|
165
|
+
message: (0, refund_receive_guard_1.refundBlockedOrderUnresolvedMessage)(),
|
|
166
|
+
type: "NOT_ALLOWED",
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
73
169
|
}
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
170
|
+
const { returns: returnList, exchanges: exchangeList } = await loadRmaForOrder(orderId, req);
|
|
171
|
+
const { orderTransactions, paymentRefunds } = await loadWalletSettleEvidence(orderId, req);
|
|
172
|
+
const decision = (0, refund_receive_guard_1.shouldBlockRefundForRma)({
|
|
173
|
+
returns: returnList,
|
|
174
|
+
exchanges: exchangeList,
|
|
175
|
+
orderTransactions,
|
|
176
|
+
paymentRefunds,
|
|
177
|
+
});
|
|
178
|
+
if (!decision.blocked) {
|
|
82
179
|
return next();
|
|
83
180
|
}
|
|
84
181
|
res.status(400).json({
|
|
85
|
-
message:
|
|
182
|
+
message: decision.message,
|
|
86
183
|
type: "NOT_ALLOWED",
|
|
87
184
|
});
|
|
88
185
|
}
|
|
89
186
|
catch (err) {
|
|
90
|
-
const message = err instanceof Error
|
|
187
|
+
const message = err instanceof Error
|
|
188
|
+
? err.message
|
|
189
|
+
: "Failed to validate refund against return status";
|
|
91
190
|
res.status(500).json({
|
|
92
191
|
message,
|
|
93
192
|
type: "INTERNAL_ERROR",
|
|
94
193
|
});
|
|
95
194
|
}
|
|
96
195
|
}
|
|
97
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
196
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2stcmVmdW5kLXVudGlsLXJldHVybi1yZWNlaXZlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvbWlkZGxld2FyZXMvYmxvY2stcmVmdW5kLXVudGlsLXJldHVybi1yZWNlaXZlZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQXNQQSx3RUErREM7QUEvU0QscURBR2tDO0FBQ2xDLDZFQU8yQztBQTBCM0MsU0FBUyxPQUFPLENBQUMsR0FBa0I7SUFDakMsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBVyxpQ0FBeUIsQ0FBQyxhQUFhLENBQUMsQ0FBQTtBQUM3RSxDQUFDO0FBRUQsU0FBUyxvQkFBb0IsQ0FBQyxHQUFrQjtJQUM5QyxNQUFNLEdBQUcsR0FBRyxHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQTtJQUMxQixNQUFNLFVBQVUsR0FBRyxPQUFPLEdBQUcsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFBO0lBQzVELElBQUksVUFBVSxFQUFFLENBQUM7UUFDZixPQUFPLFVBQVUsQ0FBQTtJQUNuQixDQUFDO0lBQ0QsbUVBQW1FO0lBQ25FLE1BQU0sSUFBSSxHQUNSLENBQUMsT0FBTyxHQUFHLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsV0FBVyxDQUFDO1FBQ3hELENBQUMsT0FBTyxHQUFHLENBQUMsR0FBRyxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDO1FBQ3hDLENBQUMsT0FBTyxHQUFHLENBQUMsSUFBSSxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDO1FBQzFDLEVBQUUsQ0FBQTtJQUNKLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQTtJQUNqRSxNQUFNLFFBQVEsR0FBRyxLQUFLLEVBQUUs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
|
|
@@ -8,24 +8,30 @@ exports.default = (0, http_1.defineMiddlewares)({
|
|
|
8
8
|
routes: [
|
|
9
9
|
{
|
|
10
10
|
matcher: "/admin/payments/:id/refund",
|
|
11
|
-
|
|
11
|
+
methods: ["POST"],
|
|
12
|
+
middlewares: [block_refund_until_return_received_1.blockRefundUntilReturnReceived],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
// Wildcard fallback when :id matchers are skipped
|
|
16
|
+
matcher: "/admin/payments/*/refund",
|
|
17
|
+
methods: ["POST"],
|
|
12
18
|
middlewares: [block_refund_until_return_received_1.blockRefundUntilReturnReceived],
|
|
13
19
|
},
|
|
14
20
|
{
|
|
15
21
|
matcher: "/store/returns",
|
|
16
|
-
|
|
22
|
+
methods: ["POST"],
|
|
17
23
|
middlewares: [(0, http_1.validateAndTransformBody)(validators_1.StoreCreateReturnSchema)],
|
|
18
24
|
},
|
|
19
25
|
{
|
|
20
26
|
matcher: "/store/orders/cancel/:order_id",
|
|
21
|
-
|
|
27
|
+
methods: ["POST"],
|
|
22
28
|
middlewares: [(0, http_1.validateAndTransformBody)(cancel_order_1.StoreCancelOrderBodySchema)],
|
|
23
29
|
},
|
|
24
30
|
{
|
|
25
31
|
matcher: "/store/guest-orders/:id/cancel",
|
|
26
|
-
|
|
32
|
+
methods: ["POST"],
|
|
27
33
|
middlewares: [(0, http_1.validateAndTransformBody)(cancel_order_1.StoreCancelOrderBodySchema)],
|
|
28
34
|
},
|
|
29
35
|
],
|
|
30
36
|
});
|
|
31
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbURBR2lDO0FBQ2pDLHlHQUFpRztBQUNqRywyREFBb0U7QUFDcEUsa0VBQTRFO0FBRTVFLGtCQUFlLElBQUEsd0JBQWlCLEVBQUM7SUFDL0IsTUFBTSxFQUFFO1FBQ047WUFDRSxPQUFPLEVBQUUsNEJBQTRCO1lBQ3JDLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUUsQ0FBQyxtRUFBOEIsQ0FBQztTQUM5QztRQUNEO1lBQ0Usa0RBQWtEO1lBQ2xELE9BQU8sRUFBRSwwQkFBMEI7WUFDbkMsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDO1lBQ2pCLFdBQVcsRUFBRSxDQUFDLG1FQUE4QixDQUFDO1NBQzlDO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsZ0JBQWdCO1lBQ3pCLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUUsQ0FBQyxJQUFBLCtCQUF3QixFQUFDLG9DQUF1QixDQUFDLENBQUM7U0FDakU7UUFDRDtZQUNFLE9BQU8sRUFBRSxnQ0FBZ0M7WUFDekMsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDO1lBQ2pCLFdBQVcsRUFBRSxDQUFDLElBQUEsK0JBQXdCLEVBQUMseUNBQTBCLENBQUMsQ0FBQztTQUNwRTtRQUNEO1lBQ0UsT0FBTyxFQUFFLGdDQUFnQztZQUN6QyxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFLENBQUMsSUFBQSwrQkFBd0IsRUFBQyx5Q0FBMEIsQ0FBQyxDQUFDO1NBQ3BFO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
|
|
@@ -1,17 +1,64 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.refundBlockedByReturnMessage = refundBlockedByReturnMessage;
|
|
4
|
+
exports.refundBlockedByExchangeMessage = refundBlockedByExchangeMessage;
|
|
5
|
+
exports.refundBlockedByWalletSettleMessage = refundBlockedByWalletSettleMessage;
|
|
6
|
+
exports.refundBlockedOrderUnresolvedMessage = refundBlockedOrderUnresolvedMessage;
|
|
7
|
+
exports.isWalletSettleOrderTransaction = isWalletSettleOrderTransaction;
|
|
8
|
+
exports.isWalletSettlePaymentRefund = isWalletSettlePaymentRefund;
|
|
9
|
+
exports.orderHasWalletSettle = orderHasWalletSettle;
|
|
4
10
|
exports.getUnreceivedReturnBlockingRefund = getUnreceivedReturnBlockingRefund;
|
|
11
|
+
exports.shouldBlockRefundForRma = shouldBlockRefundForRma;
|
|
5
12
|
exports.assertReturnReceivedForRefund = assertReturnReceivedForRefund;
|
|
6
13
|
exports.canProcessRefundForReturn = canProcessRefundForReturn;
|
|
7
14
|
const utils_1 = require("@medusajs/framework/utils");
|
|
8
15
|
const return_exchange_guard_1 = require("./return-exchange-guard");
|
|
16
|
+
const WALLET_SETTLE_REFERENCE_PREFIXES = [
|
|
17
|
+
"wallet:exchange:",
|
|
18
|
+
"wallet:return:",
|
|
19
|
+
"wallet:payment:",
|
|
20
|
+
];
|
|
9
21
|
/**
|
|
10
22
|
* Message when a refund is attempted before the return is fully received.
|
|
11
23
|
*/
|
|
12
24
|
function refundBlockedByReturnMessage(status) {
|
|
13
25
|
return `Cannot process a refund while the return is ${status}. Receive the returned items first.`;
|
|
14
26
|
}
|
|
27
|
+
function refundBlockedByExchangeMessage() {
|
|
28
|
+
return "Cannot process a refund while an exchange is in progress. Receive the returned items first.";
|
|
29
|
+
}
|
|
30
|
+
function refundBlockedByWalletSettleMessage() {
|
|
31
|
+
return "Cannot process a payment refund; this order was already settled to the customer wallet.";
|
|
32
|
+
}
|
|
33
|
+
function refundBlockedOrderUnresolvedMessage() {
|
|
34
|
+
return "Cannot process a refund: unable to resolve the order for this payment. Verify the payment is linked to an order.";
|
|
35
|
+
}
|
|
36
|
+
function isWalletSettleOrderTransaction(tx) {
|
|
37
|
+
if ((tx.reference ?? "").trim() !== "refund") {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const refId = (tx.reference_id ?? "").trim();
|
|
41
|
+
return WALLET_SETTLE_REFERENCE_PREFIXES.some((prefix) => refId.startsWith(prefix));
|
|
42
|
+
}
|
|
43
|
+
function isWalletSettlePaymentRefund(refund) {
|
|
44
|
+
if (refund.created_by === "wallet_settle") {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (refund.metadata?.wallet_settle === true) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const note = refund.note ?? "";
|
|
51
|
+
return note.startsWith("wallet_settle:");
|
|
52
|
+
}
|
|
53
|
+
function orderHasWalletSettle(input) {
|
|
54
|
+
if ((input.orderTransactions ?? []).some(isWalletSettleOrderTransaction)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if ((input.paymentRefunds ?? []).some(isWalletSettlePaymentRefund)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
15
62
|
/**
|
|
16
63
|
* First unreceived return that must block a refund (`open` | `requested` | `partially_received`).
|
|
17
64
|
* Exchange-linked returns are included — refunds are blocked until items are received.
|
|
@@ -24,6 +71,60 @@ function getUnreceivedReturnBlockingRefund(returns) {
|
|
|
24
71
|
}
|
|
25
72
|
return null;
|
|
26
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether admin payment refund should be blocked for open RMA or prior wallet settle.
|
|
76
|
+
*/
|
|
77
|
+
function shouldBlockRefundForRma(input) {
|
|
78
|
+
if (orderHasWalletSettle({
|
|
79
|
+
orderTransactions: input.orderTransactions,
|
|
80
|
+
paymentRefunds: input.paymentRefunds,
|
|
81
|
+
})) {
|
|
82
|
+
return {
|
|
83
|
+
blocked: true,
|
|
84
|
+
reason: "wallet_settle",
|
|
85
|
+
message: refundBlockedByWalletSettleMessage(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const blockingReturn = getUnreceivedReturnBlockingRefund(input.returns);
|
|
89
|
+
if (blockingReturn) {
|
|
90
|
+
return {
|
|
91
|
+
blocked: true,
|
|
92
|
+
reason: "return",
|
|
93
|
+
message: refundBlockedByReturnMessage(blockingReturn.status ?? "requested"),
|
|
94
|
+
returnId: blockingReturn.id,
|
|
95
|
+
status: blockingReturn.status ?? undefined,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
for (const exchange of input.exchanges) {
|
|
99
|
+
let linked = exchange.return ?? null;
|
|
100
|
+
if (!linked && exchange.return_id) {
|
|
101
|
+
linked =
|
|
102
|
+
input.returns.find((r) => r.id === exchange.return_id) ?? null;
|
|
103
|
+
}
|
|
104
|
+
const linkedForGuard = linked
|
|
105
|
+
? {
|
|
106
|
+
id: linked.id,
|
|
107
|
+
status: linked.status ?? undefined,
|
|
108
|
+
exchange_id: linked.exchange_id,
|
|
109
|
+
}
|
|
110
|
+
: null;
|
|
111
|
+
if ((0, return_exchange_guard_1.isActiveExchange)({
|
|
112
|
+
id: exchange.id,
|
|
113
|
+
canceled_at: exchange.canceled_at,
|
|
114
|
+
return_id: exchange.return_id,
|
|
115
|
+
return: linkedForGuard,
|
|
116
|
+
}, linkedForGuard)) {
|
|
117
|
+
return {
|
|
118
|
+
blocked: true,
|
|
119
|
+
reason: "exchange",
|
|
120
|
+
message: refundBlockedByExchangeMessage(),
|
|
121
|
+
exchangeId: exchange.id,
|
|
122
|
+
status: linked?.status ?? undefined,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { blocked: false };
|
|
127
|
+
}
|
|
27
128
|
/**
|
|
28
129
|
* Throw NOT_ALLOWED when this return is still unreceived.
|
|
29
130
|
* No return / already received / canceled → allow.
|
|
@@ -45,4 +146,4 @@ function canProcessRefundForReturn(input) {
|
|
|
45
146
|
Boolean(input.has_mapping) &&
|
|
46
147
|
!input.is_refunded);
|
|
47
148
|
}
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
149
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVmdW5kLXJlY2VpdmUtZ3VhcmQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvaGVscGVycy9yZWZ1bmQtcmVjZWl2ZS1ndWFyZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQWtEQSxvRUFFQztBQUVELHdFQUVDO0FBRUQsZ0ZBRUM7QUFFRCxrRkFFQztBQUVELHdFQVVDO0FBRUQsa0VBV0M7QUFFRCxvREFXQztBQU1ELDhFQVNDO0FBS0QsMERBbUVDO0FBTUQsc0VBYUM7QUFLRCw4REFVQztBQS9ORCxxREFBdUQ7QUFDdkQsbUVBR2dDO0FBcUNoQyxNQUFNLGdDQUFnQyxHQUFHO0lBQ3ZDLGtCQUFrQjtJQUNsQixnQkFBZ0I7SUFDaEIsaUJBQWlCO0NBQ1QsQ0FBQTtBQUVWOztHQUVHO0FBQ0gsU0FBZ0IsNEJBQTRCLENBQUMsTUFBYztJQUN6RCxPQUFPLCtDQUErQyxNQUFNLHFDQUFxQyxDQUFBO0FBQ25HLENBQUM7QUFFRCxTQUFnQiw4QkFBOEI7SUFDNUMsT0FBTyw2RkFBNkYsQ0FBQTtBQUN0RyxDQUFDO0FBRUQsU0FBZ0Isa0NBQWtDO0lBQ2hELE9BQU8seUZBQXlGLENBQUE7QUFDbEcsQ0FBQztBQUVELFNBQWdCLG1DQUFtQztJQUNqRCxPQUFPLGtIQUFrSCxDQUFBO0FBQzNILENBQUM7QUFFRCxTQUFnQiw4QkFBOEIsQ0FDNUMsRUFBa0M7SUFFbEMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxTQUFTLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDN0MsT0FBTyxLQUFLLENBQUE7SUFDZCxDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsQ0FBQyxFQUFFLENBQUMsWUFBWSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFBO0lBQzVDLE9BQU8sZ0NBQWdDLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FDdEQsS0FBSyxDQUFDLFVB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
|
package/README.md
CHANGED
|
@@ -245,7 +245,6 @@ curl "http://localhost:9000/admin/orders/order_123/refund-context" \
|
|
|
245
245
|
| Swaps routes | `/swaps`, `/swaps/:id` | Exchange listing/detail pages | Search, filters, reject/cancel exchange; uses `/admin/swaps*`. |
|
|
246
246
|
| Payments routes | `/payments`, `/payments/:id` | Transaction list + detail pages | Filter transactions and inspect gateway payload. |
|
|
247
247
|
| Refunds routes | `/refunds`, `/refunds/:id` | Refund list + detail pages | Filter by status/provider/date/amount; view computed refund context. |
|
|
248
|
-
| `order-refund-context-widget` | `order.details.after` | Refund context panel | Order checkout payment method (including guest orders), refund calculation (product amount, return charge, processed final refund), edit refund mode/details/images, upload images, mark refunded. |
|
|
249
248
|
| `order-gift-items-widget` | `order.details.side.after` | Gift item widget | Reads gift metadata and downloads generated gift card image. |
|
|
250
249
|
|
|
251
250
|
## Models & Entities
|
|
@@ -592,7 +591,7 @@ The plugin includes a `refund_payment_mapping` module: one row per return linkin
|
|
|
592
591
|
- `PUT /admin/refund-payment-mapping/:return_id` — update admin-only fields. Body: `{ refund_mode?: "offline" | "online" | null, images?: string[], details?: string | null }` (partial; only provided keys are updated).
|
|
593
592
|
- `POST /admin/refund-payment-mapping/:return_id/mark-refunded` — set `is_refunded = true` (idempotent). Requires Medusa return status `received`; otherwise `400 NOT_ALLOWED`. Already-refunded mappings still succeed without re-checking receive status.
|
|
594
593
|
- **Receive-before-refund guard**: Middleware on Medusa core `POST /admin/payments/:id/refund` rejects the request with `400 NOT_ALLOWED` when the order has any return in `open`, `requested`, or `partially_received` (including exchange-linked returns). Message: `Cannot process a refund while the return is {status}. Receive the returned items first.` Orders with no return, or only `received` / `canceled` returns, are not blocked.
|
|
595
|
-
- **Order detail integration**: `GET /admin/orders/:order_id/refund-context` — aggregated read returning `return_id`, `return_status`, `can_process_refund` (`true` only when status is `received` and the mapping exists and is not already refunded), `order_payment` (checkout `provider_id`, human-readable `label`, `is_cod` — used for guest orders that have no saved `payment_detail`), `refund_mapping` (id, payment_id, is_refunded, refund_mode, images, details), `payment_detail` (id, type, detail_json, is_default) resolved from the customer's specified or default payment detail, and `refund` (product_amount, return_charge, expected_net_amount, final_refund_amount, currency_code, is_processed, breakdown_matches, destination_label). `final_refund_amount` is the processed payment refund (or COD/offline `return.refund_amount` after mark-refunded), not `product_amount - return_charge`. Returns null blocks when no return or mapping exists.
|
|
594
|
+
- **Order detail integration**: `GET /admin/orders/:order_id/refund-context` — aggregated read returning `return_id`, `return_status`, `can_process_refund` (`true` only when status is `received` and the mapping exists and is not already refunded), `order_payment` (checkout `provider_id`, human-readable `label`, `is_cod` — used for guest orders that have no saved `payment_detail`), `refund_mapping` (id, payment_id, is_refunded, refund_mode, images, details), `payment_detail` (id, type, detail_json, is_default) resolved from the customer's specified or default payment detail, and `refund` (product_amount, return_charge, expected_net_amount, final_refund_amount, currency_code, is_processed, breakdown_matches, destination_label). `final_refund_amount` is the processed payment refund (or COD/offline `return.refund_amount` after mark-refunded), not `product_amount - return_charge`. Returns null blocks when no return or mapping exists. Used by admin refund/return detail pages; refund_mode, images, and details are admin-only via the mapping APIs above.
|
|
596
595
|
- **Store return payload**: `GET /store/returns` and `GET /store/returns/:id` attach the same `refund` object so the storefront can render the breakdown. `GET /store/orders/:order_id/refund-payment-mappings` includes `refund` per mapping.
|
|
597
596
|
- **Registration**: Add the module in `medusa-config.js` (e.g. `orderManagement.modules["refund-payment-mapping"]`) and run migrations.
|
|
598
597
|
|