/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { format, formatDuration } from "date-fns";
import { intervalToDuration } from "date-fns/esm";
import { Fragment, h, render, VNode } from "preact";
import { render as renderToString } from "preact-render-to-string";
import { Footer } from "../components/Footer";
import "../css/pure-min.css";
import "../css/style.css";
import { MerchantBackend } from "../declaration";
import { Page, InfoBox, TableExpanded, TableSimple } from "../styled";
/**
* This page creates a payment request QR code
*
* It will build into a mustache html template for server side rendering
*
* server side rendering params:
* - order_summary
* - contract_terms
* - refund_amount
*
* request params:
* - refund_amount
* - contract_terms
* - order_summary
*/
export interface Props {
btr?: boolean; // build time rendering flag
order_summary?: string;
refund_amount?: string;
contract_terms?: MerchantBackend.ContractTerms;
}
function Head({ order_summary }: { order_summary?: string }): VNode {
return (
Status of your order for{" "}
{order_summary ? order_summary : `{{ order_summary }}`}
);
}
function Location({
templateName,
location,
btr,
}: {
templateName: string;
location: MerchantBackend.Location | undefined;
btr?: boolean;
}) {
//FIXME: mustache strings show be constructed in a way that ends in the final output of the html but is not present in the
// javascript code, otherwise when mustache render engine run over the html it will also replace string in the javascript code
// that is made to run when the browser has javascript enable leading into undefined behavior.
// that's why in the next fields we are using concatenations to build the mustache placeholder.
return (
{btr && `{{` + `#${templateName}.building_name}}`}
{location?.building_name ||
(btr && `{{ ${templateName}.building_name }}`)}{" "}
{location?.building_number ||
(btr && `{{ ${templateName}.building_number }}`)}
{btr && `{{` + `/${templateName}.building_name}}`}
{btr && `{{` + `#${templateName}.country}}`}
{location?.country || (btr && `{{ ${templateName}.country }}`)}{" "}
{location?.country_subdivision ||
(btr && `{{ ${templateName}.country_subdivision }}`)}
{btr && `{{` + `/${templateName}.country}}`}
{btr && `{{` + `#${templateName}.district}}`}
{location?.district || (btr && `{{ ${templateName}.district }}`)}
{btr && `{{` + `/${templateName}.district}}`}
{btr && `{{` + `#${templateName}.post_code}}`}
{location?.post_code || (btr && `{{ ${templateName}.post_code }}`)}
{btr && `{{` + `/${templateName}.post_code}}`}
{btr && `{{` + `#${templateName}.street}}`}
{location?.street || (btr && `{{ ${templateName}.street }}`)}
{btr && `{{` + `/${templateName}.street}}`}
{btr && `{{` + `#${templateName}.town}}`}
{location?.town || (btr && `{{ ${templateName}.town }}`)}
{btr && `{{` + `/${templateName}.town}}`}
{btr && `{{` + `#${templateName}.town_location}}`}
{location?.town_location ||
(btr && `{{ ${templateName}.town_location }}`)}
{btr && `{{` + `/${templateName}.town_location}}`}
);
}
export function ShowOrderDetails({
order_summary,
refund_amount,
contract_terms,
btr,
}: Props): VNode {
const productList = btr
? [{} as MerchantBackend.Product]
: contract_terms?.products || [];
const auditorsList = btr
? [{} as MerchantBackend.Auditor]
: contract_terms?.auditors || [];
const exchangesList = btr
? [{} as MerchantBackend.Exchange]
: contract_terms?.exchanges || [];
const hasDeliveryInfo =
btr ||
!!contract_terms?.delivery_date ||
!!contract_terms?.delivery_location;
return (
Details of order{" "}
{contract_terms?.order_id || `{{ contract_terms.order_id }}`}
{btr && `{{#refund_amount}}`}
{(btr || refund_amount) && (
Refunded: The merchant refunded you{" "}
{refund_amount || `{{ refund_amount }}`} .
)}
{btr && `{{/refund_amount}}`}
Order summary:
{contract_terms?.summary || `{{ contract_terms.summary }}`}
Amount paid:
{contract_terms?.amount || `{{ contract_terms.amount }}`}
Order date:
{contract_terms?.timestamp
? contract_terms?.timestamp.t_s != "never"
? format(
contract_terms?.timestamp.t_s,
"dd MMM yyyy HH:mm:ss"
)
: "never"
: `{{ contract_terms.timestamp_str }}`}{" "}
Merchant name:
{contract_terms?.merchant.name ||
`{{ contract_terms.merchant.name }}`}
{btr && `{{#contract_terms.hasProducts}}`}
{!productList.length ? null : (
Products purchased
{btr && "{{" + "#contract_terms.products" + "}}"}
{productList.map((p, i) => {
const taxList = btr
? [{} as MerchantBackend.Tax]
: p.taxes || [];
return (
{p.description || `{{description}}`}
Quantity:
{p.quantity || `{{quantity}}`}
Price:
{p.price || `{{price}}`}
{btr && `{{#hasTaxes}}`}
{!taxList.length ? null : (
{btr && "{{" + "#taxes" + "}}"}
{taxList.map((t, i) => {
return (
{t.name || `{{name}}`}
{t.tax || `{{tax}}`}
);
})}
{btr && "{{" + "/taxes" + "}}"}
)}
{btr && `{{/hasTaxes}}`}
{btr && `{{#delivery_date}}`}
{(btr || p.delivery_date) && (
Delivered on:
{p.delivery_date
? p.delivery_date.t_s != "never"
? format(
p.delivery_date.t_s,
"dd MMM yyyy HH:mm:ss"
)
: "never"
: `{{ delivery_date_str }}`}{" "}
)}
{btr && `{{/delivery_date}}`}
{btr && `{{#unit}}`}
{(btr || p.unit) && (
Product unit:
{p.unit || `{{.}}`}
)}
{btr && `{{/unit}}`}
{btr && `{{#product_id}}`}
{(btr || p.product_id) && (
Product ID:
{p.product_id || `{{.}}`}
)}
{btr && `{{/product_id}}`}
);
})}
{btr && "{{" + "/contract_terms.products" + "}}"}
)}
{btr && `{{/contract_terms.hasProducts}}`}
{btr && `{{#contract_terms.has_delivery_info}}`}
{!hasDeliveryInfo ? null : (
Delivery information
{btr && `{{#contract_terms.delivery_date}}`}
{(btr || contract_terms?.delivery_date) && (
Delivery date:
{contract_terms?.delivery_date
? contract_terms?.delivery_date.t_s != "never"
? format(
contract_terms?.delivery_date.t_s,
"dd MMM yyyy HH:mm:ss"
)
: "never"
: `{{ contract_terms.delivery_date_str }}`}{" "}
)}
{btr && `{{/contract_terms.delivery_date}}`}
{btr && `{{#contract_terms.delivery_location}}`}
{(btr || contract_terms?.delivery_location) && (
Delivery address:
)}
{btr && `{{/contract_terms.delivery_location}}`}
)}
{btr && `{{/contract_terms.has_delivery_info}}`}
Full payment information
Amount paid:
{contract_terms?.amount || `{{ contract_terms.amount }}`}
Wire transfer method:
{contract_terms?.wire_method ||
`{{ contract_terms.wire_method }}`}
Payment deadline:
{contract_terms?.pay_deadline
? contract_terms?.pay_deadline.t_s != "never"
? format(
contract_terms?.pay_deadline.t_s,
"dd MMM yyyy HH:mm:ss"
)
: "never"
: `{{ contract_terms.pay_deadline_str }}`}{" "}
Exchange transfer deadline:
{contract_terms?.wire_transfer_deadline
? contract_terms?.wire_transfer_deadline.t_s != "never"
? format(
contract_terms?.wire_transfer_deadline.t_s,
"dd MMM yyyy HH:mm:ss"
)
: "never"
: `{{ contract_terms.wire_transfer_deadline_str }}`}{" "}
Maximum deposit fee:
{contract_terms?.max_fee || `{{ contract_terms.max_fee }}`}
Maximum wire fee:
{contract_terms?.max_wire_fee ||
`{{ contract_terms.max_wire_fee }}`}
Wire fee amortization:
{contract_terms?.wire_fee_amortization ||
`{{ contract_terms.wire_fee_amortization }}`}{" "}
transactions
Refund information
Refund deadline:
{contract_terms?.refund_deadline
? contract_terms?.refund_deadline.t_s != "never"
? format(
contract_terms?.refund_deadline.t_s,
"dd MMM yyyy HH:mm:ss"
)
: "never"
: `{{ contract_terms.refund_deadline_str }}`}{" "}
{btr && `{{#contract_terms.auto_refund}}`}
{(btr || contract_terms?.auto_refund) && (
Attempt autorefund for:
{contract_terms?.auto_refund
? contract_terms?.auto_refund.d_us != "forever"
? formatDuration(
intervalToDuration({
start: 0,
end: contract_terms?.auto_refund.d_us,
})
)
: "forever"
: `{{ contract_terms.auto_refund_str }}`}{" "}
)}
{btr && `{{/contract_terms.auto_refund}}`}
Additional order details
Public reorder URL:
-- not defined yet --
{btr && `{{#contract_terms.fulfillment_url}}`}
{(btr || contract_terms?.fulfillment_url) && (
Fulfillment URL:
{contract_terms?.fulfillment_url ||
(btr && `{{ contract_terms.fulfillment_url }}`)}
)}
{btr && `{{/contract_terms.fulfillment_url}}`}
{/* Fulfillment message:
-- not defined yet -- */}
Full merchant information
Merchant name:
{contract_terms?.merchant.name ||
`{{ contract_terms.merchant.name }}`}
Merchant address:
Merchant's jurisdiction:
Merchant URI:
{contract_terms?.merchant_base_url ||
`{{ contract_terms.merchant_base_url }}`}
Merchant's public key:
{contract_terms?.merchant_pub ||
`{{ contract_terms.merchant_pub }}`}
{/* Merchant's hash:
-- not defined yet -- */}
{btr && `{{#contract_terms.hasAuditors}}`}
{!auditorsList.length ? null : (
Auditors accepted by the merchant
{btr && "{{" + "#contract_terms.auditors" + "}}"}
{auditorsList.map((p, i) => {
return (
{p.name || `{{name}}`}
Auditor's public key:
{p.auditor_pub || `{{auditor_pub}}`}
Auditor's URL:
{p.url || `{{url}}`}
);
})}
{btr && "{{" + "/contract_terms.auditors" + "}}"}
)}
{btr && `{{/contract_terms.hasAuditors}}`}
{btr && `{{#contract_terms.hasExchanges}}`}
{!exchangesList.length ? null : (
Exchanges accepted by the merchant
{btr && "{{" + "#contract_terms.exchanges" + "}}"}
{exchangesList.map((p, i) => {
return (
Exchange's URL:
{p.url || `{{url}}`}
Public key:
{p.master_pub || `{{master_pub}}`}
);
})}
{btr && "{{" + "/contract_terms.exchanges" + "}}"}
)}
{btr && `{{/contract_terms.hasExchanges}}`}
);
}
export function mount(): void {
try {
const fromLocation = new URL(window.location.href).searchParams;
const os = fromLocation.get("order_summary") || undefined;
if (os) {
render(
, document.head);
}
const ra = fromLocation.get("refund_amount") || undefined;
const ct = fromLocation.get("contract_terms") || undefined;
let contractTerms: MerchantBackend.ContractTerms | undefined;
try {
contractTerms = JSON.parse((window as any).contractTermsStr);
} catch {}
render(
,
document.body
);
} catch (e) {
console.error("got error", e);
if (e instanceof Error) {
document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
}
}
}
export function buildTimeRendering(): { head: string; body: string } {
return {
head: renderToString(