Flutter Client

Step 1: Create New Order (REST API) ๐Ÿ”—

Please refer Order API Documentation for more details.

/create


Authorization: basic authentication with base64encoded

PayGAuthentication โ€“
<MerchantAuthenticationKey>:<MerchantAuthenticationToken>:M:<MID>
  
						
                     
                        {
                           "MID": "3EMF9915EF35567",
                           "UniqueRequestId": "TEST512scx3",
                           "UserDefinedData": {
                               "UserDefined1": ""
                           },
                           "ProductData": "{'PaymentReason':'OnlineOrder for OrderNo- 1234'}",
                           "RequestDateTime": "06232023",
                           "RedirectUrl": "https://payg.in",
                           "TransactionData": {
                               "AcceptedPaymentTypes": "",
                               "PaymentType": "UPIINTENT",
                               "SurchargeType": "",
                               "SurchargeValue": "",
                               "RefTransactionId": "",
                               "IndustrySpecificationCode": "",
                               "PartialPaymentOption": ""
                           },
                           "OrderAmount": "3",
                           "OrderType": "",
                           "OrderAmountData": {
                               "AmountTypeDesc": "3",
                               "Amount": "2"
                           },
                           "CustomerData": {
                               "CustomerId": "123456",
                               "CustomerNotes": "Mens clothing",
                               "FirstName": "Ayush",
                               "LastName": "Rane",
                               "MobileNo": "7887673322",
                               "Email": "ayushtesting12@gmail.com",
                               "EmailReceipt": "true",
                               "BillingAddress": "76, Sch. 115",
                               "BillingCity": "Indore",
                               "BillingState": "MP",
                               "BillingCountry": "India",
                               "BillingZipCode": "452011",
                               "ShippingFirstName": "Ayush",
                               "ShippingLastName": "Rane",
                               "ShippingAddress": "76, Sch. 115",
                               "ShippingCity": "Indore",
                               "ShippingState": "MP",
                               "ShippingCountry": "India",
                               "ShippingZipCode": "452011",
                               "ShippingMobileNo": "9987887733"
                           },
                           "IntegrationData": {
                               "UserName": "Ayush",
                               "Source": "3213",
                               "IntegrationType": "11",
                               "HashData": "",
                               "PlatformId": "1"
                           }
                       }
  
 
         
var data = {
   "Merchantkeyid": MerchantKeyId,
   "UniqueRequestId": getRandomString(10),
   "OrderStatus": 'Initiating',
   "UserDefinedData": {"UserDefined1": ''},
   "RequestDateTime": "11/08/2021",
   "RedirectUrl": RedirectUrl,
   "TransactionData": {
      "AcceptedPaymentTypes": '',
      "PaymentType": orderType,
      "SurchargeType": orderType,
      "SurchargeValue": orderType,
      "RefTransactionId": orderType,
      "IndustrySpecificationCode": orderType,
      "PartialPaymentOption": orderType,
   },
   "OrderAmount": orderAmountController.text,
   "OrderType": 'MOBILE',
   "OrderAmountData": {"AmountTypeDesc": amountTypeDesc, "Amount": "20"},
   "CustomerData": {
      'CustomerId': CustomerId,
     'CustomerNotes': customerNotes,
     'FirstName': orderFirstNameController.text,
     'LastName': orderLastNameController.text,
     'MobileNo': mobileNoController.text,
     'Email': email,
     'EmailReceipt': emailReceipt,
     'BillingAddress': billingAddress,
     'BillingCity': billingCity,
     'BillingState': billingState,
     'BillingCountry': billingCountry,
     'BillingZipCode': billingZipCode,
     'ShippingFirstName': shippingFirstName,
     'ShippingLastName': shippingLastName,
     'ShippingAddress': shippingAddress,
     'ShippingCity': shippingCity,
     'ShippingState': shippingState,
     'ShippingCountry': shippingCountry,
     'ShippingZipCode': shippingZipCode,
      'ShippingMobileNo': shippingMobileNo,
   },
   "IntegrationData": {
      "UserName": userName,
      "Source": 'MobileSDK',
      "IntegrationType": "11",
      "HashData": HashData,
      "PlatformId": PlatformId,
   }
   };


      
         {
            "OrderKeyId": "93281250227M10404UTEST512scx3",
            "MerchantKeyId": 10121,
            "UniqueRequestId": "TEST512scx3",
            "OrderType": "PAYMENT",
            "OrderAmount": 3,
            "OrderId": null,
            "OrderStatus": null,
            "OrderPaymentStatus": 0,
            "OrderPaymentStatusText": null,
            "PaymentStatus": 0,
            "PaymentTransactionId": null,
            "PaymentResponseCode": 0,
            "PaymentApprovalCode": null,
            "PaymentReasonCode": null,
            "PaymentTransactionRefNo": null,
            "PaymentResponseText": null,
            "PaymentMethod": null,
            "PaymentAccount": null,
            "OrderRefundTransactionId": 0,
            "RefundPaymentResponseCode": 0,
            "RefundPaymentResponseText": null,
            "RefundDateTime": null,
            "CardType": null,
            "CardLevel": null,
            "OrderNotes": null,
            "PaymentDateTime": null,
            "UpdatedDateTime": null,
            "PaymentProcessUrl": "null",
            "CustomerData": {
                "CustomerId": "123456",
                "CustomerNotes": "Mens clothing",
                "FirstName": "Ayush",
                "LastName": "Rane",
                "MobileNo": "7887673322",
                "Email": "ayushtesting12@gmail.com",
                "EmailReceipt": true,
                "BillingAddress": "76, Sch. 115",
                "BillingCity": "Indore",
                "BillingState": "MP",
                "BillingCountry": "India",
                "BillingZipCode": "452011",
                "ShippingFirstName": "Ayush",
                "ShippingLastName": "Rane",
                "ShippingAddress": "76, Sch. 115",
                "ShippingCity": "Indore",
                "ShippingState": "MP",
                "ShippingCountry": "India",
                "ShippingZipCode": "452011",
                "ShippingMobileNo": "9987887733"
            },
            "ProductData": "{'PaymentReason':'OnlineOrder for OrderNo- 1234'}",
            "OrderPaymentCustomerData": {
                "FirstName": "Ayush",
                "LastName": null,
                "Address": null,
                "City": null,
                "State": null,
                "ZipCode": null,
                "Country": null,
                "MobileNo": "7887673322",
                "Email": "ayushtesting12@gmail.com",
                "UserId": null,
                "IpAddress": null
            },
            "UpiLink": "upi://pay?pa=businesspayg@isis&am=1&tr=Y564417909M21764D543216&cu=INR&mc=2006&pn=Test Account"
            "UserDefinedData": {
                "UserDefined1": "",
                "UserDefined2": null,
                "UserDefined3": null,
                "UserDefined4": null,
                "UserDefined5": null,
                "UserDefined6": null,
                "UserDefined7": null,
                "UserDefined8": null,
                "UserDefined9": null,
                "UserDefined10": null,
                "UserDefined11": null,
                "UserDefined12": null,
                "UserDefined13": null,
                "UserDefined14": null,
                "UserDefined15": null,
                "UserDefined16": null,
                "UserDefined17": null,
                "UserDefined18": null,
                "UserDefined19": null,
                "UserDefined20": null
            },
            "SplitPaymentResponse": null,
            "Id": 93281
        }
  
 

