pos_client 0.0.2 copy "pos_client: ^0.0.2" to clipboard
pos_client: ^0.0.2 copied to clipboard

A Flutter SDK that enables Point of Sale (POS) systems to accept cryptocurrency payments through WalletConnect network

PosClient SDK #

A Flutter SDK for implementing Point of Sale (POS) cryptocurrency payments using the Reown ecosystem. This SDK enables merchants to accept crypto payments by generating QR codes that customers can scan with their Reown wallet.

Features #

  • Multi-token Support: Support for major stablecoins
  • Event-driven Architecture: Real-time payment status updates through events
  • Secure Transactions: Built-in transaction validation and status checking
  • Easy Integration: Simple API for Flutter applications

Getting Started #

Prerequisites #

  • Flutter 3.0 or higher
  • A Reown project ID (get one from Reown)

Installation #

Add the package to your pubspec.yaml, see install section

Usage #

1. Create the SDK instance #

import 'package:pos_client/pos_client.dart';

// Create metadata for your merchant application
final metadata = Metadata(
  merchantName: 'Your Store Name',
  description: 'Secure Crypto Payment Terminal',
  url: 'https://yourstore.com',
  logoIcon: 'https://yourstore.com/logo.png',
);

// Initialize PosClient instance
final posClilent = PosClient(
  projectId: 'your_project_id_here',
  deviceId: 'unique_device_id',
  metadata: metadata,
);

2. Configure Supported Tokens (and Networks) #

// Define the tokens you want to accept
final tokens = [
  PosToken(
    network: PosNetwork(name: 'Ethereum', chainId: 'eip155:1'),
    symbol: 'USDC',
    standard: 'erc20', // Token standard (erc20 for EVM tokens)
    address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC contract address on Ethereum
  ),
  PosToken(
    network: PosNetwork(name: 'Polygon', chainId: 'eip155:137'),
    symbol: 'USDC',
    standard: 'erc20',
    address: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC contract address on Polygon
  ),
];

// Set the tokens (this automatically configures supported networks)
posClilent.setTokens(tokens: tokens);

// Your configured tokens can be then accessed with
final myConfiguredTokens = posClilent.configuredTokens;

3. Subscribe to events #

// Subscribe to events to update the UI accordingly
posClilent.onPosEvent.subscribe(_onPosEvent);

4. Initialize the SDK #

// Initialize the SDK (this starts the core services)
await posClilent.init();

5. Create Payment Intent #

// Create a payment intent
final paymentIntent = PaymentIntent(
  token: PosToken(
    network: PosNetwork(name: 'Ethereum', chainId: 'eip155:1'),
    symbol: 'USDC',
    standard: 'erc20',
    address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  ),
  amount: '25.50', // Amount as string (e.g., "12.5")
  recipient: '0xYourMerchantAddress', // Your wallet address
);

// Create the payment intent (this generates QR code)
await posClilent.createPaymentIntent(
  paymentIntents: [paymentIntent],
);

6. Handle Events #

Subscribe to events to track the payment flow:

void _onPosEvent(PosEvent event) {
  if (event is QrReadyEvent) {
    // When payment intent is created and connection with the relay is established, this event will contain the pairing URI to create the QR.
    // You can use your own QR Widget but `QrImageView(data: uri)` Widget is available for you to use.
    status += '1. Scan QR code with your wallet';
  } else if (event is ConnectRejectedEvent) {
    // User rejected the wallet connection
    status += '\n2. User rejected session';
  } else if (event is ConnectFailedEvent) {
    // Connection failed with error message
    status += '\n2. Connection failed: ${event.message}';
  } else if (event is ConnectedEvent) {
    // Customer connected their wallet upon scanning the QR code with the pairing URI
    status += '\n2. Connected!';
  } else if (event is PaymentRequestedEvent) {
    // Payment has been sent to the wallet for user approval
    status += '\n3. Requesting payment...';
  } else if (event is PaymentRequestRejectedEvent) {
    // User rejected the payment request
    status += '\n3. User rejected payment.';
  } else if (event is PaymentBroadcastedEvent) {
    // User approved the payment and transaction was broadcasted
    status += '\n4. Payment broadcasted, waiting confirmation...';
  } else if (event is PaymentFailedEvent) {
    // Payment failed with error message
    status += '\n4. Payment failed: ${event.message}';
  } else if (event is PaymentSuccessfulEvent) {
    // Payment has been confirmed on the blockchain
    status += '\n4. Payment successful!. Hash: ${event.txHash}';
  } else if (event is DisconnectedEvent) {
    // Session disconnected
    status += '\n5. Disconnected';
  }
}

7. Restart and Abort #

The SDK provides a restart() function to handle payment flow interruptions and restarts:

// Abort current payment and restart the flow
await posClilent.restart();

// Full restart with reinitialization (clears all state)
await posClilent.restart(reinit: true);

Use cases:

  • Abort ongoing payment: When a customer wants to cancel or restart the payment process
  • Payment completion: After a successful or failed payment to prepare for the next transaction
  • Error recovery: When you need to reset the SDK state due to errors
  • Full reset: Use reinit: true to completely clear the instance and require calling init() and setTokens() again

What happens during restart:

  • Completes any pending status checks
  • Clears the current payment intent
  • Expires previous pairings
  • Aborts ongoing connection attempts
  • Optionally clears all configuration when reinit: true

