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

如何在React Native与Laravel后端的Stripe支付中添加iDEAL支付方式

为Laravel+React Native支付系统添加iDEAL支付方式

现有实现代码

React Native 前端代码

try {
  const response = await axios.post(
    `${API_BASE_URL}/store-order`,
    {
      company_id: cart[0].company_id,
      name: name,
      address: address,
      phone: phone,
      email: email,
      company: company,
      products: cart,
      price: getTotalPrice(),
      // delivery_at: delivery_at,
    },
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
    }
  );

  if (response.data) {
    const { clientSecret, orderId } = response.data;

    const { error } = await initPaymentSheet({
      paymentIntentClientSecret: clientSecret,
    });

    if (!error) {
      // Handle the confirmation of payment here
      handlePayment(clientSecret, orderId);
    }
  }
}

const handlePayment = async (clientSecret, orderId) => {

const { error } = await presentPaymentSheet({ clientSecret });

if (error) {
  const newClientSecret = await fetchNewClientSecret(orderId);

  if (newClientSecret) {
    // Re-initialize the PaymentSheet with the new client secret
    const { error: initError } = await initPaymentSheet({
      paymentIntentClientSecret: newClientSecret,
    });

    if (initError) {
      console.log(
        `PaymentSheet re-initialization failed: ${initError.message}`
      );
    } else {
      // Try the payment again
      await handlePayment(newClientSecret, orderId);
    }
  } else {
    console.log("Failed to fetch a new client secret");
  }
} else {
  const response = await axios.post(
    `${API_BASE_URL}/pay-order`,
    {
      order_id: orderId,
    },
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
    }
  );

  if (response.data.message == "Order paid.") {
    navigation.navigate("DrawerNavigation");
  } else {
    Alert.alert("Payment Failed", "Do you want to retry?", [
      { text: "No", style: "cancel" },
      { text: "Yes", onPress: () => handlePayment(clientSecret, orderId) },
    ]);
  }
}

Laravel 后端代码

store-order 接口

Stripe::setApiKey(env('STRIPE_SECRET'));

$paymentIntent = PaymentIntent::create([
    'amount' => $price * 100, // amount should be in cents
    'currency' => 'eur',
]);

pay-order 接口

Stripe::setApiKey(env('STRIPE_SECRET'));
$paymentIntent = PaymentIntent::retrieve($order->payment_intent);

实现iDEAL支付的修改步骤

1. Laravel后端调整

1.1 更新PaymentIntent创建逻辑

iDEAL属于异步跳转类支付,需要在创建PaymentIntent时指定支持的支付方式、关联订单元数据,并配置用户支付完成后的跳转地址:

Stripe::setApiKey(env('STRIPE_SECRET'));

// 先创建订单实例,保存用户提交的基础订单信息
$order = // 你的订单创建逻辑

$paymentIntent = PaymentIntent::create([
    'amount' => $price * 100, // 金额以分为单位
    'currency' => 'eur',
    'payment_method_types' => ['card', 'ideal'], // 同时支持信用卡和iDEAL
    'metadata' => [
        'order_id' => $order->id, // 关联订单ID,用于webhook处理
    ],
    'return_url' => 'your-app-deep-link://payment-complete?orderId=' . $order->id, // 替换为你的App Deep Link
]);

// 将PaymentIntent ID保存到订单
$order->payment_intent_id = $paymentIntent->id;
$order->save();

// 返回客户端所需参数
return response()->json([
    'clientSecret' => $paymentIntent->client_secret,
    'orderId' => $order->id
]);

1.2 配置Stripe Webhook处理异步状态

iDEAL支付结果由Stripe通过webhook异步通知,需实现payment_intent.succeeded和payment_intent.payment_failed事件的处理:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Stripe\Stripe;
use Stripe\Webhook;

class StripeWebhookController extends Controller
{
    public function handleWebhook(Request $request)
    {
        Stripe::setApiKey(env('STRIPE_SECRET'));
        
        $payload = $request->getContent();
        $sigHeader = $request->header('Stripe-Signature');
        $webhookSecret = env('STRIPE_WEBHOOK_SECRET');

        try {
            $event = Webhook::constructEvent(
                $payload, $sigHeader, $webhookSecret
            );
        } catch(\UnexpectedValueException $e) {
            return response()->json(['error' => 'Invalid payload'], 400);
        } catch(\Stripe\Exception\SignatureVerificationException $e) {
            return response()->json(['error' => 'Invalid signature'], 400);
        }

        // 处理支付成功事件
        if ($event->type === 'payment_intent.succeeded') {
            $paymentIntent = $event->data->object;
            $order = \App\Models\Order::find($paymentIntent->metadata['order_id']);
            
            if ($order) {
                $order->status = 'paid';
                $order->save();
            }
        }

        // 处理支付失败事件
        if ($event->type === 'payment_intent.payment_failed') {
            $paymentIntent = $event->data->object;
            $order = \App\Models\Order::find($paymentIntent->metadata['order_id']);
            
            if ($order) {
                $order->status = 'payment_failed';
                $order->save();
            }
        }

        return response()->json(['status' => 'success']);
    }
}

