Documentation
Feedback
Guides
Storefront Development

Storefront Development
FastStoreExtending the FastStore APIAPI extensions - Use cases
Adding Installment Information in the Product Details Page
This guide outlines how you can display available product installment options on your Product Details Page (PDP).
For detailed instructions on API extensions, see the API extensions guide.

Context

You want to display installment details (number, amount, payment system) on the PDP. This feature allows customers to view installment details directly on the product page, supporting their decision-making.

Implementation

Creating GraphQL files

First, you must configure the necessary GraphQL files to retrieve and structure the installment data.
  1. In your store repository, go to the src folder. If you don’t have it, create a graphql folder.
  2. Inside graphql, create the vtex folder.
  3. In the vtex folder, create two other subfolders, resolvers and typeDefs.
For further information on code implementation, see the vtex folder available in the playground.store repository.

_10
src
_10
â”— đź“‚ graphql
_10
â”— đź“‚vtex
_10
┣ 📂 resolvers
_10
â”— đź“‚ typeDefs

Defining the types

Now that the file structure is in place, let's define the data structure for our installment information using GraphQL.
In the typeDefs folder, create a product.graphql file to define the schema. Add the following schema definitions:
  • Installments: Specifies the structure of each installment option. Each installment has properties for:
  • installmentPaymentSystemName: Name of the payment system used (e.g., Credit Card).
  • installmentValue: Amount of each installment.
  • installmentInterest: Interest rate applied per installment.
  • installmentNumber: Total number of installments offered.
  • StoreProduct: Type that is being extended to include a new field called availableInstallments. This field is a list of installment objects, representing all the available installment options for the product.
product.graphql

_13
type Installments {
_13
installmentPaymentSystemName: String!
_13
installmentValue: Float!
_13
installmentInterest: Float!
_13
installmentNumber: Float!
_13
}
_13
_13
extend type StoreProduct {
_13
"""
_13
Retrieve available installment data extending StoreProduct
_13
"""
_13
availableInstallments: [Installments!]!
_13
}

Creating resolvers

Now that we've defined the structure of our installment information (GraphQL types), let's create the rationale for retrieving and formatting this information using a resolver function. Resolvers essentially act as gateways between your frontend and backend, processing data requests.
In the resolvers folder, create a product.ts file to handle the installment information rationale. Add the following code.
  • productResolver: Defines a resolver for the StoreProduct type.
  • availableInstallments: Retrieves the installment information from the commertialOffer property of the first seller (sellers[0]).
  • installments.length: Checks if there are any installments (installments.length). If not, an empty array is returned.
  • The code iterates through each installment and maps it to a new object with the desired properties (installmentPaymentSystemName, installmentValue, etc.).
product.graphql
product.ts

_22
import type { StoreProductRoot } from "@faststore/core/api";
_22
_22
const productResolver = {
_22
StoreProduct: {
_22
availableInstallments: (root: StoreProductRoot) => {
_22
const installments = root.sellers?.[0]?.commertialOffer?.Installments;
_22
_22
if (!installments.length) {
_22
return [];
_22
}
_22
_22
return installments.map((installment) => ({
_22
installmentPaymentSystemName: installment.PaymentSystemName,
_22
installmentValue: installment.Value,
_22
installmentInterest: installment.InterestRate,
_22
installmentNumber: installment.NumberOfInstallments,
_22
}));
_22
},
_22
},
_22
};
_22
_22
export default productResolver;

Consolidating resolvers

In the resolvers folder, create an index.ts file to consolidate the resolvers.
  • The index.ts file exports resolvers by default, acting as a central hub for all your resolver functions.
  • resolvers:Combines the imported resolver with any other resolvers you might have in your project (represented by the ellipsis ...).
product.graphql
product.ts
index.ts

_10
import { default as StoreProductResolver } from "./product";
_10
_10
const resolvers = {
_10
...StoreProductResolver,
_10
};
_10
_10
export default resolvers;

Using fragments

