Payment

Tap

Tap Payments provider — charges, 3DS redirects, refunds, and per-transaction webhooks for the MENA region.

The @commercejs/payment-tap package implements the PaymentProvider interface for Tap Payments, the leading payment gateway in the MENA region.

Installation

pnpm add @commercejs/payment-tap

Configuration

import { TapPaymentProvider } from '@commercejs/payment-tap'

const provider = new TapPaymentProvider({
  secretKey: 'sk_test_xxx',
  baseUrl: 'https://api.tap.company/v2',
  merchantId: '1632424', // optional
})
OptionTypeRequiredDescription
secretKeystringYesTap secret API key
baseUrlstringNoAPI base URL (default: https://api.tap.company/v2)
merchantIdstringNoTap merchant ID for multi-merchant setups

Creating a Charge

The createSession method creates a Tap charge and returns a PaymentSession:

const session = await provider.createSession({
  amount: 99.999,
  currency: 'BHD',
  sourceToken: 'tok_xxx',        // From Tap Card SDK tokenization
  returnUrl: 'https://myapp.com/confirm',
  webhookUrl: 'https://myapp.com/api/webhooks/tap',
  customer: {
    email: 'ahmed@example.com',
    firstName: 'Ahmed',
    lastName: 'Al-Rashid',
    phone: '+97312345678',
  },
  orderId: 'order-001',
})

The webhookUrl maps to Tap's post.url field — Tap sends charge results to this URL asynchronously.

Most card payments return a redirectUrl for 3DS verification. Redirect the customer to this URL, and Tap will redirect back to your returnUrl after verification.

Confirming a Charge

After 3DS redirect, confirm the payment by charge ID:

const confirmed = await provider.confirmSession('chg_abc123')
// confirmed.status === 'captured' | 'failed' | 'cancelled'

Tap Status Mapping

The provider maps Tap's charge statuses to PaymentSessionStatus:

Tap StatusPaymentSessionStatus
INITIATEDpending
IN_PROGRESSprocessing
CAPTUREDcaptured
FAILEDfailed
DECLINEDfailed
CANCELLEDcancelled
ABANDONEDcancelled
TIMEDOUTfailed
VOIDcancelled

Webhook Handling

Tap sends charge results to the post.url specified in the charge request. Use @commercejs/webhook-verifier to verify these events:

import { WebhookVerifier } from '@commercejs/webhook-verifier'
import { tap as tapConfig } from '@commercejs/webhook-verifier/configs'

const verifier = new WebhookVerifier({
  ...tapConfig,
  secretKey: 'sk_test_xxx',
})

const result = verifier.verify(body, headers)
if (result.isValid) {
  // Process the charge update
}
See the webhook-verifier package for detailed verification documentation.

Saved Card Support

The createSession response includes a savedCard field when a card was used for payment. This data can be stored for returning customers:

const session = await provider.createSession({ ... })

if (session.providerData?.savedCard) {
  const card = session.providerData.savedCard
  // { id, last4, brand, expMonth, expYear, ... }
}

The TapSavedCard type is exported for working with saved card data:

import type { TapSavedCard } from '@commercejs/payment-tap'

Multi-Merchant Support

For marketplace scenarios where each merchant has their own Tap account, create a provider instance per merchant:

function getProviderForMerchant(merchantId: string): TapPaymentProvider {
  const config = getMerchantConfig(merchantId)
  return new TapPaymentProvider({
    secretKey: config.tapSecretKey,
    merchantId: config.tapMerchantId,
  })
}

Supported Currencies

Tap supports currencies across the MENA region:

CurrencyCodeDecimals
Bahraini DinarBHD3
Saudi RiyalSAR2
Kuwaiti DinarKWD3
UAE DirhamAED2
Omani RialOMR3
Qatari RiyalQAR2
Egyptian PoundEGP2
US DollarUSD2