> ## Documentation Index
> Fetch the complete documentation index at: https://developer.novacpayment.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter

> Install and use the Novac Flutter plugin to accept payments seamlessly in your Flutter apps.

## Overview

The Novac Flutter plugin is the official plugin for the Novac Payment Gateway. It enables you to accept payments directly within your Flutter apps with support for cards, bank transfers, and mobile money — all with a customizable, secure checkout UI.

***

### Prerequisites

<Accordion title="See details" defaultOpen={true}>
  Before you begin, ensure that you've completed the following steps:

  * [Obtain your API keys](/docs/getting-started/obtain-api-keys): required for making authenticated API calls.
  * A Flutter project with a minimum iOS version of **12.0** and minimum Android SDK version of **21**
  * A Novac account with dashboard access
</Accordion>

### Installation

<Steps>
  <Step title="Add the Dependency">
    Add the plugin to your `pubspec.yaml` file:

    ```yaml theme={null}
        dependencies:
          novac_payment_plugin: ^1.0.0
    ```
  </Step>

  <Step title="Install the Package">
    Run the following command in your terminal to fetch the package:

    ```bash theme={null}
        flutter pub get
    ```
  </Step>
</Steps>

***

## Platform Setup

Because the SDK uses a webview for checkout, both iOS and Android need a URL scheme registered so that users are redirected back to your app after payment completes.

### iOS

Add the following to your `ios/Runner/Info.plist`. This registers a custom URL scheme that the payment gateway uses to deep link back into your app:

```xml theme={null}
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourappscheme</string>
    </array>
  </dict>
</array>
```

### Android

Add the following intent filter inside the relevant `<activity>` block in your `android/app/src/main/AndroidManifest.xml`. This tells Android to route the payment redirect back to your app:

```xml theme={null}
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="yourappscheme" />
</intent-filter>
```

Replace `yourappscheme` on both platforms with the same unique scheme. This value should also match whatever redirect URL is configured in your Novac dashboard.

***

## Initialize the SDK

Before launching any payment, call `NovacPaymentPlugin.initialize()` once. Typically, at app startup or before your first checkout. This authenticates your app with Novac and optionally applies a custom theme to the checkout UI.

```dart theme={null}
import 'package:novac_payment_plugin/novac_payment_plugin.dart';

await NovacPaymentPlugin.initialize(
  apiKey: 'your_api_key',
  primaryColor: '#007AFF',      // Optional: Customize theme
  backgroundColor: '#FFFFFF',   // Optional
  buttonTextColor: '#FFFFFF',   // Optional
);
```

***

### Launch the Checkout Flow

Once the SDK is initialized, call `launchCheckout()` to start a payment. The method is asynchronous and returns a `PaymentResult` object you can inspect to determine what happened.

```dart theme={null}
final result = await NovacPaymentPlugin.launchCheckout(
  amount: 5000,                 // Amount in smallest currency unit
  currency: 'NGN',
  redirectUrl: 'https://yourapp.com/payment-complete',
  customerData: CustomerData(
    email: 'customer@example.com',
    firstName: 'John',
    lastName: 'Doe',            // Optional
    phoneNumber: '+2348012345678', // Optional
  ),
  customizationData: CustomizationData(
    logoUrl: 'https://yourcompany.com/logo.png',
    paymentDescription: 'Payment for Order #12345',
    checkoutModalTitle: 'Complete Payment',
  ),
  transactionReference: 'unique-reference-123', // Optional: Auto-generated if not provided
);

// Handle the result
if (result.isSuccess) {
  print('Payment successful! Transaction ID: ${result.transactionId}');
} else if (result.isCancelled) {
  print('Payment cancelled by user');
} else {
  print('Payment failed: ${result.errorMessage}');
}
```

**Customer's Data**

This controls customer information.

<ResponseField name="customerData" type="Object">
  <Expandable title="properties">
    <ResponseField name="email" required="true" type="string">
      Customer's email address
    </ResponseField>

    <ResponseField name="firstName" required="true" type="string">
      Customer's first name
    </ResponseField>

    <ResponseField name="lastName" required="true" type="string">
      Customer's last name
    </ResponseField>

    <ResponseField name="phoneNumber" required="false" type="string">
      Customer's phone number
    </ResponseField>
  </Expandable>
</ResponseField>

**Customer's Data**

This Controls the visual appearance and copy shown on the checkout modal.

<ResponseField name="CustomizationData" type="Object">
  <Expandable title="properties">
    <ResponseField name="logoUrl" type="string">
      URL to your company logo
    </ResponseField>

    <ResponseField name="paymentDescription" type="string">
      Description displayed on the checkout screen
    </ResponseField>

    <ResponseField name="checkoutModalTitle" type="string">
      Title shown at the top of the checkout modal
    </ResponseField>
  </Expandable>
</ResponseField>

The object returned by `launchCheckout()`. Check its properties to determine the outcome of the payment.

<ResponseField name="PaymentResult" type="Object">
  <Expandable title="properties">
    <ResponseField name="isSuccess" type="Boolean">
      Whether the payment completed successfully
    </ResponseField>

    <ResponseField name="isCancelled" type="Boolean">
      Whether the user dismissed the checkout without paying
    </ResponseField>

    <ResponseField name="transactionId" type="string">
      Transaction ID, present if successful
    </ResponseField>

    <ResponseField name="transactionReference" type="string">
      The transaction reference used for the payment
    </ResponseField>

    <ResponseField name="errorMessage" type="string">
      Human-readable error message if the payment failed
    </ResponseField>

    <ResponseField name="errorCode" type="string">
      Error code for programmatic handling if the payment failed
    </ResponseField>
  </Expandable>
</ResponseField>

***

### Verify a Payment

After a successful checkout, you can independently verify the transaction status on your backend or client using `verifyPayment()`. Pass the transaction reference returned from the checkout result:

```dart theme={null}
final verification = await NovacPaymentPlugin.verifyPayment('transaction-reference');

print('Status: ${verification.status}');
print('Amount: ${verification.amount}');
print('Currency: ${verification.currency}');
```

The object returned by `verifyPayment()`. Use this to confirm a transaction's final status.

<ResponseField name="VerificationResult" type="Object">
  <Expandable title="properties">
    <ResponseField name="status" type="Boolean">
      Whether the payment completed successfully
    </ResponseField>

    <ResponseField name="transactionReference" type="string">
      The transaction reference used for the payment
    </ResponseField>

    <ResponseField name="double" type="Boolean">
      The transaction amount
    </ResponseField>

    <ResponseField name="currency" type="string">
      transaction currency
    </ResponseField>

    <ResponseField name="errorMessage" type="string">
      Error message if verification failed
    </ResponseField>
  </Expandable>
</ResponseField>

***

### Error Handling

Wrap your checkout call in a `try/catch` block to handle both expected payment outcomes (failure, cancellation) and unexpected runtime errors gracefully:

```dart theme={null}
try {
  final result = await NovacPaymentPlugin.launchCheckout(...);
  
  if (result.isSuccess) {
    // Handle success
  } else if (result.isCancelled) {
    // Handle cancellation
  } else {
    // Handle failure
    print('Error: ${result.errorCode} - ${result.errorMessage}');
  }
} catch (e) {
  // Handle unexpected errors
  print('Unexpected error: $e');
}
```

Keep the following in mind when testing:

* Always use a unique `reference` value for each transaction, duplicate references will be rejected.
* Start with small test amounts (₦100) to validate your integration before going live.
* Check your sandbox dashboard to confirm transaction results and inspect any errors.
