For the complete documentation index, see llms.txt. This page is also available as Markdown.

Tabby Payment Gateway

The TabbyPaymentGateway component provides seamless integration with Tabby, enabling installment and deferred payment options directly within your checkout experience. Built for compatibility with the Akinon ProjectZero platform, this extension securely manages customer data and transaction flows while maintaining flexibility for localized currency and language support.

With minimal setup, the component generates the necessary context—including order, user, and historical data—to initiate secure transactions via Tabby. The extension also handles hash-based request validation and supports easy deployment through environment-based configuration.

A single storefront may serve several countries, and Tabby issues a separate merchant installation—its own extension URL and its own hash key—per country. This guide covers both cases: a single installation, and several installations selected by currency. If you operate in more than one country, read Multiple Tabby Installations before you deploy; a single-installation setup will appear to work in staging and then fail for every currency except the default one.

This guide covers installation, configuration, usage, and internal mechanics to help you implement Tabby in your checkout flow with confidence.

Installation Method

You can use the following command to install the extension with the latest plugins:

npx @akinon/projectzero@latest --plugins

Props

Prop
Type
Required
Description

sessionId

string

Yes

The session identifier received from Tabby.

currency

string

Yes

Currency code used in the transaction (e.g., AED, SAR).

locale

string

Yes

Language/locale code used for translations (e.g., en, ar).

extensionUrl

string

Yes

The base URL for the Tabby extension server. The component does not read this from the environment—the page must resolve it and pass it in.

hashKey

string

Yes

Secret hash key used to generate a secure transaction hash. The component does not read this from the environment—the page must resolve it and pass it in.

Environment Variables

Single Installation

One country, one Tabby merchant. Add the following variables to your .env file:

Multiple Installations

One storefront, several countries. Append the uppercase currency code to each variable name:

Usage Example

Create a file in the path matching your routing mode:

Routing mode
Page path

usePzSegment: true (default)

src/app/[pz]/payment-gateway/tabby/page.tsx

Legacy (usePzSegment: false)

src/app/[commerce]/[locale]/[currency]/payment-gateway/tabby/page.tsx

For a single installation, pass the unsuffixed variables straight through:

Two pieces of the platform do the work here:

withSegmentDefaults resolves the Next.js params/searchParams promises and normalises searchParams into a URLSearchParams instance. Without it, searchParams.get is not available.

parsePzParams reads the locale and currency for the request. In pz-segment mode it decodes them out of the pz route segment; in legacy mode it reads params.locale and params.currency directly, falling back to the defaults in settings.js. Because it covers both, the page above is identical in either routing mode—only the file path changes.

Multiple Tabby Installations (Country / Currency Based)

Why This Is Needed?

Tabby onboards merchants per country. A storefront selling in both the UAE and Saudi Arabia holds two Tabby merchant accounts, each with its own extension URL and its own hash key. Because the extension resolves configuration from environment variables, those variables have to be distinguishable per country—otherwise every currency is sent to whichever single installation happens to be configured.

The currency is used as the discriminator, because it is already carried through the request by the platform.

Naming Convention

Append the uppercase currency code to the base variable name:

Country
Currency
Variables

United Arab Emirates

AED

TABBY_EXTENSION_URL_AED TABBY_HASH_KEY_AED

Saudi Arabia

SAR

TABBY_EXTENSION_URL_SAR TABBY_HASH_KEY_SAR

Kuwait

KWD

TABBY_EXTENSION_URL_KWD TABBY_HASH_KEY_KWD

The suffix is always uppercase, regardless of how the currency is written in settings.js or in the URL. The extension uppercases the active currency before building the variable name, so a currency configured as aed resolves to TABBY_EXTENSION_URL_AED.

Resolution Rules

The currency-specific variable is preferred; the unsuffixed variable is the fallback:

Configured
Result for currency AED

TABBY_HASH_KEY_AED only

Uses TABBY_HASH_KEY_AED

TABBY_HASH_KEY only

Uses TABBY_HASH_KEY

Both

Uses TABBY_HASH_KEY_AED

TABBY_HASH_KEY_AED set to an empty value

Falls back to TABBY_HASH_KEY

Neither

Availability endpoint returns HTTP 500

The extension URL follows the same rules. The two are resolved independently, so it is possible—and is a common misconfiguration—to end up with the URL of one installation and the hash key of another.

What Resolves Automatically vs. What Doesn’t

This is the single most important distinction in this page:

Part
Resolves currency-specific env?
Notes

Check Availability API route

Yes, automatically

Reads the pz-currency cookie, uppercases it, and falls back to the unsuffixed variables.

TabbyPaymentGateway component

No

Receives extensionUrl and hashKey as props. Your page must perform the selection.