8. Complete and Working Example #

Here's a complete example of a payment screen:

class PaymentScreen extends StatefulWidget {
  const PaymentScreen({super.key});

  @override
  State<PaymentScreen> createState() => _PaymentScreenState();
}

class _PaymentScreenState extends State<PaymentScreen> {
  PosEvent? _event;
  String status = '';
  late final IPosClient posClilent;

  @override
  void initState() {
    super.initState();
    // [PosClient SDK API] 1. Construct your PosClient instance
    final metadata = Metadata(
      merchantName: 'DTC Pay',
      description: 'Secure Crypto Payment Terminal',
      url: 'https://appkit-lab.reown.com',
      logoIcon: 'https://avatars.githubusercontent.com/u/179229932',
    );
    posClilent = PosClient(
      projectId: '50f81661a58229027394e0a19e9db752',
      deviceId: "sample_pos_device_${DateTime.now().microsecondsSinceEpoch}",
      metadata: metadata,
    );
    // [PosClient SDK API] 2. call setTokens to construct namespaces with your supported tokens and network
    posClilent.setTokens(
      tokens: [
        // USDC on Sepolia
        PosToken(
          network: PosNetwork(name: 'Sepolia ETH', chainId: 'eip155:11155111'),
          symbol: 'USDC',
          standard: 'erc20',
          address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
        ),
      ],
    );

    // [PosClient SDK API] 3. Subscribe to events
    posClilent.onPosEvent.subscribe(_onPosEvent);

    // [PosClient SDK API] 4. initialize PosClient SDK. Can be awaited if needed
    posClilent.init().then((_) {
      // [PosClient SDK API] 5. create a payment intent with the PaymentIntent. Can be awaited if needed
      posClilent.createPaymentIntent(
        paymentIntents: [
          PaymentIntent(
            token: posClilent.configuredTokens.first,
            amount: '1.50',
            recipient: '0xD6d146ec0FA91C790737cFB4EE3D7e965a51c340',
          ),
        ],
      );
    });
  }

  void _onPosEvent(PosEvent event) {
    setState(() {
      if (event is QrReadyEvent) {
        status += '1. Scan QR code with your wallet';
      } else if (event is ConnectRejectedEvent) {
        status += '\n2. User rejected session';
      } else if (event is ConnectFailedEvent) {
        status += '\n2. Connection failed: ${event.message}';
      } else if (event is ConnectedEvent) {
        status += '\n2. Connected!';
      } else if (event is PaymentRequestedEvent) {
        status += '\n3. Requesting payment...';
      } else if (event is PaymentRequestRejectedEvent) {
        status += '\n3. User rejected payment.';
      } else if (event is PaymentBroadcastedEvent) {
        status += '\n4. Payment broadcasted, waiting confirmation...';
      } else if (event is PaymentFailedEvent) {
        status += '\n4. Payment failed: ${event.message}';
      } else if (event is PaymentSuccessfulEvent) {
        status += '\n4. Payment successful!. Hash: ${event.txHash}';
      } else if (event is DisconnectedEvent) {
        status += '\n5. Disconnected';
      }
      _event = event;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Crypto Payment')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            (_event is QrReadyEvent)
                ? QrImageView(
                    data: (_event as QrReadyEvent).uri.toString(),
                    size: 250,
                  )
                : SizedBox.shrink(),
            SizedBox(height: 20),
            Padding(padding: const EdgeInsets.all(8.0), child: Text(status)),
          ],
        ),
      ),
    );
  }
}

Event Types #

  • QrReadyEvent - QR code is ready for scanning
  • ConnectedEvent - Customer wallet connected
  • ConnectRejectedEvent - Connection rejected by customer
  • ConnectFailedEvent - Connection failed
  • PaymentRequestedEvent - Payment request sent to wallet
  • PaymentRequestRejectedEvent - Payment request rejected by customer
  • PaymentBroadcastedEvent - Transaction broadcasted to network
  • PaymentSuccessfulEvent - Payment confirmed on blockchain
  • PaymentFailedEvent - Payment failed
  • PaymentRejectedEvent - Payment rejected by customer
  • DisconnectedEvent - Session disconnected

Error Handling #

The SDK provides comprehensive error handling through events. Always subscribe to events to handle:

  • Connection failures
  • Payment rejections
  • Transaction failures
  • Network issues

Additional Information #

For more detailed examples, check the /example folder in this package. The example demonstrates a complete payment flow with UI components and state management.

Model Structure #

The SDK uses the following key models:

  • PaymentIntent: Contains the token (with network info), amount, and recipient address
  • PosToken: Represents a token with network, symbol, standard, and contract address
  • PosNetwork: Defines a blockchain network with name and chain ID
  • Metadata: Merchant information for the wallet connection

Contributing #

Contributions are welcome! Please feel free to submit issues and pull requests.

License #

This package is licensed under the terms specified in the LICENSE file.

Testing #

Test the web implementation of this SDK at the following url: https://pos-flutter-demo.pages.dev

0
likes
140
points
164
downloads

Publisher

verified publisherreown.com

Weekly Downloads

A Flutter SDK that enables Point of Sale (POS) systems to accept cryptocurrency payments through WalletConnect network

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

unknown (license)

Dependencies

collection, event, flutter, freezed_annotation, http, json_annotation, qr_flutter_wc, reown_core, reown_sign

More

Packages that depend on pos_client