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

Flutter网站集成Stripe测试模式遇「不支持操作系统」错误求助

解决Flutter Web集成Stripe测试模式的「Unsupported operation: Platform_operating System」错误

问题描述

尝试将Stripe测试账户接入Flutter网站,代码无语法错误但无法正常运行:支付网关不显示,弹出错误提示「Unsupported operation: Platform_operating System」。错误截图如下:

错误提示截图

原代码如下:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart' show kIsWeb;

class PaymentPage extends StatefulWidget {
  @override
  _PaymentPageState createState() => _PaymentPageState();
}

class _PaymentPageState extends State<PaymentPage> {
  Map<String, dynamic>? paymentIntent;

  @override
  void initState() {
    super.initState();
    Stripe.publishableKey = kIsWeb
        ? "pk_test_your_key_for_web" 
        : "pk_test_your_key_for_mobile";
  }

  Future<void> _startPayment() async {
    try {
      // Check if the platform is web
      if (kIsWeb) {
        Fluttertoast.showToast(
          msg: 'Web platform is not supported for payments',
        );
        return;
      }

      paymentIntent = await createPaymentIntent('100', 'INR');

      await Stripe.instance.initPaymentSheet(
        paymentSheetParameters: SetupPaymentSheetParameters(
          billingDetails: BillingDetails(
            name: 'YOUR NAME',
            email: 'YOUREMAIL@gmail.com',
          ),
          paymentIntentClientSecret: paymentIntent!['client_secret'],
        ),
      );

      // Display Payment sheet
      await displayPaymentSheet();
    } catch (e) {
      print(e.toString());
      Fluttertoast.showToast(msg: e.toString());
    }
  }

  Future<void> displayPaymentSheet() async {
    try {
      // Display the payment sheet.
      await Stripe.instance.presentPaymentSheet();

      Fluttertoast.showToast(msg: 'Payment successfully completed');
    } on Exception catch (e) {
      if (e is StripeException) {
        Fluttertoast.showToast(
          msg: 'Error from Stripe: ${e.error.localizedMessage}',
        );
      } else {
        Fluttertoast.showToast(msg: 'Unforeseen error: ${e}');
      }
    }
  }

  Future<Map<String, dynamic>> createPaymentIntent(
      String amount, String currency) async {
    try {
      Map<String, dynamic> body = {
        'amount': calculateAmount(amount),
        'currency': currency,
      };

      var response = await http.post(
        Uri.parse('https://api.stripe.com/v1/payment_intents'),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: body,
      );
      return json.decode(response.body);
    } catch (err) {
      throw Exception(err.toString());
    }
  }

  String calculateAmount(String amount) {
    final calculatedAmount = (int.parse(amount)) * 100;
    return calculatedAmount.toString();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stripe Payment Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: _startPayment,
          child: Text('Make Payment'),
        ),
      ),
    );
  }
}

错误原因

  1. Web端初始化方式错误:直接给Stripe.publishableKey赋值仅支持移动端,Web端需要调用Stripe.instance.init()完成初始化。
  2. 支付组件不兼容:Payment Sheet仅支持iOS/Android平台,Web端需使用Stripe Elements组件实现支付。
  3. API调用不安全且无效:前端直接调用Stripe创建Payment Intent的接口,缺少Secret Key验证会请求失败,同时暴露Secret Key存在严重安全风险。

解决方案

步骤1:修正Stripe初始化逻辑

在initState中针对Web平台单独初始化:

@override
void initState() {
  super.initState();
  _initStripe();
}

Future<void> _initStripe() async {
  if (kIsWeb) {
    await Stripe.instance.init(
      publishableKey: "pk_test_your_key_for_web",
      stripeAccountId: null,
    );
  } else {
    Stripe.publishableKey = "pk_test_your_key_for_mobile";
  }
}

步骤2:实现Web端专属支付流程

使用Stripe Elements替代Payment Sheet,新增Web端支付方法:

Future<void> _webPayment() async {
  try {
    // 调用后端接口创建Payment Intent(必须通过后端处理)
    final paymentIntent = await _createPaymentIntentThroughBackend('100', 'INR');
    final clientSecret = paymentIntent['client_secret'];

    // 创建Stripe Elements实例与卡片组件
    final elements = Stripe.instance.elements.create();
    final cardElement = elements.create('card');

    // 将卡片组件挂载到Web页面的DOM容器
    await cardElement.mount('#card-element');

    // 确认支付
    final paymentResult = await Stripe.instance.confirmCardPayment(
      clientSecret,
      PaymentMethodParams.card(
        paymentMethodData: PaymentMethodData(
          billingDetails: BillingDetails(
            name: 'YOUR NAME',
            email: 'YOUREMAIL@gmail.com',
          ),
        ),
      ),
    );

    // 处理支付结果
    if (paymentResult.paymentIntentStatus == PaymentIntentStatus.succeeded) {
      Fluttertoast.showToast(msg: '支付成功');
    } else {
      Fluttertoast.showToast(msg: '支付失败');
    }

    // 支付完成后卸载卡片组件
    await cardElement.unmount();
  } catch (e) {
    Fluttertoast.showToast(msg: e.toString());
  }
}

步骤3:搭建后端接口创建Payment Intent

前端不能直接调用Stripe API,必须通过后端处理,以下是Node.js后端示例:

