From 90b01f868364eadc66561f32f0e21c3d25d7ea23 Mon Sep 17 00:00:00 2001 From: Nicolas Burtey Date: Thu, 15 Feb 2024 17:11:30 -0600 Subject: [PATCH] feat: adding boltcard for the POS behind a beta flag --- apps/pay/app/setuppwa/page.tsx | 4 +- .../components/layouts/username-layout.tsx | 11 - .../components/parse-pos-payment/index.tsx | 80 ++++--- apps/pay/components/parse-pos-payment/nfc.tsx | 222 ++++++++++++++++++ .../parse-pos-payment/receive-invoice.tsx | 3 + .../components/parse-pos-payment/web-nfc.d.ts | 83 +++++++ apps/pay/package.json | 1 + apps/pay/public/payment-sound.mp3 | Bin 0 -> 32256 bytes pnpm-lock.yaml | 54 ++++- 9 files changed, 406 insertions(+), 52 deletions(-) create mode 100644 apps/pay/components/parse-pos-payment/nfc.tsx create mode 100644 apps/pay/components/parse-pos-payment/web-nfc.d.ts create mode 100644 apps/pay/public/payment-sound.mp3 diff --git a/apps/pay/app/setuppwa/page.tsx b/apps/pay/app/setuppwa/page.tsx index dbc90e9dada..e978d9754eb 100644 --- a/apps/pay/app/setuppwa/page.tsx +++ b/apps/pay/app/setuppwa/page.tsx @@ -22,10 +22,10 @@ const SetupPwa = () => { const [selectedDisplayCurrency, setSelectedDisplayCurrency] = useState("USD") useEffect(() => { - if (usernameFromLocal && displayCurrencyFromLocal) { + if (router && usernameFromLocal && displayCurrencyFromLocal) { router.push(`${usernameFromLocal}?display=${displayCurrencyFromLocal}`) } - }, [displayCurrencyFromLocal, usernameFromLocal]) + }, [displayCurrencyFromLocal, usernameFromLocal, router]) const handleSubmit = (event: React.FormEvent) => { event.preventDefault() diff --git a/apps/pay/components/layouts/username-layout.tsx b/apps/pay/components/layouts/username-layout.tsx index 2e36bdba162..eba6d214dd5 100644 --- a/apps/pay/components/layouts/username-layout.tsx +++ b/apps/pay/components/layouts/username-layout.tsx @@ -171,17 +171,6 @@ const UsernameLayoutContainer = ({ children, username }: Props) => {
{children} -
- - Powered by - Galoy logo - -
) diff --git a/apps/pay/components/parse-pos-payment/index.tsx b/apps/pay/components/parse-pos-payment/index.tsx index 04523d170ec..ede009ca3e1 100644 --- a/apps/pay/components/parse-pos-payment/index.tsx +++ b/apps/pay/components/parse-pos-payment/index.tsx @@ -23,6 +23,7 @@ import { Currency } from "../../lib/graphql/generated" import DigitButton from "./digit-button" import styles from "./parse-payment.module.css" import ReceiveInvoice from "./receive-invoice" +import NFCComponent from "./nfc" function isRunningStandalone() { if (typeof window === "undefined") { @@ -366,43 +367,48 @@ function ParsePayment({ walletId={walletId} /> ) : ( -
- - - - - - - - - - {currencyMetadata.fractionDigits > 0 ? ( - - ) : ( - - )} - - - -
+ <> + + +
+ + + + + + + + + + {currencyMetadata.fractionDigits > 0 ? ( + + ) : ( + + )} + + + +
+ )}
diff --git a/apps/pay/components/parse-pos-payment/nfc.tsx b/apps/pay/components/parse-pos-payment/nfc.tsx new file mode 100644 index 00000000000..fbbd20b2086 --- /dev/null +++ b/apps/pay/components/parse-pos-payment/nfc.tsx @@ -0,0 +1,222 @@ +import React, { useState, useEffect } from "react" + +import { getParams } from "js-lnurl" +import Image from "next/image" + +import styles from "./parse-payment.module.css" + +type Props = { + paymentRequest?: string | undefined +} + +// TODO: refine the interface +interface NFCRecord { + data?: ArrayBuffer | DataView + encoding?: string +} + +function NFCComponent({ paymentRequest }: Props) { + const [hasNFCPermission, setHasNFCPermission] = useState(false) + const [nfcMessage, setNfcMessage] = useState("") + + const decodeNDEFRecord = (record: NFCRecord) => { + if (!record.data) { + console.log("No data found") + return "" + } + + let buffer: ArrayBuffer + if (record.data instanceof ArrayBuffer) { + buffer = record.data + } else if (record.data instanceof DataView) { + buffer = record.data.buffer + } else { + console.log("Data type not supported") + return "" + } + + const decoder = new TextDecoder(record.encoding || "utf-8") + return decoder.decode(buffer) + } + + const activateNfcScan = async () => { + await handleNFCScan() + alert( + "Boltcard is now active. There will be no need to active it again. Please tap your card to redeem the payment", + ) + } + + const handleNFCScan = async () => { + if (!("NDEFReader" in window)) { + console.error("NFC is not supported") + return + } + + console.log("NFC is supported, start reading") + + const ndef = new NDEFReader() + + try { + await ndef.scan() + + console.log("NFC scan started successfully.") + + ndef.onreading = (event) => { + console.log("NFC tag read.") + console.log(event.message) + + const record = event.message.records[0] + const text = decodeNDEFRecord(record) + + setNfcMessage(text) + } + + ndef.onreadingerror = () => { + console.error("Cannot read data from the NFC tag. Try another one?") + } + } catch (error) { + console.error(`Error! Scan failed to start: ${error}.`) + } + } + + useEffect(() => { + ;(async () => { + if (!("permissions" in navigator)) { + console.error("Permissions API not supported") + return + } + + let result: PermissionStatus + try { + /* eslint @typescript-eslint/ban-ts-comment: "off" */ + // @ts-ignore-next-line + result = await navigator.permissions.query({ name: "nfc" }) + } catch (err) { + console.error("Error querying NFC permission", err) + return + } + + console.log("result permission query", result) + + if (result.state === "granted") { + setHasNFCPermission(true) + } else { + setHasNFCPermission(false) + } + + result.onchange = () => { + if (result.state === "granted") { + setHasNFCPermission(true) + } else { + setHasNFCPermission(false) + } + } + })() + }, [setHasNFCPermission]) + + React.useEffect(() => { + console.log("hasNFCPermission", hasNFCPermission) + + if (hasNFCPermission) { + handleNFCScan() + } + + // handleNFCScan leads to an infinite loop + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasNFCPermission]) + + React.useEffect(() => { + ;(async () => { + if (!nfcMessage) { + return + } + + if (!nfcMessage.toLowerCase().includes("lnurl")) { + alert("Not a compatible boltcard") + return + } + + if (!paymentRequest) { + alert("add an amount and create an invoice before scanning the card") + return + } + + const sound = new Audio("/payment-sound.mp3") + sound + .play() + .then(() => { + console.log("Playback started successfully") + }) + .catch((error) => { + console.error("Playback failed", error) + }) + + const lnurlParams = await getParams(nfcMessage) + + if (!("tag" in lnurlParams && lnurlParams.tag === "withdrawRequest")) { + console.error("not a lnurl withdraw tag") + return + } + + const { callback, k1 } = lnurlParams + + const urlObject = new URL(callback) + const searchParams = urlObject.searchParams + searchParams.set("k1", k1) + searchParams.set("pr", paymentRequest) + + const url = urlObject.toString() + + const result = await fetch(url) + if (result.ok) { + const lnurlResponse = await result.json() + if (lnurlResponse?.status?.toLowerCase() !== "ok") { + console.error(lnurlResponse, "error with redeeming") + } + + console.log("payment successful") + } else { + let errorMessage = "" + try { + const decoded = await result.json() + if (decoded.reason) { + errorMessage += decoded.reason + } + if (decoded.message) { + errorMessage += decoded.message + } + } finally { + let message = `Error processing payment.\n\nHTTP error code: ${result.status}` + if (errorMessage) { + message += `\n\nError message: ${errorMessage}` + } + alert(message) + } + } + })() + }, [nfcMessage, paymentRequest]) + + return ( +
+ {!hasNFCPermission && ( +
+ +
+ )} +
+ ) +} + +export default NFCComponent diff --git a/apps/pay/components/parse-pos-payment/receive-invoice.tsx b/apps/pay/components/parse-pos-payment/receive-invoice.tsx index 17283f788ce..8275cefaaf5 100644 --- a/apps/pay/components/parse-pos-payment/receive-invoice.tsx +++ b/apps/pay/components/parse-pos-payment/receive-invoice.tsx @@ -22,6 +22,7 @@ import { extractSearchParams, safeAmount } from "../../utils/utils" import LoadingComponent from "../loading" import styles from "./parse-payment.module.css" +import NFCComponent from "./nfc" interface Props { recipientWalletCurrency?: string @@ -254,6 +255,8 @@ function ReceiveInvoice({ recipientWalletCurrency, walletId, state, dispatch }:
)}
+ + {data ? ( <>
+// TypeScript Version: 3.9 + +// This type definitions referenced to WebIDL. +// https://w3c.github.io/web-nfc/#actual-idl-index + +interface Window { + NDEFMessage: NDEFMessage +} +declare class NDEFMessage { + constructor(messageInit: NDEFMessageInit) + records: ReadonlyArray +} +declare interface NDEFMessageInit { + records: NDEFRecordInit[] +} + +declare type NDEFRecordDataSource = string | BufferSource | NDEFMessageInit + +interface Window { + NDEFRecord: NDEFRecord +} +declare class NDEFRecord { + constructor(recordInit: NDEFRecordInit) + readonly recordType: string + readonly mediaType?: string + readonly id?: string + readonly data?: DataView + readonly encoding?: string + readonly lang?: string + toRecords?: () => NDEFRecord[] +} +declare interface NDEFRecordInit { + recordType: string + mediaType?: string + id?: string + encoding?: string + lang?: string + data?: NDEFRecordDataSource +} + +declare type NDEFMessageSource = string | BufferSource | NDEFMessageInit + +interface Window { + NDEFReader: NDEFReader +} +declare class NDEFReader extends EventTarget { + constructor() + /* eslint @typescript-eslint/ban-ts-comment: "off" */ + // @ts-ignore-next-line no-implicit-any error + onreading: (this: this, event: NDEFReadingEvent) => any + // @ts-ignore-next-line no-implicit-any error + onreadingerror: (this: this, error: Event) => any + scan: (options?: NDEFScanOptions) => Promise + write: (message: NDEFMessageSource, options?: NDEFWriteOptions) => Promise + makeReadOnly: (options?: NDEFMakeReadOnlyOptions) => Promise +} + +interface Window { + NDEFReadingEvent: NDEFReadingEvent +} +declare class NDEFReadingEvent extends Event { + constructor(type: string, readingEventInitDict: NDEFReadingEventInit) + serialNumber: string + message: NDEFMessage +} +interface NDEFReadingEventInit extends EventInit { + serialNumber?: string + message: NDEFMessageInit +} + +interface NDEFWriteOptions { + overwrite?: boolean + signal?: AbortSignal +} +interface NDEFMakeReadOnlyOptions { + signal?: AbortSignal +} +interface NDEFScanOptions { + signal: AbortSignal +} diff --git a/apps/pay/package.json b/apps/pay/package.json index b3db2a80131..28a327aa2cd 100644 --- a/apps/pay/package.json +++ b/apps/pay/package.json @@ -25,6 +25,7 @@ "graphql-ws": "^5.14.0", "html2canvas": "^1.4.1", "ioredis": "^5.3.1", + "js-lnurl": "^0.6.0", "lnurl-pay": "^1.0.1", "lodash.debounce": "^4.0.8", "next": "^14.1.0", diff --git a/apps/pay/public/payment-sound.mp3 b/apps/pay/public/payment-sound.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7e43ba287f4ce84e0379ddce027884ce8b83f05f GIT binary patch literal 32256 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
rj54A+Jfrw7;{oF3CB?>YM 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�J1xeusfVyCwh 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 literal 0 HcmV?d00001 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4fcf29ba616..f6dd89da91e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -607,6 +607,9 @@ importers: ioredis: specifier: ^5.3.1 version: 5.3.2 + js-lnurl: + specifier: ^0.6.0 + version: 0.6.0 lnurl-pay: specifier: ^1.0.1 version: 1.0.1 @@ -13773,6 +13776,10 @@ packages: engines: {node: '>=10.13.0'} dev: false + /@types/aes-js@3.1.4: + resolution: {integrity: sha512-v3D66IptpUqh+pHKVNRxY8yvp2ESSZXe0rTzsGdzUhEwag7ljVfgCllkWv2YgiYXDhWFBrEywll4A5JToyTNFA==} + dev: false + /@types/aria-query@5.0.4: resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} dev: true @@ -13810,6 +13817,10 @@ packages: '@babel/types': 7.23.6 dev: true + /@types/base64-js@1.3.2: + resolution: {integrity: sha512-Q2Xn2/vQHRGLRXhQ5+BSLwhHkR3JVflxVKywH0Q6fVoAiUE8fFYL2pE5/l2ZiOiBDfA8qUqRnSxln4G/NFz1Sg==} + dev: false + /@types/basic-auth@1.1.7: resolution: {integrity: sha512-bFR3Ld3Fty5ayg45sqr3RI4e/GTXyp2W8jzMmw3WOC8RuQ19TrpsZE4y3jcw9iGSZj5f9mH6e+2biPeFUDovww==} dependencies: @@ -17797,7 +17808,6 @@ packages: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: true /cross-inspect@1.0.0: resolution: {integrity: sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==} @@ -18114,7 +18124,6 @@ packages: /decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} - dev: true /decompress-response@3.3.0: resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} @@ -20754,6 +20763,11 @@ packages: dependencies: to-regex-range: 5.0.1 + /filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + dev: false + /finalhandler@1.1.2: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} @@ -23913,6 +23927,22 @@ packages: nopt: 6.0.0 dev: true + /js-lnurl@0.6.0: + resolution: {integrity: sha512-U4hnInqlHVM9DyYMnOLk0IqlD293A7GVen8JBNWbXrq7C1IigQpTfoal+Fgz/eTZOsYtIEFEOyW9mKgFD/Oc0w==} + dependencies: + '@types/aes-js': 3.1.4 + '@types/base64-js': 1.3.2 + aes-js: 3.1.2 + base64-js: 1.5.1 + bech32: 1.1.4 + buffer: 5.7.1 + cross-fetch: 3.1.8 + query-string: 6.14.1 + safe-buffer: 5.2.1 + transitivePeerDependencies: + - encoding + dev: false + /js-md5@0.7.3: resolution: {integrity: sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==} dev: false @@ -27384,6 +27414,16 @@ packages: strict-uri-encode: 1.1.0 dev: true + /query-string@6.14.1: + resolution: {integrity: sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==} + engines: {node: '>=6'} + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + dev: false + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -28852,6 +28892,11 @@ packages: - utf-8-validate dev: true + /split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + dev: false + /split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -29017,6 +29062,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + dev: false + /string-env-interpolation@1.0.1: resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} dev: true