跳过主内容
教程

Capacitor 支付宝支付:遵守新苹果指南

了解如何在您的Capacitor应用中实现支付宝支付,处理数字商品支付,遵守苹果新指南的有效日期为2025年5月1日。

马丁·多纳迪厄

马丁·多纳迪厄

内容营销人员

Capacitor 支付宝支付:遵守新苹果指南

在Capacitor应用中实施Stripe支付链接:遵循新Apple指南

自2025年5月1日起,苹果公司根据《Epic v. Apple》案件的判决结果,对其App Store Review指南进行了重大修改 这些修改特别允许美国的应用开发者将数字产品和服务的外部支付方法链接到应用中改变了移动支付的历史

这条道路漫长而充满争议。它始于2020年8月,当时Epic Games,Fortnite的创造者,故意违反了苹果的App Store指南,通过在应用中直接支付来绕过苹果的30%佣金

苹果立即从App Store中移除了Fortnite,Epic随后提起了诉讼,挑战苹果对iOS应用分发和应用内支付的控制

经过多年的法律纠纷、上诉和反诉,法院最终裁定苹果必须允许开发者将用户指向应用外的替代支付方法

这项决定彻底改变了App Store生态系统的经济模型,这个模型自2008年成立以来一直沿用不变

What makes this ruling particularly significant is that it’s final and cannot be appealed further. The Supreme Court declined to hear Apple’s appeal in early 2025, cementing the lower court’s decision as the law of the land. This means developers can implement external payment methods with confidence that Apple cannot reverse this decision through further legal challenges.

法律确保平等的处理

最重要的是,这个判决明确指出苹果不能对使用外部支付方法的应用程序进行歧视。法院明确禁止苹果从事以下行为:

  1. 对使用外部支付方法的应用程序收取额外的费用或施加额外的要求
  2. 在搜索结果或推荐中给使用苹果内置IAP系统的应用程序优先处理
  3. 使用技术措施使外部支付体验不如苹果自己的系统
  4. 超出基本消费者信息的要求对使用外部支付方法的应用程序施加繁重的披露要求

这些明确的保护措施意味着开发者可以在不担心苹果会对他们采取隐晦报复或歧视的前提下,使用Stripe或其他外部支付提供商。竞争环境已经被法律平衡,苹果必须无论应用程序选择什么支付方法,都要对所有应用程序一视同仁。

本判决代表了对苹果围墙花园方法的最重大挑战,并标志着移动应用程序盈利方式的重大转变。对于那些长期抱怨苹果30%佣金(小企业降至15%)的开发者来说,这一判决提供了更高利润率和更大控制力来管理客户体验的途径。

使用Stripe而不是苹果内购的财务利益

本判决对开发者产生的财务影响是显著的:

  • 降低支付处理费苹果通常对内购收取30%的佣金(15%为小企业),而Stripe的费用仅为每笔交易2.9% + $0.30。这种差异可以显著提高您的收入率。

  • 加快付款与苹果相比,您通常需要等待45-90天才能收到资金。Stripe则在2-3个工作日内将付款存入您的银行账户。

  • 简化退款流程通过Stripe的控制台直接处理退款,而不是通过苹果的更复杂的退款系统。

这些成本节约和改善的现金流可以成为游戏的关键,尤其是对于较小的开发者和企业来说。

本文将探讨如何在您的Capacitor应用程序中实现Stripe支付链接,以利用这些新规则,同时确保遵守苹果的 更新指南.

This implementation is based on Stripe’s official documentation for Payment Links, adapted specifically for Capacitor apps.

理解新指南

App Store 的更新指南现在允许开发者将用户指向外部网站进行支付处理,特别是数字产品和订阅。这个变化目前仅适用于在美国 App Store 分发的应用。

要了解的关键点:

  1. 您现在可以在应用中链接到数字产品的外部支付选项
  2. 这仅适用于美国 App Store 的应用
  3. 您仍然必须遵守苹果的披露要求
  4. 您仍然负责所有客户支持和退款处理

让我们深入到技术实现:

