본문으로 이동
튜토리얼

Capacitor에서 스트라이프 결제: 새로운 애플 지침

Capacitor 앱에서 스트라이프 결제 링크를 구현하는 방법을 알아보세요. 새로운 애플 지침에 따라 디지털 상품 결제를 처리하기 위해.

마틴 도나디유

마틴 도나디유

콘텐츠 마케터

Capacitor에서 스트라이프 결제: 새로운 애플 지침

Apple의 새로운 App Store 검토 지침에 따라 Capacitor 앱에서 Stripe 결제 링크를 구현하는 방법

2025년 5월 1일부터 Apple은 Epic v. Apple 사례 판결에 따라 App Store 검토 지침을 크게 변경했습니다. 이 변경 사항은 미국의 앱 개발자에게 디지털 상품 및 서비스에 대한 외부 결제 방법에 대한 링크를 제공할 수 있도록 허용하며, Apple의 내 앱 구매 시스템에 대한 대안을 열어줍니다.모바일 결제를 영원히 바꾼 에픽의 전투

이 순간에 이르기까지의 길은 길고 논쟁의 여지가 많았습니다. 2020년 8월 에픽 게임즈, 포트나이트의 창조주가 Apple의 App Store 지침을 위반하기 위해 Apple의 30% 수수료를 피하기 위해 직접 결제 옵션을 구현했습니다. Apple은 포트나이트를 App Store에서 제거하고 에픽은 iOS 앱 배포 및 내 앱 결제에 대한 Apple의 통제를 도전하는 소송을 제기했습니다.

법적 투쟁, 항소, 반항소가 수년 동안 계속되었습니다. 최종 판결은 Apple이 사용자에게 앱 외부의 대안 결제 방법에 대한 링크를 제공할 수 있도록 허용했습니다. 이 결정은 2008년부터 시작된 App Store 생태계의 기본적인 금융 모델을 바꾸는 것입니다.

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

이 변경 사항은 미국의 앱 개발자에게 디지털 상품 및 서비스에 대한 외부 결제 방법에 대한 링크를 제공할 수 있도록 허용하며, Apple의 내 앱 구매 시스템에 대한 대안을 열어줍니다.

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.

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

Most importantly, the ruling explicitly states that Apple cannot discriminate against apps that use external payment methods. The court specifically prohibited Apple from:

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

These explicit protections mean that developers can implement Stripe or other external payment providers without fear of subtle retaliation or discrimination from Apple. The playing field has been legally leveled, and Apple must treat all apps equally regardless of their payment method choices.

이 판결은 애플의 벽돌 정원 접근 방식에 대한 가장重大한 도전 중 하나를 대표하며 모바일 앱 수익화 방식에 대한 전환점을 나타낸다. 개발자들에게는 오랜 시간 동안 애플의 30% 수수료 (소규모 기업에 대한 15%)에 대해 불만을 표명해 왔는데, 이 판결은 더 높은 수익 마진과 고객 경험에 대한 더 많은 통제를 제공하는 길을 열어준다.

스트리핑을 사용하는 데 따른 애플의 인앱 구매의 금전적 이익

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

  • 낮은 결제 처리 수수료: 애플은 일반적으로 인앱 구매에 대해 30%의 수수료 (소규모 기업에 대한 15%)를 부과하며, 스트리핑의 수수료는 거래당 약 2.9% + $0.30이다. 이 차이는 수익 마진을 크게 증가시킬 수 있다.

  • 빠른 지불: 애플에서는 일반적으로 45-90일 동안 지불을 받을 수 있다. 스트리핑은 반면에 2-3일 이내에 지불을 은행 계좌에 입금한다.

  • 간소화된 환불 처리: 스트리핑의 대시보드에서 직접 환불을 처리할 수 있다. 애플의 더 복잡한 환불 시스템을 통해 처리하는 대신.

이러한 비용 절감과 현금 흐름 개선은 특히 작은 개발자와 기업에게 게임 체인저가 될 수 있다.

이 기사에서는 Capacitor 앱에서 스트리핑 결제 링크를 구현하는 방법에 대해 설명한다. 이 새로운 규칙을 활용하면서 애플의 업데이트된 지침에 따라 준수성을 보장하는 방법을 설명한다..

This implementation is based on __CAPGO_KEEP_0__ Stripe의 공식 Payment Links 문서를 기반으로 하며 __CAPGO_KEEP_0__ 앱에 최적화되었습니다., adapted specifically for Capacitor apps.

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

중요한 점을 이해하세요:

디지털 상품에 대한 외부 결제 옵션을 앱 내에서 연결할 수 있습니다.

  1. 이 변경 사항은 미국 앱 스토어에만 적용됩니다.
  2. 애플의 disclosure 요구 사항을 준수해야 합니다.
  3. 고객 지원 및 환불 처리에 대한 책임은 여전히 개발자에게 있습니다.
  4. __CAPGO_KEEP_0__ 앱에 Stripe Payment Links 설정

