Cài đặt vào bất kỳ agent nào hỗ trợ skill. Để cập nhật, chạy npx skills update.
npx skills add adaptyteam/adapty-skills --all
Các skill bao gồm tích hợp SDK, xây dựng flow và paywall, kiểm tra trước khi phát hành, di chuyển placement và nhiều hơn nữa. Mô tả công việc bằng ngôn ngữ thường ngày và agent sẽ chọn skill phù hợp.
Để kích hoạt in-app purchase, bạn cần nắm ba khái niệm cốt lõi:
Sản phẩm – mọi thứ người dùng có thể mua (gói đăng ký, consumable, quyền truy cập trọn đời)
Flow – chuỗi màn hình giới thiệu sản phẩm tới người dùng, được xây dựng trong Flow & Paywall Builder không cần code. SDK lấy chúng qua getFlow. Nếu bạn muốn tự xây dựng giao diện trong code của mình, hãy dùng paywall thay thế — xem Triển khai paywall thủ công.
Placement – nơi và thời điểm hiển thị flow trong ứng dụng của bạn (ví dụ: main, onboarding, settings). Bạn gắn flow vào placement trong dashboard, sau đó yêu cầu chúng bằng placement ID trong code. Điều này giúp dễ dàng chạy A/B test và hiển thị các flow khác nhau cho từng nhóm người dùng.
Adapty cung cấp cho bạn ba cách để kích hoạt mua hàng trong ứng dụng. Hãy chọn một trong số đó tùy theo yêu cầu của ứng dụng:
Bạn tự triển khai giao diện paywall trong code ứng dụng, nhưng vẫn lấy đối tượng flow từ Adapty để duy trì sự linh hoạt trong việc cung cấp sản phẩm. Xem hướng dẫn.
Chế độ Observer
🔴 Khó
Bạn đã có sẵn cơ sở hạ tầng xử lý giao dịch mua hàng riêng và muốn tiếp tục sử dụng nó. Lưu ý rằng chế độ observer có những hạn chế nhất định trong Adapty. Xem bài viết.
Important
Các bước dưới đây hướng dẫn cách tích hợp một flow được tạo trong Adapty Flow & Paywall Builder.
Cài đặt và kích hoạt SDK trong mã ứng dụng của bạn. Hướng dẫn này sử dụng API của Adapty iOS SDK v4.
1. Lấy flow
Các flow của bạn được liên kết với các placement được cấu hình trong dashboard. Placement cho phép bạn chạy các flow khác nhau cho các đối tượng khác nhau hoặc chạy A/B test.
Để lấy một flow đã tạo trong Adapty Flow & Paywall Builder, bạn cần:
Lấy đối tượng flow theo ID placement bằng phương thức getFlow và kiểm tra xem nó có cấu hình view hay không.
Lấy cấu hình view bằng phương thức getFlowConfiguration. Cấu hình này chứa các thành phần UI và kiểu dáng cần thiết để hiển thị flow.
func loadFlow() async { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") guard flow.hasViewConfiguration else { print("Flow doesn't have a view configuration") return } flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow)}
2. Hiển thị flow
Bây giờ, khi bạn đã có cấu hình flow, chỉ cần thêm vài dòng code để hiển thị flow của mình.
Trong SwiftUI, khi hiển thị flow, bạn cũng cần xử lý các sự kiện. didFinishPurchase, didFailPurchase, didFinishRestore, didFailRestore và didReceiveError là bắt buộc. Khi kiểm thử, bạn có thể sao chép đoạn code bên dưới để ghi log các sự kiện này.
Tip
Flow không tự động đóng sau khi mua hàng thành công. Trong didFinishPurchase, hãy đặt binding hiển thị của bạn thành false để đóng flow, hoặc không làm gì để flow tiếp tục chuyển sang các màn hình tiếp theo.
.flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didFinishPurchase: { product, purchaseResult in print("Purchase finished successfully") flowPresented = false // or do nothing to let the flow continue }, didFailPurchase: { product, error in print("Purchase failed: \(error)") }, didFinishRestore: { profile in print("Restore finished successfully") }, didFailRestore: { error in print("Restore failed: \(error)") }, didReceiveError: { error in flowPresented = false print("Flow error: \(error)") })
Implement AdaptyFlowControllerDelegate để xử lý các sự kiện. Tối thiểu, hãy implement bốn phương thức không có cài đặt mặc định. Lưu ý rằng controller sẽ không tự dismiss sau khi mua thành công — hãy dismiss nó trong didFinishPurchase, hoặc không làm gì để flow tiếp tục sang các màn hình tiếp theo:
Để biết thêm chi tiết về cách hiển thị một flow, hãy xem hướng dẫn của chúng tôi.
3. Xử lý hành động nút
Khi người dùng nhấn các nút, iOS SDK tự động xử lý việc mua hàng, khôi phục, đóng flow và mở liên kết.
Tuy nhiên, một số nút có ID tùy chỉnh hoặc được định sẵn và cần xử lý hành động trong code của bạn. Hoặc, bạn có thể muốn ghi đè hành vi mặc định của chúng.
Ví dụ, dưới đây là cách xử lý nút đóng. Trong UIKit, SDK tự động dismiss controller khi .close được kích hoạt — chỉ ghi đè nếu bạn muốn hành vi tùy chỉnh. Trong SwiftUI, bạn phải tự đặt binding isPresented của mình thành false.
Tip
Đọc hướng dẫn của chúng tôi về cách xử lý hành động và sự kiện của nút.
.flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false // dismiss the flow when the user taps close default: break } }, didFinishPurchase: { product, purchaseResult in flowPresented = false }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false })
Bạn có câu hỏi hoặc gặp sự cố? Hãy xem diễn đàn hỗ trợ của chúng tôi — nơi bạn có thể tìm câu trả lời cho các câu hỏi thường gặp hoặc đặt câu hỏi của riêng mình. Đội ngũ và cộng đồng của chúng tôi luôn sẵn sàng giúp đỡ!
Tiếp theo, bạn cần kiểm tra mức độ truy cập của người dùng để đảm bảo bạn hiển thị flow hoặc cấp quyền truy cập vào các tính năng trả phí cho đúng người dùng.
Ví dụ đầy đủ
Dưới đây là cách tích hợp tất cả các bước trong hướng dẫn này vào ứng dụng của bạn.
struct ContentView: View { @State private var flowPresented = false @State private var flowConfiguration: AdaptyUI.FlowConfiguration? @State private var isLoading = false @State private var hasInitialized = false var body: some View { VStack { if isLoading { ProgressView("Loading...") } else { Text("Your App Content") } } .task { guard !hasInitialized else { return } await initializeFlow() hasInitialized = true } .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false default: break } }, didFinishPurchase: { product, purchaseResult in print("Purchase finished successfully") flowPresented = false // or do nothing to let the flow continue }, didFailPurchase: { product, error in print("Purchase failed: \(error)") }, didFinishRestore: { profile in print("Restore finished successfully") }, didFailRestore: { error in print("Restore failed: \(error)") }, didReceiveError: { error in print("Flow error: \(error)") flowPresented = false } ) } private func initializeFlow() async { isLoading = true defer { isLoading = false } await loadFlow() flowPresented = true } private func loadFlow() async { do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") guard flow.hasViewConfiguration else { print("Flow doesn't have a view configuration") return } flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow) } catch { print("Failed to load: \(error)") } }}