Skip to main content
강의

Capacitor에서 스티브支付: 새로운 애플 지침

Capacitor 앱에서 디지털 상품 결제를 처리하기 위해 새로운 애플 지침에 따라 스티브 결제 링크를 implement하는 방법을 배웁니다.

마틴 도나디유

마틴 도나디유

콘텐츠 마케터

Capacitor에서 스티브支付: 새로운 애플 지침

Capacitor 앱에서 새로운 애플 지침에 따라 스티브 결제 링크를 implement하는 방법

2025년 5월 1일부터 애플은 에픽 v. 애플 사례의 법정 판결을 따라 앱 스토어 리뷰 지침을 크게 변경했습니다. 이 변경 사항은 미국 앱 개발자에게 디지털 상품과 서비스에 대한 외부 결제 방법에 대한 링크를 제공하는 것을 허용하여 애플의 인앱 구매 시스템에 대한 대안을 열어줍니다. 에픽 v. 애플 사례.

모바일 결제를 영원히 바꾼 эп피크 전투

이 순간에 이르기까지의 길은 오랜 시간과 논쟁이 많았습니다. 모든 것이 2020년 8월에 시작되었습니다. Epic Games, Fortnite라는 인기 있는 게임의 제작자, Apple의 앱 스토어 지침을 위반하기 위해 의도적으로 직접 결제 옵션을 구현했습니다. Apple은 Fortnite를 앱 스토어에서 제거하고, Epic은 iOS 앱 배포 및 인앱 결제에 대한 Apple의 통제를 도전하기 위해 소송을 제기했습니다.

법적 투쟁, 항소, 반항소가 수년 동안 지속된 후, 법원은 iOS 앱 배포 및 인앱 결제에 대한 Apple의 통제를 도전하는 앱 개발자가 직접 사용자에게 대안적인 결제 방법을 알려줄 수 있도록 하라는 결정을 내렸습니다. 이 결정은 2008년부터 앱 스토어 생태계가 운영해온 기본적인 금융 모델을 바꾸게 됩니다.

최종 판결 - 더 이상 항소할 수 없습니다.

이 판결이 특히 중요한 이유는 그것이 최종적이고 더 이상 항소할 수 없다는 것입니다. Apple의 항소가 2025년 초에 미국 대법원에서 거부되면서, 하급 법원의 판결이 법률의 원칙이 됩니다. 개발자는 Apple이 이 결정을 더 이상 법적 도전으로 뒤집을 수 없다는 것을 믿고 외부 결제 방법을 구현할 수 있습니다.

법률에 의해 보장되는 평등한 대우

가장 중요한 것은 판결이 앱이 외부 결제 방법을 사용하는 앱에 대해 차별할 수 없다는 것을 명확히 밝히고 있다는 것입니다. 법원은 Apple이 다음과 같은 것을 금지했다고 명시했습니다:

  1. 외부 결제 방법을 사용하는 앱에 추가 요금을 부과하거나 추가 요구 사항을 부과하는 것
  2. Apple의 IAP 시스템을 독점적으로 사용하는 앱에 선호적 처리를 제공하거나 특집을 제공하는 것
  3. Apple의 시스템보다 외부 결제 경험을 불리하게 만드는 기술적 조치를 취하는 것
  4. 기본적인 소비자 정보 이외의 부담스러운 공개 요구를 부과하는 것

이러한 명백한 보호 조치로 개발자는 스티브의 다른 외부 결제 제공자와 함께 스티브를 implement할 수 있는 두려움 또는 차별의 조건 없이 Apple의 차별적 대우를 받을 수 있습니다. 게임 필드가 법적으로 평준화되었으며 Apple은 앱의 결제 방법 선택에 관계없이 모든 앱에 동일한 대우를 제공해야 합니다.

이 판결은 Apple의 벽으로 둘러싸인 정원 접근 방식에 대한 가장重大한 도전 중 하나를 대표하며 모바일 앱 수익화가 어떻게 작동할 수 있는지에 대한 전환점을 표시합니다. Apple의 30% 수수료에 대한 오랫동안 불만을 표명한 개발자들에게 이 판결은 더 높은 수익 마진과 고객 경험에 대한 더 많은 통제를 제공하는 길을 제공합니다.

Apple의 In-App 구매 대비 스티브를 사용하는 금융 이익

