diff --git a/apps/boltcard/app/api/activate/route.ts b/apps/boltcard/app/api/activate/route.ts
index 1b43d4ec334..f5cb5eccdf3 100644
--- a/apps/boltcard/app/api/activate/route.ts
+++ b/apps/boltcard/app/api/activate/route.ts
@@ -4,6 +4,7 @@ import { aesDecryptKey, serverUrl } from "@/services/config"
import { fetchByOneTimeCode } from "@/services/db/card-init"
interface ActivateCardResponse {
+ warning: boolean
protocol_name: string
protocol_version: number
card_name: string
@@ -36,11 +37,10 @@ export async function GET(req: NextRequest) {
)
}
+ let warningReusedCode = false
+
if (cardKeysSetup.status !== "init") {
- return NextResponse.json(
- { status: "ERROR", reason: "code has already been used" },
- { status: 400 },
- )
+ warningReusedCode = true
}
const lnurlwBase = `${serverUrl}/api/ln`
@@ -50,6 +50,7 @@ export async function GET(req: NextRequest) {
const k1DecryptKey = aesDecryptKey.toString("hex")
const response: ActivateCardResponse = {
+ warning: warningReusedCode,
protocol_name: "create_bolt_card_response",
protocol_version: 2,
card_name: "",
diff --git a/apps/boltcard/app/api/create/route.ts b/apps/boltcard/app/api/create/route.ts
index 228ed924254..8f18a75105e 100644
--- a/apps/boltcard/app/api/create/route.ts
+++ b/apps/boltcard/app/api/create/route.ts
@@ -46,7 +46,7 @@ export async function GET(req: NextRequest) {
}
const apiActivationUrl = `${serverUrl}/api/activate?a=${oneTimeCode}`
- const uiActivationUrl = `${serverUrl}/card/activate?a=${oneTimeCode}`
+ const uiActivationUrl = `${serverUrl}/card/activate/${oneTimeCode}`
return NextResponse.json({
status: "OK",
apiActivationUrl,
diff --git a/apps/boltcard/app/card/[id]/page.tsx b/apps/boltcard/app/card/[id]/page.tsx
index 8b7ef9b6c68..3c1b0001019 100644
--- a/apps/boltcard/app/card/[id]/page.tsx
+++ b/apps/boltcard/app/card/[id]/page.tsx
@@ -1,16 +1,30 @@
-import { serverApi } from "@/services/config"
+import QRCode from "qrcode"
+import Image from "next/image"
+
+import { isAdmin, serverUrl } from "@/services/config"
export default async function Card({ params }: { params: { id: string } }) {
const { id } = params
- const cardApi = `${serverApi}/card/id/${id}`
+ const cardApi = `${serverUrl}/api/card/id/${id}`
const cardResult = await fetch(cardApi, { cache: "no-store" })
const cardInfo = await cardResult.json()
- const transactionsApi = `${serverApi}/card/id/${id}/transactions`
- const transactionsResult = await fetch(transactionsApi)
+ const transactionsApi = `${serverUrl}/api/card/id/${id}/transactions`
+ const transactionsResult = await fetch(transactionsApi, { cache: "no-store" })
const transactionInfo = await transactionsResult.json()
+ let qrCode = ""
+
+ if (isAdmin) {
+ const wipeApi = `${serverUrl}/api/wipe?cardId=${id}`
+ const wipeResult = await fetch(wipeApi, { cache: "no-store" })
+ const wipeInfo = await wipeResult.json()
+
+ // generate QR code
+ qrCode = await QRCode.toDataURL(JSON.stringify(wipeInfo), { width: 400 })
+ }
+
return (
boltcard
@@ -63,12 +77,28 @@ export default async function Card({ params }: { params: { id: string } }) {
Fee: {tx.settlementDisplayFee}{" "}
{tx.settlementDisplayCurrency}
- {/* Add more fields as required */}
))}
+
+
+ Wipe Card:
+
+ Warning: This will wipe the card and remove all funds. This
+ action cannot be undone.
+
+
+
+
+
)
}
diff --git a/apps/boltcard/app/card/activate/[a]/page.tsx b/apps/boltcard/app/card/activate/[a]/page.tsx
new file mode 100644
index 00000000000..a28b3bebc35
--- /dev/null
+++ b/apps/boltcard/app/card/activate/[a]/page.tsx
@@ -0,0 +1,33 @@
+import QRCode from "qrcode"
+import Image from "next/image"
+
+import { serverUrl } from "@/services/config"
+
+export default async function ActivateCard({ params }: { params: { a: string } }) {
+ const { a } = params
+
+ const url = `${serverUrl}/api/activate/?a=${a}`
+ const res = await fetch(url, { cache: "no-store" })
+ const activationParams = await res.json()
+ const warning = activationParams.warning
+
+ const qrCode = await QRCode.toDataURL(url, { width: 400 })
+
+ return (
+ <>
+
+
Activate Card
+ {warning && (
+
{"card should not be programmed twice with the same sets of keys"}
+ )}
+
+
+ >
+ )
+}
diff --git a/apps/boltcard/app/card/activate/page.tsx b/apps/boltcard/app/card/activate/page.tsx
deleted file mode 100644
index 102b7054582..00000000000
--- a/apps/boltcard/app/card/activate/page.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { serverApi } from "@/services/config"
-
-export default async function ActivateCard({ params }: { params: { id: string } }) {
- const { id } = params
-
- const cardApi = `${serverApi}/card/id/${id}`
- const cardResult = await fetch(cardApi, { cache: "no-store" })
- const cardInfo = await cardResult.json()
-
- const transactionsApi = `${serverApi}/card/id/${id}/transactions`
- const transactionsResult = await fetch(transactionsApi)
- const transactionInfo = await transactionsResult.json()
-}
diff --git a/apps/boltcard/bats/e2e-test.bats b/apps/boltcard/bats/e2e-test.bats
index ad169a0abb6..587cbe58e3a 100644
--- a/apps/boltcard/bats/e2e-test.bats
+++ b/apps/boltcard/bats/e2e-test.bats
@@ -14,13 +14,13 @@ random_phone() {
export TOKEN_ALICE=$(read_value "alice")
RESPONSE=$(curl -s "http://localhost:3000/api/create?token=${TOKEN_ALICE}")
- CALLBACK_URL=$(echo $RESPONSE | jq -r '.apiActivationUrl')
+ CALLBACK_API_URL=$(echo $RESPONSE | jq -r '.apiActivationUrl')
+ CALLBACK_UI_URL=$(echo $RESPONSE | jq -r '.uiActivationUrl')
- # echo "CALLBACK_URL: $CALLBACK_URL"
- # exit 1
+ # TODO: test CALLBACK_UI_URL
# Making the follow-up curl request
- RESPONSE=$(curl -s "${CALLBACK_URL}")
+ RESPONSE=$(curl -s "${CALLBACK_API_URL}")
echo "$RESPONSE"
[[ $(echo $RESPONSE | jq -r '.protocol_name') == "create_bolt_card_response" ]] || exit 1
@@ -86,6 +86,12 @@ random_phone() {
[[ $(echo $result | jq -r '.status') == "OK" ]] || exit 1
}
+@test "card ui" {
+ cardId=$(read_value "cardId")
+ http_status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/card/${cardId})
+ [[ "$http_status" -eq 200 ]] || exit 1
+}
+
@test "wipecard" {
cardId=$(read_value "cardId")
result=$(curl -s http://localhost:3000/api/wipe?cardId=${cardId})
diff --git a/apps/boltcard/bun.lockb b/apps/boltcard/bun.lockb
index 8a223069851..36fb35b8225 100755
Binary files a/apps/boltcard/bun.lockb and b/apps/boltcard/bun.lockb differ
diff --git a/apps/boltcard/package.json b/apps/boltcard/package.json
index f921d43f195..bc0ca655e27 100644
--- a/apps/boltcard/package.json
+++ b/apps/boltcard/package.json
@@ -26,19 +26,21 @@
"node-aes-cmac": "^0.1.1",
"pg": "^8.11.3",
"postcss": "8.4.29",
+ "qrcode": "^1.5.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
},
"devDependencies": {
- "@types/pg": "^8.10.2",
- "encoding": "^0.1.13",
"@graphql-codegen/add": "^5.0.0",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/client-preset": "^4.1.0",
"@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1",
- "@graphql-codegen/typescript-react-apollo": "^3.3.7"
+ "@graphql-codegen/typescript-react-apollo": "^3.3.7",
+ "@types/pg": "^8.10.2",
+ "@types/qrcode": "^1.5.2",
+ "encoding": "^0.1.13"
}
}
diff --git a/apps/boltcard/services/config.ts b/apps/boltcard/services/config.ts
index 701f8972451..c49d13f24d7 100644
--- a/apps/boltcard/services/config.ts
+++ b/apps/boltcard/services/config.ts
@@ -9,3 +9,5 @@ export const AES_DECRYPT_KEY =
export const aesDecryptKey = Buffer.from(AES_DECRYPT_KEY, "hex")
export const lightningDomain = process.env.LIGHTNING_DOMAIN ?? "localhost"
+
+export const isAdmin = true