aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-11 10:45:39 -0800
committerAdam Soltys <[email protected]>2026-02-11 15:34:39 -0800
commite8e3fe413f02b9520bd3539f351c344134148c58 (patch)
treebf2508e6228c060ffc6df6efc09a39e1a3732234 /src
parent2cc4969678f4d073ebddec0e7e9f2aac707fb761 (diff)
downloadcoinos-ui-e8e3fe413f02b9520bd3539f351c344134148c58.tar.gz
coinos-ui-e8e3fe413f02b9520bd3539f351c344134148c58.zip
ark payment test
Diffstat (limited to 'src')
-rw-r--r--src/components/Account.svelte4
-rw-r--r--src/components/InvoiceData.svelte1
-rw-r--r--src/components/WalletPass.svelte4
-rw-r--r--src/routes/test/ark/send/+server.ts76
4 files changed, 84 insertions, 1 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte
index 711c5ba7..bb9aec22 100644
--- a/src/components/Account.svelte
+++ b/src/components/Account.svelte
@@ -142,6 +142,8 @@
<div
class="shadow-lg space-y-4 p-4 cursor-pointer hover:bg-base-200"
aria-label="Payments"
+ data-testid="account-card"
+ data-account-type={accountType}
role="button"
tabindex="0"
onclick={goPayments}
@@ -180,7 +182,7 @@
class="contents"
onclick={(e) => setAccount(e, "/invoice")}
>
- <button class="btn !w-auto flex-1 !bg-base-300">
+ <button class="btn !w-auto flex-1 !bg-base-300" data-testid="account-receive">
<iconify-icon
noobserver
icon="ph:hand-coins-bold"
diff --git a/src/components/InvoiceData.svelte b/src/components/InvoiceData.svelte
index e0ec0f3e..5ba950d5 100644
--- a/src/components/InvoiceData.svelte
+++ b/src/components/InvoiceData.svelte
@@ -56,6 +56,7 @@
{#if !showQr || txt?.length <= 120}
<div
+ data-testid="invoice-text"
class="break-all text-center text-secondary text-xl flex gap-1 items-center justify-center"
>
{txt}
diff --git a/src/components/WalletPass.svelte b/src/components/WalletPass.svelte
index e0dcf961..11f6978a 100644
--- a/src/components/WalletPass.svelte
+++ b/src/components/WalletPass.svelte
@@ -37,6 +37,7 @@
{#if revealPassword}
<input
name="password"
+ data-testid="walletpass-input"
type="text"
required
bind:value={password}
@@ -46,6 +47,7 @@
{:else}
<input
name="password"
+ data-testid="walletpass-input"
type="password"
required
bind:value={password}
@@ -83,6 +85,7 @@
<div class="w-full flex justify-center gap-2">
<button
type="button"
+ data-testid="walletpass-cancel"
class="border rounded-2xl font-semibold mx-auto py-5 px-6 hover:opacity-80 mx-auto grow"
onclick={cancel}
onkeydown={cancel}
@@ -91,6 +94,7 @@
</button>
<button
type="submit"
+ data-testid="walletpass-submit"
class="border rounded-2xl font-semibold mx-auto py-5 px-6 hover:opacity-80 mx-auto grow"
>
<div class="my-auto">Submit</div>
diff --git a/src/routes/test/ark/send/+server.ts b/src/routes/test/ark/send/+server.ts
new file mode 100644
index 00000000..b077c793
--- /dev/null
+++ b/src/routes/test/ark/send/+server.ts
@@ -0,0 +1,76 @@
+import { env } from "$env/dynamic/private";
+import { PUBLIC_COINOS_URL } from "$env/static/public";
+import { error, json } from "@sveltejs/kit";
+
+let walletPromise: Promise<any> | undefined;
+
+const getWallet = async () => {
+ if (!walletPromise) {
+ const privateKey = env.ARK_SERVER_WALLET_PRIVATE_KEY;
+ const arkServerUrl = env.ARK_SERVER_URL || "http://localhost:7070";
+
+ if (!privateKey) {
+ throw new Error("ARK_SERVER_WALLET_PRIVATE_KEY is not configured");
+ }
+
+ const { SingleKey, Wallet } = await import("@arkade-os/sdk");
+ const identity = SingleKey.fromHex(privateKey);
+ walletPromise = Wallet.create({ identity, arkServerUrl });
+ }
+
+ return walletPromise;
+};
+
+export async function POST({ request, cookies, fetch }) {
+ // Never expose this in production.
+ if (env.NODE_ENV === "production") error(404, "Not found");
+
+ // Require explicit opt-in for local/integration test environments.
+ if (env.ENABLE_E2E_TEST_ENDPOINTS !== "true") {
+ error(404, "Not found");
+ }
+
+ const expectedSecret = env.E2E_TEST_SECRET;
+ const providedSecret = request.headers.get("x-test-secret");
+ if (!expectedSecret || providedSecret !== expectedSecret) {
+ error(401, "Unauthorized");
+ }
+
+ const body = await request.json();
+ const address = String(body?.address || "").trim();
+ const amount = Number(body?.amount || 1000);
+ const iid = typeof body?.iid === "string" ? body.iid : undefined;
+
+ if (!/^t?ark1[a-z0-9]+$/i.test(address)) {
+ error(400, "Invalid ark address");
+ }
+
+ if (!Number.isInteger(amount) || amount <= 0 || amount > 1_000_000) {
+ error(400, "Invalid amount");
+ }
+
+ const wallet = await getWallet();
+ const txid = await wallet.sendBitcoin({ address, amount });
+
+ // Optional: mark an Ark invoice as received right after send.
+ if (iid) {
+ const token = cookies.get("token");
+ if (!token) error(401, "Missing auth token cookie");
+
+ const response = await fetch(`${PUBLIC_COINOS_URL}/ark/receive`, {
+ method: "POST",
+ headers: {
+ "content-type": "application/json",
+ accept: "application/json",
+ authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({ iid, amount, hash: txid }),
+ });
+
+ if (!response.ok) {
+ error(response.status, await response.text());
+ }
+ }
+
+ return json({ ok: true, txid, amount, address, iid: iid || null });
+}