You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter调用Flutterwave转账API遇FormatException错误求助

Flutter调用Flutterwave转账API返回HTML格式响应导致FormatException错误

错误信息

I/flutter (26554): FormatException: Unexpected character (at character 1)
I/flutter (26554): <html>
I/flutter (26554): ^

该请求在Postman中能正常返回成功响应,但Flutter代码执行时触发上述错误。以下是相关请求内容及代码:

Postman中可用的请求Payload

{
    "amount": 500,
    "narration": "Test Int'l bank transfers",
    "currency": "USD",
    "beneficiary_name": "Mark Cuban",
    "meta": [
        {
            "AccountNumber": "09182972BH",
            "RoutingNumber": "0000000002993",
            "SwiftCode": "ABJG190",
            "BankName": "BANK OF AMERICA, N.A., SAN FRANCISCO, CA",
            "BeneficiaryName": "Mark Cuban",
            "BeneficiaryAddress": "San Francisco, 4 Newton",
            "BeneficiaryCountry": "US"
        }
    ]
}

Postman返回的成功响应

{
    "status": "success",
    "message": "Transfer Queued Successfully",
    "data": {
        "id": 396427,
        "account_number": "FOREIGN-ACCOUNT",
        "bank_code": "FOREIGN-BANK",
        "full_name": "Mark Cuban",
        "created_at": "2023-03-10T20:30:52.000Z",
        "currency": "USD",
        "amount": 500,
        "fee": 40,
        "status": "NEW",
        "reference": "ebce2909fc3a57a7",
        "meta": [
            {
                "MerchantName": "TestLucas LLC",
                "SenderCountry": "NG",
                "AccountNumber": "FOREIGN-ACCOUNT",
                "RoutingNumber": "FOREIGN-BANK",
                "Sender": "TestLucas LLC",
                "Address": null,
                "MobileNumber": "23480000000000"
            }
        ],
        "narration": "Test Int'l bank transfers",
        "complete_message": "",
        "requires_approval": 0,
        "is_approved": 1,
        "bank_name": "FA-BANK"
    }
}

Flutter核心代码

class UsTransferProvider extends ChangeNotifier {
  final Future<SharedPreferences> _pref = SharedPreferences.getInstance();

  bool _isLoading = false;
  String _reqMessage = '';

  bool get isLoading => _isLoading;
  String get reqMessage => _reqMessage;

  void usTransfer({
    required String amount,
    required String narration,
    required String currency,
    required String bankName,
    required String accountNum,
    required String beneficiary_name,
    required String routingNumber,
    required String swiftCode,
    required String beneficiaryCountry,
    required String beneficiaryAddress,
  }) async {
    _isLoading = true;
    notifyListeners();

    String url = 'https://api.flutterwave.com/v3/transfers';

    String tokenUser = "FLWSECK_TEST-153740d351951b5f6d5ae8b903e0c467-X";
    final body = {
      "amount": amount,
      "narration": narration,
      "currency": currency,
      "beneficiary_name": beneficiary_name,
      "meta": [
        {
          "AccountNumber": accountNum,
          "RoutingNumber": routingNumber,
          "SwiftCode": swiftCode,
          "BankName": bankName,
          "BeneficiaryName": beneficiary_name,
          "BeneficiaryAddress": beneficiaryAddress,
          "BeneficiaryCountry": beneficiaryCountry,
        }
      ]
    };
    print(body);

    Map<String, String> headers = {
      'Content-Type': 'application/json',
      'Charset': 'utf-8',
      'Authorization': 'Bearer $tokenUser'
    };

    print(tokenUser);
    //making http request

    http.Response req = await http.post(Uri.parse(url),
        headers: headers, body: json.encode(body));

    try {
      if (req.statusCode == 200 || req.statusCode == 201) {
        print(req.body);
        _isLoading = false;
        _reqMessage = "Transfer Successful";
        pushNotification(
            'Money Transfer Complete', 'Successfully sent $amount');
        notifyListeners();
      } else {
        final res = json.decode(req.body);
        _isLoading = false;
        _reqMessage = '';
        notifyListeners();
      }
    } on SocketException catch (_) {
      _isLoading = false;
      _reqMessage = 'No Internet Connection';
      notifyListeners();
      print(_);
    } catch (e) {
      _isLoading = false;
      _reqMessage = 'Check your Internet connection';
      notifyListeners();

      print(e);
    }
  }