const stripe = require('stripe')('sk_test_your_secret_key');
const express = require('express');
const app = express();

app.use(express.json());

app.post('/create-payment-intent', async (req, res) => {
  const { amount, currency } = req.body;

  const paymentIntent = await stripe.paymentIntents.create({
    amount: parseInt(amount) * 100,
    currency: currency,
  });

  res.json({ client_secret: paymentIntent.client_secret });
});

app.listen(3000, () => console.log('Server running on port 3000'));

前端调用该后端接口的方法:

Future<Map<String, dynamic>> _createPaymentIntentThroughBackend(String amount, String currency) async {
  try {
    final response = await http.post(
      Uri.parse('http://localhost:3000/create-payment-intent'),
      headers: {'Content-Type': 'application/json'},
      body: json.encode({'amount': amount, 'currency': currency}),
    );
    return json.decode(response.body);
  } catch (err) {
    throw Exception(err.toString());
  }
}

步骤4:更新支付触发逻辑

在_startPayment中根据平台调用对应支付方法:

Future<void> _startPayment() async {
  try {
    if (kIsWeb) {
      await _webPayment();
      return;
    }

    // 保留原有移动端Payment Sheet逻辑
    paymentIntent = await _createPaymentIntentThroughBackend('100', 'INR');

    await Stripe.instance.initPaymentSheet(
      paymentSheetParameters: SetupPaymentSheetParameters(
        billingDetails: BillingDetails(
          name: 'YOUR NAME',
          email: 'YOUREMAIL@gmail.com',
        ),
        paymentIntentClientSecret: paymentIntent!['client_secret'],
      ),
    );

    await displayPaymentSheet();
  } catch (e) {
    print(e.toString());
    Fluttertoast.showToast(msg: e.toString());
  }
}

步骤5:Web端页面添加DOM容器

在Flutter Web的index.html中添加卡片组件挂载的容器:

<div id="card-element" style="width: 100%; padding: 16px; margin-bottom: 16px;"></div>

修正后的完整代码

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart' show kIsWeb;

class PaymentPage extends StatefulWidget {
  @override
  _PaymentPageState createState() => _PaymentPageState();
}

class _PaymentPageState extends State<PaymentPage> {
  Map<String, dynamic>? paymentIntent;

  @override
  void initState() {
    super.initState();
    _initStripe();
  }

  Future<void> _initStripe() async {
    if (kIsWeb) {
      await Stripe.instance.init(
        publishableKey: "pk_test_your_key_for_web",
        stripeAccountId: null,
      );
    } else {
      Stripe.publishableKey = "pk_test_your_key_for_mobile";
    }
  }

  Future<void> _startPayment() async {
    try {
      if (kIsWeb) {
        await _webPayment();
        return;
      }

      paymentIntent = await _createPaymentIntentThroughBackend('100', 'INR');

      await Stripe.instance.initPaymentSheet(
        paymentSheetParameters: SetupPaymentSheetParameters(
          billingDetails: BillingDetails(
            name: 'YOUR NAME',
            email: 'YOUREMAIL@gmail.com',
          ),
          paymentIntentClientSecret: paymentIntent!['client_secret'],
        ),
      );

      await displayPaymentSheet();
    } catch (e) {
      print(e.toString());
      Fluttertoast.showToast(msg: e.toString());
    }
  }

  Future<void> _webPayment() async {
    try {
      final paymentIntent = await _createPaymentIntentThroughBackend('100', 'INR');
      final clientSecret = paymentIntent['client_secret'];

      final elements = Stripe.instance.elements.create();
      final cardElement = elements.create('card');

      await cardElement.mount('#card-element');

      final paymentResult = await Stripe.instance.confirmCardPayment(
        clientSecret,
        PaymentMethodParams.card(
          paymentMethodData: PaymentMethodData(
            billingDetails: BillingDetails(
              name: 'YOUR NAME',
              email: 'YOUREMAIL@gmail.com',
            ),
          ),
        ),
      );

      if (paymentResult.paymentIntentStatus == PaymentIntentStatus.succeeded) {
        Fluttertoast.showToast(msg: '支付成功');
      } else {
        Fluttertoast.showToast(msg: '支付失败');
      }

      await cardElement.unmount();
    } catch (e) {
      Fluttertoast.showToast(msg: e.toString());
    }
  }

  Future<void> displayPaymentSheet() async {
    try {
      await Stripe.instance.presentPaymentSheet();
      Fluttertoast.showToast(msg: '支付成功');
    } on Exception catch (e) {
      if (e is StripeException) {
        Fluttertoast.showToast(
          msg: 'Stripe错误: ${e.error.localizedMessage}',
        );
      } else {
        Fluttertoast.showToast(msg: '未知错误: ${e}');
      }
    }
  }

  Future<Map<String, dynamic>> _createPaymentIntentThroughBackend(String amount, String currency) async {
    try {
      final response = await http.post(
        Uri.parse('http://localhost:3000/create-payment-intent'),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({'amount': amount, 'currency': currency}),
      );
      return json.decode(response.body);
    } catch (err) {
      throw Exception(err.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stripe支付示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Web端显示卡片输入区域
            if (kIsWeb)
              const SizedBox(
                width: 300,
                height: 50,
                child: HtmlElementView(viewType: 'card-element'),
              ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _startPayment,
              child: const Text('发起支付'),
            ),
          ],
        ),
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:39:57