Command Palette

Search for a command to run...

Integrate T-Kassa Payment to Storefront

To integrate the T-Kassa payment provider in a Next.js storefront, start by adding the required UI components. So, the provider is displayed on the checkout page alongside other available payment methods.

When T-Kassa is selected, the storefront should call with the necessary parameters. This will create a payment session through the T-Kassa API and prepare the customer for redirection. The Place Order button should then send the customer to the T-Kassa payment page, where he can select his preferred payment method.

Once the payment is completed, T-Kassa will concurrently send a webhook and redirect the customer back to the storefront. Whichever arrives first will complete the cart and create a new order in Medusa.

For the Next.js start you need to make the following changes:

1. Payment Provider Configuration

To make T-Kassa available as a payment method on the storefront checkout page, you must add its configuration to the payment provider mapping in your storefront’s constants file. This mapping determines how each payment provider is displayed in the UI.

Open and add the following:

Directory structure in the Medusa Storefront after updating the file for constants

export const paymentInfoMap: Record<
string,
{ title: string; icon: React.ReactNode }
> = {
// ... other providers
pp_tkassa_tkassa: {
title: "T-Kassa",
icon: <CreditCard />,
},
}
// Helper to check if a provider is T-Kassa
export const isTkassa = (providerId?: string) => {
return providerId?.startsWith("pp_tkassa")
}

You extend the object to include a entry. This entry defines the title and the icon that will be shown for T-Kassa on the checkout page.

The helper function checks whether a given belongs to T-Kassa. This is useful when rendering provider-specific UI-components.

When integrating T-Kassa, you need to adjust your cookie policy to allow cross-domain payment redirects. Some payment providers require more permissive cookie settings so the payment session can be preserved when the customer is redirected back to the storefront.

Open and update the cookie configuration as follows:

Directory structure in the Medusa Storefront after updating the file for cookies

export const setCartId = async (cartId: string) => {
cookies.set("_medusa_cart_id", cartId, {
// ... other cookie settings
sameSite: "lax", // Changed from "strict" for payment redirects
})
}

This helper function stores the cart ID in a cookie named .

The option is set to instead of . This change ensures the cookie is sent with cross-site requests during the T-Kassa redirect flow, preventing the payment session from being lost.

3. Payment Session Initialization

To redirect a customer to T-Kassa, the payment session must be properly initialized with the required parameters, including the return URLs for both success and failure outcomes.

Open and update the payment initialization logic to include T-Kassa’s redirect URLs:

Directory structure in the Medusa Storefront after updating the file for payment component

await initiatePaymentSession(cart, {
provider_id: selectedPaymentMethod,
data: {
SuccessURL: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`,
FailURL: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`,
cart: cart,
}
})

When initiating the payment session for T-Kassa, the and parameters define where the customer will be redirected after attempting payment. Both URLs are dynamically constructed using the storefront’s base URL, the cart ID, and the selected country code.

The object is included in the initialization data to build a receipt in accordance with Federal Law No. 54.

4. Payment Button Component

Medusa storefront requires a dedicated payment button component for each payment provider to handle the checkout flow after the customer confirms his order. This component leverages the payment session data and navigates the customer to the T-Kassa payment page.

Open and add the following code:

Directory structure in the Medusa Storefront after updating the file for payment button component

