Transactions
Show a user's order and payment history with <u-transaction-list>. It follows the same template pattern as tickets — <transaction-value> placeholders, filter attribute, pagination — over transaction data synced into Unidy from your shop or ERP.
View this guide as MarkdownTransaction history table
A paginated order-history table. transaction-value reads any field from the transaction — reference, totals, dates, payment status — and the financial_status is stamped onto a badge for CSS coloring.
- <transaction-value> — reference, placed_at, total, financial_status, …
- target rendering + pagination — identical mechanics to u-ticketable-list
- Skeleton loading — placeholders while the page loads
<u-signed-in>
<!-- Order/purchase history. Same template pattern as tickets, with
transaction-value placeholders and filter/pagination support. -->
<u-transaction-list
id="transaction-history"
target="#transactions-body"
limit="5"
skeleton-all-text="true">
<div class="overflow-x-auto">
<table class="min-w-full rounded-lg border border-border bg-white">
<thead
class="bg-background-light text-left text-xs uppercase tracking-wider text-text-muted">
<tr>
<th class="px-4 py-3">Reference</th>
<th class="px-4 py-3">Placed</th>
<th class="px-4 py-3">Total</th>
<th class="px-4 py-3">Payment</th>
</tr>
</thead>
<tbody id="transactions-body" class="divide-y divide-border"></tbody>
</table>
</div>
<div class="mt-4 flex items-center gap-2">
<u-pagination-button
direction="prev"
class-name="px-3 py-2 rounded-lg border border-border bg-white text-sm cursor-pointer hover:bg-background-light disabled:opacity-50 disabled:cursor-not-allowed">
</u-pagination-button>
<u-pagination-page class-name="px-3 py-2 text-sm"></u-pagination-page>
<u-pagination-button
direction="next"
class-name="px-3 py-2 rounded-lg border border-border bg-white text-sm cursor-pointer hover:bg-background-light disabled:opacity-50 disabled:cursor-not-allowed">
</u-pagination-button>
</div>
<template>
<tr class="transition-colors hover:bg-background-light">
<td class="whitespace-nowrap px-4 py-3 font-medium">
<transaction-value name="reference" default="—"></transaction-value>
</td>
<td class="whitespace-nowrap px-4 py-3 text-sm text-text-light">
<transaction-value name="placed_at" date-format="dd.MM.yyyy HH:mm"></transaction-value>
</td>
<td class="whitespace-nowrap px-4 py-3 font-semibold text-primary">
<transaction-value name="total" default="—"></transaction-value>
<transaction-value name="currency"></transaction-value>
</td>
<td class="whitespace-nowrap px-4 py-3">
<span class="state-badge" unidy-attr unidy-attr-data-state="{{financial_status}}">
<transaction-value name="financial_status" default="unknown"></transaction-value>
</span>
</td>
</tr>
</template>
<!-- shown when the user has no transactions -->
<p slot="empty" class="py-6 text-center text-sm text-text-light">No transactions yet.</p>
</u-transaction-list>
</u-signed-in>
<u-signed-in not>
<p class="text-text-light">
Sign in on the <a href="/auth" class="text-primary underline">Auth page</a> to see your transactions.
</p>
</u-signed-in> | Reference | Placed | Total | Payment |
|---|
No transactions yet.
Sign in on the Auth page to see your transactions.
Filtering by payment status
Set the filter attribute (e.g. financial_status=paid) and the list re-fetches. Filters support state, financial_status, order_type, source_platform and external_id — combine with ";".
- Live filter attribute — no custom fetch code
- Rich filter keys — state, financial_status, order_type, source_platform
<u-signed-in>
<div class="mb-3 flex items-center justify-end">
<select
id="financial-status-filter"
class="rounded-lg border border-border bg-white px-3 py-2 text-sm">
<option value="">All payment states</option>
<option value="paid">Paid</option>
<option value="pending">Pending</option>
<option value="refunded">Refunded</option>
</select>
</div>
<u-transaction-list id="filtered-transactions" limit="3" container-class="grid gap-3">
<template>
<div class="flex items-center justify-between rounded-lg border border-border bg-white p-4">
<div>
<p class="font-medium">
<transaction-value name="reference" default="(no reference)"></transaction-value>
</p>
<p class="text-sm text-text-light">
<transaction-value name="order_type" default="order"></transaction-value>
via <transaction-value name="source_platform" default="unknown"></transaction-value>
</p>
</div>
<div class="text-right">
<p class="font-semibold text-primary">
<transaction-value name="total" default="—"></transaction-value>
</p>
<span class="state-badge" unidy-attr unidy-attr-data-state="{{financial_status}}">
<transaction-value name="financial_status" default="unknown"></transaction-value>
</span>
</div>
</div>
</template>
<!-- shown when no transactions match the filter -->
<p slot="empty" class="py-6 text-center text-sm text-text-light">
No transactions match this filter.
</p>
</u-transaction-list>
<script is:inline>
document.getElementById("financial-status-filter").addEventListener("change", (event) => {
// filter uses key=value pairs joined by ";"
document
.getElementById("filtered-transactions")
.setAttribute("filter", event.target.value ? `financial_status=${event.target.value}` : "");
});
</script>
</u-signed-in>
<u-signed-in not>
<p class="text-text-light">
Sign in on the <a href="/auth" class="text-primary underline">Auth page</a> to filter transactions.
</p>
</u-signed-in> Create test transactions through the Admin API
An authenticated demo-only control creates a transaction for the currently signed-in user through a protected server route and can remove the item again.
- Authenticated user only — the SDK token determines ownership
- Server-side credentials — Admin API access stays on the server
- Create and clean up — add a test transaction and delete it again
<u-signed-in>
<div class="rounded-lg border border-amber-200 bg-amber-50/40 p-5 space-y-4">
<div class="flex flex-wrap items-center gap-2">
<span
class="text-xs font-semibold uppercase tracking-wider text-amber-700 bg-amber-100 border border-amber-200 px-2 py-0.5 rounded-full"
>Demo Controls</span
>
<p class="text-sm text-text-light">
Create a transaction through the Admin API for the signed-in user. Admin credentials stay
server-side.
</p>
</div>
<div class="flex flex-wrap items-center gap-3">
<button id="create-demo-transaction" class="btn btn-primary !min-h-0 !px-4 !py-2 text-sm"
>+ Create Demo Transaction</button
>
<button
id="delete-demo-transaction"
class="!min-h-0 !px-4 !py-2 text-sm rounded-lg border border-red-200 text-red-600 bg-white hover:bg-red-50 transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
disabled>Delete Last</button
>
<span id="demo-transaction-status" class="text-xs text-text-muted" role="status"></span>
</div>
<p class="text-xs text-text-muted">
Reload after creating or deleting to refresh the SDK list above.
</p>
</div>
</u-signed-in>
<u-signed-in not>
<p class="text-sm text-text-light italic">Sign in to use the Admin API demo controls.</p>
</u-signed-in>
<script type="module">
import { Auth } from "@unidy.io/sdk";
const createButton = document.getElementById("create-demo-transaction");
const deleteButton = document.getElementById("delete-demo-transaction");
const status = document.getElementById("demo-transaction-status");
let lastId = null;
function setStatus(message, failed = false) {
status.textContent = message;
status.className = `text-xs ${failed ? "text-red-500" : "text-green-600"}`;
}
async function authenticatedFetch(url, options = {}) {
const auth = await Auth.getInstance();
const token = await auth.getToken();
if (typeof token !== "string") throw new Error("Please sign in again.");
return fetch(url, {
...options,
headers: { ...options.headers, Authorization: `Bearer ${token}` },
});
}
createButton?.addEventListener("click", async () => {
createButton.disabled = true;
setStatus("Creating…");
try {
const response = await authenticatedFetch("/api/demo/transactions", { method: "POST" });
const data = await response.json();
if (!response.ok) throw new Error(data.error || "Create failed");
lastId = data.id;
deleteButton.disabled = !lastId;
setStatus(`Created${data.reference ? `: ${data.reference}` : ""}.`);
} catch (error) {
setStatus(error instanceof Error ? error.message : "Create failed", true);
} finally {
createButton.disabled = false;
}
});
deleteButton?.addEventListener("click", async () => {
if (!lastId) return;
deleteButton.disabled = true;
setStatus("Deleting…");
try {
const response = await authenticatedFetch(
`/api/demo/transactions?id=${encodeURIComponent(lastId)}`,
{ method: "DELETE" }
);
const data = await response.json();
if (!response.ok) throw new Error(data.error || "Delete failed");
lastId = null;
setStatus("Deleted.");
} catch (error) {
setStatus(error instanceof Error ? error.message : "Delete failed", true);
deleteButton.disabled = false;
}
});
</script> Create a transaction through the Admin API for the signed-in user. Admin credentials stay server-side.
Reload after creating or deleting to refresh the SDK list above.
Sign in to use the Admin API demo controls.