Now that we've defined the availableInstallments field using GraphQL types and resolvers, we need to specify where this data will be used in your storefront queries. This is achieved through GraphQL fragments.
  1. In the src folder of your store code, create a fragments folder.
  2. In the fragments folder, create the following files:
    • ClientProduct.ts: Defines the client-side fragment.
    • ServerProduct.ts: Defines the server-side fragment.
  3. In the ClientProduct.ts file, defines the GraphQL fragment for retrieving installment data.
  • gql: Imported from @faststore/core/api to construct the GraphQL fragment.
  • ClientProduct: Fragment to indicate it extends the product query.
  • The fragment defines the specific fields we want to retrieve from the availableInstallments data structure.
product.graphql
product.ts
index.ts
ClientProduct.ts

_14
import { gql } from "@faststore/core/api";
_14
_14
export const fragment = gql(`
_14
fragment ClientProduct on Query {
_14
product(locator: $locator) {
_14
availableInstallments {
_14
installmentPaymentSystemName
_14
installmentValue
_14
installmentInterest
_14
installmentNumber
_14
}
_14
}
_14
}
_14
`);

Adding server-side product fragment for installment details

Add a product fragment focusing on installment details to ensure consistency in data retrieval on the client and server sides. In the ServerProduct.ts file, add the following:
For further details on code implementation, see the fragments folder available in the playground.store repository.
product.graphql
product.ts
index.ts
ClientProduct.ts
ServerProduct.ts

_14
import { gql } from "@faststore/core/api";
_14
_14
export const fragment = gql(`
_14
fragment ServerProduct on Query {
_14
product(locator: $locator) {
_14
availableInstallments {
_14
installmentPaymentSystemName
_14
installmentValue
_14
installmentInterest
_14
installmentNumber
_14
}
_14
}
_14
}
_14
`);

Formatting installment values

For a more user-friendly display, you can format the installment values to include currency symbols and proper locale formatting.
  1. In the src folder of your store code, create a utils folder.
  2. Inside utils, create the priceFormatter.ts file and add the following:
  • FaststoreConfig: Imported to access the current store's locale and currency settings.
  • priceFormatter: This function takes a numeric value and formats it according to the store's locale and currency.
For further details on code implementation, see the utils folder available in the playground.store repository.
product.graphql
product.ts
index.ts
ClientProduct.ts
ServerProduct.ts
priceFormatter.ts

_10
import FaststoreConfig from "../../discovery.config.js";
_10
_10
export const priceFormatter = (value: number) => {
_10
return value.toLocaleString(FaststoreConfig.session.locale, {
_10
style: "currency",
_10
currency: FaststoreConfig.session.currency.code,
_10
});
_10
};

Creating a new component with installments

Now that we've established the rationale for retrieving installment data, let's create a new component to display this information alongside the buy button on the PDP.
  1. In the src/components folder, create two new folders:
    • BuyButtonWithDetails: Keeps the component for our enhanced buy button with installment details.
    • sections/CustomProductDetails: Keeps the rationale for overriding the default PDP buy button component.
  2. Inside the BuyButtonWithDetails folder, create two new files:
    • BuyButtonWithDetails.tsx
    • buy-button-with-details.module.scss
  3. Inside BuyButtonWithDetails.tsx, add the following code.
  • priceFormatter: Format installment values.
  • styles: Stylesheet for the BuyButtonWithDeatils imported from the .scss file.
  • usePDP: Hook to retrieve the PDP context containing product and extension data.
  • We conditionally display the first installment option if it's interest-free (you can modify this to show all installments).
  • The component renders a section containing the installment information and the buy button.
product.graphql
product.ts
index.ts
ClientProduct.ts
ServerProduct.ts
priceFormatter.ts
BuyButtonWithDetails.tsx

