> ## Documentation Index
> Fetch the complete documentation index at: https://paystack-sdk.efobi.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Transactions Guide

> Learn how to accept and manage payments with the Paystack TypeScript SDK

The Transaction API allows you to accept payments from customers using various payment channels. This guide covers common transaction scenarios and best practices.

## Overview

Transactions represent payment attempts from your customers. The typical flow involves:

1. Initializing a transaction to get a payment URL
2. Redirecting customers to complete payment
3. Verifying the transaction after payment
4. Handling the response

## Initialize a Transaction

Create a payment session and get an authorization URL for your customer.

<Steps>
  <Step title="Import and initialize the SDK">
    ```typescript theme={null}
    import { Paystack } from '@efobi/paystack';

    const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);
    ```
  </Step>

  <Step title="Initialize the transaction">
    ```typescript theme={null}
    const { data, error } = await paystack.transaction.initialize({
      email: 'customer@email.com',
      amount: '50000', // Amount in kobo (₦500.00)
      currency: 'NGN',
      reference: `TXN-${Date.now()}`, // Optional: unique reference
      callback_url: 'https://yoursite.com/verify'
    });

    if (error) {
      console.error('Initialization failed:', error);
      return;
    }

    // Redirect customer to authorization_url
    console.log('Payment URL:', data.data.authorization_url);
    console.log('Access Code:', data.data.access_code);
    console.log('Reference:', data.data.reference);
    ```
  </Step>

  <Step title="Redirect the customer">
    ```typescript theme={null}
    // In a web application
    window.location.href = data.data.authorization_url;

    // Or return the URL to your frontend
    return Response.json({ 
      paymentUrl: data.data.authorization_url,
      reference: data.data.reference 
    });
    ```
  </Step>
</Steps>

### Initialize Parameters

<ParamField path="email" type="string" required>
  Customer's email address
</ParamField>

<ParamField path="amount" type="string" required>
  Amount to charge in kobo (multiply by 100 for naira)
</ParamField>

<ParamField path="currency" type="string" default="NGN">
  Currency code: `NGN`, `USD`, `GHS`, `ZAR`, `KES`, or `XOF`
</ParamField>

<ParamField path="reference" type="string">
  Unique transaction reference (auto-generated if not provided)
</ParamField>

<ParamField path="callback_url" type="string">
  URL to redirect customer after payment
</ParamField>

<ParamField path="channels" type="array">
  Payment channels to enable: `card`, `bank`, `ussd`, `qr`, `mobile_money`, `bank_transfer`, `eft`, `apple_pay`
</ParamField>

<ParamField path="metadata" type="object">
  Custom data to store with the transaction
</ParamField>

<ParamField path="split_code" type="string">
  Split payment configuration code
</ParamField>

## Verify a Transaction

After payment, verify the transaction status using the reference.

```typescript theme={null}
const { data, error } = await paystack.transaction.verify(reference);

if (error) {
  console.error('Verification failed:', error);
  return;
}

if (data.data.status === 'success') {
  console.log('Payment successful!');
  console.log('Amount paid:', data.data.amount / 100);
  console.log('Customer:', data.data.customer.email);
  console.log('Reference:', data.data.reference);
  
  // Grant access to product/service
  await fulfillOrder(data.data);
} else {
  console.log('Payment status:', data.data.status);
}
```

<Note>
  Always verify transactions on your server before fulfilling orders. Never rely solely on frontend confirmations or webhooks without verification.
</Note>

## List Transactions

Retrieve a paginated list of transactions on your integration.

```typescript theme={null}
const { data, error } = await paystack.transaction.list({
  perPage: 50,
  page: 1,
  status: 'success',
  from: '2024-01-01',
  to: '2024-12-31'
});

if (data) {
  console.log('Total transactions:', data.meta.total);
  
  data.data.forEach(transaction => {
    console.log(`${transaction.reference}: ₦${transaction.amount / 100}`);
  });
}
```

### List Parameters

<ParamField path="perPage" type="number" default="50">
  Number of transactions per page (max: 100)
</ParamField>

<ParamField path="page" type="number" default="1">
  Page number to retrieve
</ParamField>

<ParamField path="status" type="string">
  Filter by status: `success`, `failed`, `abandoned`
</ParamField>

<ParamField path="customer" type="string">
  Filter by customer ID
</ParamField>

<ParamField path="from" type="string">
  Start date (ISO 8601 format)
</ParamField>

<ParamField path="to" type="string">
  End date (ISO 8601 format)
</ParamField>

## Get Single Transaction

Fetch details of a specific transaction by ID.

```typescript theme={null}
const transactionId = 12345678;

const { data, error } = await paystack.transaction.getTransactionById(
  transactionId
);

if (data) {
  console.log('Transaction details:', {
    reference: data.data.reference,
    amount: data.data.amount / 100,
    status: data.data.status,
    channel: data.data.channel,
    paidAt: data.data.paidAt
  });
}
```

## Charge Authorization

Charge a customer using a previously authorized payment method.

<Warning>
  Only use this for authorized recurring charges. Ensure you have the customer's permission.
</Warning>

```typescript theme={null}
const { data, error } = await paystack.transaction.chargeAuthorization({
  authorization_code: 'AUTH_xxxxxxxxx',
  email: 'customer@email.com',
  amount: 50000, // Amount in kobo
  currency: 'NGN',
  reference: `REC-${Date.now()}`,
  metadata: {
    subscription_id: 'sub_123',
    billing_period: 'monthly'
  }
});

if (data && data.data.status === 'success') {
  console.log('Charge successful!');
  console.log('Transaction ID:', data.data.id);
  console.log('Amount charged:', data.data.amount / 100);
} else {
  console.log('Charge failed:', data?.data.gateway_response);
}
```