首先,在您的 Stripe 控制台中创建付款链接:

  1. 导航到您的 Stripe 控制台中的付款链接部分
  2. 点击“+ 新建”以创建一个新的付款链接
  3. 定义您的产品或订阅详细信息
  4. 在“付款后”设置中,选择“不显示确认页面”
  5. 将通用链接设置为您的成功 URL(我们稍后会配置此设置)
  6. 点击“创建链接”以生成您的付款链接

为了在付款完成后将用户重定向回您的应用,请配置通用链接:

  1. 在您的域上创建一个 apple-app-site-association 文件
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appIDs": ["YOURTEAMID.com.yourdomain.yourapp"],
        "components": [
          {
            "/": "/checkout_redirect*",
            "comment": "Matches any URL whose path starts with /checkout_redirect"
          }
        ]
      }
    ]
  }
}
  1. 在本地托管这个文件 https://yourdomain.com/.well-known/apple-app-site-association

  2. 确保它以正确的 MIME 类型服务 application/json

  3. 配置您的 Capacitor 应用程序以处理 universal 链接,通过添加适当的特权。首先,在您的 capacitor.config.ts:

import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  // Your existing app configuration (appId, appName, etc.)
  plugins: {
    Geolocation: {
      // Request precise location access on iOS
      iosLocationAccuracy: 'reduced'
    }
  }
};

export default config;
  1. 向您的 Xcode 项目添加关联域名特权:
    • 打开您的 Xcode 项目
    • 选择您的应用程序目标
    • 转到“签名和功能”
    • 点击“+功能”并选择“关联域名”
    • 添加 applinks:yourdomain.com

步骤 3:创建一个 fallback 页面

创建一个 fallback 页面来处理应用程序未安装的情况:

<!DOCTYPE html>
<html>
<head>
  <title>Redirecting...</title>
  <meta http-equiv="refresh" content="0;url=https://yourdomain.com/app-download">
</head>
<body>
  <p>Redirecting to download page...</p>
</body>
</html>

步骤 4:在您的 Capacitor 应用程序中实现付款按钮

现在,添加支付按钮到您的应用中:

import { Capacitor } from '@capacitor/core';

export async function openPaymentLink(userEmail, userId) {
  // Use your actual Stripe payment link
  const baseUrl = 'https://buy.stripe.com/your_payment_link';
  
  // Add URL parameters to customize the experience
  const params = new URLSearchParams({
    prefilled_email: encodeURIComponent(userEmail),
    client_reference_id: userId
  });

  const fullUrl = `${baseUrl}?${params.toString()}`;
  
  // Simple window.open works in both web and Capacitor
  // Using _blank opens in Safari on iOS which is important for users with saved Stripe Link credentials
  window.open(fullUrl, '_blank');
}

为什么Safari很重要?:在Safari(通过 window.open)中打开支付链接,而不是在应用内浏览器中打开,这是有利的,因为那些以前使用Stripe Link保存了支付信息的用户会自动拥有他们的凭证。这会创建一个更平滑的结账体验,用户不需要重新输入信用卡信息,显著减少了摩擦和放弃率。

配置您的应用以处理在用户被重定向回来的情况下处理 universal links:

  1. 首先,安装 App 插件:
npm install @capacitor/app
  1. 在您的应用中注册 App 插件:
import { App } from '@capacitor/app';

// In your initialization code
App.addListener('appUrlOpen', (event) => {
  // Example URL: https://yourdomain.com/checkout_redirect?session_id=cs_test_...
  const url = new URL(event.url);
  
  if (url.pathname.startsWith('/checkout_redirect')) {
    // Extract any parameters you need
    const params = new URLSearchParams(url.search);
    const sessionId = params.get('session_id');
    
    // Handle successful payment
    if (sessionId) {
      // Verify the payment on your server if needed
      verifyPayment(sessionId);
      
      // Update UI to reflect successful purchase
      updatePurchaseStatus(true);
    }
  }
});

async function verifyPayment(sessionId) {
  // Call your backend to verify the payment
  // This is optional if you're relying on webhooks
}

function updatePurchaseStatus(success) {
  // Update your app UI to reflect purchase status
}

第 6 步:为订单完成设置 Webhook

最后,在您的服务器上配置一个 webhook 来处理成功支付:

// Using Express.js as an example
const express = require('express');
const stripe = require('stripe')('sk_test_your_stripe_secret_key');
const app = express();

// Use raw body parser for webhook signature verification
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const webhookSecret = 'whsec_your_webhook_secret';
  
  let event;
  
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
  } catch (err) {
    console.log(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  
  // Handle the checkout.session.completed event
  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    
    // Retrieve client_reference_id (your user ID)
    const userId = session.client_reference_id;
    
    // Grant access to the purchased content
    await grantAccess(userId, session.id);
  }
  
  res.status(200).send();
});

async function grantAccess(userId, sessionId) {
  // Your logic to grant access to the purchased content
  // This could be updating a database, sending a notification, etc.
}

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

Android 兼容性

让我们明确一下:Epic v. Apple 案例已经根本改变了移动支付的格局。它不仅直接影响 iOS 应用,还加强了 Android 开发者使用外部支付方法的位置。

Android 开发者现在可以完全信心地实施外部付款解决方案了。 苹果的裁决先例有效地保护了跨平台的开发者免受潜在的未来限制。这一法院裁决已经验证了许多Android开发者多年来一直在做的事情 - 提供具有较低费用的小费选项。

Google Play 商店一直比苹果更少限制外部付款方法,现在随着法律先例的建立,几乎没有风险在您的Android应用中实施Stripe或其他外部付款提供商。您可以继续这些实施,知道您站在坚实的法律基础上。

我们在iOS上所涵盖的实现几乎与Android设备相同。由于Google Play商店对外部付款方法没有苹果相同的限制,因此您可以使用相同的Stripe支付链接方法而无需特殊的披露对话框。

要处理深度链接(与iOS上的universal links相当),您需要:

  1. 在您的 AndroidManifest.xml 中设置App Links来处理重定向URL
  2. 在您的域上创建一个 .well-known/assetlinks.json 文件,包含您的应用的详细信息
  3. 使用相同的 appUrlOpen 监听器逻辑来处理成功付款

Capacitor的美妙之处在于,一旦您实施了平台特定的配置,实际付款流程code在两种平台上保持一致。

创建支付 UI

您可以将以下 Vue 支付按钮组件添加到您的 Capacitor 应用程序中:

<template>
  <div class="payment-container">
    <div class="pricing-card">
      <h2 class="mb-4 text-xl font-bold">{{ product.name }}</h2>
      <p class="mb-6 text-gray-600">{{ product.description }}</p>
      <div class="mb-6 price-tag">
        <span class="text-2xl font-bold">${{ product.price }}</span>
        <span v-if="product.isSubscription" class="text-sm text-gray-500">/month</span>
      </div>
      <button 
        @click="handlePayment" 
        class="py-3 w-full font-medium text-white bg-indigo-600 rounded-lg transition-colors hover:bg-indigo-700"
      >
        Purchase Now
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { Dialog } from '@capacitor/dialog';

const props = defineProps({
  product: {
    type: Object,
    required: true
  },
  userEmail: {
    type: String,
    default: ''
  },
  userId: {
    type: String,
    required: true
  }
});

const isLoading = ref(false);

async function showExternalPaymentDisclosure() {
  const { value } = await Dialog.confirm({
    title: 'Leaving App for Payment',
    message: 'You are about to leave this app to make a payment. Apple is not responsible for the privacy or security of payments that are not made through the App Store. All payment-related issues, including refunds, must be handled by our support team.',
    okButtonTitle: 'Continue',
    cancelButtonTitle: 'Cancel'
  });
  
  return value;
}

async function openPaymentLink() {
  // Use your actual Stripe payment link
  const baseUrl = 'https://buy.stripe.com/your_payment_link';
  
  // Add URL parameters to customize the experience
  const params = new URLSearchParams({
    prefilled_email: encodeURIComponent(props.userEmail),
    client_reference_id: props.userId
  });

  const fullUrl = `${baseUrl}?${params.toString()}`;
  
  // Simple window.open works in both web and Capacitor
  // Using _blank opens in Safari on iOS which is important for users with saved Stripe Link credentials
  window.open(fullUrl, '_blank');
}

