vonpay-medusa 1.1.1 → 1.2.1
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 +286 -81
- package/.medusa/server/src/admin/index.mjs +287 -82
- package/.medusa/server/src/api/admin/vonpay/holds/resolve/route.js +6 -0
- package/.medusa/server/src/api/admin/vonpay/holds/route.js +2 -1
- package/.medusa/server/src/api/admin/vonpay/setup-check/move-to-held/route.js +14 -0
- package/.medusa/server/src/api/middlewares.js +1 -0
- package/.medusa/server/src/lib/errors.js +9 -1
- package/.medusa/server/src/lib/held-charge-copy.js +176 -0
- package/.medusa/server/src/lib/money.js +4 -0
- package/.medusa/server/src/modules/vonpay-jobs/index.js +11 -0
- package/.medusa/server/src/modules/vonpay-jobs/key.js +4 -0
- package/.medusa/server/src/modules/vonpay-jobs/migrations/Migration20261105120000.js +15 -0
- package/.medusa/server/src/modules/vonpay-jobs/models/job-beat.js +20 -0
- package/.medusa/server/src/modules/vonpay-jobs/service.js +7 -0
- package/.medusa/server/src/providers/vonpay/admin-holds.js +57 -38
- package/.medusa/server/src/providers/vonpay/charge-hold.js +124 -0
- package/.medusa/server/src/providers/vonpay/deadline.js +26 -0
- package/.medusa/server/src/providers/vonpay/descriptor.js +4 -4
- package/.medusa/server/src/providers/vonpay/held-list.js +168 -26
- package/.medusa/server/src/providers/vonpay/job-beat.js +57 -0
- package/.medusa/server/src/providers/vonpay/move-to-held.js +95 -0
- package/.medusa/server/src/providers/vonpay/options.js +11 -1
- package/.medusa/server/src/providers/vonpay/reading.js +25 -2
- package/.medusa/server/src/providers/vonpay/recovery.js +44 -11
- package/.medusa/server/src/providers/vonpay/refund-after-order.js +35 -0
- package/.medusa/server/src/providers/vonpay/renewal-check.js +435 -0
- package/.medusa/server/src/providers/vonpay/renewal-recovery.js +425 -25
- package/.medusa/server/src/providers/vonpay/renewal.js +542 -39
- package/.medusa/server/src/providers/vonpay/service.js +604 -99
- package/.medusa/server/src/providers/vonpay/session-data.js +248 -9
- package/.medusa/server/src/providers/vonpay/setup-check.js +514 -5
- package/.medusa/server/src/providers/vonpay/upsell-accept.js +99 -35
- package/.medusa/server/src/scripts/vonpay-check.js +23 -1
- package/.medusa/server/src/subscribers/vonpay-order-placed.js +2 -0
- package/README.md +249 -9
- package/bin/vonpay-medusa-check.cjs +23 -3
- package/bin/vonpay-medusa.cjs +241 -8
- package/package.json +4 -3
- package/storefront/src/cache-tag.ts +7 -0
- package/storefront/src/card-form-keep.ts +13 -0
- package/storefront/src/cart-fields.ts +1 -0
- package/storefront/src/checkout-steps.ts +27 -0
- package/storefront/src/context.tsx +1 -1
- package/storefront/src/copy.ts +7 -0
- package/storefront/src/csp.ts +1 -1
- package/storefront/src/index.ts +2 -0
- package/storefront/src/offer-prepare.ts +3 -2
- package/storefront/src/outcome.ts +12 -0
- package/storefront/src/payment-step.ts +99 -0
- package/storefront/src/reload-once.ts +21 -0
- package/storefront/src/server.ts +49 -12
- package/storefront/src/shared.ts +2 -0
- package/storefront/src/shown-total.ts +21 -0
- package/storefront/src/vonpay-card-payment.tsx +204 -16
- package/storefront/src/vonpay-payment-view.tsx +1 -1
- package/storefront/src/vora-csp.json +17 -3
- package/storefront/src/vora.ts +11 -1
|
@@ -9,6 +9,47 @@ const icons = require("@medusajs/icons");
|
|
|
9
9
|
const react = require("react");
|
|
10
10
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
11
11
|
const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
|
|
12
|
+
const TIME_SLOT = "{time}";
|
|
13
|
+
function fillTime(text, at, format) {
|
|
14
|
+
return at && text.includes(TIME_SLOT) ? text.split(TIME_SLOT).join(format(at)) : text;
|
|
15
|
+
}
|
|
16
|
+
const NOT_SAVED_TEXT = "That couldn't be saved. The list is read again: check the row as it stands now before you try again.";
|
|
17
|
+
const CHARGE_REASON_LABEL = {
|
|
18
|
+
renewal_outcome_unknown: "Renewal charge — result unknown",
|
|
19
|
+
renewal_not_settled: "Renewal charge not completed",
|
|
20
|
+
renewal_charged_unrecorded: "Renewal charged, order unpaid",
|
|
21
|
+
renewal_paid_twice: "Renewal charged — order also paid another way",
|
|
22
|
+
renewal_buyer_mismatch: "Renewal charged to a different customer",
|
|
23
|
+
upsell_outcome_unknown: "Offer charge — result unknown",
|
|
24
|
+
upsell_not_settled: "Offer charge not completed",
|
|
25
|
+
upsell_paid_not_added: "Offer paid, item not added",
|
|
26
|
+
upsell_paid_unsettled: "Offer paid — check the order",
|
|
27
|
+
upsell_paid_unrecorded: "Offer paid, payment not recorded",
|
|
28
|
+
upsell_hold_not_released: "Offer payment held — release not confirmed",
|
|
29
|
+
upsell_bank_check_open: "Offer bank check still open",
|
|
30
|
+
upsell_refund_failed: "Late offer charge — refund failed",
|
|
31
|
+
upsell_refund_unclear: "Late offer charge — refund unclear",
|
|
32
|
+
upsell_refund_pending: "Late offer charge — refund in progress",
|
|
33
|
+
upsell_refunded_late: "Late offer charge refunded",
|
|
34
|
+
upsell_hold_unsettled: "Offer payment held — not finished yet",
|
|
35
|
+
upsell_take_unconfirmed: "Offer payment — collection not confirmed"
|
|
36
|
+
};
|
|
37
|
+
const CHARGE_RESOLVED_HOW_LABEL = {
|
|
38
|
+
closed_blocking: "Closed by a person — no more renewal charges for this order",
|
|
39
|
+
freed: "Freed — later renewals of this order may charge the customer",
|
|
40
|
+
closed_no_change: "Closed by a person — no change to money"
|
|
41
|
+
};
|
|
42
|
+
function chargeHandStep(paymentIntentId) {
|
|
43
|
+
return { title: "Check this order in Vonpay", body: `Find ${paymentIntentId ? `payment ${paymentIntentId}` : "any payment"} in the Vonpay dashboard by the order id, and settle it there if it needs it.` };
|
|
44
|
+
}
|
|
45
|
+
const OFFER_CLOSED_MONEY_TEXT = {
|
|
46
|
+
upsell_paid_not_added: "The buyer paid for this offer. Closing this row did not refund it or add the item: that was yours to settle in Vonpay or on the order.",
|
|
47
|
+
upsell_paid_unsettled: "The buyer paid for this offer. Closing this row did not refund it or add the item: that was yours to settle in Vonpay or on the order.",
|
|
48
|
+
upsell_paid_unrecorded: "The buyer paid, and the item is on the order. Closing this row did not mark that balance paid in Medusa: that was yours to do.",
|
|
49
|
+
upsell_refund_failed: "The buyer was charged and the item was not added. Closing this row sent no refund: that was yours to send in Vonpay.",
|
|
50
|
+
upsell_refund_unclear: "The buyer was charged and the item was not added. Closing this row sent no refund: that was yours to send in Vonpay."
|
|
51
|
+
};
|
|
52
|
+
const OFFER_CLOSED_MONEY_DEFAULT = "Closing this row moved no money. What Vonpay holds for this payment is as it was.";
|
|
12
53
|
const REASON_COPY = {
|
|
13
54
|
deleted_while_charged: {
|
|
14
55
|
label: "Charged, then cart changed",
|
|
@@ -82,78 +123,7 @@ const REASON_COPY = {
|
|
|
82
123
|
label: "Paid, order not created",
|
|
83
124
|
what: "The buyer paid, but Medusa did not create the order, even after the plugin retried it automatically. Check the cart and the payment in Vonpay, then complete the order with the buyer or refund it."
|
|
84
125
|
},
|
|
85
|
-
|
|
86
|
-
label: "Renewal charge — result unknown",
|
|
87
|
-
what: "A subscription renewal charged the customer's saved card, but Vonpay's answer could not be read, so the customer may have been charged. No further renewal charge is sent for this order. Check the payment in Vonpay before charging or refunding anything."
|
|
88
|
-
},
|
|
89
|
-
renewal_not_settled: {
|
|
90
|
-
label: "Renewal charge not completed",
|
|
91
|
-
what: "Vonpay answered the subscription renewal charge, but the payment had not completed. No further renewal charge is sent for this order. Check the payment in Vonpay."
|
|
92
|
-
},
|
|
93
|
-
renewal_charged_unrecorded: {
|
|
94
|
-
label: "Renewal charged, order unpaid",
|
|
95
|
-
what: "Vonpay charged the customer's saved card for this subscription renewal, but Medusa's records do not show the payment, even after the plugin retried it automatically (or Medusa recorded it and then cancelled it). The customer has paid; the order shows unpaid. No further renewal charge is sent for this order. Find the payment in Vonpay by the order id, then mark the order paid in Medusa — do not charge again."
|
|
96
|
-
},
|
|
97
|
-
renewal_paid_twice: {
|
|
98
|
-
label: "Renewal charged — order also paid another way",
|
|
99
|
-
what: "Vonpay charged the customer's saved card for this subscription renewal, and the order was also marked paid another way. The customer may have paid twice. Check the payment in Vonpay and the order in Medusa, then refund the extra payment. No further renewal charge is sent for this order."
|
|
100
|
-
},
|
|
101
|
-
renewal_buyer_mismatch: {
|
|
102
|
-
label: "Renewal charged to a different customer",
|
|
103
|
-
what: "Vonpay recorded the subscription renewal charge against a different customer than the order's. No further renewal charge is sent for this order. Check the payment in Vonpay."
|
|
104
|
-
},
|
|
105
|
-
upsell_outcome_unknown: {
|
|
106
|
-
label: "Offer charge — result unknown",
|
|
107
|
-
what: "The buyer accepted an offer after their order and the plugin charged their card for it, but Vonpay's answer could not be read, so the buyer may have been charged. The item was NOT added to the order, and the plugin will not try that offer again. Check the payment in Vonpay by the order id: if it was charged, add the item to the order (or refund it in Vonpay); if not, cancel the open order edit in Medusa."
|
|
108
|
-
},
|
|
109
|
-
upsell_not_settled: {
|
|
110
|
-
label: "Offer charge not completed",
|
|
111
|
-
what: "The buyer accepted an offer after their order, and Vonpay answered the charge, but the payment had not completed. The item was NOT added to the order. Check the payment in Vonpay by the order id, then cancel the open order edit in Medusa if nothing was charged."
|
|
112
|
-
},
|
|
113
|
-
upsell_paid_not_added: {
|
|
114
|
-
label: "Offer paid, item not added",
|
|
115
|
-
what: "The buyer accepted an offer after their order and paid for it, but the item was NOT added to the order, even after the plugin retried. The buyer has paid. Either add the item to the order in Medusa, or refund the charge in Vonpay — not both, and do not charge again."
|
|
116
|
-
},
|
|
117
|
-
upsell_paid_unsettled: {
|
|
118
|
-
label: "Offer paid — check the order",
|
|
119
|
-
what: "The buyer accepted an offer after their order and paid for it, but the plugin could not finish recording it. Open the order first. If the item is on it, mark that balance paid in Medusa (do NOT refund — the item ships). If the item is not on it, either add it or refund the charge in Vonpay — not both. Never charge again."
|
|
120
|
-
},
|
|
121
|
-
upsell_paid_unrecorded: {
|
|
122
|
-
label: "Offer paid, payment not recorded",
|
|
123
|
-
what: "The buyer accepted an offer after their order and paid for it, and the item IS on the order, but Medusa did not record the payment, even after the plugin retried — so the order shows an unpaid balance. The buyer has paid. Find the payment in Vonpay by the order id and mark that balance paid in Medusa. Do NOT refund it (the item ships) and do not charge again."
|
|
124
|
-
},
|
|
125
|
-
upsell_hold_not_released: {
|
|
126
|
-
label: "Offer payment held — release not confirmed",
|
|
127
|
-
what: "The buyer approved an offer after their order with Apple Pay or Google Pay, which only held the money. The item could not be added, so the plugin released the hold, but Vonpay has not confirmed the release. Nothing was taken and the item was NOT added. The plugin keeps releasing it on every run. If this row stays, find the payment in Vonpay by the order id and void it there. Do not take it."
|
|
128
|
-
},
|
|
129
|
-
upsell_bank_check_open: {
|
|
130
|
-
label: "Offer bank check still open",
|
|
131
|
-
what: "The offer ended while the buyer's bank check was still open, so nothing was added and the order change was undone. If the bank approves it later, the plugin refunds it automatically. Nothing to do unless this stays for a few days; then check the payment in Vonpay by the order id."
|
|
132
|
-
},
|
|
133
|
-
upsell_refund_failed: {
|
|
134
|
-
label: "Late offer charge — refund failed",
|
|
135
|
-
what: "The buyer was charged for an offer whose item was NOT added to the order — the bank approved it after the offer had ended, the item could not be added, or an earlier declined attempt was approved after all — so the plugin tried to refund that charge in full, but the refund did not go through (refused, or not settled within 48 hours). Refund the payment in Vonpay by the order id. Do not add the item."
|
|
136
|
-
},
|
|
137
|
-
upsell_refund_unclear: {
|
|
138
|
-
label: "Late offer charge — refund unclear",
|
|
139
|
-
what: "The buyer was charged for an offer whose item was NOT added to the order, and the plugin's automatic refund got an unclear answer: Vonpay reported an error, then recorded that refund as failed, but the money may have gone back anyway. The plugin will not send another refund. First check the payment in Vonpay by the order id: refund it there ONLY if no refund went through. Do not add the item."
|
|
140
|
-
},
|
|
141
|
-
upsell_refund_pending: {
|
|
142
|
-
label: "Late offer charge — refund in progress",
|
|
143
|
-
what: "The buyer's bank approved an offer after the offer had ended, so nothing was added and the plugin is refunding the charge in full. It keeps retrying. If this row stays for a day, refund the payment in Vonpay by the order id."
|
|
144
|
-
},
|
|
145
|
-
upsell_refunded_late: {
|
|
146
|
-
label: "Late offer charge refunded",
|
|
147
|
-
what: "The buyer's bank approved an offer after the offer had ended, so nothing was added and the plugin refunded the charge in full. Nothing to do."
|
|
148
|
-
},
|
|
149
|
-
upsell_hold_unsettled: {
|
|
150
|
-
label: "Offer payment held — not finished yet",
|
|
151
|
-
what: "The buyer approved an offer after their order with Apple Pay or Google Pay, which only held the money, and the plugin has not finished it yet. Nothing was taken. The plugin keeps working on it: it adds the item and takes the money, or releases the hold if the item cannot be added. Do not take or void it yourself unless this row stays for more than a day; then check the payment in Vonpay by the order id."
|
|
152
|
-
},
|
|
153
|
-
upsell_take_unconfirmed: {
|
|
154
|
-
label: "Offer payment — collection not confirmed",
|
|
155
|
-
what: "The buyer approved an offer after their order with Apple Pay or Google Pay, and the plugin asked Vonpay to take the held money, but Vonpay has not confirmed whether it was taken. The plugin re-checks it on every run: if it was taken, the plugin adds the item; if it was released, it closes the open order edit. Check the payment in Vonpay by the order id. Do not take it again."
|
|
156
|
-
},
|
|
126
|
+
...Object.fromEntries(Object.entries(CHARGE_REASON_LABEL).map(([reason, label]) => [reason, { label }])),
|
|
157
127
|
cancel_refund_missing: {
|
|
158
128
|
label: "Cancelled, refund failed",
|
|
159
129
|
what: "The order was cancelled, but Medusa's refund to the buyer did not go through. The buyer may still be charged."
|
|
@@ -207,6 +177,7 @@ const RESOLVED_HOW_LABEL = {
|
|
|
207
177
|
refunded_earlier: "Already refunded with this payment's key (for example from the command line)",
|
|
208
178
|
nothing_left: "Vonpay showed nothing left to refund — confirm in the Vonpay dashboard that the buyer got it back",
|
|
209
179
|
marked: "Marked resolved",
|
|
180
|
+
...CHARGE_RESOLVED_HOW_LABEL,
|
|
210
181
|
auto_closed: "Closed automatically — Vonpay confirmed nothing was charged and the payment can no longer be used",
|
|
211
182
|
paid_after_auto_close: "Re-opened automatically — money arrived after the automatic close",
|
|
212
183
|
paid_after_resolution: "Re-opened automatically — Vonpay reported this payment as paid after it was marked resolved",
|
|
@@ -250,6 +221,9 @@ function isConfirmedRefund(row) {
|
|
|
250
221
|
function canRefund(row) {
|
|
251
222
|
return row.vp_session_id !== null && !isConfirmedRefund(row) && !isAutoClosed(row) && !isUnrecognisedRecord(row) && REFUNDABLE_REASONS.includes(row.reason);
|
|
252
223
|
}
|
|
224
|
+
function isChargeRow(row) {
|
|
225
|
+
return row.source === "renewal" || row.source === "upsell";
|
|
226
|
+
}
|
|
253
227
|
function canRelease(row) {
|
|
254
228
|
return row.source === "collection" && row.vp_session_id !== null && row.status === "open" && WALLET_REASONS.includes(row.reason);
|
|
255
229
|
}
|
|
@@ -257,9 +231,20 @@ function canComplete(row) {
|
|
|
257
231
|
return row.source === "collection" && row.vp_session_id !== null && row.status === "open" && row.reason === "wallet_held_no_order";
|
|
258
232
|
}
|
|
259
233
|
function canMarkResolved(row) {
|
|
234
|
+
if (isChargeRow(row)) return false;
|
|
260
235
|
return row.vp_session_id !== null && row.status !== "resolved" && !isUnrecognisedRecord(row) && !WALLET_REASONS.includes(row.reason) && !TAKE_REASONS.includes(row.reason);
|
|
261
236
|
}
|
|
262
237
|
function locatorFor(row) {
|
|
238
|
+
if (isChargeRow(row)) {
|
|
239
|
+
return {
|
|
240
|
+
source: row.source,
|
|
241
|
+
payment_collection_id: row.payment_collection_id,
|
|
242
|
+
...row.order_id ? { order_id: row.order_id } : {},
|
|
243
|
+
...row.medusa_payment_session_id ? { medusa_payment_session_id: row.medusa_payment_session_id } : {},
|
|
244
|
+
...row.acceptance_id ? { acceptance_id: row.acceptance_id } : {},
|
|
245
|
+
...row.prior_intent_id ? { prior_intent_id: row.prior_intent_id } : {}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
263
248
|
return {
|
|
264
249
|
source: row.source,
|
|
265
250
|
vp_session_id: row.vp_session_id ?? "",
|
|
@@ -269,6 +254,9 @@ function locatorFor(row) {
|
|
|
269
254
|
};
|
|
270
255
|
}
|
|
271
256
|
function resolutionSteps(row) {
|
|
257
|
+
if (isChargeRow(row)) {
|
|
258
|
+
return [chargeHandStep(row.vp_payment_intent_id)];
|
|
259
|
+
}
|
|
272
260
|
if (!row.vp_session_id || row.reason === "unrecognised") {
|
|
273
261
|
return [{ title: "Check this payment in Vonpay", body: "Find the payment in the Vonpay dashboard before refunding or fulfilling anything." }];
|
|
274
262
|
}
|
|
@@ -348,6 +336,14 @@ function reconcileCopy(o) {
|
|
|
348
336
|
function markConfirmMatches(typed, row) {
|
|
349
337
|
return row.vp_session_id !== null && typed.trim() === row.vp_session_id;
|
|
350
338
|
}
|
|
339
|
+
function closedMoneyNote(row) {
|
|
340
|
+
if (row.status !== "resolved" || row.resolution?.how !== "closed_no_change") return null;
|
|
341
|
+
return OFFER_CLOSED_MONEY_TEXT[row.reason] ?? OFFER_CLOSED_MONEY_DEFAULT;
|
|
342
|
+
}
|
|
343
|
+
function chargedUncheckedNote(count) {
|
|
344
|
+
if (!(count > 0)) return null;
|
|
345
|
+
return `${count} paid ${count === 1 ? "renewal was" : "renewals were"} not checked against Medusa on this load, because a load checks a limited number or Medusa did not answer. One that Medusa has no record of may be missing from this list. Reload to check the next ones.`;
|
|
346
|
+
}
|
|
351
347
|
const REFUSAL_TEXT = {
|
|
352
348
|
hold_release_not_eligible: "Only a held wallet payment can be released here. Nothing was changed.",
|
|
353
349
|
hold_release_payment_active: "This held payment belongs to an order. Take it or cancel it from the order. Nothing was changed.",
|
|
@@ -369,7 +365,7 @@ const REFUSAL_TEXT = {
|
|
|
369
365
|
hold_refund_auto_closed: "This payment was closed automatically because Vonpay confirmed nothing was charged. There's nothing to refund.",
|
|
370
366
|
hold_refund_not_eligible: "This held payment has no single charge that can be refunded from here. Nothing was refunded — check it in Vonpay.",
|
|
371
367
|
hold_record_unrecognised: "This payment's record in the list isn't one the plugin recognises. Nothing was changed — check it in Vonpay.",
|
|
372
|
-
refund_refused: "Vonpay refused the refund. Nothing was refunded.",
|
|
368
|
+
refund_refused: "Vonpay refused the refund. Nothing was refunded. If the buyer already has the money back (a dispute, or a refund made in the Vonpay dashboard), close this row with Mark resolved.",
|
|
373
369
|
refund_exceeds_remaining: "Vonpay shows nothing left to refund on this payment. Nothing new was refunded.",
|
|
374
370
|
hold_provider_unavailable: "The Vonpay payment provider couldn't be reached from the admin. Nothing was changed — try again.",
|
|
375
371
|
hold_provider_ambiguous: "More than one Vonpay provider could own this payment. Nothing was changed — check the store's payment setup.",
|
|
@@ -377,7 +373,7 @@ const REFUSAL_TEXT = {
|
|
|
377
373
|
hold_refund_key_conflict: "A refund with this payment's key was already made with different details (for example a partial amount from the command line). Nothing new was refunded — check it in Vonpay.",
|
|
378
374
|
refund_declined: "The payment provider declined the refund. No money moved.",
|
|
379
375
|
refund_not_completed: "Vonpay recorded this refund as failed. No money moved.",
|
|
380
|
-
hold_refund_not_refundable_now: "Vonpay says nothing can be refunded on this payment right now (not settled yet, disputed, or already refunded). Nothing was refunded.",
|
|
376
|
+
hold_refund_not_refundable_now: "Vonpay says nothing can be refunded on this payment right now (not settled yet, disputed, or already refunded). Nothing was refunded. Once the buyer has the money back (a dispute, or a refund made in the Vonpay dashboard), close this row with Mark resolved; if it has not settled yet, try again later.",
|
|
381
377
|
hold_checkout_busy: "The buyer's checkout for this payment is in progress right now. Nothing was changed — try again in a moment.",
|
|
382
378
|
hold_checkout_lock_unavailable: "This payment's checkout couldn't be locked, so nothing was changed. Check the store's locking setup, or refund from the command line.",
|
|
383
379
|
hold_recheck_failed: "The order couldn't be re-checked just before acting. Nothing was changed — try again.",
|
|
@@ -387,6 +383,14 @@ const REFUSAL_TEXT = {
|
|
|
387
383
|
hold_refund_payment_active: "This payment now belongs to an order that isn't cancelled. Nothing was refunded — refund it from the order.",
|
|
388
384
|
hold_resolve_wallet_held: "Use Release for a held wallet payment. It checks Vonpay first, then releases the hold, lists it for a refund, or clears it with its order. Nothing was changed.",
|
|
389
385
|
hold_resolve_payment_active: "This payment now belongs to an order that isn't cancelled, so it isn't held. Nothing was changed.",
|
|
386
|
+
move_request_invalid: "That request was missing the payment's ids. Run the check again. Nothing was changed.",
|
|
387
|
+
hold_resolve_confirm_mismatch: "To mark a payment resolved, type its Vonpay session id exactly as the row shows it. Nothing was changed.",
|
|
388
|
+
move_not_listed: "This payment isn't one the check lists any more (it may have become an order, or already be on Held payments). Run the check again. Nothing was changed.",
|
|
389
|
+
move_not_paid: "Vonpay doesn't show money taken or held on this payment, so there is nothing to move. Nothing was changed.",
|
|
390
|
+
move_refunded: "Vonpay shows this payment refunded in full (or a full refund still in progress), so there is nothing to move. Nothing was changed.",
|
|
391
|
+
move_not_this_checkout: "Vonpay's record of this payment names a different checkout. Check it in the Vonpay dashboard. Nothing was changed.",
|
|
392
|
+
move_lock_not_shared: "The command can't move a payment on a store without a shared lock (Redis or Postgres locking). Use the Move to Held payments button in the admin instead. Nothing was changed.",
|
|
393
|
+
move_vonpay_unavailable: "Vonpay couldn't be reached to check this payment again. Nothing was changed — try again shortly.",
|
|
390
394
|
hold_not_found: "This held payment is no longer on the list. It may already have been completed, released or refunded. Reload the list and check the order."
|
|
391
395
|
};
|
|
392
396
|
function outcomeCopy(o) {
|
|
@@ -456,6 +460,23 @@ function completeOutcomeCopy(o) {
|
|
|
456
460
|
return UNKNOWN_OUTCOME;
|
|
457
461
|
}
|
|
458
462
|
}
|
|
463
|
+
function moveOutcomeCopy(o) {
|
|
464
|
+
switch (o.outcome) {
|
|
465
|
+
case "moved":
|
|
466
|
+
return {
|
|
467
|
+
variant: "success",
|
|
468
|
+
text: o.reason === "wallet_held_no_order" ? "Moved to Held payments. Release the held payment or complete the order there." : "Moved to Held payments. Refund it or mark it resolved there."
|
|
469
|
+
};
|
|
470
|
+
case "already_held":
|
|
471
|
+
return { variant: "success", text: "It is already on Held payments. Nothing was changed." };
|
|
472
|
+
case "ordered":
|
|
473
|
+
return { variant: "warning", text: "An order holds this payment now, so it was not moved. Nothing was changed." };
|
|
474
|
+
case "refused":
|
|
475
|
+
return { variant: "error", text: o.code && REFUSAL_TEXT[o.code] || "That couldn't be done. Nothing was changed." };
|
|
476
|
+
default:
|
|
477
|
+
return UNKNOWN_OUTCOME;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
459
480
|
function resolveOutcomeCopy(o) {
|
|
460
481
|
if (o.outcome === "refused") return { variant: "error", text: o.code && REFUSAL_TEXT[o.code] || "That couldn't be done. Nothing was changed." };
|
|
461
482
|
if (!o.recorded) return { variant: "warning", text: "Couldn't save that. Try again." };
|
|
@@ -466,6 +487,7 @@ const authType = typeof __AUTH_TYPE__ !== "undefined" && __AUTH_TYPE__ ? __AUTH_
|
|
|
466
487
|
const sdk = new Medusa__default.default({ baseUrl: backendUrl, auth: { type: authType } });
|
|
467
488
|
const HELD_PAGE_SIZE = 20;
|
|
468
489
|
const LIST_KEY = "vonpay-held-payments";
|
|
490
|
+
const HELD_LIST_KEY = LIST_KEY;
|
|
469
491
|
function useHeldPayments(page) {
|
|
470
492
|
const status = page.status ?? "open";
|
|
471
493
|
return reactQuery.useQuery({
|
|
@@ -528,6 +550,22 @@ function useMarkResolved() {
|
|
|
528
550
|
onSettled: () => qc.invalidateQueries({ queryKey: [LIST_KEY] })
|
|
529
551
|
});
|
|
530
552
|
}
|
|
553
|
+
function useChargeAction() {
|
|
554
|
+
const qc = reactQuery.useQueryClient();
|
|
555
|
+
return reactQuery.useMutation({
|
|
556
|
+
mutationFn: (v) => sdk.client.fetch("/admin/vonpay/holds/resolve", {
|
|
557
|
+
method: "POST",
|
|
558
|
+
body: {
|
|
559
|
+
...locatorFor(v.row),
|
|
560
|
+
action: v.action,
|
|
561
|
+
...v.typed_order_id !== void 0 ? { confirm_order_id: v.typed_order_id.trim(), confirm_checked_in_vonpay: v.checked_in_vonpay === true } : {},
|
|
562
|
+
...v.action === "close_unreadable" && v.row.record_sha256 ? { confirm_record_sha256: v.row.record_sha256 } : {}
|
|
563
|
+
}
|
|
564
|
+
}),
|
|
565
|
+
retry: false,
|
|
566
|
+
onSettled: () => qc.invalidateQueries({ queryKey: [LIST_KEY] })
|
|
567
|
+
});
|
|
568
|
+
}
|
|
531
569
|
function useReconcileHold() {
|
|
532
570
|
const qc = reactQuery.useQueryClient();
|
|
533
571
|
return reactQuery.useMutation({
|
|
@@ -632,8 +670,42 @@ function useSetupCheck() {
|
|
|
632
670
|
retry: false
|
|
633
671
|
});
|
|
634
672
|
}
|
|
673
|
+
function useMoveToHeld() {
|
|
674
|
+
const qc = reactQuery.useQueryClient();
|
|
675
|
+
return reactQuery.useMutation({
|
|
676
|
+
mutationFn: (m) => sdk.client.fetch("/admin/vonpay/setup-check/move-to-held", { method: "POST", body: { vp_session_id: m.vp_session_id, medusa_payment_session_id: m.medusa_payment_session_id } }),
|
|
677
|
+
retry: false,
|
|
678
|
+
onSettled: () => qc.invalidateQueries({ queryKey: [HELD_LIST_KEY] })
|
|
679
|
+
});
|
|
680
|
+
}
|
|
635
681
|
const LABEL = { pass: "OK", warn: "Check", fail: "Fix", not_checked: "Not checked" };
|
|
636
682
|
const COLOR = { pass: "green", warn: "orange", fail: "red", not_checked: "grey" };
|
|
683
|
+
function uniqueMoves(moves) {
|
|
684
|
+
const seen = /* @__PURE__ */ new Set();
|
|
685
|
+
return (moves ?? []).filter((m) => !seen.has(m.vp_session_id) && (seen.add(m.vp_session_id), true));
|
|
686
|
+
}
|
|
687
|
+
const MoveRow = ({ move }) => {
|
|
688
|
+
const mutation = useMoveToHeld();
|
|
689
|
+
const [result, setResult] = react.useState(null);
|
|
690
|
+
const done = result !== null && result.variant !== "error";
|
|
691
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-col gap-y-1 sm:flex-row sm:items-center sm:gap-x-3", children: [
|
|
692
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "font-mono", children: move.vp_session_id }),
|
|
693
|
+
!done && /* @__PURE__ */ jsxRuntime.jsx(
|
|
694
|
+
ui.Button,
|
|
695
|
+
{
|
|
696
|
+
size: "small",
|
|
697
|
+
variant: "secondary",
|
|
698
|
+
isLoading: mutation.isPending,
|
|
699
|
+
onClick: () => mutation.mutate(move, {
|
|
700
|
+
onSuccess: (o) => setResult(moveOutcomeCopy(o)),
|
|
701
|
+
onError: () => setResult({ variant: "error", text: "That couldn't be done. Nothing was changed — try again." })
|
|
702
|
+
}),
|
|
703
|
+
children: "Move to Held payments"
|
|
704
|
+
}
|
|
705
|
+
),
|
|
706
|
+
result && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: result.variant === "error" ? "text-ui-fg-error" : "text-ui-fg-subtle", children: result.text })
|
|
707
|
+
] });
|
|
708
|
+
};
|
|
637
709
|
const SetupCheckPanel = () => {
|
|
638
710
|
const check = useSetupCheck();
|
|
639
711
|
const items = check.data?.items ?? [];
|
|
@@ -652,7 +724,8 @@ const SetupCheckPanel = () => {
|
|
|
652
724
|
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: COLOR[i.status], className: "w-fit shrink-0", children: LABEL[i.status] }),
|
|
653
725
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
654
726
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: i.detail }),
|
|
655
|
-
i.fix && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: i.fix })
|
|
727
|
+
i.fix && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: i.fix }),
|
|
728
|
+
uniqueMoves(i.moves).length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-2 flex flex-col gap-y-2", "aria-label": "Paid checkouts with no order", children: uniqueMoves(i.moves).map((m) => /* @__PURE__ */ jsxRuntime.jsx(MoveRow, { move: m }, m.vp_session_id)) })
|
|
656
729
|
] })
|
|
657
730
|
] }, `${i.id}:${i.provider ?? ""}:${n}`)) })
|
|
658
731
|
] })
|
|
@@ -825,6 +898,130 @@ const MarkResolvedAction = ({ row, onResult }) => {
|
|
|
825
898
|
] }) })
|
|
826
899
|
] });
|
|
827
900
|
};
|
|
901
|
+
const localTime = (iso) => new Date(iso).toLocaleString();
|
|
902
|
+
const TIMER_MAX_MS = 2147483647;
|
|
903
|
+
const REPOLL_MS = 15e3;
|
|
904
|
+
const REPOLL_MAX = 8;
|
|
905
|
+
const ChargeActions = ({ row, listAt, onResult }) => {
|
|
906
|
+
const [answered, setAnswer] = react.useState(null);
|
|
907
|
+
const answer = answered && !(listAt > answered.receivedAt) ? answered : null;
|
|
908
|
+
const [asking, setAsking] = react.useState(null);
|
|
909
|
+
const [typed, setTyped] = react.useState("");
|
|
910
|
+
const [checked, setChecked] = react.useState(false);
|
|
911
|
+
const act = useChargeAction();
|
|
912
|
+
const qc = reactQuery.useQueryClient();
|
|
913
|
+
const rowSays = JSON.stringify([row.held_at, row.actions ?? null, row.hint ?? null]);
|
|
914
|
+
react.useEffect(() => setAnswer(null), [rowSays]);
|
|
915
|
+
const actions = answer?.actions ?? row.actions ?? [];
|
|
916
|
+
const hint = answer ? answer.hint : row.hint ?? null;
|
|
917
|
+
const closedUntil = actions.filter((a) => !a.available && a.available_at).map((a) => Date.parse(a.available_at)).filter((t) => Number.isFinite(t));
|
|
918
|
+
const opensAt = closedUntil.filter((t) => t > Date.now());
|
|
919
|
+
const nextOpen = opensAt.length > 0 ? Math.min(...opensAt) : null;
|
|
920
|
+
react.useEffect(() => {
|
|
921
|
+
if (nextOpen === null) return;
|
|
922
|
+
const timer = setTimeout(() => {
|
|
923
|
+
setAnswer(null);
|
|
924
|
+
void qc.invalidateQueries({ queryKey: [HELD_LIST_KEY] });
|
|
925
|
+
}, Math.min(TIMER_MAX_MS, Math.max(0, nextOpen - Date.now()) + 1e3));
|
|
926
|
+
return () => clearTimeout(timer);
|
|
927
|
+
}, [nextOpen, qc]);
|
|
928
|
+
const overdue = closedUntil.filter((t) => t <= Date.now());
|
|
929
|
+
const pastDue = overdue.length > 0 ? Math.min(...overdue) : null;
|
|
930
|
+
const repolls = react.useRef(0);
|
|
931
|
+
react.useEffect(() => {
|
|
932
|
+
if (pastDue === null || repolls.current >= REPOLL_MAX) return;
|
|
933
|
+
const timer = setTimeout(() => {
|
|
934
|
+
repolls.current += 1;
|
|
935
|
+
void qc.invalidateQueries({ queryKey: [HELD_LIST_KEY] });
|
|
936
|
+
}, REPOLL_MS);
|
|
937
|
+
return () => clearTimeout(timer);
|
|
938
|
+
}, [pastDue, listAt, qc]);
|
|
939
|
+
const run = (a) => act.mutate(
|
|
940
|
+
{ row, action: a.id, ...a.confirm === "type_order_id" ? { typed_order_id: typed, checked_in_vonpay: checked } : {} },
|
|
941
|
+
{
|
|
942
|
+
onSuccess: (o) => {
|
|
943
|
+
if (o.recorded === false || o.outcome === "not_saved") {
|
|
944
|
+
setAnswer({ ...o, actions: [], hint: null, receivedAt: Date.now() });
|
|
945
|
+
onResult({ variant: "warning", text: NOT_SAVED_TEXT });
|
|
946
|
+
void qc.invalidateQueries({ queryKey: [HELD_LIST_KEY] });
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
setAnswer({ ...o, receivedAt: Date.now() });
|
|
950
|
+
onResult({ variant: o.tone, text: fillTime(o.text, o.at, localTime) });
|
|
951
|
+
},
|
|
952
|
+
onError: (e) => {
|
|
953
|
+
const f = postFailure(e);
|
|
954
|
+
onResult(f.variant === "error" ? f : { variant: "warning", text: "Couldn't confirm the outcome. Reload the list." });
|
|
955
|
+
},
|
|
956
|
+
onSettled: () => setAsking(null)
|
|
957
|
+
}
|
|
958
|
+
);
|
|
959
|
+
const typedOk = !asking || asking.confirm !== "type_order_id" || typed.trim() === row.order_id && checked;
|
|
960
|
+
const inputId = `vonpay-charge-confirm-${row.payment_collection_id}-${row.medusa_payment_session_id ?? row.acceptance_id ?? ""}`;
|
|
961
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
962
|
+
hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: fillTime(hint.text, hint.at, localTime) }),
|
|
963
|
+
actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: actions.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
964
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
965
|
+
ui.Button,
|
|
966
|
+
{
|
|
967
|
+
size: "small",
|
|
968
|
+
variant: a.id === "check" ? "primary" : "secondary",
|
|
969
|
+
disabled: !a.available || act.isPending,
|
|
970
|
+
isLoading: act.isPending && (asking?.id === a.id || a.id === "check" && !asking),
|
|
971
|
+
onClick: () => {
|
|
972
|
+
if (!a.available) return;
|
|
973
|
+
if (!a.confirm) {
|
|
974
|
+
run(a);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
setTyped("");
|
|
978
|
+
setChecked(false);
|
|
979
|
+
setAsking(a);
|
|
980
|
+
},
|
|
981
|
+
children: a.label
|
|
982
|
+
}
|
|
983
|
+
),
|
|
984
|
+
!a.available && a.available_at && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", className: "text-ui-fg-muted", children: [
|
|
985
|
+
"Opens ",
|
|
986
|
+
localTime(a.available_at)
|
|
987
|
+
] })
|
|
988
|
+
] }, a.id)) }),
|
|
989
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt, { open: asking !== null, onOpenChange: (o) => !act.isPending && !o && setAsking(null), variant: "confirmation", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
990
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
991
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Title, { children: [
|
|
992
|
+
asking?.label.replace(/…$/, ""),
|
|
993
|
+
"?"
|
|
994
|
+
] }),
|
|
995
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: asking?.confirm_text })
|
|
996
|
+
] }),
|
|
997
|
+
asking?.confirm === "type_order_id" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2 px-6 pb-4", children: [
|
|
998
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { htmlFor: inputId, size: "small", children: [
|
|
999
|
+
"Type the order id to confirm: ",
|
|
1000
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: row.order_id })
|
|
1001
|
+
] }),
|
|
1002
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { id: inputId, size: "small", autoComplete: "off", value: typed, onChange: (e) => setTyped(e.target.value) }),
|
|
1003
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-x-2", children: [
|
|
1004
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked, onChange: (e) => setChecked(e.target.checked) }),
|
|
1005
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small", children: "I checked this order in the Vonpay dashboard" })
|
|
1006
|
+
] })
|
|
1007
|
+
] }),
|
|
1008
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
|
|
1009
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { disabled: act.isPending, children: "Cancel" }),
|
|
1010
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1011
|
+
ui.Prompt.Action,
|
|
1012
|
+
{
|
|
1013
|
+
disabled: act.isPending || !typedOk,
|
|
1014
|
+
onClick: (e) => {
|
|
1015
|
+
e.preventDefault();
|
|
1016
|
+
if (asking && typedOk) run(asking);
|
|
1017
|
+
},
|
|
1018
|
+
children: asking?.label.replace(/…$/, "")
|
|
1019
|
+
}
|
|
1020
|
+
)
|
|
1021
|
+
] })
|
|
1022
|
+
] }) })
|
|
1023
|
+
] });
|
|
1024
|
+
};
|
|
828
1025
|
const WalletHoldAction = ({ row, kind, onResult }) => {
|
|
829
1026
|
const [open, setOpen] = react.useState(false);
|
|
830
1027
|
const release = useReleaseHold();
|
|
@@ -860,7 +1057,7 @@ const WalletHoldAction = ({ row, kind, onResult }) => {
|
|
|
860
1057
|
] }) })
|
|
861
1058
|
] });
|
|
862
1059
|
};
|
|
863
|
-
const HoldDrawer = ({ row, onClose }) => {
|
|
1060
|
+
const HoldDrawer = ({ row, listAt, onClose }) => {
|
|
864
1061
|
const copy = row ? reasonCopy(row.reason) : void 0;
|
|
865
1062
|
const known = row ? isKnownStatus(row.status) && !isUnrecognisedRecord(row) : false;
|
|
866
1063
|
const showRefund = !!row && known && canRefund(row);
|
|
@@ -877,7 +1074,7 @@ const HoldDrawer = ({ row, onClose }) => {
|
|
|
877
1074
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: row !== null, onOpenChange: (open) => !open && close(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
878
1075
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Header, { children: [
|
|
879
1076
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: copy?.label ?? "Held payment" }),
|
|
880
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Description, { children: copy?.what })
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Description, { children: row && isChargeRow(row) ? void 0 : copy?.what })
|
|
881
1078
|
] }),
|
|
882
1079
|
row && /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: [
|
|
883
1080
|
/* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-x-4 gap-y-2", children: [
|
|
@@ -902,6 +1099,10 @@ const HoldDrawer = ({ row, onClose }) => {
|
|
|
902
1099
|
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "txt-compact-small text-ui-fg-subtle", children: "Order" }),
|
|
903
1100
|
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: /* @__PURE__ */ jsxRuntime.jsx(Mono, { value: row.order_id, label: "order id" }) })
|
|
904
1101
|
] }),
|
|
1102
|
+
row.vp_payment_intent_id && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1103
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "txt-compact-small text-ui-fg-subtle", children: "Vonpay payment" }),
|
|
1104
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: /* @__PURE__ */ jsxRuntime.jsx(Mono, { value: row.vp_payment_intent_id, label: "Vonpay payment id" }) })
|
|
1105
|
+
] }),
|
|
905
1106
|
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "txt-compact-small text-ui-fg-subtle", children: "Vonpay session" }),
|
|
906
1107
|
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: /* @__PURE__ */ jsxRuntime.jsx(Mono, { value: row.vp_session_id, label: "Vonpay session id" }) }),
|
|
907
1108
|
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "txt-compact-small text-ui-fg-subtle", children: "Medusa payment session" }),
|
|
@@ -930,6 +1131,7 @@ const HoldDrawer = ({ row, onClose }) => {
|
|
|
930
1131
|
] })
|
|
931
1132
|
] }),
|
|
932
1133
|
result && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: result.variant, children: result.text }),
|
|
1134
|
+
closedMoneyNote(row) && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "warning", children: closedMoneyNote(row) }),
|
|
933
1135
|
(showRefund || showMark || showRelease || showComplete || showOpenOrder) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
934
1136
|
showOpenOrder && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "primary", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: orderPath(row.order_id), children: "Open order" }) }),
|
|
935
1137
|
showComplete && /* @__PURE__ */ jsxRuntime.jsx(WalletHoldAction, { row, kind: "complete", onResult: setResult }),
|
|
@@ -938,6 +1140,7 @@ const HoldDrawer = ({ row, onClose }) => {
|
|
|
938
1140
|
showRefund && /* @__PURE__ */ jsxRuntime.jsx(CheckWithVonpayAction, { row, onResult: setResult }),
|
|
939
1141
|
showMark && /* @__PURE__ */ jsxRuntime.jsx(MarkResolvedAction, { row, onResult: setResult })
|
|
940
1142
|
] }),
|
|
1143
|
+
row && isChargeRow(row) && /* @__PURE__ */ jsxRuntime.jsx(ChargeActions, { row, listAt, onResult: setResult }, `${row.payment_collection_id}:${row.medusa_payment_session_id ?? row.acceptance_id ?? ""}:${row.prior_intent_id ?? ""}`),
|
|
941
1144
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
942
1145
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Or do it by hand" }),
|
|
943
1146
|
steps.map((step) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
@@ -948,7 +1151,7 @@ const HoldDrawer = ({ row, onClose }) => {
|
|
|
948
1151
|
/* @__PURE__ */ jsxRuntime.jsx(ui.CodeBlock.Body, {})
|
|
949
1152
|
] })
|
|
950
1153
|
] }, step.title)),
|
|
951
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: STILL_LISTED_NOTE })
|
|
1154
|
+
!isChargeRow(row) && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: STILL_LISTED_NOTE })
|
|
952
1155
|
] })
|
|
953
1156
|
] })
|
|
954
1157
|
] }) });
|
|
@@ -958,11 +1161,11 @@ const HeldPaymentsPage = () => {
|
|
|
958
1161
|
const [pageIndex, setPageIndex] = react.useState(0);
|
|
959
1162
|
const [filter, setFilter] = react.useState("open");
|
|
960
1163
|
const [opened, setOpened] = react.useState(null);
|
|
961
|
-
const { data, isLoading, isError, refetch } = useHeldPayments({ limit: HELD_PAGE_SIZE, offset: pageIndex * HELD_PAGE_SIZE, status: filter });
|
|
1164
|
+
const { data, dataUpdatedAt, isLoading, isError, refetch } = useHeldPayments({ limit: HELD_PAGE_SIZE, offset: pageIndex * HELD_PAGE_SIZE, status: filter });
|
|
962
1165
|
const count = data?.count ?? 0;
|
|
963
1166
|
const remainder = count % HELD_PAGE_SIZE;
|
|
964
1167
|
const pageCount = Math.max(1, (count - remainder) / HELD_PAGE_SIZE + (remainder > 0 ? 1 : 0));
|
|
965
|
-
const keyOf = (row) => `${row.payment_collection_id}:${row.vp_session_id ?? "unreadable"}:${row.source}:${row.payment_id ?? ""}:${row.medusa_payment_session_id ?? ""}`;
|
|
1168
|
+
const keyOf = (row) => `${row.payment_collection_id}:${row.vp_session_id ?? "unreadable"}:${row.source}:${row.payment_id ?? ""}:${row.medusa_payment_session_id ?? ""}:${row.acceptance_id ?? ""}:${row.prior_intent_id ?? ""}`;
|
|
966
1169
|
const open = opened ? data?.holds.find((r) => keyOf(r) === keyOf(opened)) ?? opened : null;
|
|
967
1170
|
const onRowKey = (row) => (e) => {
|
|
968
1171
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -994,6 +1197,8 @@ const HeldPaymentsPage = () => {
|
|
|
994
1197
|
) })
|
|
995
1198
|
] }),
|
|
996
1199
|
data && !data.scan_complete && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "warning", children: "This list may be incomplete: the store has more records than one page load can check." }) }),
|
|
1200
|
+
data?.job?.filter((j) => j.banner).map((j) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "warning", children: j.banner }) }, j.mode)),
|
|
1201
|
+
data && chargedUncheckedNote(data.charged_unchecked) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "warning", children: chargedUncheckedNote(data.charged_unchecked) }) }),
|
|
997
1202
|
isError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-y-3 px-6 py-6", children: [
|
|
998
1203
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "Couldn't load held payments." }),
|
|
999
1204
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => refetch(), children: "Try again" })
|
|
@@ -1045,7 +1250,7 @@ const HeldPaymentsPage = () => {
|
|
|
1045
1250
|
}
|
|
1046
1251
|
)
|
|
1047
1252
|
] }),
|
|
1048
|
-
/* @__PURE__ */ jsxRuntime.jsx(HoldDrawer, { row: open, onClose: () => setOpened(null) })
|
|
1253
|
+
/* @__PURE__ */ jsxRuntime.jsx(HoldDrawer, { row: open, listAt: dataUpdatedAt, onClose: () => setOpened(null) })
|
|
1049
1254
|
] }),
|
|
1050
1255
|
/* @__PURE__ */ jsxRuntime.jsx(SetupCheckPanel, {})
|
|
1051
1256
|
] });
|