__CAPGO_KEEP_0__

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

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

결제 완료 후 사용자에게 앱으로 리다이렉트 하기 위해 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. 이 파일을 호스팅하세요 https://yourdomain.com/.well-known/apple-app-site-association

  2. 정확한 MIME 타입으로 제공되도록 확인하세요 application/json

  3. universal 링크를 처리하기 위해 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. Xcode 프로젝트에 Associated Domains 권한을 추가하세요:
    • Xcode 프로젝트를 열어보세요
    • 앱 목표를 선택하세요
    • “Signing & Capabilities”로 이동하세요
    • Associated Domains” 권한을 선택하세요
    • 추가하세요 applinks:yourdomain.com

Step 3: Fallback 페이지 만들기

앱이 설치되지 않은 경우 처리할 수 있는 리다이렉트 URL에서 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>

Step 4: Capacitor 앱에 결제 버튼을 implement하세요

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

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

Why Safari Matters: Safari를 통해( window.open) 인앱 브라우저보다 결제 링크를 열 때 유용합니다. 사용자가 이전에 스티프 링크에서 결제 정보를 저장한 경우, 자동으로 자격 증명이 제공되기 때문에 사용자가 다시 신용 카드 정보를 입력할 필요가 없으며, 이는 사용자 경험을 개선하고 중단률을 크게 줄이는 데 도움이 됩니다.

사용자가 다시 리다이렉트 될 때 universal links를 처리하는 앱을 구성하세요.

  1. First, 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
}

Step 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 호환성

우리는 분명히 말합니다: 에픽 v. 애플 판결은 모바일 결제 지형을根本적으로 바꾸었습니다. iOS 앱에 직접적인 영향을 미치며, iOS 앱 이외에도 외부 결제 방법을 사용하는 안드로이드 개발자들의 입지를 강화합니다.

Android 개발자들은 이제 완전한 신뢰감으로 외부 결제 솔루션을 implement할 수 있습니다. Apple 판결의 전례는 개발자들이 플랫폼을 막론하고 미래의 제약으로부터 보호합니다. 이 법적 판결은 많은 Android 개발자가 수년 동안 수행해 왔던 것과 동일한 결과를 냈습니다 - 낮은 수수료의 대안 결제 옵션을 제공합니다.

Google Play Store는 Apple보다 항상 외부 결제 방법에 대한 제약이 적었으며, 법적 전례가established되었으므로, Stripe 또는 다른 외부 결제 제공자와 함께 Android 앱에 구현할 때 virtually의 위험이 없습니다. 이러한 구현을 진행할 때, 법적 기반에 서있는 것을 알면, 안심할 수 있습니다.

iOS용 구현을 다루었듯이, Android 기기에도 거의 동일한 구현이 가능합니다. Google Play Store는 외부 결제 방법에 대한 제약이 없기 때문에, Stripe Payment Links 접근 방식과 동일하게 사용할 수 있습니다. 특별한 disclosure dialogs가 필요하지 않습니다.

깊이 연결 (iOS의 universal links와 동일)을 처리하려면, 다음을 수행해야 합니다:

  1. App Links를 설정하여 AndroidManifest.xml redirect URL을 처리하기 위해
  2. domain에 app의 정보를 포함하는 .well-known/assetlinks.json 파일을 생성해야 합니다.
  3. same appUrlOpen listener logic를 사용하여 성공적인 결제를 처리해야 합니다.

Capacitor의 아름다움은 플랫폼별 구성을 implement한 후, 실제 결제 흐름이 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 지침은 미국 앱 스토어에만 적용되므로 사용자 지역을 감지하고 적절한 결제 방법을 적용하는 전략이 필요합니다. 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);
  }
}

이 방법은 사용자의 국가를 IP 주소에 기반하여 결정하는 무료 서비스를 사용합니다. MaxMind와 같은 다른 지오로케이션 서비스를 사용하거나 서버 측에서 이 체크를 추가로 구현하여 보안성을 높일 수 있습니다. ipapi.co 주의

IP 주소 기반의 지오로케이션은 항상 100% 정확하지 않습니다. mission-critical 애플리케이션에 사용하는 경우, 여러 감지 방법을 사용하거나 사용자가 지역을 수동으로 선택하도록 허용하는 것이 좋습니다.__CAPGO_KEEP_0__ 플러그인으로 더 정확한 위치 감지를 위한 __CAPGO_KEEP_0__ Geolocation 플러그인과 @__CAPGO_KEEP_1__/__CAPGO_KEEP_2__-nativegeocoder를 사용하는 방법

더 정확한 위치 감지를 위해 Capacitor Geolocation 플러그인과 @__CAPGO_KEEP_1__/__CAPGO_KEEP_2__-nativegeocoder를 사용하는 방법