Step 2: Below details you can use for launch a specific UPI app to complete a payment. ๐Ÿ”—

Once your order is created successfully,then you will UPI link in response. This payment url is used to redirect to actual payment screen.

UPI Link is for UPI payment. You can use either payment process URL or UPI based on your requirement.

rana_pay_upi โ€” Usage Guide ๐Ÿ”—

A Flutter plugin to discover installed UPI (Unified Payments Interface) apps on an Android device and launch a specific UPI app to complete a payment.

1. Installation ๐Ÿ”—

Add the package to your pubspec.yaml (adjust source depending on whether you're publishing to pub.dev or using a local):

            
                dependencies:
                flutter:
                sdk: flutter
                payg_payment_package:
                path: payg_payment_package 
            

Then run:

            
                flutter pub get
            
2. Android Setup ๐Ÿ”—

Since UPI apps are launched via Android intents, you need to declare the UPI apps you intend to query/launch in your AndroidManifest.xml (required from Android 11 / API 30 onward due to package visibility restrictions):

            
                <queries>
                <intent>
                <action android:name="android.intent.action.VIEW" />
                <data android:scheme="upi" />
                </intent>
                </queries>
            

Without this block, getUpiApps() may return an empty list on newer Android versions even if UPI apps are installed.

3.Importing the Package ๐Ÿ”—
            
                import 'package:payg_payment_package/payg_payment.dart';
            
4.API Reference & Examples ๐Ÿ”—
How to use required details ๐Ÿ”—
            
                final payment = PaygPayment(
              mid: "3EMF9915EF35567",
              paymentReason: "OnlineOrder for OrderNo- 1234",
              orderAmount: "1",
              mobileNo: "8875292912",
              shippingZipCode: "500001",
              shippingMobileNo: "9876543210",
              context: context,
              isUAT: false,
              authKey: 'gt44e35823e54f088ea2e41da9c098d4',
              authtoken: 'y56tf2782aa348d6tb1a73a89438t4rd',
              merchantName: 'Bloom Retail Pvt. Ltd.',
            );
            await payment.initPaygPayment().then((v) {
              Utility.logPrint("response initPaygPayment: $v");
            });
  
5. iOS Requirements ๐Ÿ”—

UPI itself has no formal iOS equivalent of Android's intent-based app discovery, so iOS support works fundamentally differently and needs its own native setup. This matters because your UI (as shown above) falls back to aDropdownButtonFormField on iOS instead of the app grid used on Android โ€” which only works if the plugin's iOS side is actually able to detect, open, and receive a callback from installed UPI apps.

There are two separate pieces of iOS setup: (A) letting your app query and open other UPI apps, and (B) letting other UPI apps call back into your app once the payment is done. Both are required.

5.1 Query other UPI apps โ€” LSApplicationQueriesSchemes ๐Ÿ”—

iOS can't enumerate "anything that handles upi://" the way Android'sPackageManager can. Each app's scheme must be checked one by one viacanOpenURL, and must be whitelisted in Info.plist:

            
                <key>LSApplicationQueriesSchemes</key>
<array>  
     <string>tez</string>          
      <string>phonepe</string>      
     <string>paytmmp</string>     
     <string>credpay</string>      
     <string>bhim</string>         
     <string>upi</string>                  
    </array>
              

Without this, canOpenURL (and therefore getUpiApps() on iOS) will report every app as "not installed," even if it is.

5.2 Register your own callback scheme โ€” CFBundleURLTypes ๐Ÿ”—

For a UPI app to hand control back to your app after the payment completes, your app needs its own registered URL scheme that you pass as the callback/return URL when building the UPI link. Add this to Info.plist:

            
            <key>CFBundleURLTypes</key>
            <array>
            <dict>
            <key>CFBundleURLName</key>
            <string>com.payg.upi.callback</string>
            <key>CFBundleURLSchemes</key>
            <array>
            <string>payg</string>
            </array>
            </dict>
            </array>
        

Here payg is the scheme the plugin listens on โ€” when a UPI app finishes and redirects to something like payg://response?Status=SUCCESS&..., iOS routes that back into your app rather than dropping it.

5.3 Handle the callback in AppDelegate.swift ๐Ÿ”—

Your AppDelegate needs to forward the incoming URL to the plugin so it can parse it and resolve the Future that launchUpiApp() is waiting on:

            
                // MARK: - Handle URL Scheme Callbacks
override func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
    print("๐Ÿ“ฑ AppDelegate - Received URL: \(url.absoluteString)")

    // Let the RanaPayUpiPlugin handle the URL
    if let plugin = RanaPayUpiPlugin.shared {
        let handled = plugin.handleOpenURL(url)
        if handled {
            print("โœ… RanaPayUpiPlugin handled URL successfully")
            return true
        }
    }
    // If not handled by plugin, pass to Flutter's default handler
    print("โš ๏ธ URL not handled by plugin, passing to Flutter")
    return super.application(app, open: url, options: options)
}