const PaymentButton: React.FC<PaymentButtonProps> = ({
cart,
"data-testid": dataTestId,
}) => {
// ...
switch (true) {
// ... other cases
case isTkassa(paymentSession?.provider_id):
return (
<TkassaPaymentButton
notReady={notReady}
cart={cart}
data-testid={dataTestId}
/>
)
// ... other cases
}
}
// ... other payment button's components
type TkassaPaymentProps = {
cart: HttpTypes.StoreCart
notReady: boolean
"data-testid"?: string
}
const TkassaPaymentButton: React.FC<TkassaPaymentProps> = ({
cart,
notReady,
"data-testid": dataTestId,
}) => {
const [submitting, setSubmitting] = useState(false)
const [errorMessage, setErrorMessage] = useState<string | null>(null)
const router = useRouter()
const paymentSession = cart.payment_collection?.payment_sessions?.find(
session =>
session.provider_id === "pp_tkassa_tkassa"
)
const handlePayment = () => {
setSubmitting(true)
const paymentUrl = (paymentSession?.data as any).PaymentURL
if (paymentUrl) {
router.push(paymentUrl)
} else {
setErrorMessage("Payment URL отсутствует")
setSubmitting(false)
}
}
return (
<>
<Button
disabled={notReady}
isLoading={submitting}
onClick={handlePayment}
data-testid={dataTestId}
size="large"
>
Place order
</Button>
<ErrorMessage
error={errorMessage}
data-testid="tkassa-payment-error-message"
/>
</>
)
}

This component locates the for T-Kassa in the current cart and retrieves the provided by the backend. When the Place order button is clicked, the customer is redirected to this URL to complete the transaction on the T-Kassa payment page.

If the is missing, the component displays an error message without proceeding. The state provides visual feedback while the redirection is being prepared.

The component determines whether the current payment session belongs to T-Kassa by using the helper function. If it does, it renders the to handle the payment flow.

Integrating this component ensures that T-Kassa’s payment process is seamlessly triggered from the checkout flow.

5. Payment Capture API Route

After the customer completes payment on the T-Kassa page, he is redirected back to the storefront. You need an API route to handle this callback, verify the payment status, and complete the cart.

Create the file with the following content:

Directory structure in the Medusa Storefront after creating the file for API route

import { NextRequest, NextResponse } from "next/server"
import { revalidateTag } from "next/cache"
import {
getCacheTag,
getAuthHeaders,
removeCartId
} from "@lib/data/cookies"
import { sdk } from "@lib/config"
import { placeOrder } from "@lib/data/cart"
type Params = Promise<{ cartId: string }>
export async function GET(req: NextRequest, { params }: { params: Params }) {
const { cartId } = await params
const { origin, searchParams } = req.nextUrl
const countryCode = searchParams.get("country_code") || ""
const headers = { ...(await getAuthHeaders()) }
// Retreive fresh cart values
const cartCacheTag = await getCacheTag("carts")
revalidateTag(cartCacheTag)
const { cart } = await sdk.store.cart.retrieve(cartId, {
fields: "id, order_link.order_id"
},
headers
)
if (!cart) {
return NextResponse.redirect(`${origin}/${countryCode}`)
}
const orderId = (cart as unknown as Record<string, any>).order_link?.order_id
if (!orderId) {
await placeOrder(cartId)
// Fail when payment not authorized
return NextResponse.redirect(
`${origin}/${countryCode}/checkout?step=review&error=payment_failed`
)
}
const orderCacheTag = await getCacheTag("orders")
revalidateTag(orderCacheTag)
removeCartId()
return NextResponse.redirect(
`${origin}/${countryCode}/order/${orderId}/confirmed`
)
}

This API route handles the redirect from T-Kassa after a payment attempt. It retrieves the latest state of the cart to ensure any updates made during payment are reflected.

If the cart does not contain an associated order ID, the route tries to place an order. If successful, the customer is redirected to the order confirmation page. If any error happens during cart completion, the customer is redirected back to the checkout page indicating an error, and he can proceed the checkout once again.

When the payment is successful, the route revalidates the cached cart and order data, removes the cart cookie, and redirects the customer to the order confirmation page. This ensures a consistent post-payment experience while keeping storefront data up to date.

Example

You can refer to the modifications made in the Medusa Next.js Starter Template, which are located in the directory.

The complete integration diff can be viewed in the comparison page, open the "Files changed" tab, and explore the differences under the directory. Or run diff in the terminal:

git clone https://github.com/gorgojs/medusa-plugins
cd medusa-plugins
git diff @gorgo/medusa-payment-tkassa@0.0.1...main -- examples/payment-tkassa/medusa-storefront