이 변화의 금융적 영향은 개발자에게 다음과 같은 것들을 의미합니다:

  • 낮은 결제 처리 수수료: Apple은 일반적으로 In-App 구매에 30% 수수료를 부과합니다(소기업의 경우 15%), 반면 스티브의 수수료는 거래당 약 2.9% + $0.30입니다. 이 차이는 수익 마진을 크게 증가시킬 수 있습니다.

  • 빠른 지불애플과 달리, 스티프에서는 2-3일 이내에 고객의 은행 계좌로 지불금을 입금합니다.

  • 간소화된 환불 처리 과정Stripe의 대시보드에서 직접 환불을 처리하세요. 애플의 복잡한 환불 시스템을 거치지 않아도 됩니다.

이러한 비용 절감과 향상된 현금 흐름은 특히 작은 개발자와 기업에게 게임 체이저일 수 있습니다.

이 글에서는 애플의 규정 준수와 함께 스트라이프 결제 링크를 사용하여 Capacitor 앱에서 이 새로운 규칙을 활용하는 방법을 살펴보겠습니다. 업데이트된 지침.

이 구현은 Stripe의 공식 Payment Links 문서Capacitor 앱을 위해 특별히 적합한 것으로 개발되었습니다.

새로운 지침 이해

업데이트된 App Store 리뷰 지침은 개발자가 사용자에게 디지털 상품 및 구독과 관련된 결제 처리를 위한 외부 웹사이트로 연결할 수 있도록 허용합니다. 이 변경 사항은 현재 미국 App Store에서 배포되는 앱에만 적용됩니다.

Stripe 결제를 위한 주요 이해점:

  1. __CAPGO_KEEP_0__ 앱에서 디지털 상품에 대한 외부 결제 옵션에 연결할 수 있습니다.
  2. 이 기능은 미국 앱 스토어에 있는 앱에만 적용됩니다.
  3. 애플의 공개 요건을 준수해야 합니다.
  4. 고객 지원 및 환불 처리에 대한 모든 책임은 여전히 고객에게 있습니다.

기술 구현에 대해 자세히 알아보겠습니다.

먼저 Stripe 대시보드에서 결제 링크를 생성하세요.

  1. Stripe 대시보드의 결제 링크 섹션으로 이동하세요.
  2. 새 결제 링크를 생성하려면 '+' 버튼을 클릭하세요.
  3. 제품 또는 구독 정보를 정의하세요.
  4. 결제 후 설정에서 '결제 확인 페이지를 표시하지 않음'을 선택하세요.
  5. 성공 URL로 universal 링크를 설정하세요 (이것은 나중에 설정할 것입니다)
  6. 결제 링크를 생성하기 위해 "Create Link"을 클릭하세요

결제 완료 후 사용자를 앱으로 되돌려 보내려면 universal 링크를 구성하세요:

  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. 이 파일이 올바른 MIME 타입으로 제공되는지 확인하세요 https://yourdomain.com/.well-known/apple-app-site-association

  2. __CAPGO_KEEP_0__ 앱을 universal 링크를 처리하도록 구성하려면 올바른 권한을 추가하세요. 먼저 Xcode 프로젝트에서: application/json

  3. Configure your Capacitor app to handle universal links by adding the proper entitlement. First, in your 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. 앱 목표를 선택하세요
    • Add the Associated Domains entitlement to your Xcode project:
    • Open your Xcode project
    • “Signing & Capabilities”로 이동하세요.
    • “+ Capability”을 클릭하고 “Associated Domains”을 선택하세요.
    • 추가 applinks:yourdomain.com

3단계: 폴백 페이지 만들기

폴백 페이지를 만들기 위해 리다이렉트 URL에서 폴백 페이지를 만들 수 있습니다. 앱이 설치되지 않은 경우:

<!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>

Step 4: Implement the Payment Button in Your Capacitor App

결제 버튼을 앱에 추가하세요.

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에서 결제 링크를 열면 (via Safari에서 결제 링크를 열면) 앱 내 브라우저 대신 Safari를 사용하는 것이 유용합니다. Stripe Link에서 이전에 결제 정보를 저장한 사용자들은 자동으로 인증 정보가 제공되기 때문에, 사용자가 다시 신용 카드 정보를 입력하지 않아도 체크아웃 경험을 더 편리하게 사용할 수 있습니다. 이는 사용자가 다시 신용 카드 정보를 입력하지 않아도 체크아웃 경험을 더 편리하게 사용할 수 있으므로, 사용자 탈출률을 크게 줄일 수 있습니다. window.open5단계: Universal Links를 앱에 구현하세요.

Step 3: 폴백 페이지 만들기

  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단계: 주문 완료를 위한 웹훅 설정

마지막으로, 서버에서 성공적인 결제를 처리하는 웹훅을 구성하세요:

// 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'));

안드로이드 호환성

iOS 앱에 대한 에픽 v. 애플 판결은 모바일 결제 지형을 근본적으로 바꾸었습니다. iOS 앱만 직접적으로 영향을 받는 것은 물론, Android 개발자들은 이미 외부 결제 방법을 사용해 왔습니다.

Android 개발자들은 이제 완전한 자신감으로 외부 결제 솔루션을 implement할 수 있습니다. 애플 판결에 의해 설정된 전례는 플랫폼을 막론하고 개발자들이 잠재적인 미래 제한으로부터 보호합니다. 이 법적 판결은 많은 Android 개발자들이 이미 몇 년 동안 수행해 왔던 것과 동일한 것을 확인했습니다 - 낮은 수수료의 대안 결제 옵션을 제공합니다.

구글 플레이 스토어는 항상 애플보다 외부 결제 방법에 대한 제한이 적었으며, 법적 전례가established되었으므로, Stripe 또는 다른 외부 결제 제공자들을 Android 앱에 implement하는 데 virtually의 위험이 없습니다. 이러한 implement에 대해 앞으로 진행할 수 있습니다. 이는 법적 근거에 서 서실히 서 있습니다.

iOS에 대한 implement는 거의 동일한 안드로이드 기기에서 작동합니다. 구글 플레이 스토어는 외부 결제 방법에 대한 제한이 없기 때문에, Stripe Payment Links 접근 방식을 사용할 수 있습니다. disclosure dialog가 필요하지 않습니다.

deep linking을 처리하려면 (iOS의 universal links와 동일한 것), 다음을 수행해야 합니다:

  1. 설치 AndroidManifest.xml URL을 처리하는
  2. 생성 .well-known/assetlinks.json 도메인에 있는 파일에 앱의 세부 정보를
  3. 같은 appUrlOpen 성공적인 결제를 처리하는

의 아름다움은 Capacitor에서 플랫폼별 구성이 implement되면 실제 결제 흐름 code이 두 플랫폼 모두에서 동일하게 유지됩니다.

결제 UI를 만들기

Here’s an example of a payment button component in Vue that you can add to your Capacitor app:

<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>

다른 지역을 처리하는

새로운 애플 지침은 미국 앱 스토어에만 적용되므로 사용자 지역을 감지하고 적절한 결제 방법을 적용하는 전략이 필요합니다. 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 애플리케이션의 경우 여러 감지 방법을 사용하거나 사용자가 지역을 수동으로 선택할 수 있도록 허용하는 것이 좋습니다.

More Accurate Location Detection with Capacitor Plugins

더 정확한 위치 감지를 위해 Capacitor 플러그인을 사용할 수 있습니다. @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 기반 감지를 사용합니다.

필요한 권한을 __CAPGO_KEEP_0__ 프로젝트의 info.plist 파일과 AndroidManifest.xml 파일에 추가하세요.

For 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>

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

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

이 방법을 사용하면 사용자가 새로운 애플 지침 하위에서 외부 결제 옵션에 대해 적합한지 가장 정확하게 판단할 수 있습니다.

구독 관리

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. Apple의 정책에 따라 앱을 떠나는 사용자에게 알리는 모달 시트를 구현하십시오.
  3. 앱 내에서 구매를 통해 Apple의 수수료를 피하려 하지 마십시오.
  4. 사용자에게 Apple이 거래에 책임이 없다는 것을 명확하게 알려주십시오.

Apple의 정책에 따라 필요한 공개 모달을 구현하는 예제입니다.

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);
  }
}

구현 테스트

구현 테스트를 위해 다음 단계를 따르십시오.

  1. 앱 내에서 결제 버튼을 클릭하여 공개 모달이 표시되고 Stripe 결제 페이지로 열리는지 확인하십시오.
  2. Stripe 테스트 카드를 사용하여 테스트 결제를 완료하십시오. 4242 4242 4242 4242
  3. 결제 후 앱으로 돌아오는지 universal link를 통해 확인하십시오.
  4. 웹 훅이 이벤트를 받았는지 확인하십시오. checkout.session.completed event

결론

