In this tutorial, we will understand how to programmatically create/modify a dynamic way to collect charges from customers and add custom data to your products by using Blockonomics checkout API. We will also learn how to pass data to and retrieve data from the checkout API.
Resources
Source Code | https://github.com/blockonomics/book-store |
Youtube Video | https://www.youtube.com/watch?v=CstYDj09-ts |
Tech Stack
- Next.js as the React framework
- Next.js API router for server-side API routes as the backend
- Prisma as the ORM for migrations and database access
- PostgreSQL as the database
- TypeScript as the programming language
Setting Up
The entire code for this demo is available on GitHub and you can fork it here. The step-by-step instructions on how to set up the demo are provided in the README
.
What is Checkout API?
Blockonomics provides high flexibility when it comes to configuration. The Checkout API provides a dynamic way to collect charges from customers and add custom data to your products. This is achieved by creating temporary products that inherit the properties of the parent product via API which can be shown via a payment button/link/widget.
This API enables you to generate a temporary child product that can be stored in the database for a period of seven days before it is automatically deleted. The order data will be saved in the system after the temporary product is deleted. Please refer to the documentation for more details.
In this tutorial, we will create child products with dynamic prices, and subsequently, process payments using the Blockonomics payment link/button/widget.
The Logic
In this demo, we are having an online book store where we are offering different types of Books. Each Book has different prices and once you click on any of them to make a purchase, you are provided with a few options like the type of book such as hardcover, paperback, or kiddle along with that the type of shipping services. Now, depending on the offer you choose, the price of the product will vary. We are going to use the concept of parent-child products by passing the data to the checkout API and creating a dynamic payment link for the same.
To pass this data we need the parent product uid that is generated on the Blockonomics merchant site. You can pass the product name, product description, and product price as well as any additional custom product data if needed.
Create Temporary Child Product using Checkout API
In this step, we create an api/checkout
to create a temporary product using the Blockonomics create_temp_product API.
This api receives transaction details such as the product, description, id, price, and the offer chosen by the buyer. Subsequently, we send the product data as the request body to the create_temp_product API, which generates a temporary product. To successfully utilize this API, you will need an API key, which must be included in the authorization header. You can obtain the authorization API key from the merchant store. Once the temporary product is created, you can access it using the data.uid, which can be passed to the Blockonomics payment/link/widget.
export default async function handle(req, res) {
const body = JSON.parse(req.body)
const reqData = await fetch('https://www.blockonomics.co/api/create_temp_product', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.BLOCKONOMICS_API_KEY
},
body: JSON.stringify(body),
});
const data = await reqData.json();
res.json(data);
}
Trigger Blockonomics payment widget/link/button using Checkout API
When you visit pages/product/[slug].tsx, you will find the HTML syntax for the Blockonomics payment button, which includes an onClick
function called buyProduct
. This function is executed when the Blockonomics button is clicked and it handles dynamic payments for the child product.
<button
type="button"
className="text-white bg-[#FF9119] hover:bg-[#FF9119]/80 focus:ring-4 focus:outline-none focus:ring-[#FF9119]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:hover:bg-[#FF9119]/80 dark:focus:ring-[#FF9119]/40 mr-2 mb-2"
onClick={buyProduct}
>
<svg className="w-4 h-4 mr-2 -ml-1" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="bitcoin" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.1-111 248-248 248S8 392.1 8 256 119 8 256 8s248 111 248 248zm-141.7-35.33c4.937-32.1-20.19-50.74-54.55-62.57l11.15-44.7-27.21-6.781-10.85 43.52c-7.154-1.783-14.5-3.464-21.8-5.13l10.93-43.81-27.2-6.781-11.15 44.69c-5.922-1.349-11.73-2.682-17.38-4.084l.031-.14-37.53-9.37-7.239 29.06s20.19 4.627 19.76 4.913c11.02 2.751 13.01 10.04 12.68 15.82l-12.7 50.92c.76 .194 1.744 .473 2.829 .907-.907-.225-1.876-.473-2.876-.713l-17.8 71.34c-1.349 3.348-4.767 8.37-12.47 6.464 .271 .395-19.78-4.937-19.78-4.937l-13.51 31.15 35.41 8.827c6.588 1.651 13.05 3.379 19.4 5.006l-11.26 45.21 27.18 6.781 11.15-44.73a1038 1038 0 0 0 21.69 5.627l-11.11 44.52 27.21 6.781 11.26-45.13c46.4 8.781 81.3 5.239 95.99-36.73 11.84-33.79-.589-53.28-25-65.99 17.78-4.098 31.17-15.79 34.75-39.95zm-62.18 87.18c-8.41 33.79-65.31 15.52-83.75 10.94l14.94-59.9c18.45 4.603 77.6 13.72 68.81 48.96zm8.417-87.67c-7.673 30.74-55.03 15.12-70.39 11.29l13.55-54.33c15.36 3.828 64.84 10.97 56.85 43.03z"></path></svg>
{`Pay with Bitcoin - $${total}`}
</button>
<div id="payment_area">
</div>
Upon navigating to pages/product/[slug].tsx, you will notice that we have triggered an API endpoint called /api/checkout
which we have created in our previous step. The body of the API includes details such as the parent UID, product name, description, ID, and total price. The parent UID is obtained from the Blockonomics Merchant store and is necessary to pass to the checkout API.
const buyProduct = async () => {
try {
const res = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({
parent_uid: process.env.BLOCKONOMICS_PARENT_UID,
product_name: product?.name,
product_description: product?.shortDescription,
product_id: product?.id,
value: total,
}),
});
} catch (e) {
console.log(e);
}
};
Inside the buyProduct
function, we also trigger the Blockonomics payment widget, which facilitates the payment process. The parent uid parameter is obtained from the above /api/checkout
API. Subsequently, this uid is passed to the Blockonomics widget uid, enabling the creation of a dynamic payment link for the child product.
const buyProduct = async () => {
try {
Blockonomics.widget({
msg_area: 'payment_area',
uid: data.uid,
email: 'customer@email.com',
});
} catch (e) {
console.log(e);
}
};
Implement Blockonomics PaymentCallback
Within the buyProduct
function, we also create a global function variable called blockonomicsPaymentCallback
, this function is triggered by Blockonomics when a payment is made. Here we navigate to /thankyou
page with uid, product Id, and transaction id in the query param using URLSearchParams
.
window.blockonomicsPaymentCallback = function (payment) {
const params = new URLSearchParams({
...payment,
uid: data.uid,
productId: product?.id,
}).toString();
router.push('/thankyou?' + params);
};
Order Hook URL Endpoint Logic
Blockonomics utilizes a GET request to the endpoint, providing the Bitcoin address and transaction status associated with the given address. By utilizing the Get Order API, we can obtain user details related to the transaction made using the provided Bitcoin address. The email can be used as an identification source, though other custom logic can also be implemented. Once the user is identified using the email, the status of the transaction can be updated or a new transaction can be created in the user’s and order database using the information provided by Blockonomics.
export default async function handle(req, res) {
const { uuid, status } = req.query;
const d = await fetch(
`https://www.blockonomics.co/api/merchant_order/${uuid}`,
{
headers: {
Authorization: 'Bearer ' + process.env.BLOCKONOMICS_API_KEY,
},
}
);
const j = await d.json();
const email = j.data.emailid;
const name = j.data.name;
const amount = j.value;
const state = 'confirmed';
await prisma.user.upsert({
where: {
email: email,
},
create: {
email: email,
name: name,
},
update: {
name: name,
},
});
const result = await prisma.order.create({
data: {
orderTotal: amount,
user: { connect: { email: email } },
status: state,
uuid: uuid,
},
});
res.json(result);
}
The End
Thus, we have configured the Checkout API in our website to dynamically collect charges from customers.