Skip to content

Repository files navigation

Xendit Components iOS

A drop-in payment UI SDK for iOS that lets you accept payments through Xendit with minimal integration effort. Present a fully featured payment sheet in just a few lines of code.

Build Status Version License Platform

Features

  • Pre-built payment sheet — Full-screen payment UI with channel selection, form validation, and error handling out of the box.
  • 3DS & redirect handling — Built-in WebView for 3D Secure challenges and redirect-based payment flows.
  • Customizable appearance — Configure colors, fonts, and corner radius to match your brand.
  • SwiftUI & UIKit — Works with both UI frameworks.
  • Secure by default — Client-side encryption of sensitive card data using ECDH + AES-GCM.

Requirements

Requirement Minimum
iOS 15.0
Xcode 14.0
Swift 5.7

Getting Started

Quick Start (UIKit)

import XenditComponents

// 1. Initialize once at app startup
XenditComponents.initialize(appearance: XenditAppearance())

// 2. Present the payment sheet
XenditComponents.present(
    from: viewController,
    componentsSdkKey: "<your_components_sdk_key>"
) { result in
    switch result {
    case .success(let paymentRequestId, let channelCode):
        print("Payment succeeded: \(paymentRequestId)")
    case .failed(let error):
        print("Payment failed: \(error.message)")
    case .canceled:
        print("Session canceled")
    case .expired:
        print("Session expired")
    case .dismissed:
        print("User dismissed")
    }
}

Quick Start (SwiftUI)

import SwiftUI
import XenditComponents

struct CheckoutView: View {
    var body: some View {
        Button("Pay with Xendit") {
            guard let rootVC = UIApplication.shared.connectedScenes
                .compactMap({ $0 as? UIWindowScene })
                .first?.windows.first?.rootViewController else { return }

            XenditComponents.initialize(appearance: XenditAppearance())
            XenditComponents.present(
                from: rootVC,
                componentsSdkKey: "<your_components_sdk_key>"
            ) { result in
                switch result {
                case .success(let id, _): print("Paid: \(id)")
                case .failed(let error):  print("Error: \(error.message)")
                case .canceled, .expired, .dismissed: break
                }
            }
        }
    }
}

Quick Start (Objective-C)

@import XenditComponents;

// 1. Initialize once at app startup
XDTAppearance *appearance = [XDTAppearance new];
appearance.colorPrimary = UIColor.systemBlueColor;
appearance.borderRadius = 12;
[XDTComponents initializeWithAppearance:appearance];

// 2. Present the payment sheet
[XDTComponents presentFromViewController:self
                       componentsSdkKey:@"<your_components_sdk_key>"
                               onResult:^(XDTPaymentResult *result) {
    switch (result.status) {
        case XDTPaymentStatusSuccess:
            NSLog(@"Paid — ID: %@", result.paymentRequestId);
            break;
        case XDTPaymentStatusFailed:
            NSLog(@"Error: %@", result.error.message);
            break;
        default:
            break;
    }
}];

The components_sdk_key is obtained from the Create Session API response on your backend.

Payment Method Preference

Merchants can control which payment methods are shown in the payment sheet, and in what order, by passing merchantPreferredPaymentMethod to present(from:componentsSdkKey:merchantPreferredPaymentMethod:onResult:).

When provided, only the listed types are shown and they appear in the specified order. Pass nil (the default) to show all available payment methods in the server's default order.

The following values are supported:

  • .cards
  • .ewallet
  • .qrCode

Swift

XenditComponents.present(
    from: viewController,
    componentsSdkKey: "<your_components_sdk_key>",
    merchantPreferredPaymentMethod: [.ewallet, .cards]
) { result in
    switch result {
    case .success(let id, _): print("Paid: \(id)")
    case .failed(let error):  print("Error: \(error.message)")
    case .canceled, .expired, .dismissed: break
    }
}

Objective-C

