Skip to main content
The zbd_ramp package provides a native Flutter widget for integrating ZBD Ramp into your iOS, Android, and Web applications.

Features

Flutter Optimized

Built specifically for Flutter with native WebView integration

Cross-Platform

Works seamlessly on iOS, Android, and Web

Type Safe

Full Dart type safety with comprehensive type definitions

Real-Time Events

PostMessage communication for error handling, logging, and step tracking

Installation

Add the package to your pubspec.yaml:
dependencies:
  zbd_ramp: ^1.0.0
  http: ^1.1.0
Then run:
flutter pub get

Quick Start

1. Create Session Token

First, initialize a ramp session from your backend or directly in your Flutter app:
  • Email Authentication
  • Access Token Authentication
import 'package:zbd_ramp/zbd_ramp.dart';

final response = await initRampSession(InitRampSessionConfig(
  apikey: 'your-zbd-api-key',
  email: '[email protected]',
  destination: '[email protected]',
  quoteCurrency: QuoteCurrency.USD,
  baseCurrency: BaseCurrency.BTC,
  webhookUrl: 'https://your-webhook-url.com',
  referenceId: 'order-123',
  metadata: {'userId': '456', 'plan': 'premium'},
));

if (response.success) {
  final sessionToken = response.data.sessionToken;
  // Use sessionToken with ZBDRampWidget
} else {
  print('Failed to create session: ${response.error}');
}

2. Display the Ramp Widget

Add the widget to your Flutter app:
import 'package:flutter/material.dart';
import 'package:zbd_ramp/zbd_ramp.dart';

class PaymentScreen extends StatelessWidget {
  final String sessionToken;

  const PaymentScreen({Key? key, required this.sessionToken}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Buy Bitcoin')),
      body: ZBDRampWidget(
        config: RampConfig(sessionToken: sessionToken),
        callbacks: RampCallbacks(
          onSuccess: (data) {
            print('Payment successful: $data');
            // Handle successful payment
          },
          onError: (error) {
            print('Payment error: ${error.message}');
            // Handle error
          },
          onStepChange: (step) {
            print('Current step: $step');
            // Track user progress
          },
        ),
        height: 600,
      ),
    );
  }
}

API Reference

initRampSession()

Creates a new session token for the ZBD Ramp widget.

Parameters

class InitRampSessionConfig {
  final String apikey;                            // Required: Your ZBD API key
  final String? email;                            // Email authentication
  final String? accessToken;                      // Access token authentication
  final String destination;                       // Required: Lightning address or username
  final QuoteCurrency quoteCurrency;              // Required: Quote currency (USD)
  final BaseCurrency baseCurrency;                // Required: Base currency (BTC)
  final String? webhookUrl;                       // Optional: Webhook URL
  final String? referenceId;                      // Optional: Your reference ID
  final Map<String, dynamic>? metadata;           // Optional: Additional metadata
}
Either email OR accessToken must be provided for authentication.

Returns

class InitRampSessionResponse {
  final InitRampSessionData data;
  final String? error;
  final bool success;
  final String message;
}

class InitRampSessionData {
  final String sessionToken;                     // Session token for widget
  final String expiresAt;                        // Token expiration time
  final String widgetUrl;                        // Direct widget URL
}

refreshAccessToken()

Refreshes an expired access token using a refresh token.
Token Lifecycle:
  • Access tokens expire after 30 days
  • Refresh tokens expire after 90 days
  • Both tokens are received via webhook after user completes OTP login with email

Parameters

class RefreshAccessTokenConfig {
  final String apikey;                            // Required: Your ZBD API key
  final String accessTokenId;                     // Required: ID of access token to refresh
  final String refreshToken;                      // Required: Refresh token
}

Returns

class RefreshAccessTokenResponse {
  final RefreshAccessTokenData data;
  final String? error;
  final bool success;
  final String message;
}

class RefreshAccessTokenData {
  final String accessTokenId;
  final String accessToken;                       // New access token
  final String refreshToken;                      // New refresh token
  final String accessTokenExpiresAt;
  final String refreshTokenExpiresAt;
}

Example

try {
  final response = await refreshAccessToken(RefreshAccessTokenConfig(
    apikey: 'your-zbd-api-key',
    accessTokenId: '7b585ffa-9473-43ca-ba1d-56e9e7e2263b',
    refreshToken: 'user-refresh-token',
  ));

  if (response.success) {
    final newAccessToken = response.data.accessToken;
    final newRefreshToken = response.data.refreshToken;
    // Store the new tokens securely
  }
} catch (error) {
  print('Token refresh error: $error');
}

