Step 7: receive bitcoin

The actual Bitcoin transactions and Lightning Network invoices are going to be handled by core – it’s not yet part of this Pull Request, but we’re preparing for it by adding backend-shared, a library with interfaces and enums shared between backend and core, see angular.json:

Here, we introduce the first two core API urls in core-api-urls.enum.ts:

The backend is going to communicate with core via a REST API, see core-api.ts:

We simply POST our requests to core. For now, we only need two things: to create a new Bitcoin address on new user registration, and to make a new Lightning Invoice, which is created on request from user profile page. All requests and responses are strictly typed, see core-api.interface.ts:

New Bitcoin address is requested by backend from core for each new user in users.ts:

Here, we just call CoreApi.createBitcoinAddress(). The errors are handled in all routes now with try/catch, see for example auth.ts:

If anything goes wrong, we return HTTP status code 400 to the frontend, and print the error to stdout with console.error – we will collect all this output with logstash and it will end up in kibana for analysis eventually.

Our Receive Bitcoin page looks like this:

You can see it for yourself by pulling the code, running npm start and going to http://localhost:4200/good@email.com. You’ll notice a few changes in this PR – there’s a menu in the header now, with three items: Receive Bitcoin, Send Bitcoin and Transaction History. Receive Bitcoin page is also a user profile page, featuring user’s name, picture served by Gravatar service, About Me block and 2 ways to receive Bitcoin: on Blockchain or via Lightning Network. Let’s take a look at the template found in user-profile-page.component.html:

We use ngxGravatar to display user’s picture, ngx-kjua component to render a QR code – Angular is great, there’s a component for everything! We use the same little pattern as on log in form to handle form validation for the Lightning Invoice form here, note the hint-under-form-field blocks under the amount field and under the description field; errorFromServer is where we’ll show an error if something goes really wrong and we receive a 400 from the server (coming from those try/catch blocks on the backend). The other 2 error blocks, errorInvoiceAmount and errorInvoiceNotes are for frontend validation errors.

Two little markdown blocks – #markdownBlockchain and #markdownLightning, – have to be handled in the component TypeScript code, unfortunately markdown cannot handle language switching automatically, so we need to help it a little in user-profile-page.component.ts:

Bitcoin address is rendered as a scannable QR code as well as the input with a button to copy the address to clipboard; we introduce a component with selector app-copy-to-clipboard-button for this. The template is found in copy-to-clipboard-button.component.html:

And the TypeScript code for the component is found in copy-to-clipboard-button.component.ts:

Tooltips are a little moody in Material, so we have to dance around them a little with 3 different event handlers – all we want to do is to change the text of the tooltip to “Copied”, but it takes a little work to do. Here’s how it looks:

We also introduce a component for entering amount in Bitcoin called AmountInputComponent, see the template in amount-input.component.html:

We style an input and a mat-select to appear in one line, so the user can enter amount in either satoshis or BTC, see amount-input.component.scss:

The code is found in amount-input.component.ts:

It’s almost empty; we rely on BitcoinUnitService to save preferred user Bitcoin unit in cookies similar to how we handle language, see bitcoin-unit.service.ts:

The service is initialized by our AppInitializerService along with auth and language, see app-initializer.service.ts:

Once user is done entering the amount and notes for the Lightning Invoice, they press btnCreateInvoice button and we open a modal dialog in createInvoice(), see user-profile-page.component.ts:

Let’s follow this request. It starts here on the frontend in ApiService, goes to the backend, then travels to core, and comes back as a paymentRequest string, which is a Lightning Network invoice hash, generated by lnd (eventually). First step is in api.service.ts:

This is handled on the backend in user.ts:

Request to core is sent from invoices.ts:

The call to CoreApi.createInvoice goes to core; if it errors out, we catch the exception with try/catch block and report HTTP status code 400 to the frontend, if everything goes well, we save the invoice record in the database and the frontend opens the modal that looks like this:

This dialog is implemented in invoice-dialog.component.html:

Here we are using ngx-kjua again to render the QR code, and app-copy-to-clipboard-button to make the payment request easy to copy for the user. Invoice amount and notes are shown at the top of the dialog. Note how we are injecting data into our dialog, see invoice-dialog.component.ts:

This way we encapsulate knowledge about the appearance (things like maxWidth) in the dialog itself, and making it easy to inject data into the dialog by exporting the interface and making it the only parameter to the config class constructor.

We cover the new functionality with an end to end test quite thoroughly in user-profile.spec.ts:

The new table for invoices is added to the database in the migration file 191219-invoices.ts:

You can run the app with npm run start:local and see the invoices being added to the new table in pgAdmin4 (by going to http://localhost:8432/):

Note that we don’t yet have core; core is stubbed out, see 2 new endpoints in stub-server.json:

Well, it’s 3 endpoints – create-invoice is for backend, but the other 2 are for core. Note also the new field trackHeaders – we’ve enhanced the stub server to track custom headers, because we want to ensure we’re sending correct Authorization header with our requests to protected API endpoints; for example, in Log In end to end tests, see log-in.spec.ts:

Note the new { headers: 'Bearer authToken1' } here – we are making sure we are doing authentication correctly.

One more thing we did here, is we got rid of backdrop in the user menu. For some reason, Material menu and selects create a backdrop, so when the menu is opened, all clicks outside the menu are captured by the backdrop, which is very annoying and makes no sense (if you know a good reason for this, please do let us know what is it!) We couldn’t find a way to get rid of this for the select, but the menu can be fixed, see header.component.html:

Unfortunately it’s not as easy as simply adding [hasBackdrop]="false", there’s more code to make it work correctly in header.component.ts:

This covers most of the code in this pull request. It’s pretty big, I know… oh well. Let me know if you have any questions of feedback and hey, Happy Holidays ๐Ÿ™‚

Cheers,
Andrew