Setup
In our Next JS app that lets issuers surface pending tasks in the UI, and approve or reject them, we’ll use:- A singleton in-memory DB
- ShadCN UI components
- Lucide icons
Scaffold your project
Start the app
Folder structure
Next, you’re going to want to create a folder struture similar to this layout.folder structure
api/webhook folder. We’re going to opt for creating a separate route for each webhook event to keep a clear separation of concerns and since we’re using Next JS’s app based routing system, that means we need to create the following files
Webhook POST handler
Signature Verification
Finally, we’ll add a helper function to validate the webhook signature from incoming events sent by Paylias. Open up thelib/signature.ts file and add in the following code.
signature.ts
Data Layer
Next up, we’ll define our basic data models to handle our internal representation of an incoming Payment Request, create a simple in-memory database and a repository layer that will let us perform our CRUD operations. Create a file undertypes called task.ts if you haven’t already done so and paste this inside
task.ts
class that manages a Map. Open up the database/memory-db.ts file (or create one if you haven’t) and add the following code inside
memory-db.ts
taskStore variable is not defined. Because we have chosen to use an in-memory database, in Next, (whether in dev with HMR or in production on Vercel’s serverless functions), the data in this module can be torn down and re-evaluated at any time, so tasks gets reset. In order to avoid this and truly get one bucket of memory to live across all imports (& HMR reloads) in a single Node process, you need to hang it on the Node “global”. So create a new file inside the database folder called global-db.ts and add this code
global-db.ts
memory-db.ts file to fix the compile errors.
memory-db.ts
memory-db. So go ahead and open up the file repository/taskRepository.ts and add the following code inside.
taskRepository.ts
Webhook implementation
Now that we’ve set up the data layer, we’ll look into implementing the relevant webhooks required to build out our issuer app! If you haven’t already, we recommend going through the Webhook guides to get an understanding of what webhooks are and how they work. Once you’ve completed the guides, you can start implementing the webhooks for your issuer app. Since we’re opting to create an individual endpoint for each combination ofrecord_type and event_type, the first step we have to complete is to create webhook subscriptions on Paylias for each endpoint. You can follow the Webhook Implementation guide to create the endpoint subscriptions on Paylias.
Since Paylias cannot deliver webhooks to localhost, we’re going to first have to expose our app through a tunnel. We recommend using ngrok. You may need to create a free account and install it on your system before proceeding further.
Side step
You can run this command in another terminal window to expose your app through ngrok to the internet.Implementation
Now, we’ll implement only the four Paylias webhooks an Issuer needs1
`payment-admission:created`
When Paylias notifies us that a new payment admission has been created, we will
- Parse the payload to extract the relevant information
- Validate the signature in the header
- Create a new Task record in your database with the amount and currency in the payment and status
PENDING - Add a placeholder to trigger any risk-check logic
- Send a response back to Paylias to acknowledge receipt of the webhook
2
`payment-admission-task:created
When Paylias notifies us that a new payment admission task has been created, we will
- Parse the payload to extract the relevant information
- Validate the signature in the header
- Look up the original task
- Update the task to either
PENDINGorREJECTED - Send a response back to Paylias to acknowledge receipt of the webhook
3
payment-exception:created
If Paylias sends an event notifying us that there was an exception during the payment processing flow, we’ll update the task to
REJECTED and save the error message.4
payment-transaction:created
The final step in the payment journey is marking the payment complete. When you receive this event, the payment is considered as final and any deductions in the user’s balance and ledger updates should be made on your platform (preferably asynchronously so you can respond back to Paylias quickly). The Transaction Resource guide explains this in more details.
Front-End UI
Now that we’ve set up majority of the application at the backend, its time to move on to the frontend so that our users can see and respond to their payment requests. The general functionality we’re going to develop on the frontend will include- Displaying a list of all
Tasksin a column view - Polling for all tasks in the background and updating the list
- Taking action on
PENDINGtasks to allow our user to either accept or reject them.
This is a fairly simple UI/UX implementation meant for demostration purposes only. In your production application you would most likely use push notifications as new tasks come in to your system and have a separate notification page or tray to display any pending tasks to your user.
Install shad-cn components
First off, we’ll install the relevant Shad Cn components needed to build our UI. Open up a terminal window andcd into the root directory of your app. Once there copy-paste this command
Create components
Once these have been installed, add the following components under thecomponents directory.
Fetch All Tasks
Now that we’ve created the basic components needed to display a Task, we’re going to create an endpoint that will return us a list of tasks for us to render in a list. Open upapp/api/tasks/route.ts and add in the following code
route.ts
TasksList component to poll for all tasks every 5 seconds. Lets create the TasksList component now. Create a new file under components called TasksLists.tsx and add in the following code
TasksList.tsx
app/page.tsx and replace the existing code with the following
page.tsx
PENDING task.
Updating a task
Updating the task is a two step process. When our user clicks on a task they want to action on, we’ll open up a dialog that will first fetch the task along with its details from our database. Then, depending on whether they want to approve or reject the payment request, we’ll update the task accordingly and respond to Paylias with our decision. To make this work, lets first start with implementing the two endpoints we’re going to need. Open up the/api/tasks/[task-id]route.ts file and add in the code below to implement both the GET and PUT endpoints.
PUT endpoint we’re also calling the Update Admission Task endpoint. You’ll notice that we’re not updating the task in our database yet. If we reject the task, Paylias will trigger an Exception event which our /api/webhook/payment-exception/created endpoint will catch. This is where our the task will be updated in our system. Similarly, if we approve the task, Paylias will trigger a Transaction event which our /api/webhook/payment-transaction/created endpoint will catch. This is where we will mark the task as COMPLETED.
The final step in this guide is to implement the UI for updating a task. We’ll accomplish this using the <Dialog> component from Shad Cn along with implementing parallel and intercepting routes from Next JS.
Create a new file under the components directory called UpdateTask.tsx and add in the following code
UpdateTask.tsx
app directory.