Integrate the iOS Native SDK
The TingeePaySDK library lets native iOS apps easily embed an intuitive payment screen as a Bottom Sheet or in full screen.
1. Install the SDK
You can install TingeePaySDK in your iOS app in either of the following ways:
- Swift Package Manager (SPM)
- XCFramework (Manual)
This is the recommended installation method.
- Open your project in Xcode.
- Select File > Add Packages... (or Add Package Dependencies...).
- Enter the URL of the GitHub repository that hosts the TingeePay SDK:
https://github.com/tingeedev/tingee-pay-sdk-ios.git - Choose a version rule (e.g. Up to Next Major Version) and click Add Package.
If you do not use SPM, you can add the XCFramework manually:
- Download the package: TingeePaySDK.xcframework.zip (a zip of the
TingeePaySDK.xcframeworkfolder). - Unzip it and drag the
TingeePaySDK.xcframeworkfolder into your Xcode project (make sure Copy items if needed and Create groups are selected). - Select your app target and go to the General tab.
- Under Frameworks, Libraries, and Embedded Content, find
TingeePaySDK.xcframeworkand set it to Embed & Sign.
2. Configure system permissions
To support saving the payment QR code to the device's photo album (when the user taps the "Download QR code" button on the gateway interface), you must configure photo library access in the Info.plist file.
Open Info.plist as Source Code and add the following key:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Ứng dụng cần quyền để lưu mã QR thanh toán vào thư viện ảnh của bạn.</string>
[!WARNING] Without this configuration, the app crashes immediately when the user taps the save QR code button on the payment screen.
3. Quick integration guide
Depending on whether your project uses UIKit or SwiftUI, follow the corresponding guide:
- UIKit (Swift)
- SwiftUI
In the UIViewController where you want to start the payment (e.g. when the pay button is tapped):
import UIKit
import TingeePaySDK
class CheckoutViewController: UIViewController {
func openPayment(checkoutUrlString: String) {
guard let checkoutUrl = URL(string: checkoutUrlString) else { return }
// Call the SDK to show the payment gateway
TingeePay.presentCheckout(
from: self,
checkoutUrl: checkoutUrl,
style: .bottomSheet, // Or .fullScreen
delegate: self
)
}
}
For SwiftUI, write a wrapper struct conforming to UIViewControllerRepresentable so you can easily reuse it and handle the returned events.
Step 1: Declare a wrapper for the SDK
import SwiftUI
import TingeePaySDK
struct TingeePayView: UIViewControllerRepresentable {
let checkoutUrl: URL
var themeColor: String? = nil
var style: TingeePayStyle = .fullScreen
var onFinished: ((TingeePaymentResult) -> Void)?
var onCancelled: (() -> Void)?
var onFailed: ((Error) -> Void)?
class Coordinator: NSObject, TingeePayCheckoutDelegate {
var parent: TingeePayView
init(parent: TingeePayView) {
self.parent = parent
}
func tingeePayCheckoutDidFinish(with result: TingeePaymentResult) {
parent.onFinished?(result)
}
func tingeePayCheckoutDidCancel() {
parent.onCancelled?()
}
func tingeePayCheckoutDidFail(with error: Error) {
parent.onFailed?(error)
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> TingeePayCheckoutViewController {
let vc = TingeePayCheckoutViewController(
checkoutUrl: checkoutUrl,
themeColor: themeColor,
style: style
)
vc.delegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: TingeePayCheckoutViewController, context: Context) {}
}
Step 2: Use the wrapper in your View
import SwiftUI
struct ContentView: View {
@State private var showPayment = false
let checkoutUrl = URL(string: "https://pay.tingee.vn/your-order-id")!
var body: some View {
Button("Thanh toán với Tingee") {
showPayment = true
}
.sheet(isPresented: $showPayment) {
TingeePayView(
checkoutUrl: checkoutUrl,
themeColor: "#FF5733", // Optional color
style: .fullScreen, // Or .bottomSheet
onFinished: { result in
print("Kết quả: \(result.status.rawValue)")
showPayment = false
},
onCancelled: {
print("Người dùng đã huỷ thanh toán")
showPayment = false
},
onFailed: { error in
print("Lỗi tải trang: \(error.localizedDescription)")
showPayment = false
}
)
}
}
}
4. Listen for & handle results
To receive events from the payment screen, you need to conform to the TingeePayCheckoutDelegate protocol:
extension CheckoutViewController: TingeePayCheckoutDelegate {
/// Event: The payment completed or ended (Success, Error, Expired...)
func tingeePayCheckoutDidFinish(with result: TingeePaymentResult) {
print("Trạng thái thanh toán: \(result.status.rawValue)")
print("Mã đơn hàng: \(result.orderId ?? "")")
switch result.status {
case .success:
// Handle a successful payment (e.g. go to a thank-you screen)
print("Thanh toán thành công!")
case .failed:
// The payment transaction failed
print("Lỗi thanh toán: \(result.errorMessage ?? "")")
case .cancelled:
// The transaction was cancelled on the gateway
print("Giao dịch đã bị huỷ.")
case .expired:
// The payment time expired
print("Đơn hàng đã hết hạn thanh toán.")
case .error:
// A system error occurred
print("Lỗi hệ thống cổng thanh toán.")
}
}
/// Event: The user tapped the "Close" (X) button or swiped the Bottom Sheet away to exit
func tingeePayCheckoutDidCancel() {
print("Người dùng đã thoát màn hình thanh toán.")
}
/// Event: An error occurred while the SDK was loading the payment gateway (e.g. invalid URL format, no Internet connection...)
func tingeePayCheckoutDidFail(with error: Error) {
print("Lỗi khởi tạo/tải màn hình thanh toán: \(error.localizedDescription)")
}
}
5. Data models
TingeePaymentResult
Object containing the transaction result that the SDK returns through the tingeePayCheckoutDidFinish callback.
| Property | Data type | Description |
|---|---|---|
status | TingeePaymentStatus | Final status of the transaction. |
orderId | String? | Order ID in your system (the ID you sent when creating the payment link). |
errorMessage | String? | Detailed error message (if any). |
TingeePaymentStatus
An Enum representing the transaction statuses:
.success: The transaction succeeded..failed: The transaction failed..cancelled: The transaction was cancelled..expired: The transaction's payment time expired..error: The transaction hit a system error.
6. Troubleshooting
The "Download QR code" button does not work
- Cause: The
NSPhotoLibraryAddUsageDescriptionkey is not configured inInfo.plist. - Fix: Go back to the system permissions step and add this key to your plist file.
Testing in the Sandbox environment
- The mobile SDK switches environments automatically based on the URL. If
checkoutUrlstarts with Tingee's Sandbox URL, the SDK detects it and shows the Sandbox interface.