chore: remove Bitrefill custom app in favor of standard NWC connection (#2420)

* chore: remove Bitrefill custom app in favor of standard NWC connection

Bitrefill now supports Nostr Wallet Connect directly, so the custom
embedded iframe app is no longer needed. Remove the internal Bitrefill
screen and route, convert the app store entry to a standard NWC
connectable app, and drop the embed.bitrefill.com frame-src CSP
exceptions from both the backend header and the dev Vite config.

Closes #2283

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update bitrefill instructions and add mobile links

* chore: remove accidentally committed .claude/worktrees gitlinks

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Roland 2026-06-09 20:11:04 +07:00 committed by GitHub
parent 4585ac6da3
commit 10771b7c0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 34 additions and 145 deletions

View file

@ -20,7 +20,6 @@ import fountain from "src/assets/suggested-apps/fountain.png";
import hablanews from "src/assets/suggested-apps/habla-news.png";
import iris from "src/assets/suggested-apps/iris.png";
import jumble from "src/assets/suggested-apps/jumble.png";
import satoraLogo from "src/assets/suggested-apps/satora.png";
import lightningMessageboard from "src/assets/suggested-apps/lightning-messageboard.png";
import lnbits from "src/assets/suggested-apps/lnbits.png";
import lnvps from "src/assets/suggested-apps/lnvps.png";
@ -36,6 +35,7 @@ import primal from "src/assets/suggested-apps/primal.png";
import pullthatupjamie from "src/assets/suggested-apps/pullthatupjamie.png";
import runstr from "src/assets/suggested-apps/runstr.png";
import satsorter from "src/assets/suggested-apps/sat-sorter.png";
import satoraLogo from "src/assets/suggested-apps/satora.png";
import sats4ai from "src/assets/suggested-apps/sats4ai.png";
import simpleboost from "src/assets/suggested-apps/simple-boost.png";
import snort from "src/assets/suggested-apps/snort.png";
@ -2233,10 +2233,40 @@ export const appStoreApps: AppStoreApp[] = (
title: "Bitrefill",
description: "Live on bitcoin",
extendedDescription: "Buy gift cards and e-sims with no KYC",
internal: true,
webLink: "https://bitrefill.com",
logo: bitrefill,
categories: ["shopping"],
appleLink:
"https://apps.apple.com/us/app/bitrefill-esims-gift-cards/id1378102623",
playLink:
"https://play.google.com/store/apps/details?id=com.bitrefill.app&hl=en",
installGuide: (
<>
<p className="text-muted-foreground">
Open{" "}
<ExternalLink
to="https://bitrefill.com"
className="font-medium text-foreground underline"
>
Bitrefill
</ExternalLink>{" "}
in your browser, or download the app on iOS or Android
</p>
</>
),
finalizeGuide: (
<>
<div>
<h3 className="font-medium">In Bitrefill</h3>
<ul className="list-inside list-decimal text-muted-foreground">
<li>
Go to settings {"->"} wallets {"->"} lightning {"->"} payments.
</li>
<li>Paste the connection secret to connect Alby Hub</li>
</ul>
</div>
</>
),
},
{
id: "bringin",

View file

@ -37,7 +37,6 @@ import { FirstChannel } from "src/screens/channels/first/FirstChannel";
import { OpenedFirstChannel } from "src/screens/channels/first/OpenedFirstChannel";
import { OpeningFirstChannel } from "src/screens/channels/first/OpeningFirstChannel";
import { AlbyCliSkill } from "src/screens/internal-apps/AlbyCliSkill";
import { Bitrefill } from "src/screens/internal-apps/Bitrefill";
import { BuzzPay } from "src/screens/internal-apps/BuzzPay";
import { LightningMessageboard } from "src/screens/internal-apps/LightningMessageboard";
import { SimpleBoost } from "src/screens/internal-apps/SimpleBoost";
@ -349,10 +348,6 @@ const routes: RouteObject[] = [
path: "zapplanner",
element: <ZapPlanner />,
},
{
path: "bitrefill",
element: <Bitrefill />,
},
{
path: "alby-cli-skill",
element: <AlbyCliSkill />,

View file

@ -1,136 +0,0 @@
import { Invoice } from "@getalby/lightning-tools";
import React, { useEffect } from "react";
import { toast } from "sonner";
import AppHeader from "src/components/AppHeader";
import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount";
import FormattedFiatAmount from "src/components/FormattedFiatAmount";
import Loading from "src/components/Loading";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "src/components/ui/alert-dialog";
import { PayInvoiceResponse } from "src/types";
import { request } from "src/utils/request";
export function Bitrefill() {
const [paymentDialogOpen, setPaymentDialogOpen] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const [invoice, setInvoice] = React.useState<Invoice>();
useEffect(() => {
const handleIframeMessage = (event: MessageEvent) => {
if (event.origin === "https://embed.bitrefill.com" && event.data) {
// Some of the events have objects in event.data 🤷‍♂️
try {
const parsedData = JSON.parse(event.data);
if (parsedData.event === "payment_intent") {
setPaymentDialogOpen(true);
const invoice = new Invoice({ pr: parsedData.paymentAddress });
setInvoice(invoice);
}
} catch {
/* empty */
}
}
};
window.addEventListener("message", handleIframeMessage);
return () => {
window.removeEventListener("message", handleIframeMessage);
};
}, []);
async function confirmPayment() {
if (!invoice) {
return;
}
try {
setLoading(true);
const payInvoiceResponse = await request<PayInvoiceResponse>(
`/api/payments/${invoice.paymentRequest}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
}
);
if (!payInvoiceResponse?.preimage) {
throw new Error("No preimage in response");
}
setPaymentDialogOpen(false);
setInvoice(undefined);
toast("Payment successful");
} catch (e) {
toast.error("Failed to send payment", {
description: "" + e,
});
console.error(e);
} finally {
setLoading(false);
}
}
return (
<>
<div className="flex flex-col gap-5 h-full">
<AppHeader
pageTitle="Bitrefill"
title="Bitrefill"
description="Live on bitcoin by purchasing digital gift cards, eSIMs, and phone refills"
/>
<iframe
width="100%"
className="grow rounded-xl"
src={`https://embed.bitrefill.com/?ref=V6DBkUhx&showPaymentInfo=false&paymentMethod=lightning&showPaymentInfo=false&utm_source=alby_hub`}
sandbox="allow-same-origin allow-popups allow-scripts allow-forms"
></iframe>
</div>
<AlertDialog open={paymentDialogOpen} onOpenChange={setPaymentDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Confirm payment</AlertDialogTitle>
</AlertDialogHeader>
<div className="flex flex-col gap-3">
<div>
<div className="font-medium">Description</div>
<div>{invoice?.description}</div>
</div>
<div>
<div className="font-medium">Amount</div>
<div className="flex flex-row gap-2 items-center">
<span className="font-medium slashed-zero">
<FormattedBitcoinAmount
amountMsat={(invoice?.satoshi || 0) * 1000}
/>
</span>
<FormattedFiatAmount
className="text-muted-foreground"
amountSat={invoice?.satoshi || 0}
/>
</div>
</div>
</div>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction disabled={loading} onClick={confirmPayment}>
{loading && <Loading />}
Pay now
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}

View file

@ -98,7 +98,7 @@ const insertDevCSPPlugin: Plugin = {
"<head>",
`<head>
<!-- DEV-ONLY CSP - when making changes here, also update the CSP header in http_service.go (without the nonce!) -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self' ${DEVELOPMENT_NONCE}; img-src 'self' https://uploads.getalby-assets.com https://cdn.getalby-assets.com https://getalby.com; connect-src 'self' https://api.getalby.com https://getalby.com https://zapplanner.albylabs.com wss://relay.getalby.com wss://relay2.getalby.com; frame-src https://embed.bitrefill.com https://www.youtube-nocookie.com" />`
<meta http-equiv="Content-Security-Policy" content="default-src 'self' ${DEVELOPMENT_NONCE}; img-src 'self' https://uploads.getalby-assets.com https://cdn.getalby-assets.com https://getalby.com; connect-src 'self' https://api.getalby.com https://getalby.com https://zapplanner.albylabs.com wss://relay.getalby.com wss://relay2.getalby.com; frame-src https://www.youtube-nocookie.com" />`
);
},
},

View file

@ -66,7 +66,7 @@ func (httpSvc *HttpService) RegisterSharedRoutes(e *echo.Echo) {
e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
ContentTypeNosniff: "nosniff",
XFrameOptions: "DENY",
ContentSecurityPolicy: "default-src 'self'; img-src 'self' https://uploads.getalby-assets.com https://cdn.getalby-assets.com https://getalby.com; connect-src 'self' https://api.getalby.com https://getalby.com https://zapplanner.albylabs.com wss://relay.getalby.com wss://relay2.getalby.com; frame-src https://embed.bitrefill.com https://www.youtube-nocookie.com",
ContentSecurityPolicy: "default-src 'self'; img-src 'self' https://uploads.getalby-assets.com https://cdn.getalby-assets.com https://getalby.com; connect-src 'self' https://api.getalby.com https://getalby.com https://zapplanner.albylabs.com wss://relay.getalby.com wss://relay2.getalby.com; frame-src https://www.youtube-nocookie.com",
ReferrerPolicy: "no-referrer",
}))
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{