diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ff5302b..85263f7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -41,7 +41,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build - run: pnpm build + run: pnpm build:github - name: Setup Pages uses: actions/configure-pages@v4 diff --git a/README.md b/README.md index 4135bcf..55d9ee9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Bunch – Bitcoin Loyalty Punch Cards +[![Edit with Shakespeare](https://shakespeare.diy/badge.svg)](https://shakespeare.diy/clone?url=https%3A%2F%2Fgithub.com%2FNotThatKindOfDrLiz%2Fbunch.git) + Bunch is a drop-in loyalty layer for Bitcoin-accepting merchants. It tracks punches locally alongside existing payment flows, without touching invoices or custody. Made for the btc++ Taipei hackathon. ## Architecture highlights @@ -45,7 +47,7 @@ When a payment is confirmed in BTCPay Server or LNbits, send a webhook to your b // Backend webhook handler (Node.js example) app.post('/webhooks/btcpay', async (req, res) => { const { invoiceId, status, metadata } = req.body - + if (status === 'paid' && metadata.purchaseNonce) { // Notify merchant's Bunch instance via WebSocket or Server-Sent Events notifyMerchant({ @@ -55,7 +57,7 @@ app.post('/webhooks/btcpay', async (req, res) => { amount: req.body.amount }) } - + res.status(200).send('OK') }) ``` @@ -81,12 +83,12 @@ Merchant's Bunch instance polls payment system API to check invoice status: const checkPaymentStatus = async (purchaseNonce: string) => { // Get invoice ID from purchase nonce metadata const invoiceId = await getInvoiceIdForNonce(purchaseNonce) - + // Poll BTCPay Server API const invoice = await fetch(`https://your-btcpay-server.com/api/invoices/${invoiceId}`, { headers: { 'Authorization': `token ${BTCPAY_API_KEY}` } }).then(r => r.json()) - + if (invoice.status === 'paid') { await markPaid(purchaseNonce, { amount: invoice.amount }) } @@ -123,7 +125,7 @@ const invoice = await btcpay.createInvoice({ // 2. Webhook receives payment confirmation btcpay.on('invoice.paid', async (invoice) => { const { purchaseNonce } = invoice.metadata - + // 3. Verify and award punch if (await verifyPurchaseNonce(purchaseNonce)) { await merchantStore.markPaid(purchaseNonce, { diff --git a/index.html b/index.html index d156f18..4928fc1 100644 --- a/index.html +++ b/index.html @@ -2,8 +2,8 @@ - - + + Bunch – Bitcoin Loyalty Punch Cards diff --git a/package.json b/package.json index c7d2d2f..0008646 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", + "build:github": "tsc && vite build --mode github", "preview": "vite preview" }, "devDependencies": { diff --git a/public/404.html b/public/404.html index 72903fd..e43b5e7 100644 --- a/public/404.html +++ b/public/404.html @@ -2,36 +2,29 @@ - + Bunch – Bitcoin Loyalty Punch Cards diff --git a/src/main.tsx b/src/main.tsx index 609eca7..d0470e5 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -14,24 +14,16 @@ declare global { window.bunchVersion = '0.1.0' -// Handle GitHub Pages 404 redirect -// When 404.html redirects here with ?/path, extract and navigate -if (window.location.search.includes('?/')) { - const search = window.location.search - const pathMatch = search.match(/\?\/+(.+?)(?:&|$)/) - if (pathMatch) { - const path = pathMatch[1].replace(/~and~/g, '&') - const newPath = path.startsWith('/') ? path : '/' + path - const baseUrl = import.meta.env.BASE_URL.endsWith('/') - ? import.meta.env.BASE_URL.slice(0, -1) - : import.meta.env.BASE_URL - window.history.replaceState({}, '', `${baseUrl}${newPath}`) - } +// Handle 404 redirect for client-side routing +const redirectPath = sessionStorage.getItem('redirectPath') +if (redirectPath) { + sessionStorage.removeItem('redirectPath') + window.history.replaceState({}, '', redirectPath) } ReactDOM.createRoot(document.getElementById('root')!).render( - + } /> } /> diff --git a/src/screens/MerchantApp.tsx b/src/screens/MerchantApp.tsx index 54b01bb..60046c6 100644 --- a/src/screens/MerchantApp.tsx +++ b/src/screens/MerchantApp.tsx @@ -9,6 +9,7 @@ import { CardStats } from '../components/CardStats' import { EmptyStateCard } from '../components/EmptyStateCard' import { SessionCard } from '../components/SessionCard' import { MerchantStatusPanel } from '../components/MerchantStatusPanel' +import { getAssetPath, getRoutePath } from '../utils/paths' export const MerchantApp = () => { const { @@ -62,7 +63,7 @@ export const MerchantApp = () => {
- Bunch + Bunch

Merchant

Drop-in Bitcoin loyalty punch cards

@@ -85,10 +86,7 @@ export const MerchantApp = () => {