how to integrate Phonepe payment gateway on flutter with android sdk or plugin?

3.7k views Asked by At

I'm working on a Flutter app and I need to integrate the PhonePe payment gateway into it. Unfortunately, there is no official Flutter plugin available for PhonePe. However, PhonePe does provide an Android SDK for integrating their payment gateway.

I've gone through the PhonePe documentation (link provided below) and I'm looking for guidance on how to integrate the PhonePe payment gateway Android SDK in my Flutter app.*** I'm open to both solutions that involve writing native Android code and those that don't require native code.***

PhonePe Documentation: [https://developer.phonepe.com/v1/docs/android-pg-sdk-integration]

Ideally, I would like to find a tutorial or step-by-step instructions that explain the integration process in an easy-to-follow manner. If there are existing Flutter plugins or packages that can simplify this integration, please let me know.

Any help, code snippets, or references to existing resources would be greatly appreciated. Thank you!

I have successfully implemented a PhonePe checkout process using a WebView and a Node.js backend to create the checkout link. However, I now require a more robust UI to display the checkout process within my Flutter app. To achieve this, I need to integrate the PhonePe payment gateway Android SDK.

2

There are 2 answers

1
Taimoor Ghafar On

Implement PhonePe Payment Gateway to the native side and call it from the flutter side. You will need method channels and event channels for it to communicate between the native and flutter sides. Read more here https://docs.flutter.dev/platform-integration/platform-channels

1
AKHIL P S On
import 'dart:developer';

import 'package:flutter/material.dart';
import 'dart:convert';

import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});`enter code here`

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _incrementCounter() async {
    final data = {
      "merchantId": "MERCHANTUAT",
      "merchantTransactionId": "MT7850590068188104",
      "merchantUserId": "MU933037302229373",
      "amount": 100,
      "callbackUrl": "https://webhook.site/callback-url",
      "mobileNumber": "9999999999",
      "deviceContext": {"deviceOS": "ANDROID"},
      "paymentInstrument": {
        "type": "UPI_INTENT",
        "targetApp": "com.phonepe.app",
        "accountConstraints": [
          {
            //Optional. Required only for TPV Flow.
            "accountNumber": "420200001892",
            "ifsc": "ICIC0000041"
          }
        ]
      }
    };

    final b64 = jsonEncode(data).toBase64;

    print(b64);

    const saltKey = "099eb0cd-02cf-4e2a-8aca-3e6c6aff0399";

    final sha = '$b64/pg/v1/pay$saltKey'.toSha256;
    print(sha);
    try {
      final res = await http.post(
        Uri.parse('https://api-preprod.phonepe.com/apis/pg-sandbox/pg/v1/pay'),
        headers: {
          'Content-Type': 'application/json',
          'X-VERIFY': '$sha###1',
        },
      );

      print(res.body.toString());
    } catch (e) {
      log(e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // TRY THIS: Try changing the color here to a specific color (to
        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
        // change color while the other colors stay the same.
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: test,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  void test() async {
    const saltKey = "099eb0cd-02cf-4e2a-8aca-3e6c6aff0399";
    const saltIndex = 1;
    const apiEndpoint = "/pg/v1/pay";

    final jsonData = {
      "merchantId": "MERCHANTUAT",
      "merchantTransactionId": "MT7850590068188104",
      "merchantUserId": "MUID123",
      "amount": '100',
      "redirectUrl": "https://webhook.site/redirect-url",
      "redirectMode": "POST",
      "callbackUrl": "https://webhook.site/callback-url",
      "mobileNumber": "9999999999",
      "paymentInstrument": {"type": "PAY_PAGE"}
    };

    String jsonString = jsonEncode(jsonData);
    String base64Data = jsonString.toBase64;
    String dataToHash = base64Data + apiEndpoint + saltKey;
    String sHA256 = generateSha256Hash(dataToHash);

    print(base64Data);
    print('#' * 10);
    print("$sHA256###$saltIndex");

    final response = await http.post(
      Uri.parse('https://api-preprod.phonepe.com/apis/pg-sandbox/pg/v1/pay'),
      headers: {
        "accept": "application/json",
        'X-VERIFY': '$sHA256###$saltIndex',
        'Content-Type': 'application/json',
      },
      body: jsonEncode({'request': base64Data}),
    );

    log(response.body.toString());
  }

  String generateSha256Hash(String input) {
    var bytes = utf8.encode(input);
    var digest = sha256.convert(bytes);
    return digest.toString();
  }
}

/// EncodingExtensions
extension EncodingExtensions on String {
  /// To Base64
  /// This is used to convert the string to base64
  String get toBase64 {
    return base64.encode(toUtf8);
  }

  /// To Utf8
  /// This is used to convert the string to utf8
  List<int> get toUtf8 {
    return utf8.encode(this);
  }

  /// To Sha256
  /// This is used to convert the string to sha256
  String get toSha256 {
    return sha256.convert(toUtf8).toString();
  }
}