async function handlePayment() {
  isLoading.value = true;
  try {
    // Only show the disclosure on iOS
    if (window.Capacitor?.getPlatform() === 'ios') {
      const userConfirmed = await showExternalPaymentDisclosure();
      if (!userConfirmed) return;
    }
    
    await openPaymentLink();
  } catch (error) {
    console.error('Payment error:', error);
    await Dialog.alert({
      title: 'Payment Error',
      message: 'There was an error initiating the payment. Please try again.'
    });
  } finally {
    isLoading.value = false;
  }
}
</script>

处理不同地区

由于新 Apple 指南仅适用于美国 App Store 应用程序,因此您需要策略来检测用户地区并应用适当的支付方法。使用 IP 地理位置的以下更可靠的方法:

import { Capacitor } from '@capacitor/core';

async function determinePaymentMethod() {
  // Always use Stripe for Android
  if (Capacitor.getPlatform() !== 'ios') {
    return 'external';
  }
  
  try {
    // Use a geolocation service to determine user's country
    const response = await fetch('https://ipapi.co/json/');
    const locationData = await response.json();
    
    // Check if the user is in the United States
    if (locationData.country_code === 'US') {
      return 'external'; // Can use Stripe Payment Links
    } else {
      return 'iap'; // Must use In-App Purchases
    }
  } catch (error) {
    console.error('Error detecting region:', error);
    return 'iap'; // Default to IAP to be safe
  }
}

export async function processPayment(product, userEmail, userId) {
  const paymentMethod = await determinePaymentMethod();
  
  if (paymentMethod === 'external') {
    // Use Stripe Payment Links
    await initiateExternalPayment(userEmail, userId);
  } else {
    // Use Apple's In-App Purchase
    await initiateInAppPurchase(product.appleProductId);
  }
}

该方法使用免费的 ipapi.co 服务根据用户的 IP 地址确定用户的国家。您也可以使用其他地理位置服务,如 MaxMind,或者在服务器端实现此检查以增加安全性。

注意:虽然该方法有效,但请记住 IP 地理位置并不总是 100% 准确。对于 mission-critical 应用程序,考虑使用多个检测方法或允许用户手动选择其地区。

使用 Capacitor 插件进行更准确的位置检测

要获得更准确的位置检测,使用 Capacitor Geolocation 插件和 @capgo/capacitor-nativegeocoder 可以确定用户的国家具有更高的精度:

  1. 首先安装所需的插件:
npm install @capacitor/geolocation @capgo/capacitor-nativegeocoder
  1. 在您的 Capacitor 项目中配置插件。将以下内容添加到您的 capacitor.config.ts:
import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  // Your existing app configuration (appId, appName, etc.)
  plugins: {
    Geolocation: {
      // Request precise location access on iOS
      iosLocationAccuracy: 'reduced'
    }
  }
};

export default config;
  1. 实现地理位置区域检测:
import { Capacitor } from '@capacitor/core';
import { Geolocation } from '@capacitor/geolocation';
import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder';

async function isUserInUSA() {
  try {
    // Request permission first
    const permissionStatus = await Geolocation.requestPermissions();
    
    if (permissionStatus.location === 'granted') {
      // Get current position
      const position = await Geolocation.getCurrentPosition({
        timeout: 10000,
        enableHighAccuracy: false
      });
      
      // Use NativeGeocoder to reverse geocode the coordinates
      const results = await NativeGeocoder.reverseGeocode({
        latitude: position.coords.latitude,
        longitude: position.coords.longitude,
        useLocale: true,
        maxResults: 1
      });
      
      if (results.addresses.length > 0) {
        // Check if the user is in the USA
        return results.addresses[0].countryCode === 'US';
      }
    }
    
    // If we couldn't determine location or permission denied, fall back to IP detection
    return await isUserInUSAByIP();
  } catch (error) {
    console.error('Error detecting location:', error);
    // Fall back to IP detection on error
    return await isUserInUSAByIP();
  }
}

async function isUserInUSAByIP() {
  try {
    const response = await fetch('https://ipapi.co/json/');
    const data = await response.json();
    return data.country_code === 'US';
  } catch (error) {
    console.error('Error detecting IP location:', error);
    return false; // Default to false to be safe
  }
}