_32
import { usePDP } from "@faststore/core";
_32
import { Button as UIButton, ButtonProps } from "@faststore/ui";
_32
import { priceFormatter } from "../../utils/priceFormatter";
_32
_32
import styles from "./buy-button-with-details.module.scss";
_32
_32
export function BuyButtonWithDetails(props: ButtonProps) {
_32
const context = usePDP();
_32
_32
const installment = context?.data?.product?.availableInstallments[0];
_32
const interestFree = installment.installmentInterest === 0 ?? false;
_32
_32
return (
_32
<section className={styles.buyButtonWithDetails}>
_32
{interestFree && (
_32
<span>
_32
{`${installment.installmentNumber} interest-free installment(s)`}
_32
<br />
_32
{`of ${priceFormatter(installment.installmentValue)} with ${
_32
installment.installmentPaymentSystemName
_32
}`}
_32
</span>
_32
)}
_32
_32
<UIButton {...props} variant="primary">
_32
Buy Button
_32
</UIButton>
_32
</section>
_32
);
_32
}
_32
_32
export default BuyButtonWithDetails;

Creating a stylesheet for the component

Inside buy-button-with-details.module.scss, add the code below. The stylesheet styles the component using SCSS. It defines styles for the installment information container and the buy button itself.
product.graphql
product.ts
index.ts
ClientProduct.ts
ServerProduct.ts
priceFormatter.ts
BuyButtonWithDetails.tsx
buy-button-with-details.module.scss

_16
.buyButtonWithDetails {
_16
> span {
_16
display: flex;
_16
justify-content: center;
_16
margin-bottom: var(--fs-spacing-4);
_16
text-align: center;
_16
font-size: var(--fs-text-size-1);
_16
}
_16
_16
[data-fs-button] {
_16
width: 100%;
_16
[data-fs-button-wrapper] {
_16
background-color: var(--fs-color-neutral-7);
_16
}
_16
}
_16
}

Creating a custom section

By including the CustomProductDetails section in the PDP, you replace the default buy button component with your enhanced BuyButtonWithDetails component, which displays both the buy button and the installment information.
In the CustomProductDetails folder , create the CustomProductDetails.tsx and add the following code.
For further details on code implementation, see the utils folder available in the playground.store repository.
product.graphql
product.ts
index.ts
ClientProduct.ts
ServerProduct.ts
priceFormatter.ts
BuyButtonWithDetails.tsx
buy-button-with-details.module.scss
CustomProductDetails.tsx

_11
import { getOverriddenSection, ProductDetailsSection } from "@faststore/core";
_11
import { BuyButtonWithDetails } from "../../BuyButtonWithDetails/BuyButtonWithDetails";
_11
_11
const CustomProductDetails = getOverriddenSection({
_11
Section: ProductDetailsSection,
_11
components: {
_11
BuyButton: { Component: BuyButtonWithDetails },
_11
},
_11
});
_11
_11
export default CustomProductDetails;

Creating GraphQL files

First, you must configure the necessary GraphQL files to retrieve and structure the installment data.
  1. In your store repository, go to the src folder. If you don’t have it, create a graphql folder.
  2. Inside graphql, create the vtex folder.
  3. In the vtex folder, create two other subfolders, resolvers and typeDefs.
For further information on code implementation, see the vtex folder available in the playground.store repository.

Defining the types

Now that the file structure is in place, let's define the data structure for our installment information using GraphQL.
In the typeDefs folder, create a product.graphql file to define the schema. Add the following schema definitions:
  • Installments: Specifies the structure of each installment option. Each installment has properties for:
  • installmentPaymentSystemName: Name of the payment system used (e.g., Credit Card).
  • installmentValue: Amount of each installment.
  • installmentInterest: Interest rate applied per installment.
  • installmentNumber: Total number of installments offered.
  • StoreProduct: Type that is being extended to include a new field called availableInstallments. This field is a list of installment objects, representing all the available installment options for the product.

Creating resolvers

Now that we've defined the structure of our installment information (GraphQL types), let's create the rationale for retrieving and formatting this information using a resolver function. Resolvers essentially act as gateways between your frontend and backend, processing data requests.
In the resolvers folder, create a product.ts file to handle the installment information rationale. Add the following code.
  • productResolver: Defines a resolver for the StoreProduct type.
  • availableInstallments: Retrieves the installment information from the commertialOffer property of the first seller (sellers[0]).
  • installments.length: Checks if there are any installments (installments.length). If not, an empty array is returned.
  • The code iterates through each installment and maps it to a new object with the desired properties (installmentPaymentSystemName, installmentValue, etc.).

Consolidating resolvers

