commit e2cee7a88fb22824eddab146aa462d9dd2dacea6
parent 9d6d313afb68bb4e22926a34867148f01001d923
Author: Sebastian <sebasjm@taler-systems.com>
Date: Fri, 5 Dec 2025 10:38:25 -0300
fix #10725
Diffstat:
1 file changed, 82 insertions(+), 55 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
@@ -32,7 +32,7 @@ import {
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format, formatDistance } from "date-fns";
import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
import { FormProvider } from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
@@ -51,7 +51,6 @@ import { mergeRefunds } from "../../../../utils/amount.js";
import { RefundModal } from "../list/Table.js";
import { Event, Timeline } from "./Timeline.js";
-
const TALER_SCREEN_ID = 44;
type Entity = TalerMerchantApi.MerchantOrderStatusResponse;
@@ -74,15 +73,15 @@ type Claimed = TalerMerchantApi.CheckPaymentClaimedResponse;
function ContractTerms({ value }: { value: CT }) {
if (value.version === undefined) {
- return <ContractTerms_V0 value={value} />
+ return <ContractTerms_V0 value={value} />;
}
switch (value.version) {
case MerchantContractVersion.V0:
- return <ContractTerms_V0 value={value} />
+ return <ContractTerms_V0 value={value} />;
case MerchantContractVersion.V1:
- return <ContractTerms_V1 value={value} />
+ return <ContractTerms_V1 value={value} />;
default:
- assertUnreachable(value.version)
+ assertUnreachable(value.version);
}
}
@@ -265,7 +264,6 @@ function ClaimedPage({
id: string;
order: TalerMerchantApi.CheckPaymentClaimedResponse;
}) {
-
const now = new Date();
const refundable =
order.contract_terms.refund_deadline.t_s !== "never" &&
@@ -360,11 +358,11 @@ function ClaimedPage({
{order.contract_terms.timestamp.t_s === "never"
? i18n.str`Never`
: format(
- new Date(
- order.contract_terms.timestamp.t_s * 1000,
- ),
- datetimeFormatForSettings(settings),
- )}
+ new Date(
+ order.contract_terms.timestamp.t_s * 1000,
+ ),
+ datetimeFormatForSettings(settings),
+ )}
</p>
</div>
</div>
@@ -439,14 +437,34 @@ function PaidPage({
onRefund: (id: string) => void;
}) {
const { config } = useSessionContext();
+ const [value, valueHandler] = useState<Partial<Paid>>(order);
+ const { state } = useSessionContext();
+ const { i18n } = useTranslationContext();
const now = new Date();
- const refundable =
+ const merchantCanRefund =
order.contract_terms.refund_deadline.t_s !== "never" &&
now.getTime() < order.contract_terms.refund_deadline.t_s * 1000;
+ const hasBeenWired =
+ order.contract_terms.wire_transfer_deadline.t_s !== "never" &&
+ order.contract_terms.wire_transfer_deadline.t_s * 1000 < now.getTime();
+ const [_, reload] = useState(false);
+ useEffect(() => {
+ if (hasBeenWired) return;
+ const deadline = order.contract_terms.wire_transfer_deadline;
+ if (deadline.t_s === "never") return;
+ const diff = deadline.t_s * 1000 - new Date().getTime();
+ setTimeout(() => {
+ reload(true);
+ }, diff + 100);
+ }, []);
+
const events: Event[] = [];
- if (order.contract_terms.refund_deadline.t_s !== "never" && refundable) {
+ if (
+ order.contract_terms.refund_deadline.t_s !== "never" &&
+ merchantCanRefund
+ ) {
events.push({
when: new Date(order.contract_terms.refund_deadline.t_s * 1000),
description: "refund deadline",
@@ -481,26 +499,24 @@ function PaidPage({
}
});
- const [value, valueHandler] = useState<Partial<Paid>>(order);
- const { state } = useSessionContext();
- const { i18n } = useTranslationContext();
-
-
- const orderAmounts = getOrderAmountAndWirefee(order)
+ const orderAmounts = getOrderAmountAndWirefee(order);
if (orderAmounts === "v1-without-index") {
- return <i18n.Translate>The contract terms has a v1 order without choices_index.</i18n.Translate>
+ return (
+ <i18n.Translate>
+ The contract terms has a v1 order without choices_index.
+ </i18n.Translate>
+ );
}
if (orderAmounts === "v1-wrong-index") {
- return <i18n.Translate>The contract terms has a v1 order with a bad choices_index.</i18n.Translate>
-
- }
- const { amount, wireFee } = orderAmounts
- if (order.contract_terms.version === MerchantContractVersion.V1) {
-
- order.choice_index
+ return (
+ <i18n.Translate>
+ The contract terms has a v1 order with a bad choices_index.
+ </i18n.Translate>
+ );
}
+ const { amount, wireFee } = orderAmounts;
const ra = !order.refunded ? undefined : Amounts.parse(order.refund_amount);
- // const am = Amounts.parseOrThrow(order.contract_terms.amount);
+
if (ra && Amounts.cmp(ra, amount) === 1) {
if (order.wire_details && order.wire_details.length) {
if (order.wire_details.length > 1) {
@@ -557,11 +573,13 @@ function PaidPage({
return e.when.getTime() > now.getTime();
});
-
- const refundurl = stringifyRefundUri({
- merchantBaseUrl: state.backendUrl.href as HostPortPath,
- orderId: order.contract_terms.order_id,
- });
+ const refundURI =
+ !order.refunded || hasBeenWired
+ ? undefined
+ : stringifyRefundUri({
+ merchantBaseUrl: state.backendUrl.href as HostPortPath,
+ orderId: order.contract_terms.order_id,
+ });
const refund_taken = order.refund_details.reduce((prev, cur) => {
if (cur.pending) return prev;
@@ -610,14 +628,15 @@ function PaidPage({
<span
class="has-tooltip-left"
data-tooltip={
- refundable
+ merchantCanRefund
? i18n.str`Refund order`
: i18n.str`Not refundable`
}
>
- <button type="button"
+ <button
+ type="button"
class="button is-danger"
- disabled={!refundable}
+ disabled={!merchantCanRefund}
onClick={() => onRefund(id)}
>
<i18n.Translate>Refund</i18n.Translate>
@@ -705,13 +724,13 @@ function PaidPage({
{order.order_status_url}
</a>
</TextField>
- {order.refunded && (
+ {refundURI && (
<TextField<Paid>
name="order_status_url"
label={i18n.str`Refund URI`}
>
- <a target="_blank" rel="noreferrer" href={refundurl}>
- {refundurl}
+ <a target="_blank" rel="noreferrer" href={refundURI}>
+ {refundURI}
</a>
</TextField>
)}
@@ -797,9 +816,9 @@ function UnpaidPage({
{order.creation_time.t_s === "never"
? i18n.str`Never`
: format(
- new Date(order.creation_time.t_s * 1000),
- datetimeFormatForSettings(settings),
- )}
+ new Date(order.creation_time.t_s * 1000),
+ datetimeFormatForSettings(settings),
+ )}
</p>
</div>
</div>
@@ -903,19 +922,27 @@ export function DetailPage({ id, selected, onRefunded, onBack }: Props): VNode {
</Fragment>
);
}
-export function getOrderAmountAndWirefee(order: TalerMerchantApi.CheckPaymentPaidResponse | TalerMerchantApi.CheckPaymentPaidResponse) {
- if (order.contract_terms.version === undefined || order.contract_terms.version === MerchantContractVersion.V0) {
- const amount = Amounts.parseOrThrow(order.contract_terms.amount)
- const wireFee = Amounts.parseOrThrow(order.contract_terms.wire_method)
- return { amount, wireFee }
+export function getOrderAmountAndWirefee(
+ order:
+ | TalerMerchantApi.CheckPaymentPaidResponse
+ | TalerMerchantApi.CheckPaymentPaidResponse,
+) {
+ if (
+ order.contract_terms.version === undefined ||
+ order.contract_terms.version === MerchantContractVersion.V0
+ ) {
+ const amount = Amounts.parseOrThrow(order.contract_terms.amount);
+ const wireFee = Amounts.parseOrThrow(order.contract_terms.max_fee);
+ return { amount, wireFee };
}
if (order.contract_terms.version === MerchantContractVersion.V1) {
- if (order.choice_index === undefined) return "v1-without-index" as const
- if (order.choice_index > order.contract_terms.choices.length) return "v1-wrong-index" as const
- const choice = order.contract_terms.choices[order.choice_index]
- const amount = Amounts.parseOrThrow(choice.amount)
- const wireFee = Amounts.parseOrThrow(choice.max_fee)
- return { amount, wireFee }
+ if (order.choice_index === undefined) return "v1-without-index" as const;
+ if (order.choice_index > order.contract_terms.choices.length)
+ return "v1-wrong-index" as const;
+ const choice = order.contract_terms.choices[order.choice_index];
+ const amount = Amounts.parseOrThrow(choice.amount);
+ const wireFee = Amounts.parseOrThrow(choice.max_fee);
+ return { amount, wireFee };
}
- assertUnreachable(order.contract_terms.version)
+ assertUnreachable(order.contract_terms.version);
}