<Tip>
  You can get the `authorization_code` from successful transactions where `authorization.reusable` is `true`.
</Tip>

## Transaction Timeline

View the event history of a transaction.

```typescript theme={null}
const { data, error } = await paystack.transaction.viewTxnTimeline(
  'transaction_reference'
);

if (data) {
  console.log('Timeline:', data.data.history);
  data.data.history.forEach(event => {
    console.log(`${event.type}: ${event.message}`);
  });
}
```

## Transaction Totals

Get total volume of transactions on your integration.

```typescript theme={null}
const { data, error } = await paystack.transaction.getTxnTotals({
  perPage: 1,
  page: 1
});

if (data) {
  console.log('Total transactions:', data.data.total_transactions);
  console.log('Total volume:', data.data.total_volume);
  
  data.data.total_volume_by_currency.forEach(volume => {
    console.log(`${volume.currency}: ${volume.amount / 100}`);
  });
}
```

## Export Transactions

Export transactions to a CSV file.

```typescript theme={null}
const { data, error } = await paystack.transaction.exportTxns({
  perPage: 50,
  page: 1,
  from: '2024-01-01',
  to: '2024-12-31'
});

if (data) {
  console.log('Export path:', data.data.path);
  console.log('Expires at:', data.data.expiresAt);
  
  // Download the CSV file
  const response = await fetch(data.data.path);
  const csv = await response.text();
}
```

## Partial Debit

Retrieve part of a payment from a customer.

<Note>
  This feature is typically used for split payments where the full amount isn't needed upfront.
</Note>

```typescript theme={null}
const { data, error } = await paystack.transaction.partialDebit({
  authorization_code: 'AUTH_xxxxxxxxx',
  email: 'customer@email.com',
  amount: 25000, // Partial amount in kobo
  currency: 'NGN',
  reference: `PART-${Date.now()}`
});

if (data && data.data.status === 'success') {
  console.log('Partial debit successful!');
  console.log('Amount debited:', data.data.amount / 100);
  console.log('Requested amount:', data.data.requested_amount / 100);
}
```

## Error Handling

Handle transaction errors gracefully:

```typescript theme={null}
try {
  const { data, error } = await paystack.transaction.initialize({
    email: 'customer@email.com',
    amount: '50000'
  });

  if (error) {
    // Validation error from Zod
    console.error('Validation error:', error.flatten());
    return { success: false, message: 'Invalid transaction data' };
  }

  if (!data.status) {
    // API error response
    console.error('API error:', data.message);
    return { success: false, message: data.message };
  }

  return { success: true, data: data.data };

} catch (err) {
  // Network or unexpected error
  console.error('Unexpected error:', err);
  return { success: false, message: 'Something went wrong' };
}
```

## Best Practices

<Tip>
  1. **Always verify transactions** on your server after payment
  2. **Store transaction references** in your database for reconciliation
  3. **Use unique references** for each transaction to prevent duplicates
  4. **Handle webhooks** for real-time payment notifications
  5. **Log all transactions** for audit trails and debugging
  6. **Test with test keys** before going live
  7. **Implement proper error handling** at every step
</Tip>

## Common Scenarios

### One-time Payment

```typescript theme={null}
async function processPayment(customerEmail: string, amount: number) {
  // Step 1: Initialize transaction
  const { data, error } = await paystack.transaction.initialize({
    email: customerEmail,
    amount: (amount * 100).toString(), // Convert to kobo
    callback_url: `${process.env.APP_URL}/verify`
  });

  if (error || !data.status) {
    throw new Error('Failed to initialize payment');
  }

  // Step 2: Store reference in database
  await db.transactions.create({
    reference: data.data.reference,
    email: customerEmail,
    amount: amount,
    status: 'pending'
  });

  // Step 3: Return payment URL
  return data.data.authorization_url;
}

// After customer pays and returns
async function verifyPayment(reference: string) {
  const { data, error } = await paystack.transaction.verify(reference);

  if (error) {
    throw new Error('Verification failed');
  }

  // Update database
  await db.transactions.update(
    { reference },
    { 
      status: data.data.status,
      paidAt: data.data.paidAt
    }
  );

  if (data.data.status === 'success') {
    // Fulfill order
    await fulfillOrder(reference);
  }

  return data.data;
}
```

### Recurring Charges

```typescript theme={null}
async function chargeSubscription(subscription: Subscription) {
  const { data, error } = await paystack.transaction.chargeAuthorization({
    authorization_code: subscription.authorizationCode,
    email: subscription.customerEmail,
    amount: subscription.amount * 100,
    reference: `SUB-${subscription.id}-${Date.now()}`,
    metadata: {
      subscription_id: subscription.id,
      billing_period: subscription.currentPeriod
    }
  });

  if (error) {
    // Handle payment failure
    await notifyCustomer(subscription, 'payment_failed');
    return false;
  }

  if (data.data.status === 'success') {
    await recordPayment(subscription, data.data);
    await extendSubscription(subscription);
    return true;
  }

  return false;
}
```

## Related Resources

* [Webhooks Guide](/guides/webhooks) - Handle real-time payment events
* [Transaction API Reference](/api-reference/transaction) - Full API documentation
* [Split API Reference](/api-reference/split) - Split revenue between accounts
