This tutorial will guide you through the process of building a simple HR platform that allows HRs to payout employees using the Payout Chimoney Endpoint.
Prerequisites
- Basic knowledge of JavaScript, React, and Node.js
- Node.js and npm installed on your machine
- A text editor, such as Visual Studio Code
- A Chimoney API key (see how to create one here )
Step 1: Create a New React App
- Open your terminal.
- Run the following command to create a new React app named
hr-platform
:npx create-react-app hr-platform
Step 2: Set Up the Server
- Create a
server
folder in the root directory of the project and add aserver.js
file - Navigate to your
server
directory and initialize a new Node.js project by runningnpm init -y
in your terminal. - Install the necessary dependencies by running
npm install dotenv express nodemailer cors @chimoney/v0.2.3
. - Create a
.env
file in theserver
directory of your project and add your environment variables:
REACT_APP_API_KEY=your_chimoney_api_key
API_BASE_URL=your_api_base_url
EMAIL_USER=your_email_user
EMAIL_PASS=your_email_password
PORT=5000
Step 3: Implement the Payout Endpoint in Server
- In the
server
directory, add aserver.js
file - In the
server.js
file, require the necessary modules and set up the Express app.
require("dotenv").config();
const express = require("express");
const sdk = require("@api/chimoney");
const nodemailer = require("nodemailer");
const cors = require("cors");
const app = express();
app.use(express.json());
app.use(cors());
- Initialize the Chimoney SDK with your API key and server URL.
sdk.auth(process.env.REACT_APP_API_KEY);
sdk.server(process.env.API_BASE_URL);
- Create a POST endpoint
/payout
to handle payout requests.
app.post("/payout", async (req, res) => {
const { email, valueInUSD, currency } = req.body;
try {
const response = await sdk.postV02PayoutsChimoney({
chimoneys: [{ email, valueInUSD, currency }],
});
let transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
let info = await transporter.sendMail({
from: '"Chimoney" <[email protected]>',
to: email,
subject: "Chimoney Payout",
text: `You have received a payout. Redeem it here: ${response.data.paymentLink}`,
});
console.log("Email sent: %s", info.messageId);
res.json({ status: "success", data: response.data });
} catch (error) {
console.error("Error during payout:", error);
console.error(error.stack);
res.status(500).json({
status: "error",
message: "Error when trying to payout",
error: error.message,
stack: error.stack,
});
}
});
- Start the server to listen on the specified port.
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Step 4: Set Up the Client-side
- Navigate back to your project's root directory.
- Install Axios by running
npm install axios
. - Replace the contents of
App.js
with the following code:
import React, { useState } from "react";
import "./App.css";
import axios from "axios";
function App() {
const [isPaymentSuccessful, setIsPaymentSuccessful] = useState(null);
const [showPayoutForm, setShowPayoutForm] = useState(false);
const [recipients, setRecipients] = useState([
{ email: "", currency: "USD", amount: "" },
]);
const handleInputChange = (index, event) => {
const newRecipients = [...recipients];
newRecipients[index][event.target.name] = event.target.value;
setRecipients(newRecipients);
};
const handlePayout = async (event) => {
event.preventDefault();
try {
for (let recipient of recipients) {
const response = await axios.post("http://localhost:5000/payout", {
email: recipient.email,
valueInUSD: recipient.amount,
currency: recipient.currency,
});
if (response.data.status !== "success") {
console.error("Payment failed for recipient:", recipient);
setIsPaymentSuccessful(false);
return;
}
}
console.log("Payment successful!");
setIsPaymentSuccessful(true);
} catch (error) {
console.error("Payment failed!", error);
setIsPaymentSuccessful(false);
}
setTimeout(() => {
setIsPaymentSuccessful(null);
}, 5000);
};
const handleShowPayoutForm = () => {
setShowPayoutForm(true);
};
const handleAddRecipient = () => {
setRecipients([...recipients, { email: "", currency: "USD", amount: "" }]);
};
const handleRemoveRecipient = (index) => {
const newRecipients = recipients.filter((_, i) => i !== index);
setRecipients(newRecipients);
};
return (
<div className="App">
<header className="App-header">
<h1>Welcome HR!</h1>
<p>Manage your employee payouts here.</p>
{!showPayoutForm && (
<button onClick={handleShowPayoutForm}>Pay Employees</button>
)}
{showPayoutForm && (
<form onSubmit={handlePayout}>
{recipients.map((recipient, index) => (
<div key={index} className="recipient-box">
<button
type="button"
className="remove-recipient"
onClick={() => handleRemoveRecipient(index)}
>
×
</button>
<label htmlFor={`email-${index}`}>Employee Email</label>
<input
type="email"
id={`email-${index}`}
name="email"
value={recipient.email}
onChange={(e) => handleInputChange(index, e)}
required
/>
<label htmlFor={`currency-${index}`}>Currency</label>
<select
id={`currency-${index}`}
name="currency"
value={recipient.currency}
onChange={(e) => handleInputChange(index, e)}
>
<option value="USD">USD</option>
<option value="CAD">CAD</option>
<option value="NGN">NGN</option>
</select>
<label htmlFor={`amount-${index}`}>Amount</label>
<input
type="number"
id={`amount-${index}`}
name="amount"
value={recipient.amount}
onChange={(e) => handleInputChange(index, e)}
required
/>
</div>
))}
<button
type="button"
onClick={handleAddRecipient}
className="add-recipient"
>
+ Add a Recipient
</button>
<button type="submit">Pay Employees</button>
</form>
)}
{isPaymentSuccessful !== null && (
<div className="payout-message">
{isPaymentSuccessful ? (
<h2 className="success">Congratulations, Payout sent!</h2>
) : (
<h3 className="error">Error when paying out.</h3>
)}
</div>
)}
</header>
</div>
);
}
export default App;
To style the application, you can add this styling to your App.css
code:
@import url("https://fonts.googleapis.com/css2?family=Comfortaa:wght@700&display=swap");
body {
margin: 0;
font-family: "Comfortaa", sans-serif;
background-color: #ffffff;
color: #333;
}
.App {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 20px;
}
.App-header {
background-color: #f2f3f4;
padding: 40px;
border-radius: 8px;
box-shadow: 0 12px 16px rgba(63, 58, 58, 0.1);
text-align: center;
color: white;
width: 100%;
max-width: 600px;
}
h1 {
font-size: 2.5em;
margin-bottom: 0.5em;
color: #746f6f;
}
p {
font-size: 1.2em;
margin-bottom: 1.5em;
color: #746f6f;
}
button {
background-color: #02b975;
color: #ffffff;
font-size: 1em;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #378638;
}
form {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
max-width: 400px;
margin: 20px auto;
}
input,
select {
width: 90%;
padding: 10px;
margin-bottom: 1.5em;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 0.9em;
font-family: "Comfortaa", sans-serif;
color: #746f6f;
}
input[type="number"] {
-moz-appearance: textfield;
appearance: textfield;
font-family: "Comfortaa", sans-serif;
font-size: 0.8em;
color: #746f6f;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.payout-message {
margin-top: 20px;
padding: 10px;
border-radius: 4px;
width: 100%;
text-align: center;
}
.success {
color: #4caf50;
background-color: #e8f5e9;
}
.error {
color: #f44336;
background-color: #ffebee;
}
.add-recipient {
background-color: #02b975;
margin-bottom: 20px;
}
.add-recipient:hover {
background-color: #28a745;
}
.payout-message {
margin-top: 20px;
}
.success {
color: green;
}
.error {
color: red;
}
.recipient {
margin-bottom: 20px;
}
.separator {
border: none;
height: 1px;
background-color: #02b975;
margin: 20px 0;
}
.recipient-box {
background: #f9f9f9;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(22, 21, 21, 0.1);
margin-bottom: 20px;
position: relative;
}
.recipient-box label {
color: #333;
}
.remove-recipient {
background: transparent;
border: none;
color: #ff0000;
font-size: 20px;
top: -20px;
right: -24px;
cursor: pointer;
position: absolute;
}
.remove-recipient:hover {
color: #cc0000;
}
Step 5: Run the Application
- Start the server by running
node server.js
in the server directory of your project. - Start the client by navigating to the root directory and running
npm start
.
You should now have a simple HR platform that allows HRs to pay employees using the Chimoney API.
Demo Platform
This platform includes a form for entering employee information and a button for initiating payouts. After a payout is initiated, the platform sends an email to the employee with a link to redeem their payout.
Link to Demo Platform