ZBDRampWidget

Main Flutter widget that renders the ZBD Ramp interface.

Constructor

ZBDRampWidget({
  Key? key,
  required RampConfig config,
  required RampCallbacks callbacks,
  double? width,
  double? height,
})

RampConfig

Configuration for the widget:
class RampConfig {
  final String sessionToken;                     // Required: Session token
  final String? secret;                          // Optional: Widget secret
}

RampCallbacks

Event handlers for widget lifecycle:
class RampCallbacks {
  final OnSuccessCallback? onSuccess;            // Payment successful
  final OnErrorCallback? onError;                // Error occurred
  final OnStepChangeCallback? onStepChange;      // User navigated to new step
  final OnLogCallback? onLog;                    // Debug/info logging
  final OnReadyCallback? onReady;                // Widget fully loaded
  final OnCloseCallback? onClose;                // User closed widget
}

Complete Example

Here’s a full implementation with all callbacks:
import 'package:flutter/material.dart';
import 'package:zbd_ramp/zbd_ramp.dart';

class BuyBitcoinScreen extends StatefulWidget {
  @override
  _BuyBitcoinScreenState createState() => _BuyBitcoinScreenState();
}

class _BuyBitcoinScreenState extends State<BuyBitcoinScreen> {
  String? sessionToken;
  bool isLoading = true;
  String? error;

  @override
  void initState() {
    super.initState();
    _createSession();
  }

  Future<void> _createSession() async {
    try {
      final response = await initRampSession(InitRampSessionConfig(
        apikey: 'your-zbd-api-key',
        email: '[email protected]',
        destination: '[email protected]',
        quoteCurrency: QuoteCurrency.USD,
        baseCurrency: BaseCurrency.BTC,
        webhookUrl: 'https://your-webhook.com',
        referenceId: 'user_123',
      ));

      if (response.success) {
        setState(() {
          sessionToken = response.data.sessionToken;
          isLoading = false;
        });
      } else {
        setState(() {
          error = response.error;
          isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        error = e.toString();
        isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Buy Bitcoin')),
      body: isLoading
          ? Center(child: CircularProgressIndicator())
          : error != null
              ? Center(child: Text('Error: $error'))
              : ZBDRampWidget(
                  config: RampConfig(sessionToken: sessionToken!),
                  callbacks: RampCallbacks(
                    onSuccess: (data) {
                      print('✅ Payment successful: $data');
                      Navigator.of(context).pop();
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(content: Text('Payment successful!')),
                      );
                    },
                    onError: (error) {
                      print('❌ Error: ${error.message}');
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(
                          content: Text('Error: ${error.message}'),
                          backgroundColor: Colors.red,
                        ),
                      );
                    },
                    onStepChange: (step) {
                      print('📍 Step changed: $step');
                    },
                    onReady: () {
                      print('✅ Widget ready');
                    },
                    onClose: () {
                      print('👋 Widget closed');
                      Navigator.of(context).pop();
                    },
                    onLog: (log) {
                      print('📝 Log: $log');
                    },
                  ),
                  height: 600,
                ),
    );
  }
}

Error Handling

Handle errors gracefully in your application:
void _handleError(RampError error) {
  // Error structure: { code: string, message: string, details?: any }
  print('Error Code: ${error.code}');
  print('Error Message: ${error.message}');

  if (error.details != null) {
    print('Error Details: ${error.details}');
  }

  // Show user-friendly message
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('Payment Error'),
      content: Text(error.message),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('OK'),
        ),
      ],
    ),
  );
}

Platform Setup

iOS Configuration

Add the following to your ios/Runner/Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>Camera access is required for KYC verification</string>

Android Configuration

Add permissions to your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Try the Example App

The quickest way to see the SDK in action:
1

Clone the Repository

git clone https://github.com/zbdpay/ramp-flutter.git
cd ramp-flutter/example
2

Install Dependencies

flutter pub get
3

Run the Example

flutter run
4

Test with Your Credentials

  • Enter your ZBD API Key
  • Fill in email and Lightning destination
  • Tap “Create Session & Load Ramp”

Resources

Other SDKs

  • TypeScript
  • React
  • React Native
Core TypeScript/JavaScript package for web applications.View TypeScript SDK →

Support

Need help? Create an issue on GitHub or reach out to our support team.