  void clear() {
    _reqMessage = '';
    notifyListeners();
  }
}

调用代码

Consumer<UsTransferProvider>(builder: (context, auth, child) {
            WidgetsBinding.instance.addPostFrameCallback((_) {
              if (auth.reqMessage != '') {
                showMessage(
                  message: auth.reqMessage,
                  context: context,
                );
              }
              auth.clear();
            });
            return customButton(
                tap: () {
                  if (amountController.text.isEmpty ||
                      narrationController.text.isEmpty ||
                      accountController.text.isEmpty ||
                      routingController.text.isEmpty ||
                      beneficiaryAddressController.text.isEmpty ||
                      beneficiaryCountryController.text.isEmpty ||
                      beneficiaryNameController.text.isEmpty ||
                      swifcodeController.text.isEmpty ||
                      _mybank.toString().isEmpty) {
                    showMessage(
                      message: "All field is required",
                      context: context,
                    );
                  } else {
                    auth.usTransfer(
                        amount: amountController.text,
                        narration: narrationController.text,
                        currency: selectCurrency,
                        bankName: _mybank.toString().trim(),
                        accountNum: accountController.text,
                        beneficiary_name: beneficiaryNameController.text,
                        routingNumber: routingController.text,
                        swiftCode: swifcodeController.text,
                        beneficiaryCountry: beneficiaryCountryController.text,
                        beneficiaryAddress: beneficiaryAddressController.text);
                  }
                },
                text: 'Send Money',
                textColor: white,
                color: primaryColor,
                status: auth.isLoading,
                borderColor: primaryColor);
          })

排查与解决步骤

  1. 打印完整响应内容定位问题
    错误提示返回HTML,说明接口可能返回了错误页面(如401、403、502)。在catch块中新增打印完整响应的代码,明确错误原因:

    catch (e) {
      _isLoading = false;
      _reqMessage = 'Request failed';
      notifyListeners();
      print('Full response: ${req.body}');
      print(e);
    }
    
  2. 修正参数类型差异
    Postman中amount是数字类型,但Flutter代码中传入的是字符串。将字符串转为数字后再入参:

    final body = {
      "amount": double.parse(amount), // 或int.parse(amount),根据API要求调整
      // 其他参数不变
    };
    
  3. 对齐请求头与Postman配置
    移除多余的Charset字段,保持请求头简洁,避免服务器拦截:

    Map<String, String> headers = {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer $tokenUser'
    };
    
  4. 检查网络环境差异
    移动端可能存在代理、VPN或运营商网络限制,导致请求被重定向到HTML页面。可切换网络(如WiFi换4G)测试,或直接在手机浏览器访问API域名确认连通性。

  5. 增加非JSON响应的容错处理
    先判断响应是否为JSON格式再解析,避免直接调用json.decode抛出异常:

    try {
      final res = json.decode(req.body);
      if (req.statusCode == 200 || req.statusCode == 201) {
        print(req.body);
        _isLoading = false;
        _reqMessage = "Transfer Successful";
        pushNotification('Money Transfer Complete', 'Successfully sent $amount');
      } else {
        _reqMessage = res['message'] ?? 'Transfer failed';
      }
      notifyListeners();
    } catch (e) {
      _isLoading = false;
      _reqMessage = 'Request error: ${req.statusCode}';
      notifyListeners();
      print('Non-JSON response: ${req.body}');
      print(e);
    }
    

内容的提问来源于stack exchange,提问作者Pamela

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:18:09