export async function determinePaymentMethod() {
  // Always use Stripe for Android
  if (Capacitor.getPlatform() !== 'ios') {
    return 'external';
  }
  
  // Check if user is in the USA
  const isUSA = await isUserInUSA();
  return isUSA ? 'external' : 'iap';
}

export async function processPayment(product, userEmail, userId) {
  const paymentMethod = await determinePaymentMethod();
  
  if (paymentMethod === 'external') {
    // Use Stripe Payment Links
    await initiateExternalPayment(userEmail, userId);
  } else {
    // Use Apple's In-App Purchase
    await initiateInAppPurchase(product.appleProductId);
  }
}

本实现提供了一个更准确的方法来确定用户是否在物理上位于美国。它首先尝试使用设备的GPS和原生地理编码器来确定国家。如果这失败(由于权限问题或其他错误),它会回退到基于IP的检测。

请记住在你的 info.plist (iOS)和 AndroidManifest.xml (Android)文件中添加必要的权限:

对于iOS(ios/App/App/Info.plist):

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to determine which payment method to use based on regional availability.</string>

对于Android(android/app/src/main/AndroidManifest.xml):

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

使用这种方法可以为您提供最准确的方法来确定用户是否符合新Apple指南下的外部付款选项。

管理订阅

使用Stripe进行付款的关键优势是可以提供和管理订阅。以下是如何在你的Capacitor应用中处理订阅管理:

1. 创建订阅管理页面

在你的应用中添加一个订阅管理页面来显示用户的活跃订阅:

<template>
  <div class="subscription-manager">
    <div v-if="isLoading" class="loading-indicator">
      Loading subscription data...
    </div>
    
    <div v-else-if="subscription" class="subscription-info">
      <h2 class="mb-4 text-xl font-bold">Your Subscription</h2>
      
      <div class="mb-6 plan-details">
        <p><span class="font-medium">Plan:</span> {{ subscription.planName }}</p>
        <p><span class="font-medium">Status:</span> {{ subscription.status }}</p>
        <p><span class="font-medium">Renews:</span> {{ formatDate(subscription.currentPeriodEnd) }}</p>
      </div>
      
      <button 
        @click="manageSubscription" 
        class="py-3 w-full font-medium text-white bg-indigo-600 rounded-lg transition-colors hover:bg-indigo-700"
      >
        Manage Subscription
      </button>
    </div>
    
    <div v-else class="no-subscription">
      <p class="mb-4">You don't have an active subscription.</p>
      <button 
        @click="goToPricingPage" 
        class="py-3 w-full font-medium text-white bg-indigo-600 rounded-lg transition-colors hover:bg-indigo-700"
      >
        View Plans
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { getUserSubscription } from '../services/subscription';

const subscription = ref(null);
const isLoading = ref(true);

onMounted(async () => {
  try {
    const userData = await getUserSubscription();
    subscription.value = userData.subscription;
  } catch (error) {
    console.error('Failed to load subscription:', error);
  } finally {
    isLoading.value = false;
  }
});

function formatDate(timestamp) {
  return new Date(timestamp * 1000).toLocaleDateString();
}

function manageSubscription() {
  // Open Stripe Customer Portal
  window.open(subscription.value.portalUrl, '_blank');
}

function goToPricingPage() {
  // Navigate to pricing page
  // router.push('/pricing');
}
</script>

2. 客户端门户管理订阅

Stripe 提供了一个客户端门户,允许用户管理他们的订阅。您可以从您的服务器创建一个指向此门户的链接:

// Server-side code (Node.js)
const stripe = require('stripe')('sk_your_stripe_secret_key');

async function createPortalSession(customerId) {
  const session = await stripe.billingPortal.sessions.create({
    customer: customerId,
    return_url: 'https://yourdomain.com/account',
  });
  
  return session.url;
}

确保应用商店遵守

要确保您的实现符合苹果的指南:

  1. 包含适当的关于外部购买的披露
  2. 实现一个模态窗口,通知用户他们离开了应用(苹果要求)
  3. 不要试图绕过苹果对应用内购买的佣金
  4. 清晰地向用户说明苹果不负责交易