For more accurate location detection, you can use the Capacitor Geolocation plugin along with @capgo/capacitor-nativegeocoder to determine the user’s country with higher precision:

  1. __CAPGO_KEEP_0__ 프로젝트에서 플러그인을 구성합니다. __CAPGO_KEEP_0__에 다음을 추가합니다.
npm install @capacitor/geolocation @capgo/capacitor-nativegeocoder
  1. Capacitor Geolocation 플러그인과 @__CAPGO_KEEP_1__/__CAPGO_KEEP_2__-nativegeocoder를 사용하여 사용자의 국가를 더 높은 정확도로 결정하는 방법 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" />

이 방법을 사용하면 사용자가 새로운 애플 지침에 따라 외부 결제 옵션을 사용할 수 있는지 가장 정확하게 판단할 수 있습니다.

구독 관리:

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. 앱을 떠나는 사용자에게 알리는 모달 시트를 implement하십시오 (애플에 의해 요구됨)
  3. 앱 내에서 구매한 애플의 수수료를 circumvent하지 마십시오
  4. 사용자에게 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);
  }
}

구현을 테스트하려면:

앱 내의 결제 버튼을 클릭하여 disclosure를 보여주고 Stripe 결제 페이지를 열 수 있도록 하십시오

  1. 구현을 테스트하는 방법
  2. Stripe 테스트 카드를 사용하여 테스트 결제를 완료하세요. 4242 4242 4242 4242
  3. 결제 후, Universal Link를 통해 앱으로 돌아가야 합니다.
  4. __CAPGO_KEEP_0__에서 웹훅이 이벤트를 받았는지 확인하세요. checkout.session.completed __CAPGO_KEEP_0__

결론

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

Stripe Payment Links를 사용하여 Capacitor, Apple의 지침을 준수하면서도 streamlined checkout 경험을 구현할 수 있습니다. 이 접근 방식은 또한 Stripe의 강력한 결제 인프라, 낮은 처리 수수료 (3% vs 30%), 빠른ayout (일주일 대월)와 같은 Apple의 내 앱 구매 시스템과 비교하여 많은 이점을 제공합니다.

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

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

FAQ

Q: 이 접근 방식은 Apple의 지침에 부합합니까?
A: 2025년 5월 1일부터, 미국 앱 스토어에 배포된 앱에서 디지털 상품 및 서비스에 대한 외부 결제 방법에 대한 링크를 포함하는 경우, Apple은 필요한 고지 사항을 포함하는 경우 이 접근 방식이 Apple의 지침에 부합한다고 허용합니다.

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

Q: 이 새로운 규칙을 이용하려면 회사도 미국에 기반을 두어야 하나요?
A: 아니오, 이 새로운 규칙을 이용하려면 어디서나 회사든 미국 앱 스토어에 앱을 배포하고 미국에 거주하는 사용자가 결제를 하는 경우에만 외부 결제 방법을 구현할 수 있습니다. 판결은 시장(미국 앱 스토어)과 사용자의 위치, 회사 위치와는 별도로 적용됩니다. 따라서 유럽, 아시아, 남미, 또는 어디서든 개발자는 미국 고객을 위해 스트라이프 결제 링크를 구현할 수 있습니다.

Q: 미국 밖의 사용자가 외부 결제 옵션을 사용하려고 할 때 어떻게 되나요?
A: 미국 사용자만 외부 결제 옵션을 제공하기 위해 지역 감지 기능을 구현해야 합니다. 다른 지역의 사용자는 여전히 애플의 인앱 구매 시스템을 사용해야 합니다.

Q: 이 방법을 물리적 제품이나 서비스를 사용할 때도 사용할 수 있나요?
A: 물리적 제품이나 서비스를 사용할 때도 항상 외부 결제 방법을 사용할 수 있습니다(예: 택시나 음식 배달).

Capacitor에서 Stripe 결제를 계속하세요: 새로운 애플 지침

__CAPGO_KEEP_0__에서 Stripe 결제를 사용하는 경우 Stripe Payments in Capacitor: New Apple Guidelines __CAPGO_KEEP_0__에서 Stripe 결제를 계속하세요: 새로운 애플 지침 암호화 암호화 구현 세부 정보에 대한 암호화 법적 준수 법적 준수 구현 세부 정보에 대한 법적 준수 Capgo 보안 스캐너 Capgo 보안 스캐너 제품 워크플로우에 대한 Capgo 보안 스캐너 Capgo 신뢰 센터 Capgo 신뢰 센터 제품 워크플로우에 대한 Capgo 신뢰 센터 Capgo 보안 Capgo 보안 제품 워크플로우에 대한 Capgo 보안

Capacitor 앱에 대한 실시간 업데이트

웹-layer 버그가 활성화된 경우, 앱 스토어 승인 대기 없이 Capgo를 통해 패치를 배포하세요. 사용자는 배경에서 업데이트를 받으며, 네이티브 변경 사항은 일반적인 검토 경로에 남아 있습니다.

시작하기

블로그에서 최신 뉴스

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