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

Swift 4 iOS应用结合Firebase,如何用Stripe实现订阅制内容访问?

Hey there! Let’s break down exactly how to implement monthly subscriptions with Stripe in your Swift 4 iOS app, using Firebase for auth and data storage. I’ve built similar flows before, so I’ll walk you through each step clearly.

1. Pre-Configuration Setup

First, get your services lined up:

  • Stripe Dashboard: Create a monthly subscription product and note its priceId (you’ll need this later). Enable test mode to experiment without real payments.
  • Firebase: Set up Firebase Auth (for user login) and Firestore (to store user subscription data). Also, enable Firebase Cloud Functions—we’ll use these for secure server-side Stripe interactions (never expose your Stripe secret key in the iOS app!).
  • Dependencies: Add the Stripe SDK to your project via CocoaPods:
    pod 'Stripe'
    

Every subscriber needs a Stripe Customer ID tied to their Firebase user. We’ll use a Cloud Function to create this safely:

Cloud Function (Node.js)

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const stripe = require("stripe")(functions.config().stripe.secret_key);
admin.initializeApp();

exports.createStripeCustomer = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError("unauthenticated", "You must be logged in to create a customer.");
  }
  
  const user = context.auth;
  const customer = await stripe.customers.create({
    email: user.token.email,
    metadata: { firebaseUID: user.uid }
  });
  
  // Save the Stripe Customer ID to Firestore
  await admin.firestore().collection("users").doc(user.uid).set(
    { stripeCustomerId: customer.id },
    { merge: true }
  );
  
  return { customerId: customer.id };
});

Swift 4 Client Call

Invoke this function right after a user logs in:

import FirebaseAuth
import FirebaseFunctions

func linkUserToStripe() {
    guard Auth.auth().currentUser != nil else { return }
    
    let functions = Functions.functions()
    functions.httpsCallable("createStripeCustomer").call { result, error in
        if let error = error as NSError? {
            print("Failed to create Stripe customer: \(error.localizedDescription)")
            return
        }
        // Customer ID is now saved to Firestore automatically via the function
        print("Stripe customer linked successfully!")
    }
}
3. Implement the Subscription Payment Flow

We’ll use Stripe’s Payment Sheet for a secure, pre-built checkout experience. First, create a Cloud Function to generate a subscription, then trigger the Payment Sheet from your app.

Cloud Function for Subscription Creation

exports.createSubscription = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError("unauthenticated", "You must be logged in to subscribe.");
  }
  
  const { customerId, priceId } = data;
  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: priceId }],
    payment_behavior: "default_incomplete",
    expand: ["latest_invoice.payment_intent"]
  });
  
  return {
    subscriptionId: subscription.id,
    clientSecret: subscription.latest_invoice.payment_intent.client_secret
  };
});

Swift 4 Payment Sheet Integration

import Stripe
import FirebaseFirestore

func startSubscriptionFlow() {
    guard let user = Auth.auth().currentUser else { return }
    
    // Fetch the user's Stripe Customer ID from Firestore
    let db = Firestore.firestore()
    db.collection("users").document(user.uid).getDocument { document, error in
        guard let document = document, document.exists, 
              let customerId = document["stripeCustomerId"] as? String else {
            print("No Stripe customer found for user")
            return
        }
        
        // Call the subscription creation function
        let functions = Functions.functions()
        let payload = [
            "customerId": customerId,
            "priceId": "YOUR_MONTHLY_PRICE_ID" // Replace with your Stripe price ID
        ]
        
        functions.httpsCallable("createSubscription").call(payload) { result, error in
            if let error = error as NSError? {
                print("Subscription creation failed: \(error.localizedDescription)")
                return
            }
            
            guard let response = result?.data as? [String: String],
                  let clientSecret = response["clientSecret"] else { return }
            
            // Initialize and present the Payment Sheet
            var config = STPPaymentSheetConfiguration()
            config.merchantDisplayName = "Your App Name"
            
            let paymentSheet = STPPaymentSheet(
                paymentIntentClientSecret: clientSecret,
                configuration: config
            )
            
            paymentSheet.present(from: self) { result in
                switch result {
                case .completed:
                    DispatchQueue.main.async {
                        print("Subscription successful!")
                        // Update your UI to show premium content
                    }
                case .canceled:
                    print("User canceled payment")
                case .failed(let error):
                    print("Payment failed: \(error.localizedDescription)")
                }
            }
        }
    }
}
4. Sync Subscription Status with Webhooks