这是实现所需披露模态窗口的示例

import { Dialog } from '@capacitor/dialog';

async function showExternalPaymentDisclosure() {
  const { value } = await Dialog.confirm({
    title: 'Leaving App for Payment',
    message: 'You are about to leave this app to make a payment. Apple is not responsible for the privacy or security of payments that are not made through the App Store. All payment-related issues, including refunds, must be handled by our support team.',
    okButtonTitle: 'Continue',
    cancelButtonTitle: 'Cancel'
  });
  
  return value;
}

export async function initiateExternalPayment(userEmail, userId) {
  const userConfirmed = await showExternalPaymentDisclosure();
  
  if (userConfirmed) {
    await openPaymentLink(userEmail, userId);
  }
}

测试您的实现

点击您的支付按钮,在您的应用中,应该显示披露并打开Stripe 支付页面

  1. 点击您的支付按钮,在您的应用中,应该显示披露并打开Stripe 支付页面
  2. 使用 Stripe 测试卡完成测试付款 4242 4242 4242 4242
  3. 付款后,您应该通过 universal 链接返回到您的应用
  4. 检查您的 webhook 是否接收到了 checkout.session.completed 事件

结论

在 iOS 应用中使用外部付款选项购买数字商品是一个重大变化,给开发者带来了更多的灵活性。虽然这个变化目前只适用于美国 App Store 的应用,但它提供了一个重要的替代方案,避免了 Apple 的内购系统。

通过使用 Stripe Payment Links 和 Capacitor,您可以快速实现一个流畅的支付体验,同时保持了遵守 Apple 指引的兼容性。对于美国以外的用户,Apple 的内购系统仍然适用, @capgo/native-purchases 实现了 StoreKit 和 Google Play Billing 直接接口。这一方法还给您带来了 Stripe 强大的支付基础设施、更低的处理费率(3% vs 30%)以及更快的结算(天而不是月)相比于 Apple 的内购系统。

请记住,您需要直接处理所有客户支持和退款问题,因为这些交易发生在 Apple 的生态系统之外。

您是否在您的 Capacitor 应用中实现了 Stripe Payment Links?在评论中分享您的体验!

常见问题解答

Q: 这种方法是否符合苹果的指南?
A: 是的,截至2025年5月1日,苹果允许在美国App Store分发的应用程序中链接到外部支付方法,提供必要的披露信息.

Q: 使用外部支付方法时,我是否需要支付苹果的佣金?
A: 不,新规则的一个主要好处是,处理在苹果系统外的支付不受苹果佣金的约束.

Q: 我的公司是否需要位于美国才能利用这些新规则?
A: 不,任何公司,无论其所在地在世界的哪个角落,只要您的应用程序在美国App Store可用,且用户位于美国,您就可以实施外部支付方法。

Q: 如果一个位于美国以外的用户尝试使用外部支付选项会发生什么?
A: 您应该实现区域检测(如文章中所示),只向美国用户提供外部支付选项。对于其他地区,您应该继续使用苹果的内购系统.

Q: 我可以使用这个来购买实物商品或在应用程序外部消费的服务吗?
A: 是的,苹果一直允许在应用外部使用的物理商品和服务(如出租车或食品配送)进行外部付款方法。

从 Stripe Payments 在 Capacitor: 新苹果指南继续。

如果您正在使用 Stripe Payments 在 Capacitor: 新苹果指南 来规划安全性和合规性,连接它与 加密 加密 在加密中实现细节 合规 Capgo Security Scanner Capgo 安全扫描器 在 Capgo 安全扫描器中产品工作流程 为产品工作流程在Capgo安全中 Capgo信任中心 为产品工作流程在Capgo信任中心中

Capacitor 应用的实时更新

当 web-layer 的 bug 活跃时,通过 Capgo 将修复推送到用户,而不是等待几天的应用商店审批。用户在后台接收更新,而原生变化保持在正常的审批路径中。

立即开始

最新博客文章

Capgo 为您提供创建真正专业的移动应用所需的最佳见解。