[XDTComponents presentFromViewController:self
                       componentsSdkKey:@"<your_components_sdk_key>"
          merchantPreferredPaymentMethod:@[@(XDTPaymentMethodEwallet), @(XDTPaymentMethodCards)]
                               onResult:^(XDTPaymentResult *result) {
    switch (result.status) {
        case XDTPaymentStatusSuccess:
            NSLog(@"Paid: %@", result.paymentRequestId);
            break;
        case XDTPaymentStatusFailed:
            NSLog(@"Error: %@", result.error.message);
            break;
        default:
            break;
    }
}];

Appearance Customization

Use XenditAppearance to match the payment sheet to your brand. All properties are optional — any you omit fall back to the SDK's built-in defaults.

Property Reference

Property Type Description
fontFamily XenditFontFamily? Custom fonts per weight (regular, medium, semiBold, bold). Unset weights fall back to the bundled Inter font.
colorPrimary UIColor? Primary CTA color — pay button, selected states, checkmarks.
colorText UIColor? Main body text color.
colorTextSecondary UIColor? Subtitle and caption text color.
colorTextPlaceholder UIColor? Placeholder text inside input fields.
colorDisabled UIColor? Background of disabled buttons and controls.
colorDanger UIColor? Error messages and invalid field borders.
colorBorder UIColor? Input field outlines and dividers.
colorBackground UIColor? Sheet and page background.
qrForegroundColor UIColor? Tint of the container shown around the QR code image.
qrBackgroundColor UIColor? Background of the container box behind the QR code image.
borderRadius CGFloat? Corner radius (pt) for buttons, fields, and cards.
customLoadingView AnyView? SwiftUI view shown during loading. Defaults to a standard ProgressView. Swift only — not available in Objective-C.

SwiftUI

import SwiftUI
import XenditComponents

struct CheckoutView: View {
    var body: some View {
        Button("Pay with Xendit") {
            let appearance = XenditAppearance(
                fontFamily: XenditFontFamily(
                    regular:  UIFont(name: "Inter-Regular",  size: 16),
                    medium:   UIFont(name: "Inter-Medium",   size: 16),
                    semiBold: UIFont(name: "Inter-SemiBold", size: 16),
                    bold:     UIFont(name: "Inter-Bold",     size: 16)
                ),
                colorPrimary:         UIColor(Color.accentColor),
                colorText:            UIColor(Color.primary),
                colorTextSecondary:   UIColor(Color.secondary),
                colorTextPlaceholder: UIColor(Color.secondary.opacity(0.6)),
                colorDanger:          UIColor(Color.red),
                colorBorder:          UIColor(Color.gray.opacity(0.3)),
                colorBackground:      UIColor(Color(.systemBackground)),
                borderRadius:         12,
                customLoadingView:    AnyView(
                    ProgressView().tint(Color.accentColor)
                )
            )

            guard let rootVC = UIApplication.shared.connectedScenes
                .compactMap({ $0 as? UIWindowScene })
                .first?.windows.first?.rootViewController else { return }

            XenditComponents.initialize(appearance: appearance)
            XenditComponents.present(
                from: rootVC,
                componentsSdkKey: "<your_components_sdk_key>"
            ) { result in
                switch result {
                case .success(let id, _): print("Paid: \(id)")
                case .failed(let error):  print("Error: \(error.message)")
                case .canceled, .expired, .dismissed: break
                }
            }
        }
    }
}

UIKit (Swift)

import UIKit
import XenditComponents

class CheckoutViewController: UIViewController {

    func presentPayment() {
        let appearance = XenditAppearance(
            fontFamily: XenditFontFamily(
                regular:  UIFont(name: "Inter-Regular",  size: 16),
                medium:   UIFont(name: "Inter-Medium",   size: 16),
                semiBold: UIFont(name: "Inter-SemiBold", size: 16),
                bold:     UIFont(name: "Inter-Bold",     size: 16)
            ),
            colorPrimary:         UIColor.systemBlue,
            colorText:            UIColor.label,
            colorTextSecondary:   UIColor.secondaryLabel,
            colorTextPlaceholder: UIColor.placeholderText,
            colorDisabled:        UIColor.systemGray4,
            colorDanger:          UIColor.systemRed,
            colorBorder:          UIColor.separator,
            colorBackground:      UIColor.systemBackground,
            qrForegroundColor:    UIColor.black,
            qrBackgroundColor:    UIColor.white,
            borderRadius:         12
        )

        XenditComponents.initialize(appearance: appearance)
        XenditComponents.present(
            from: self,
            componentsSdkKey: "<your_components_sdk_key>"
        ) { result in
            switch result {
            case .success(let paymentRequestId, let channelCode):
                print("Payment succeeded: \(paymentRequestId)")
            case .failed(let error):
                print("Payment failed: \(error.message)")
            case .canceled:
                print("Session canceled")
            case .expired:
                print("Session expired")
            case .dismissed:
                print("User dismissed")
            }
        }
    }
}

