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

Flutter Web部署后Razorpay支付页面404错误解决方案求助

Flutter Web部署服务器后Razorpay支付页面404问题解决方案

核心问题原因

本地Web、Android/iOS端正常,但服务器部署后出现404,本质是**assets/payments.html路径无法被服务器正确解析**:本地Flutter环境会自动映射assets资源路径,但服务器部署时,要么assets未被正确打包到部署目录,要么相对路径在服务器环境下失效。


解决方案1:修正Assets路径与打包配置

1. 确认Assets声明

确保payments.html在pubspec.yaml中正确注册:

flutter:
  assets:
    - assets/payments.html

2. 替换为绝对路径加载IFrame

将Flutter代码中IFrame的src从相对路径改为基于服务器根目录的绝对路径,避免环境差异导致的路径错误:

// 替换原来的element.src赋值
element.src = '${window.location.origin}/assets/payments.html?name=${widget.name}&price=${widget.price}&keyrzor=${widget.keyrzor}&orderId=${widget.orderId}&email=${widget.email}&mobileNo=${widget.mobileNo}';

3. 服务器配置检查

  • 确保服务器(Nginx/Apache等)允许访问/assets目录下的静态文件,无权限限制。
  • Linux服务器注意路径大小写,确认文件名与代码中的引用完全一致。

解决方案2:直接通过JS注入初始化Razorpay(推荐)

彻底抛弃本地HTML文件,直接在Flutter Web中调用Razorpay JS SDK,从根源解决路径问题:

步骤1:在Web入口引入SDK

在web/index.html的<head>标签内添加Razorpay脚本:

<script src="https://checkout.razorpay.com/v1/checkout.js"></script>

步骤2:重构Flutter支付页面代码

import 'dart:js' as js;
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:dio/dio.dart';

class AcademyWebpayment extends StatefulWidget {
  final String? name;
  final String? keyrzor;
  final int? price;
  final String? orderId;
  final String? email;
  final String? mobileNo;
  final int? subscriptionId;

  AcademyWebpayment({
    this.name,
    this.price,
    this.keyrzor,
    this.orderId,
    this.email,
    this.mobileNo,
    this.subscriptionId,
  });

  @override
  _AcademyWebpaymentState createState() => _AcademyWebpaymentState();
}

class _AcademyWebpaymentState extends State<AcademyWebpayment> {
  bool _isLoading = false;

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

  void _initRazorpay() {
    // 构造Razorpay配置参数
    final options = js.JsObject.jsify({
      "key": widget.keyrzor,
      "amount": widget.price,
      "currency": "INR",
      "name": widget.name,
      "description": "Sports",
      "order_id": widget.orderId,
      "prefill": {
        "name": widget.name,
        "email": widget.email,
        "contact": "+91-${widget.mobileNo}",
      },
      "notes": {"address": "Autofy"},
      "theme": {"color": "#DF0145"},
      "modal": {"ondismiss": _handlePaymentClose}
    });

    // 创建Razorpay实例并绑定回调
    final rzp = js.JsObject(js.context['Razorpay'], [options]);
    rzp.callMethod('on', ['payment.success', _handlePaymentSuccess]);
    rzp.callMethod('on', ['payment.error', (_) => _handlePaymentClose()]);

    // 打开支付弹窗
    rzp.callMethod('open');
  }

  void _handlePaymentSuccess(js.JsObject response) async {
    setState(() => _isLoading = true);

    final paymentId = response['razorpay_payment_id'] as String;
    final orderId = response['razorpay_order_id'] as String;

    SharedPreferences prefs = await SharedPreferences.getInstance();
    int? userId = prefs.getInt('userId');

    final requestBody = {
      "status": "paid",
      "user_id": userId,
      "order_id": orderId,
      "payment_id": paymentId,
      "subscription_id": widget.subscriptionId,
    };

    try {
      Dio dio = Dio();
      final resp = await dio.put(
        "https://api.replsports.com/api/academySubscription/payment",
        data: requestBody,
      );

      setState(() => _isLoading = false);
      if (resp.statusCode == 200) {
        Navigator.push(context, MaterialPageRoute(builder: (_) => BottomNavigation()));
        Fluttertoast.showToast(msg: "Paid Successfully");
      } else {
        Fluttertoast.showToast(msg: "Payment Failed!");
      }
    } catch (e) {
      setState(() => _isLoading = false);
      Fluttertoast.showToast(msg: "Payment Failed!");
      print('Payment processing error: $e');
    }
  }

  void _handlePaymentClose() {
    Navigator.pop(context);
    Fluttertoast.showToast(msg: "Payment Failed!");
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        const SizedBox.expand(), // 空占位容器
        if (_isLoading)
          Container(
            color: Colors.black.withOpacity(0.5),
            child: const Center(child: CircularProgressIndicator()),
          ),
      ],
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 01:03:11