Stripe sends webhook events when subscriptions change (e.g., canceled, expired, payment failed). Use a Cloud Function to update Firestore with these changes:

Webhook Handler Cloud Function

exports.handleStripeWebhook = functions.https.onRequest(async (req, res) => {
  const sig = req.headers["stripe-signature"];
  let event;
  
  try {
    event = stripe.webhooks.constructEvent(
      req.rawBody,
      sig,
      functions.config().stripe.webhook_secret
    );
  } catch (err) {
    res.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }
  
  switch (event.type) {
    case "customer.subscription.updated":
      const subscription = event.data.object;
      const firebaseUID = subscription.metadata.firebaseUID;
      if (!firebaseUID) break;
      
      await admin.firestore().collection("users").doc(firebaseUID).set({
        subscriptionStatus: subscription.status,
        subscriptionId: subscription.id,
        currentPeriodEnd: subscription.current_period_end
      }, { merge: true });
      break;
      
    case "customer.subscription.deleted":
      const deletedSub = event.data.object;
      const deletedUID = deletedSub.metadata.firebaseUID;
      if (!deletedUID) break;
      
      await admin.firestore().collection("users").doc(deletedUID).set({
        subscriptionStatus: "canceled",
        subscriptionId: null
      }, { merge: true });
      break;
      
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }
  
  res.status(200).end();
});

Pro Tip: In the Stripe Dashboard, set up your webhook endpoint to point to this Cloud Function’s URL, and select the events customer.subscription.updated, customer.subscription.deleted, and invoice.payment_failed.

5. Control Access to Premium Content

Check the user’s subscription status from Firestore before granting access to premium features:

func checkSubscriptionAccess(completion: @escaping (Bool) -> Void) {
    guard let user = Auth.auth().currentUser else {
        completion(false)
        return
    }
    
    let db = Firestore.firestore()
    db.collection("users").document(user.uid).addSnapshotListener { snapshot, error in
        guard let document = snapshot, document.exists else {
            completion(false)
            return
        }
        
        let status = document["subscriptionStatus"] as? String ?? "inactive"
        // Allow access if status is active or in a trial period
        let hasAccess = status == "active" || status == "trialing"
        completion(hasAccess)
    }
}

// Usage example when accessing premium content
checkSubscriptionAccess { hasAccess in
    DispatchQueue.main.async {
        if hasAccess {
            self.showPremiumContent()
        } else {
            self.showSubscriptionPrompt()
        }
    }
}
6. Let Users Cancel Subscriptions

Add a cancel button that triggers a Cloud Function to end the subscription:

Cancel Subscription Cloud Function

exports.cancelSubscription = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError("unauthenticated", "You must be logged in to cancel.");
  }
  
  const { subscriptionId } = data;
  await stripe.subscriptions.del(subscriptionId);
  return { success: true };
});

Swift 4 Client Call

func cancelSubscription() {
    guard let user = Auth.auth().currentUser else { return }
    
    let db = Firestore.firestore()
    db.collection("users").document(user.uid).getDocument { document, error in
        guard let document = document, document.exists, 
              let subscriptionId = document["subscriptionId"] as? String else {
            print("No active subscription found")
            return
        }
        
        let functions = Functions.functions()
        functions.httpsCallable("cancelSubscription").call(["subscriptionId": subscriptionId]) { result, error in
            if let error = error as NSError? {
                print("Cancel failed: \(error.localizedDescription)")
                return
            }
            print("Subscription canceled successfully")
            // Update UI to reflect canceled status
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:42:43