// MARK: - Handle Universal Links
override func application(
    _ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
       let url = userActivity.webpageURL {
        print("๐Ÿ“ฑ Received universal link: \(url.absoluteString)")

        if let plugin = RanaPayUpiPlugin.shared {
            if plugin.handleOpenURL(url) {
                print("โœ… Plugin handled universal link")
                return true
            }
        }
    }
    return super.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
           
What each piece is doing: ๐Ÿ”—

application(_:open:options:) fires when a UPI app redirects back into your app via the payg:// custom scheme (ยง6.2). It hands the URL toRanaPayUpiPlugin.shared.handleOpenURL(url), which is presumably what parses the query string and completes the pending launchUpiApp() call on the Dart side.

application(_:continue:restorationHandler:) covers the case where the redirect comes back as a universal link (https://...) instead of a custom scheme โ€” some UPI apps prefer this. Both paths funnel into the samehandleOpenURL, so you don't need separate Dart-side handling for each.

The Facebook (fb...) scheme check is just making sure this callback handler doesn't swallow unrelated URL callbacks your app already relies on โ€” keep any other third-party SDK scheme checks alongside it if you add more in the future.

If neither the plugin nor a known third-party scheme handles it, it falls through to Flutter's own default URL handling so nothing else in your app breaks.

            
                โš ๏ธ Make sure this code lives in your ios/Runner/AppDelegate.swift, and that RanaPayUpiPlugin.shared is actually being set somewhere in the plugin's register(with:) โ€” otherwise plugin will be nil and every callback silently falls through to Flutter's default handler, meaninglaunchUpiApp() will simply hang/timeout on iOS regardless of correctInfo.plist setup.
              
6. General Requirements ๐Ÿ”—

AndroidManifest.xml <queries> block for package visibility (Android 11+).

Info.plist LSApplicationQueriesSchemes entries for iOS (querying apps).

Info.plist CFBundleURLTypes entry for iOS (receiving the callback).

AppDelegate.swift URL + universal-link handlers wired to

RanaPayUpiPlugin.shared.handleOpenURL(url).

Real UPI-enabled bank account and VPA (pa) for testing actual payments.

A backend payment-status endpoint โ€” required on both platforms, but especially load-bearing on iOS (see ยง6.4).