Emmanuel Ugwu
Posted on March 17, 2023
Previously, web development was exclusively associated with the creation of intranet and internet-based web pages and websites. However, currently, it is more like developing web applications that outperform the complexity and size of traditional mobile and desktop programs. Choosing the correct framework is critical because it substantially impacts an application's speed and performance.
What we will be building
This post discusses using a React-based framework, Refine, a Node.js server, Reloadly’s, and Stripe’s Session API to build a crypto gift card store.
We’ll make use of the following dependencies below to achieve this post.
- axios: A promise-based HTTP client for making API requests.
- dotenv: A module for loading separately stored environmental variables.
- express: A Node JS application framework - this will be used extensively in our server
- concurrently: An npm package to run multiple commands together.
The source code for this post is available on Github.
Prerequisite
To get the most out of this article, the following are required:
- Node and its package manager,
npm
. Run the commandnode -v && npm -v
to verify that you have them installed, or install them from here. - A basic understanding of JavaScript, React, TypeScript, and Refine.
- A Stripe account is needed — sign up here.
- A Reloadly account is also required— sign up here.
- Familiarity with axios and express. Setting up the Refine application
Create a Refine app in a new folder by running the following command in the terminal:
npx superplate-cli -p refine-react <project-name>
Follow the CLI wizard to select options and start creating your project.
? What will be the name of your app:
> <project-name>
? Do you want to use a UI Framework?:
❯ Ant Design
? Do you want a customized theme?:
❯ Default theme
? Router Provider:
❯ React Router v6
? Data Provider:
❯ REST API
? Auth Provider:
❯ None
? Do you want to add example pages?:
❯ No
? Do you want a customized layout?:
❯ No
? Do you want to add Kbar command pallete?:
❯ No
? i18n - Internationalization:
❯ No
Navigate into the project directory and install the required dependencies.
cd <project-name>
npm install axios dotenv express stripe concurrently
Running npm run start
starts the project on the local development server at https://localhost:3000 in our browser.
NOTE:
<project-name>
above stands for the name of your app. Call it whatever name you want.
Building the App Component
Create a .env
file in the root folder of our app to protect sensitive credentials we’ll use to build and integrate our crypto gift card store. Head on to Reloadly to get an access token to integrate Reloadly Gift Card API, then go to Stripe to obtain a Secret Key. Add the Reloadly Access Token and Stripe Secret Key to the .env
file.
//.env
STRIPE_KEY='STRIPE_SECRET_KEY_HERE'
RELOADLY_TOKEN='Bearer RELOADLY_ACCESS_TOKEN_HERE
Before integrating Stripe’s API, we need to create a product and price model for the crypto gift card we’ll use. Read this Stripe documentation on how to manage products and price models here.
Next, we create a Homepage.tsx
component file in the src
folder:
import { Link } from "react-router-dom";
import axios from 'axios';
export const Homepage: React.FC = () => {
return (
<section>
<h2 className='title'>Buy Crypto Gift Card</h2>
<p className='desc'>Buy crypto gift cards in an instant </p>
<div className='crypto'>
<div className="cards">
<div className="bin">
<img src="https://res.cloudinary.com/ugwutotheeshoes/image/upload/v1661613925/binance_logo.png"
alt="The cover of Binance USD"
/>
<div className="header">
<h2>Binance US</h2>
<p> US$50.00</p>
</div>
</div>
<Link to='/checkout' onClick={getUsers}>buy now</Link>
</div>
</div>
</section>
)
}
In the code snippet above, we created a link to help route our Homepage.tsx
component to a checkout page we’ll build later. We’ll also use an onClick
event listener to trigger/run a POST request.
Our application will look like this:
Requesting data from Reloadly's API
We want to request a $50 Bitcoin gift card; for that, we’ll have to make a POST request via axios to Reloadly, requesting data relating to the Bitcoin gift card.
interface Crypto {
body: string,
headers: object,
data: string
}
const getUsers = async () => {
try {
const { data, status } = await axios.post<Crypto>(
`https://giftcards.reloadly.com/orders`,
{
productId: 16235,
quantity: 1,
unitPrice: 50,
senderName: 'John Doe',
recipientEmail: 'anyone@email.com'
}, {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer TOKEN'
},
},
)
return data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.log('error: ', error.message);
return error.message
} else {
console.log('unexpected error: ', error);
return error
}
}
}
Using the body parameters in the code snippet shown above, we can manipulate the data from Reloadly to whatever use case we want. Still, in this scenario, we want to confirm if we’re requesting the right gift card.
Building a Checkout Page
Now we’ll add a page to show a preview of the user’s order, which is the Checkout page. Let’s create a Checkout.jsx
component file in the src
folder.
const ProductDisplay = () => (
<section>
<h2 className="check">Checkout</h2>
<div className="description">
<div className="product">
<img src="https://res.cloudinary.com/ugwutotheeshoes/image/upload/v1661613925/binance_logo.png"
alt="The cover of Binance USD"
/>
<h2>Binance USD</h2>
</div>
<p>US$50.00</p>
</div>
<form action="/create-checkout-session" method="POST">
<div className="btn">
<button className="checkout" type="submit">
Checkout
</button>
</div>
</form>
</section>
);
Next, we'll create a component in the Checkout.jsx
file to show a success or error message if our payment passes or fails.
import React, { useState, useEffect } from "react";
const Message = ({ message }) => (
<section>
<p>{message}</p>
</section>
);
export default function Checkout() {
const [message, setMessage] = useState("");
useEffect(() => {
// Check to see if this is a redirect back from Checkout
const query = new URLSearchParams(window.location.search);
if (query.get("success")) {
setMessage("Order placed! You will receive an email confirmation.");
console.log("yaay");
}
if (query.get("canceled")) {
setMessage(
"Order canceled -- continue to shop around and checkout when you're ready."
);
console.log("nahh");
}
}, []);
return message ? <Message message={message} /> : <ProductDisplay />;
}
This is what our application will look like after building the checkout page.
Routing our App Components
Let's use Refine as a medium for routing to navigate among views of various components in our application. Route the app components in the App.tsx
file as shown below.
import React from "react"
import { Homepage } from "./Homepage";
import { Refine } from "@pankod/refine-core";
import routerProvider from "@pankod/refine-react-router-v6";
import dataProvider from "@pankod/refine-simple-rest";
import Checkout from "./Checkout"
import { Success } from './Success';
import { Error } from "./Error";
function App() {
return (
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
resources={[{
name: "home",
list: Homepage
},{
name: "checkout",
list: Checkout,
},{
name: "success",
list: Success,
},{
name: "error",
list: Error,
},]}
Layout={({ children }) => (
<div>
<div>{children}</div>
</div>
)}
/>
);
}
export default App;
Building the Node Server
Create a server.js
file in the project's root folder and import the required dependencies we installed earlier.
// .server.js
const stripe = require('stripe')(process.env.STRIPE_KEY);
const express = require('express');
const app = express();
app.use(express.static('public'));
Next, we’ll make a POST request to Stripe’s API to create a checkout session. The checkout session controls what the user sees on the checkout page, such as the currency and price of the gift card. For that, we need to integrate the Price ID of the product model we created earlier.
// .server.js
const YOUR_DOMAIN = 'http://localhost:4242';
app.post('/create-checkout-session', async (req, res) => {
const session = await stripe.checkout.sessions.create({
line_items: [
{
// Provide the exact Price ID (for example, pr_1234) of the product you want to sell
price: 'price_1Lb9G7CJuM28O4MVY4uZ3lYV',
quantity: 1,
},
],
mode: 'payment',
success_url: `${YOUR_DOMAIN}?success=true`,
cancel_url: `${YOUR_DOMAIN}?canceled=true`,
});
res.redirect(303, session.url);
});
app.listen(4242, () => console.log('Running on port 4242'));
In the code snippet above, we also specified URLs for success and error pages if our payment passes or fails and to redirect users to them.
Next, we need to run our node server and gift card store simultaneously. This is where the npm package, concurrently
comes in. Head to the package.json
file and apply these configurations between the dependencies
and eslintConfig
JSON schema.
// package.json
"homepage": "http://localhost:3000",
"proxy": "http://localhost:4242",
"scripts": {
"start-client": "react-scripts start",
"start-server": "node server.js",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"start": "concurrently \"npm run start-client\" \"npm run start-server\""
},
After applying these configurations, run npm run start
to restart our application. This is what our crypto gift card store will look like fully set up:
Conclusion
Integrating Refine provides many out-of-the-box capabilities for rapid development while maintaining incredible customizability as it broadens your learning curve and scales web applications using a different approach.
Resources
Refer to these resources while reading this post as well:
Posted on March 17, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.