1.3 修改pay-order接口逻辑

由于iDEAL支付是异步的,需改为检查PaymentIntent或订单的实际状态:

Stripe::setApiKey(env('STRIPE_SECRET'));

$order = \App\Models\Order::find(request()->order_id);
if (!$order) {
    return response()->json(['message' => 'Order not found'], 404);
}

$paymentIntent = PaymentIntent::retrieve($order->payment_intent_id);

if ($paymentIntent->status === 'succeeded') {
    if ($order->status !== 'paid') {
        $order->status = 'paid';
        $order->save();
    }
    return response()->json(['message' => 'Order paid.']);
} elseif ($paymentIntent->status === 'requires_action') {
    return response()->json(['message' => 'Payment requires action', 'clientSecret' => $paymentIntent->client_secret]);
} else {
    return response()->json(['message' => 'Payment not completed yet.']);
}

2. React Native前端调整

2.1 完善PaymentSheet初始化配置

添加商家名称和返回地址,确保iDEAL支付流程正常:

const { error } = await initPaymentSheet({
  paymentIntentClientSecret: clientSecret,
  merchantDisplayName: '你的商家名称', // 显示在支付界面
  returnURL: 'your-app-deep-link://payment-complete', // 和后端配置一致
});

2.2 监听Deep Link并轮询订单状态

用户完成iDEAL支付后会通过Deep Link返回App,需监听该链接并轮询订单状态:

import { Linking, useEffect } from 'react-native';

// 组件挂载时监听Deep Link
useEffect(() => {
  const subscription = Linking.addEventListener('url', handleDeepLink);
  return () => subscription.remove();
}, []);

const handleDeepLink = async (event) => {
  const url = event.url;
  if (url.includes('payment-complete')) {
    const params = new URLSearchParams(new URL(url).search);
    const orderId = params.get('orderId');
    await pollOrderStatus(orderId);
  }
};

// 轮询订单状态函数
const pollOrderStatus = async (orderId) => {
  const interval = setInterval(async () => {
    try {
      const response = await axios.post(
        `${API_BASE_URL}/pay-order`,
        { order_id: orderId },
        { headers: { Authorization: `Bearer ${token}` } }
      );

      if (response.data.message === 'Order paid.') {
        clearInterval(interval);
        navigation.navigate("DrawerNavigation");
      } else if (response.data.message.includes('Payment failed')) {
        clearInterval(interval);
        Alert.alert("Payment Failed", "Do you want to retry?", [
          { text: "No", style: "cancel" },
          { text: "Yes", onPress: () => handlePayment(clientSecret, orderId) },
        ]);
      }
    } catch (err) {
      console.error('Error checking order status:', err);
    }
  }, 3000); // 每3秒查询一次
};

2.3 调整handlePayment函数逻辑

移除支付完成后立即调用pay-order的逻辑,改为启动状态轮询:

const handlePayment = async (clientSecret, orderId) => {
  const { error } = await presentPaymentSheet({ clientSecret });

  if (error) {
    // 保留原有错误处理逻辑
    const newClientSecret = await fetchNewClientSecret(orderId);

    if (newClientSecret) {
      const { error: initError } = await initPaymentSheet({
        paymentIntentClientSecret: newClientSecret,
      });

      if (initError) {
        console.log(`PaymentSheet re-initialization failed: ${initError.message}`);
      } else {
        await handlePayment(newClientSecret, orderId);
      }
    } else {
      console.log("Failed to fetch a new client secret");
    }
  } else {
    // 启动订单状态轮询
    await pollOrderStatus(orderId);
  }
};

注意事项

  • 需在Stripe Dashboard中启用iDEAL支付方式。
  • 测试iDEAL可使用Stripe提供的测试银行账户(如NL91ABNA0417164300)。
  • 需完成React Native的Deep Link配置(iOS修改Info.plist,Android修改AndroidManifest.xml),确保用户支付后能正确跳转回App。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:33:05