processSavedInstrumentPayment method

Future<void> processSavedInstrumentPayment(
  1. String instrumentRef,
  2. Map<String, dynamic> shopperDetails
)

Process payment for the Swipe Action

Implementation

Future<void> processSavedInstrumentPayment(String instrumentRef, Map<String, dynamic> shopperDetails) async {

  // 1. Prepare Headers
  // Note: We generate a unique ID for the request and use the Session Token for auth
  final Map<String, String> headers = {
    'X-Request-Id': _generateRandomRequestId(),
    'Authorization': 'Session $shopperToken',
    'X-Client-Connector-Name' : 'Flutter SDK',
    'Content-Type': 'application/json'
  };
  final deviceData = await _getDeviceDetails();

  // 2. Prepare body
  final Map<String, dynamic> body = {
    "browserData": {
      "screenHeight": MediaQuery.of(context).size.height.toInt().toString(),
      "screenWidth": MediaQuery.of(context).size.width.toInt().toString(),
      "acceptHeader": "application/json",
      "userAgentHeader": "Flutter-SDK/1.0 (Android)", // You can fetch real UserAgent if needed
      "browserLanguage": "en_US",
      "ipAddress": null, // The server usually detects this automatically
      "colorDepth": 24,
      "javaEnabled": true,
      "timeZoneOffSet": DateTime.now().timeZoneOffset.inMinutes,
      "packageId": "com.boxpay.checkout.flutter" // Replace with actual package ID
    },
    "instrumentDetails": {
      "type": "card/token",
      "savedCard": {
        "instrumentRef": instrumentRef // <--- The token from the Swipe UI
      }
    },
    "shopper" : shopperDetails,
    "deviceDetails": deviceData
  };

  try {
    // 3. Make the API Call
    final response = await http.post(
      Uri.parse(_baseUrl),
      headers: headers,
      body: jsonEncode(body),
    );
    if (response.statusCode == 200) {
      final result = jsonDecode(response.body);


      String? htmlContent;
      String? redirectUrl;
      String? transactionRequest;

      // 1. Check for "RequiresAction" status
      final statusObj = result['status'];
      if (statusObj != null && statusObj['status'] == 'RequiresAction') {

          // 2. Check Actions Array
          if (result['actions'] != null && (result['actions'] as List).isNotEmpty) {
              final action = result['actions'][0];

              // Case A: Type is HTML (The JSON you provided)
              if (action['type'] == 'html') {
                  htmlContent = action['htmlPageString'];
              }
              // Case B: Type is URL/Redirect (Just in case)
              else if (action['type'] == 'url') {
                  redirectUrl = action['url'];
                  if (action['data'] != null) {
                      transactionRequest = action['data']['txnreq'].toString();
                  }
              }
          }
      }

      // --- NAVIGATION LOGIC ---

      if (htmlContent != null || redirectUrl != null) {

        // Open 3DS Page and wait for result
        final navResult = await Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => BoxPay3DSPage(
              htmlContent: htmlContent, // This contains your form
              redirectUrl: redirectUrl,
              transactionRequest: transactionRequest,
            ),
          ),
        );

        // Handle Polling Result
       if (navResult == "completed") {

           final statusData = await fetchTransactionStatus();
           final finalStatus = statusData['status'];
           final transactionId = statusData['transactionId']?.toString() ?? "";

           if (finalStatus == 'PAID' || finalStatus == 'SUCCESS' || finalStatus == 'APPROVED') {
              onPaymentResult(PaymentResultObject("Success", transactionId));
           } else {
              throw Exception("Payment Status: $finalStatus");
           }

        } else {
           // User pressed 'X' or Back
           throw Exception("Payment Cancelled by user");
        }
      } else {
        onPaymentResult(PaymentResultObject(statusObj['status'], result['transactionId']));
      }
    } else {
      // 6. Handle API Errors
      _showPaymentFailedDialog();
      throw Exception("Payment failed with status: ${response.statusCode}");
    }
  } catch (e) {
    _showPaymentFailedDialog();
    throw e;
  }
}