Tingg Class

The main Tingg provider class for processing payments across multiple African countries.

Constructor

new Tingg(config: TinggConfig)

Parameters

config
TinggConfig
required
Configuration object for the Tingg provider

TinggConfig Interface

interface TinggConfig {
  apiKey: string;
  publicKey: string;
  serviceCode: string;
  paymentOptionCode: string;
  timeout?: number;
  retries?: number;
  retryDelay?: number;
  logger?: Logger;
}

Configuration Properties

apiKey
string
required
Your Tingg API key (client secret) for OAuth authentication
publicKey
string
required
Your Tingg public key (client ID) for OAuth authentication
serviceCode
string
required
The service code assigned to your merchant account on the Tingg portal
paymentOptionCode
string
required
The payer client code for the mobile money operator (e.g., ‘SAFKE’, ‘MTNKE’, ‘AIRKE’)
timeout
number
default:"30000"
Request timeout in milliseconds
retries
number
default:"3"
Number of retry attempts for failed requests
retryDelay
number
default:"1000"
Delay between retry attempts in milliseconds
logger
Logger
Custom logger instance for debugging and monitoring

Methods

collection()

Initiates a payment collection request.
async collection(
  tx: Transaction,
  environment?: 'sandbox' | 'production',
  user?: string,
  fundkitApiKey?: string
): Promise<CollectionResult>

Parameters

tx
Transaction
required
Transaction details including amount, currency, operator, and account number
environment
'sandbox' | 'production'
default:"production"
Environment mode for the transaction
user
string
default:"anonymous"
User identifier for the transaction
fundkitApiKey
string
FundKit API key for sandbox routing

Returns

returns
CollectionResult
Result object containing transaction details and status

Example

const tingg = new Tingg({
  apiKey: 'your_api_key',
  publicKey: 'your_public_key',
  serviceCode: 'YOUR_SERVICE_CODE',
  paymentOptionCode: 'SAFKE',
});

const transaction = {
  amount: 10000,
  currency: 'KES',
  operator: 'safaricom',
  accountNumber: '254701234567',
  externalId: 'order_123',
  reason: 'Product purchase',
};

try {
  const result = await tingg.collection(transaction);
  console.log('Payment initiated:', result.data.transactionId);
} catch (error) {
  console.error('Payment failed:', error.message);
}

getTransaction()

Retrieves the status of a transaction.
async getTransaction(
  tx: string,
  environment?: 'sandbox' | 'production',
  fundkitApiKey?: string
): Promise<TransactionResult>

Parameters

tx
string
required
Transaction ID to query
environment
'sandbox' | 'production'
default:"production"
Environment mode for the query
fundkitApiKey
string
FundKit API key for sandbox routing

Returns

returns
TransactionResult
Transaction details including status, amount, and metadata

Example

try {
  const transaction = await tingg.getTransaction('tx_123456789');
  console.log('Transaction status:', transaction.data.status);
  console.log('Amount:', transaction.data.amount);
} catch (error) {
  console.error('Failed to get transaction:', error.message);
}

getCoverage()

Returns the coverage map for supported countries and currencies.
getCoverage(): CoverageMap

Returns

returns
CoverageMap
Coverage information including supported countries, currencies, and transaction limits

Example

const coverage = tingg.getCoverage();
console.log('Supported currencies:', Object.keys(coverage));
console.log('Kenya limits:', coverage.KES);

Types

TinggCollectionRequest

interface TinggCollectionRequest {
  charge_msisdn: string;
  charge_amount: number;
  country_code: string;
  currency_code: string;
  merchant_transaction_id: string;
  service_code: string;
  payment_mode_code: string;
  payment_option_code: string;
}

TinggCollectionResponse

interface TinggCollectionResponse {
  status: {
    status_code: number;
    status_description: string;
  };
  results: {
    checkout_request_id: string;
    merchant_transaction_id: string;
    charge_request_id: number;
    payment_instructions: string;
    language_code: string;
    charge_msisdn: number;
    charge_amount: number;
    charge_request_date: string;
  };
}

TinggTransactionStatus

interface TinggTransactionStatus {
  status: {
    status_code: string;
    status_description: string;
  };
  results: {
    checkout_request_id: number;
    merchant_receipt: string;
    payments: Array<{
      id: number;
      msisdn: number;
      payment_option_transaction_id: string;
      account_number: string;
      customer_name: string;
      amount_paid: number;
      gateway_transaction_id: string;
      date_payment_received: string;
      gateway_overall_status: number;
      currency_id: string;
      country_id: string;
      service_id: string;
      payer_narration: string;
      checkout_request_id: number;
      merchant_receipt: string;
      receiver_narration: string;
      date_payment_acknowledged: string;
      created_at: string;
      payment_option_id: number;
    }>;
  };
}

Status Codes

Transaction Status Codes

Tingg uses specific status codes to indicate transaction states:

130

New Request - Transaction has been created and is waiting for customer action

178

Full Payment - Complete payment received and processed

183

Accepted - Payment received and acknowledged by merchant

188

Received - Payment received, awaiting merchant acknowledgment

99

Failed Payment - Payment failed but request remains open

129

Expired - Request expired with no payment received

179

Expired with Partial - Request expired with partial payments (will be reversed)

180

Rejected - Payment received but rejected by merchant

176

Partial Payment - Partial payment received and marked as closed

184

Refund Initiated - Partial refund initiated

185

Full Refund Initiated - Full refund initiated

186

Partial Refund Complete - Partial refund successfully processed

187

Full Refund Complete - Full refund successfully processed

191

Refund Expired - Refund request has expired

422

Invalid Request - Request contains invalid data

HTTP Status Codes

200