Resolving the Configuration on Your Page

Add a small helper to your application. It applies the same rules the API route applies—uppercase the currency, prefer the suffixed variable, fall back to the unsuffixed one:

Then use it in the gateway page, taking the currency from parsePzParams:

The currency parsePzParams returns and the pz-currency cookie the API route reads are written from the same resolved value in the same middleware pass, so the page and the availability endpoint always select the same installation. Keeping the fallback operator as ||—not ??—matches the route exactly: an empty value falls through to the unsuffixed variable in both.

Example Environment Files

Single Installation — United Arab Emirates Only

Multiple Installations — UAE & Saudi Arabia

Multiple Installations with a Default

Currencies without their own pair fall back to the unsuffixed values. Use this only when the fallback installation is genuinely correct for every remaining currency:

API Routes

Check Availability API

To enable Tabby payment availability checks, create an API route at src/app/api/tabby-check-availability/route.ts:

This endpoint checks whether Tabby is available for a given order amount, email, phone number and currency. It reads the currency from the pz-currency cookie—set by the platform middleware—and selects the matching installation as described above. It then validates both the outgoing request and the incoming response with hash-based security measures.

Responses:

Status
Body
Meaning

200

{ salt, hash, is_available }

Availability resolved and the response hash verified.

400

Currency not found in cookies

The pz-currency cookie is absent.

400

Missing required fields

One of amount, phone, email, name is missing.

400

Invalid response hash

The extension server signed its reply with a different hash key.

500

TABBY_HASH_KEY environment variable is not set

Neither the currency-specific nor the fallback key is defined.

500

TABBY_EXTENSION_URL environment variable is not set

Neither the currency-specific nor the fallback URL is defined.

The 500 messages name the unsuffixed variables even when a suffixed one was expected. Read them as "no value could be resolved for the active currency", not as "define TABBY_HASH_KEY".

Using checkTabbyAvailability Mutation

The extension provides a Redux mutation hook for availability checks:

The mutation returns:

  • is_available: boolean indicating whether Tabby payment is available

  • salt: string used for hash verification

  • hash: string for response validation

The currency is not a parameter—it is taken from the cookie on the server. Sending a different currency in the request body has no effect on which installation is consulted.

Context Object (Auto-generated Internally)

The TabbyPaymentGateway component internally generates a context object using:

  • preOrder data from the current checkout session

  • userProfile and wishlist details

  • Historical orders and previous purchases

This context includes:

This object is passed to the FormComponent for completing the payment via Tabby. The commerce requests that build it are sent with an X-Currency header taken from the currency prop, so passing a currency that does not match the resolved installation produces a context describing one country and a signature belonging to another.

Hash Reference

All hashes are SHA-512 over the values joined by |. Knowing the inputs makes a mismatch straightforward to diagnose:

Where
Hashed value

Payment gateway form

salt | sessionId | hashKey

Availability request

salt | amount | email | phone | hashKey

Availability response check

salt | "True" or "False" | hashKey

The hash key is the only secret in each of these. If a hash is rejected and the other inputs are demonstrably correct, the key belongs to a different installation.

Troubleshooting

Symptom
Likely cause
Resolution

Availability check succeeds, but the payment form is rejected by the extension server

The API route resolved the currency-specific installation while the gateway page passed the unsuffixed one.

Resolve extensionUrl and hashKey in the page as shown in Resolving the configuration in your page.

Customer is redirected to the wrong country's Tabby checkout

TABBY_EXTENSION_URL_<CURRENCY> is missing for that currency, so the fallback URL was used.

Define the pair for every currency you sell in, or confirm the fallback is correct for the remainder.

HTTP 400 Invalid response hash

The hash key does not match the installation the request was sent to.

Confirm the URL and key come from the same installation. A URL/key pair split across two installations produces exactly this.

HTTP 400 Currency not found in cookies

The pz-currency cookie was not set on the request.

Confirm the request passes through the platform middleware and that the storefront was reached on a currency-bearing route.

HTTP 500 ... environment variable is not set

Neither the suffixed nor the unsuffixed variable is defined for the active currency.

Add the pair for that currency, or an unsuffixed fallback pair.

The suffixed variable is defined but appears to be ignored

The suffix does not match the uppercased currency, or the value is an empty string.

Match the suffix to the uppercase currency code exactly, and give it a non-empty value—an empty value falls through to the fallback.

The gateway page renders nothing

sessionId, currency or locale is missing.

The component returns an empty fragment when any of the three is absent. Verify the query string and the routing mode.

Environment variable changes have no effect

The values are read at request time on the server, but the deployment was not restarted.

Restart the application after changing environment variables.

Last updated

Was this helpful?