Skip to content

Commit

Permalink
Merge pull request #157 from getAlby/feat/pay-button
Browse files Browse the repository at this point in the history
feat: add bc-pay-button web component
  • Loading branch information
rolznz authored Dec 20, 2023
2 parents 7a761a4 + f2f3053 commit 94a4f42
Show file tree
Hide file tree
Showing 17 changed files with 261 additions and 47 deletions.
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ _Continue further down for the full Bitcoin Connect API._
### React

```jsx
import {Button, init, launchModal, launchPaymentModal, closeModal, requestProvider, Connect, SendPayment} from '@getalby/bitcoin-connect-react';
import {Button, PayButton, init, launchModal, launchPaymentModal, closeModal, requestProvider, Connect, SendPayment} from '@getalby/bitcoin-connect-react';

// Initialize Bitcoin Connect
init({
Expand All @@ -95,6 +95,14 @@ init({
const {preimage} = await provider.sendPayment("lnbc...");
}}/>

// render a "Pay Now" button
// invoice can be unset initially - using the onClick function is a good time to fetch the invoice
// set the `payment` prop to override the payment status if a payment was made externally
<PayButton invoice={invoice} onClick={() => {
invoice = fetchInvoice();
setInvoice(invoice)
}} onPaid={(response) => alert("Paid! " + response.preimage)} payment={{preimage: 'my-preimage'}}/>

// render the connect flow on its own without the modal
<Connect/>

Expand Down Expand Up @@ -189,14 +197,23 @@ _Use another popular framework? please let us know or feel free to create a PR f
Bitcoin Connect exposes the following web components for allowing users to connect their desired Lightning wallet:

- `<bc-button/>` - launches the Bitcoin Connect Modal on click
- Arguments:
- `title` - (optional) change the title of the button
- `<bc-pay-button/>` - launches the Bitcoin Connect Payment Modal on click
- Arguments:
- `invoice` - BOLT11 invoice. Modal will only open if an invoice is set
- `title` - (optional) change the title of the button
- `preimage` - (optional) set this if you received an external payment
- Events:
- `click` - fires when the button is clicked. You can load an invoice here and set it on the button using `setAttribute('invoice', 'lnbc...')` which will then automatically launch the modal
- `bc:onpaid` - fires event with WebLN payment response in `event.detail` (contains `preimage`)
- `<bc-connect/>` - render connect wallet UI without modal
- `<bc-payment/>` - render a payment request UI without modal
- Arguments:
- `invoice` - BOLT11 invoice
- `paid` - set to true to mark payment was made externally
- `paid` - **Experimental** set to true to mark payment was made externally (This will change to `preimage` in v4)
- Events:
- `bc:onpaid` **Experimental** - fires event with WebLN payment response in `event.detail` (contains `preimage`)
- _more components coming soon_
- `bc:onpaid` - fires event with WebLN payment response in `event.detail` (contains `preimage`)

### Bitcoin Connect API

Expand Down Expand Up @@ -567,6 +584,7 @@ This project is powered by Lit.
See [Get started](https://lit.dev/docs/getting-started/) on the Lit site for more information.

## BOLT FUN

Bitcoin Connect is a BOLT FUN Legends of Lightning vol.2 finalist. [Follow our project and journey](https://bolt.fun/project/bitcoin-connect).

## License
Expand Down
2 changes: 1 addition & 1 deletion demos/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@getalby/bitcoin-connect-react": "^3.0.0",
"@getalby/bitcoin-connect-react": "^3.1.0-beta.4",
"@getalby/lightning-tools": "^5.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
26 changes: 19 additions & 7 deletions demos/react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Connect,
Payment,
launchModal,
PayButton,
} from '@getalby/bitcoin-connect-react';
import toast, {Toaster} from 'react-hot-toast';
import {SendPaymentResponse} from '@webbtc/webln-types';
Expand Down Expand Up @@ -93,12 +94,19 @@ function App() {
<Button
onConnected={(provider) => {
console.log('WebLN connected', provider);
toast('Connected!');
toast('<Button/>: Connected!');
}}
onConnecting={() => toast('Connecting!')}
onDisconnected={() => toast('Disconnected!')}
onModalOpened={() => toast('Modal opened!')}
onModalClosed={() => toast('Modal closed!')}
onConnecting={() => toast('<Button/>: Connecting!')}
onDisconnected={() => toast('<Button/>: Disconnected!')}
onModalOpened={() => toast('<Button/>: Modal opened!')}
onModalClosed={() => toast('<Button/>: Modal closed!')}
/>
<br />
<PayButton
invoice={invoice?.paymentRequest}
onPaid={(response) => toast('<PayButton/>: Paid! ' + response.preimage)}
onClick={() => toast('<PayButton/>: Clicked!')}
payment={paymentResponse}
/>
<div style={{marginTop: '16px'}}>
{preimage ? (
Expand Down Expand Up @@ -127,7 +135,11 @@ function App() {
}
const {setPaid} = launchPaymentModal({
invoice: invoice.paymentRequest,
onPaid: (result) => setPreimage(result.preimage),
onPaid: (response) => {
toast('launchPaymentModal(): onPaid ' + response.preimage);
setPreimage(response.preimage);
},
onCancelled: () => toast(`launchPaymentModal(): cancelled`),
});
setPaymentModalSetPaidFunction(() => setPaid);
}}
Expand All @@ -144,7 +156,7 @@ function App() {
<Payment
invoice={invoice.paymentRequest}
onPaid={(response) =>
toast('Paid! preimage: ' + response.preimage, {
toast('<Payment/>: Paid! ' + response.preimage, {
style: {
wordBreak: 'break-all',
},
Expand Down
18 changes: 9 additions & 9 deletions demos/react/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,17 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6"
integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==

"@getalby/bitcoin-connect-react@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@getalby/bitcoin-connect-react/-/bitcoin-connect-react-3.0.0.tgz#c768ec291d92dccec3c244609d51bf4b29bf3ade"
integrity sha512-ngYKLpR8sh8YjbaHFBYdkZsmLIFpFLuAm9vtDkR/DRPbcTYEBNeWawRnGzt28+85gkofOI3VFxiuIsp5hnq8qg==
"@getalby/bitcoin-connect-react@^3.1.0-beta.4":
version "3.1.0-beta.4"
resolved "https://registry.yarnpkg.com/@getalby/bitcoin-connect-react/-/bitcoin-connect-react-3.1.0-beta.4.tgz#5b18c066483ace5a67d289adbc2de617877452b5"
integrity sha512-FzAHXkrOGbr3VgfAHuISVuf4s2AtS7yHoR6/5yg8PecenJqjDVNSF8thRRIIIobs2g9N8a+STIEWu+aRH/ENng==
dependencies:
"@getalby/bitcoin-connect" "^3.0.0"
"@getalby/bitcoin-connect" "^3.1.0-beta.3"

"@getalby/bitcoin-connect@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@getalby/bitcoin-connect/-/bitcoin-connect-3.0.0.tgz#d8c77fdd03ec691291d1e84f43ae744d7a48bf3f"
integrity sha512-s6D/pkjS2ojmfVYDiZehsbGjmvtwVuBZvivx8izWPhpsTwSR/J7zmxX5E4epZjijXxFEAuXU4baUBzY7NjF3Hg==
"@getalby/bitcoin-connect@^3.1.0-beta.3":
version "3.1.0-beta.3"
resolved "https://registry.yarnpkg.com/@getalby/bitcoin-connect/-/bitcoin-connect-3.1.0-beta.3.tgz#9b5448eb59b898523c94b4656c60db041a912236"
integrity sha512-dOl0vKC8L4whUiSrLja8LvC4NnO0+V/jmFvhf9FR7x3593wDSg5hG62H7awfA6uOf7vx0JPHvc/GRMeRm76Drg==
dependencies:
"@getalby/lightning-tools" "^5.0.0"
"@getalby/sdk" "^3.0.0"
Expand Down
10 changes: 10 additions & 0 deletions dev/vite/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
document
.getElementById('send-payment')
.setAttribute('invoice', invoice.paymentRequest);
document.getElementById('pay-button').addEventListener('click', () => {
setTimeout(() => {
// fake a delay fetching an invoice
document
.getElementById('pay-button')
.setAttribute('invoice', invoice.paymentRequest);
}, 1000);
});
document
.getElementById('payment-flow')
.setAttribute('invoice', invoice.paymentRequest);
Expand Down Expand Up @@ -200,6 +208,8 @@ <h2>Payment Flow</h2>
<h1>Components</h1>
<h2>Button</h2>
<bc-button></bc-button>
<h2>Pay Button</h2>
<bc-pay-button id="pay-button"></bc-pay-button>

<h2>Modal header</h2>
<bc-modal-header show-help>Example Title</bc-modal-header>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getalby/bitcoin-connect",
"version": "3.0.0",
"version": "3.1.0-beta.3",
"description": "Web components to connect to a lightning wallet and power a website with WebLN",
"type": "module",
"source": "src/index.ts",
Expand Down
4 changes: 2 additions & 2 deletions react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getalby/bitcoin-connect-react",
"version": "3.0.0",
"version": "3.1.0-beta.4",
"type": "module",
"source": "src/index.ts",
"main": "./dist/index.cjs",
Expand All @@ -21,7 +21,7 @@
"build": "microbundle --globals react=React --jsx React.createElement --jsxFragment React.Fragment --jsxImportSource react"
},
"dependencies": {
"@getalby/bitcoin-connect": "^3.0.0"
"@getalby/bitcoin-connect": "^3.1.0-beta.3"
},
"devDependencies": {
"@types/react": "^18.2.21",
Expand Down
44 changes: 44 additions & 0 deletions react/src/components/PayButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import '@getalby/bitcoin-connect';
import {ComponentProps} from '../types/ComponentProps';
import {useCoreEvents} from '../hooks/useCoreEvents';
import {SendPaymentResponse} from '@webbtc/webln-types';
import {useOnPaid} from '../hooks/useOnPaid';

type PayButtonProps = ComponentProps & {
/**
* Bolt 11 invoice to pay
*/
invoice?: string;
/**
* @param response response of the WebLN send payment call
*/
onPaid?: (response: SendPaymentResponse) => void;
/**
* Mark that an external payment was made
*/
payment?: SendPaymentResponse;

/**
* Listen to when the pay button is clicked.
* This is a good time to fetch an invoice to pay.
*/
onClick?: () => void;
};

export const PayButton: React.FC<PayButtonProps> = (props) => {
useCoreEvents(props);

const {onPaid, payment} = props;
useOnPaid(onPaid, payment);

return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<bc-pay-button
invoice={props.invoice}
preimage={payment?.preimage}
onClick={props.onClick}
/>
);
};
23 changes: 5 additions & 18 deletions react/src/components/Payment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import '@getalby/bitcoin-connect';
import {ComponentProps} from '../types/ComponentProps';
import {useCoreEvents} from '../hooks/useCoreEvents';
import {SendPaymentResponse} from '@webbtc/webln-types';
import {useOnPaid} from '../hooks/useOnPaid';

type PaymentProps = ComponentProps & {
/**
* Bolt 11 invoice to pay
*/
invoice: string;
/**
* @param response response of the WebLN send payment call
Expand All @@ -20,24 +24,7 @@ export const Payment: React.FC<PaymentProps> = (props) => {
useCoreEvents(props);

const {onPaid, payment} = props;
React.useEffect(() => {
const onPaidEventHandler = (event: Event) => {
onPaid?.((event as CustomEvent).detail);
};
window.addEventListener('bc:onpaid', onPaidEventHandler);

if (payment) {
window.dispatchEvent(
new CustomEvent('bc:onpaid', {
detail: payment,
})
);
}

return () => {
window.removeEventListener('bc:onpaid', onPaidEventHandler);
};
}, [onPaid, payment]);
useOnPaid(onPaid, payment);

return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand Down
27 changes: 27 additions & 0 deletions react/src/hooks/useOnPaid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {SendPaymentResponse} from '@webbtc/webln-types';
import React from 'react';

export function useOnPaid(
onPaid?: (response: SendPaymentResponse) => void,
payment?: SendPaymentResponse
) {
React.useEffect(() => {
const onPaidEventHandler = (event: Event) => {
onPaid?.((event as CustomEvent).detail);
};
window.addEventListener('bc:onpaid', onPaidEventHandler);

if (payment) {
// TODO: remove once bc-send-payment accepts preimage
window.dispatchEvent(
new CustomEvent('bc:onpaid', {
detail: payment,
})
);
}

return () => {
window.removeEventListener('bc:onpaid', onPaidEventHandler);
};
}, [onPaid, payment]);
}
1 change: 1 addition & 0 deletions react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './components/Button';
export * from './components/PayButton';
export * from './components/Connect';
export * from './components/Payment';
export {
Expand Down
8 changes: 4 additions & 4 deletions react/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1018,10 +1018,10 @@
"@babel/helper-validator-identifier" "^7.22.5"
to-fast-properties "^2.0.0"

"@getalby/bitcoin-connect@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@getalby/bitcoin-connect/-/bitcoin-connect-3.0.0.tgz#d8c77fdd03ec691291d1e84f43ae744d7a48bf3f"
integrity sha512-s6D/pkjS2ojmfVYDiZehsbGjmvtwVuBZvivx8izWPhpsTwSR/J7zmxX5E4epZjijXxFEAuXU4baUBzY7NjF3Hg==
"@getalby/bitcoin-connect@^3.1.0-beta.3":
version "3.1.0-beta.3"
resolved "https://registry.yarnpkg.com/@getalby/bitcoin-connect/-/bitcoin-connect-3.1.0-beta.3.tgz#9b5448eb59b898523c94b4656c60db041a912236"
integrity sha512-dOl0vKC8L4whUiSrLja8LvC4NnO0+V/jmFvhf9FR7x3593wDSg5hG62H7awfA6uOf7vx0JPHvc/GRMeRm76Drg==
dependencies:
"@getalby/lightning-tools" "^5.0.0"
"@getalby/sdk" "^3.0.0"
Expand Down
2 changes: 1 addition & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export function launchPaymentModal({
setPaid: (sendPaymentResponse: SendPaymentResponse) => {
// The app needs to add an event listener manually (or use the React wrapper).
// Inconsistency: bc:onpaid is fired by different components (bc-send-payment, bc-payment, React wrapper)
// TODO: find a better way than firing bc:onpaid (also for React wrapper)
// TODO: remove once bc-send-payment accepts preimage
sendPaymentFlowElement.setAttribute('paid', 'paid');
sendPaymentFlowElement.dispatchEvent(
new CustomEvent('bc:onpaid', {
Expand Down
Loading

0 comments on commit 94a4f42

Please sign in to comment.