Success - Request processed successfully

400

Bad Request - Invalid request parameters

401

Unauthorized - Invalid or expired authentication

403

Forbidden - Access denied or insufficient permissions

404

Not Found - Resource not found

422

Unprocessable Entity - Request validation failed

500

Internal Server Error - Tingg system error

1001

No Request Found - Transaction ID not found

1007

Missing Country Code - Country code not provided

1013

Not JSON - Request body is not valid JSON

1014

Missing Transaction ID - Merchant transaction ID not provided

1015

Missing Checkout ID - Checkout request ID not provided

1017

Invalid Phone - Phone number format is invalid

1027

Invalid Amount - Amount outside allowed limits

Error Handling

TinggError Class

class TinggError extends Error {
  code: string;
  status: number;
  details?: any;
  provider: string;
  endpoint: string;
}

Common Error Codes

TINGG_INSUFFICIENT_FUNDS - Customer account has insufficient balance TINGG_CUSTOMER_CANCELLED - Customer declined the payment TINGG_PAYMENT_TIMEOUT - Customer didn’t respond in time TINGG_INVALID_PIN - Customer entered incorrect PIN
TINGG_INVALID_PHONE - Phone number format is incorrect TINGG_PHONE_NOT_REGISTERED - Phone not registered for mobile money TINGG_ACCOUNT_SUSPENDED - Customer account is suspended TINGG_DAILY_LIMIT_EXCEEDED - Customer exceeded daily limits
TINGG_NETWORK_ERROR - Mobile network unavailable TINGG_SERVICE_UNAVAILABLE - Tingg service temporarily down TINGG_RATE_LIMITED - Too many requests TINGG_AUTHENTICATION_FAILED - Invalid API credentials
TINGG_SERVICE_CODE_REQUIRED - Service code not provided TINGG_PAYMENT_OPTION_REQUIRED - Payment option code not provided TINGG_INVALID_SERVICE_CODE - Service code is invalid TINGG_INVALID_PAYMENT_OPTION - Payment option code is invalid

Examples

Basic Payment Collection

import { Tingg } from '@fundkit/tingg';

const tingg = new Tingg({
  apiKey: process.env.TINGG_API_KEY!,
  publicKey: process.env.TINGG_PUBLIC_KEY!,
  serviceCode: process.env.TINGG_SERVICE_CODE!,
  paymentOptionCode: 'SAFKE', // Safaricom Kenya
});

const transaction = {
  amount: 5000,
  currency: 'KES',
  operator: 'safaricom',
  accountNumber: '254701234567',
  externalId: 'order_12345',
  reason: 'Product purchase',
};

try {
  const result = await tingg.collection(transaction);
  console.log('Payment initiated:', result.data.transactionId);
} catch (error) {
  console.error('Payment failed:', error.message);
}

Multi-Country Payment Processing

const countries = [
  { code: 'KE', currency: 'KES', operator: 'safaricom', paymentOption: 'SAFKE' },
  { code: 'UG', currency: 'UGX', operator: 'mtn', paymentOption: 'MTNUG' },
  { code: 'TZ', currency: 'TZS', operator: 'vodacom', paymentOption: 'SAFTZ' },
];

async function processMultiCountryPayment(country: any, amount: number, phone: string) {
  const tingg = new Tingg({
    apiKey: process.env.TINGG_API_KEY!,
    publicKey: process.env.TINGG_PUBLIC_KEY!,
    serviceCode: process.env.TINGG_SERVICE_CODE!,
    paymentOptionCode: country.paymentOption,
  });

  const transaction = {
    amount: amount,
    currency: country.currency,
    operator: country.operator,
    accountNumber: phone,
    externalId: `payment_${country.code}_${Date.now()}`,
    reason: `Payment for ${country.code}`,
  };

  return await tingg.collection(transaction);
}

Transaction Status Monitoring

async function monitorTransaction(transactionId: string) {
  const tingg = new Tingg({
    apiKey: process.env.TINGG_API_KEY!,
    publicKey: process.env.TINGG_PUBLIC_KEY!,
    serviceCode: process.env.TINGG_SERVICE_CODE!,
    paymentOptionCode: 'SAFKE',
  });

  const maxAttempts = 10;
  const interval = 5000; // 5 seconds

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const transaction = await tingg.getTransaction(transactionId);
      const status = transaction.data.status;

      console.log(`Attempt ${attempt}: Status = ${status}`);

      if (status === 'SUCCESS' || status === 'FAILED' || status === 'EXPIRED') {
        return transaction;
      }

      if (attempt < maxAttempts) {
        await new Promise(resolve => setTimeout(resolve, interval));
      }
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message);
      if (attempt === maxAttempts) throw error;
    }
  }

  throw new Error('Transaction monitoring timeout');
}

Error Handling with Retry Logic

async function collectionWithRetry(transaction: Transaction, maxRetries: number = 3) {
  const tingg = new Tingg({
    apiKey: process.env.TINGG_API_KEY!,
    publicKey: process.env.TINGG_PUBLIC_KEY!,
    serviceCode: process.env.TINGG_SERVICE_CODE!,
    paymentOptionCode: 'SAFKE',
  });

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await tingg.collection(transaction);
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message);

      // Don't retry configuration errors
      if (
        error.code === 'TINGG_SERVICE_CODE_REQUIRED' ||
        error.code === 'TINGG_PAYMENT_OPTION_REQUIRED'
      ) {
        throw error;
      }

      // Don't retry validation errors
      if (error.code === 'TINGG_INVALID_PHONE' || error.code === 'TINGG_INVALID_AMOUNT') {
        throw error;
      }

      if (attempt === maxRetries) {
        throw error;
      }

      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
}