In the resolvers folder, create an index.ts file to consolidate the resolvers.
  • The index.ts file exports resolvers by default, acting as a central hub for all your resolver functions.
  • resolvers:Combines the imported resolver with any other resolvers you might have in your project (represented by the ellipsis ...).

Using fragments

Now that we've defined the availableInstallments field using GraphQL types and resolvers, we need to specify where this data will be used in your storefront queries. This is achieved through GraphQL fragments.
  1. In the src folder of your store code, create a fragments folder.
  2. In the fragments folder, create the following files:
    • ClientProduct.ts: Defines the client-side fragment.
    • ServerProduct.ts: Defines the server-side fragment.
  3. In the ClientProduct.ts file, defines the GraphQL fragment for retrieving installment data.
  • gql: Imported from @faststore/core/api to construct the GraphQL fragment.
  • ClientProduct: Fragment to indicate it extends the product query.
  • The fragment defines the specific fields we want to retrieve from the availableInstallments data structure.

Adding server-side product fragment for installment details

Add a product fragment focusing on installment details to ensure consistency in data retrieval on the client and server sides. In the ServerProduct.ts file, add the following:
For further details on code implementation, see the fragments folder available in the playground.store repository.

Formatting installment values

For a more user-friendly display, you can format the installment values to include currency symbols and proper locale formatting.
  1. In the src folder of your store code, create a utils folder.
  2. Inside utils, create the priceFormatter.ts file and add the following:
  • FaststoreConfig: Imported to access the current store's locale and currency settings.
  • priceFormatter: This function takes a numeric value and formats it according to the store's locale and currency.
For further details on code implementation, see the utils folder available in the playground.store repository.

Creating a new component with installments

Now that we've established the rationale for retrieving installment data, let's create a new component to display this information alongside the buy button on the PDP.
  1. In the src/components folder, create two new folders:
    • BuyButtonWithDetails: Keeps the component for our enhanced buy button with installment details.
    • sections/CustomProductDetails: Keeps the rationale for overriding the default PDP buy button component.
  2. Inside the BuyButtonWithDetails folder, create two new files:
    • BuyButtonWithDetails.tsx
    • buy-button-with-details.module.scss
  3. Inside BuyButtonWithDetails.tsx, add the following code.
  • priceFormatter: Format installment values.
  • styles: Stylesheet for the BuyButtonWithDeatils imported from the .scss file.
  • usePDP: Hook to retrieve the PDP context containing product and extension data.
  • We conditionally display the first installment option if it's interest-free (you can modify this to show all installments).
  • The component renders a section containing the installment information and the buy button.

Creating a stylesheet for the component

Inside buy-button-with-details.module.scss, add the code below. The stylesheet styles the component using SCSS. It defines styles for the installment information container and the buy button itself.

Creating a custom section

By including the CustomProductDetails section in the PDP, you replace the default buy button component with your enhanced BuyButtonWithDetails component, which displays both the buy button and the installment information.
In the CustomProductDetails folder , create the CustomProductDetails.tsx and add the following code.
For further details on code implementation, see the utils folder available in the playground.store repository.

_10
src
_10
â”— đź“‚ graphql
_10
â”— đź“‚vtex
_10
┣ 📂 resolvers
_10
â”— đź“‚ typeDefs

Results

Once you have set your development mode, you can view changes locally by accessing a PDP, such as https://localhost:3000/adidas-mens-performance-polo-green-night-99984111/p, and you will see the installment option for the product.

Before

The PDP before adding the installment:
{"base64":"  ","img":{"width":948,"height":571,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":215607,"url":"https://vtexhelp.vtexassets.com/assets/docs/src/before-installment___c06c92a26445e0dc97a33981f893358e.png"}}

After

After applying the installment option, the information will become available above the Buy Button:
{"base64":"  ","img":{"width":929,"height":676,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":240060,"url":"https://vtexhelp.vtexassets.com/assets/docs/src/after-installment___77227bfcffcf9a25800effe9790100e1.png"}}
Contributors
1
Photo of the contributor
+ 1 contributors
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
1
Photo of the contributor
+ 1 contributors
On this page