iOS 앱에서 디지털 상품에 대한 외부 결제 옵션을 사용할 수 있는 능력은 개발자에게 더 많은 유연성을 제공하는 중요한 변화입니다. 이 변화는 현재 미국 앱 스토어에 있는 앱에만 적용되지만, 애플의 내 앱 구매 시스템에 대한 중요한 대안을 제공합니다.

Stripe Payment Links를 사용하여 Capacitor, 미국 앱 스토어의 애플 가이드라인에 준하는 체크아웃 경험을 제공하면서도 빠르게 구현할 수 있습니다. 미국 밖의 사용자에게는 여전히 애플의 내 앱 구매 시스템이 적용되므로 @capgo/native-purchases 스토어 키트와 구글 플레이 빌링을 직접 implement하는 __CAPGO_KEEP_0__의 이 접근 방식은 또한 애플의 내 앱 구매 시스템보다 Stripe의 강력한 결제 인프라, 낮은 처리 수수료(3% vs 30%), 빠른ayout(일 단위 vs 월 단위)를 제공합니다.

고객 지원 및 환불 문제를 직접 처리해야 하다는 점을 기억하세요. 이 거래는 애플의 생태계 외부에서 발생하기 때문입니다.

Capacitor 앱에서 Stripe Payment Links를 이미 구현했나요? 아래 댓글에 경험을 공유해 보세요!

FAQs

Q: 이 접근 방식은 애플의 가이드라인에 준합합니까?
A: 2025년 5월 1일부터, 미국 앱 스토어에 배포되는 앱에서 디지털 상품과 서비스에 대한 외부 결제 방법에 대한 링크를 포함하는 경우, 애플은 외부 결제 방법을 사용하는 경우 애플의 내 앱 구매 시스템에 대한 수수료를 지불할 필요가 없습니다.

Q: 외부 결제 방법을 사용할 때 애플의 수수료를 지불해야 합니까?
Q: 미국의 새로운 규칙 중 하나는 애플 시스템 외부에서 처리되는 결제는 애플의 수수료를 받지 않는다는 것입니다.

Q: 이러한 새로운 규칙을 활용하기 위해 회사은 미국에 기반을 두고 있어야 하나요?
A: 아니, 세계 어디에서나 회사일 수 있고, 앱이 미국 앱 스토어에 등록되어 있고, 구매를 하는 사용자가 미국에 거주하는 경우에만 외부 결제 방법을 implement할 수 있습니다. 이 규정은 시장(미국 앱 스토어)과 사용자의 위치, 회사 위치와는 별개로 적용됩니다. 따라서 유럽, 아시아, 남미, 또는 어디서나 개발자가 미국 고객에게 Stripe Payment Links를 implement할 수 있습니다.

Q: 사용자가 미국 밖에서 외부 결제 옵션을 사용하려고 하면 어떻게 되나요?
A: 사용자 위치를 감지하여(기사에서 설명한 것처럼) 미국 밖의 사용자에게만 외부 결제 옵션을 제공해야 합니다. 다른 지역의 사용자는 애플의 인앱 구매 시스템을 계속 사용해야 합니다.

Q: 물리적 상품이나 앱 외부에서 소비되는 서비스를 사용할 수 있나요?
A: 예, 애플은 항상 물리적 상품과 앱 외부에서 소비되는 서비스(라이드 셰어링이나 음식 배달 등)와 같은 경우에 외부 결제 방법을 허용했습니다.

Q: 계속해서 Capacitor:

Q: __CAPGO_KEEP_0__ Q: Capacitor Q: __CAPGO_KEEP_0__ Q: __CAPGO_KEEP_0__ Capacitor에서 US Stripe 결제 구현하기 Compliance Capacitor에서 Compliance 구현하기 Capgo 보안 스캐너 Capacitor에서 Capgo 보안 스캐너의 제품 워크플로우 Capgo 보안 Capacitor에서 Capgo 보안의 제품 워크플로우, 그리고 Capgo 신뢰 센터 Capacitor에서 Capgo 신뢰 센터의 제품 워크플로우.

Capacitor 앱에 대한 즉시 업데이트

웹层 버그가 활성화되면 Capgo를 통해修正을 배포하는 대신 앱 스토어 승인까지 며칠 기다리지 마세요. 사용자는 배경에서 업데이트를 받으면서 네이티브 변경은 일반적인 검토 경로를 유지합니다.

마틴의 인간 지원

시작하기

최신 뉴스

Capgo은 전문적인 모바일 앱을 만들기 위해 필요한 최고의洞察력을 제공합니다.