Objective-C

@import XenditComponents;

- (void)presentPayment {
    XDTFontFamily *fonts = [XDTFontFamily new];
    fonts.regular  = [UIFont fontWithName:@"Inter-Regular"  size:16];
    fonts.medium   = [UIFont fontWithName:@"Inter-Medium"   size:16];
    fonts.semiBold = [UIFont fontWithName:@"Inter-SemiBold" size:16];
    fonts.bold     = [UIFont fontWithName:@"Inter-Bold"     size:16];

    XDTAppearance *appearance = [XDTAppearance new];
    appearance.fontFamily            = fonts;
    appearance.colorPrimary          = UIColor.systemBlueColor;
    appearance.colorText             = UIColor.labelColor;
    appearance.colorTextSecondary    = UIColor.secondaryLabelColor;
    appearance.colorTextPlaceholder  = UIColor.placeholderTextColor;
    appearance.colorDisabled         = UIColor.systemGray4Color;
    appearance.colorDanger           = UIColor.systemRedColor;
    appearance.colorBorder           = UIColor.separatorColor;
    appearance.colorBackground       = UIColor.systemBackgroundColor;
    appearance.qrForegroundColor     = UIColor.blackColor;
    appearance.qrBackgroundColor     = UIColor.whiteColor;
    appearance.borderRadius          = 12;

    [XDTComponents initializeWithAppearance:appearance];
    [XDTComponents presentFromViewController:self
                           componentsSdkKey:@"<your_components_sdk_key>"
                                   onResult:^(XDTPaymentResult *result) {
        switch (result.status) {
            case XDTPaymentStatusSuccess:
                NSLog(@"Payment succeeded: %@", result.paymentRequestId);
                break;
            case XDTPaymentStatusFailed:
                NSLog(@"Payment failed: %@", result.error.message);
                break;
            case XDTPaymentStatusCanceled:
                NSLog(@"Session canceled");
                break;
            case XDTPaymentStatusExpired:
                NSLog(@"Session expired");
                break;
            case XDTPaymentStatusDismissed:
                NSLog(@"User dismissed");
                break;
        }
    }];
}

Note (Objective-C): customLoadingView requires SwiftUI and is not available from Objective-C. To supply a custom loading indicator, use XenditAppearance from a Swift file in your project.

Calling initialize more than once

XenditComponents.initialize(appearance:) can be called multiple times. Each call replaces the active appearance for all subsequent present(...) calls. This lets you apply different themes at runtime without restarting the session.

Installation

Swift Package Manager

Xcode

  1. In Xcode, go to File → Add Package Dependencies…
  2. Enter the repository URL:
    https://github.com/xendit/xendit-components-ios.git
    
  3. Select Up to Next Major Version with 1.0.0.
  4. Add XenditComponents to your app target.

Package.swift

Add the dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/xendit/xendit-components-ios.git", from: "1.0.0")
]

Then add XenditComponents to your target's dependencies:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "XenditComponents", package: "xendit-components-ios")
    ]
)

Documentation & Examples

Resource Description
API Reference Full API documentation for session creation and payment flows.
SwiftUI Example Sample SwiftUI app demonstrating SDK integration.
UIKit Example Sample UIKit app demonstrating SDK integration.
Obj-C Example Sample Objective-C app using the SDK bridge.

Privacy

See PRIVACY.md for a full breakdown of data collected, how it is used, whether it is linked to the end user's identity, and whether it is used for tracking — formatted for App Store Privacy Nutrition Labels.

License

Xendit Components iOS is available under the MIT license. See the LICENSE file for more info.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages