Save Card For Future Use

First, create a card intent with the Card intent endpoint. This step does not use MoneyHash's iOS SDK and is standard for all MoneyHash's integrations, except for HPP. This endpoint requires authentication to be executed properly. You need to provide the Account API Key as a header and send the required data in the request body. Check the Create a Card Intent page for further explanation.

Don't forget to include "card_token_type": "UNIVERSAL" while creating the card intent.


Step 1: Initialize MoneyHash SDK

Before proceeding, initialize the MoneyHash SDK with your Public API Key:

import MoneyHashSDK

let moneyHashSDK = MoneyHashSDKBuilder()
    .setPublicKey("<account_public_api_key>")
    .build()

Step 2: Collect Card Data

Once the card form is set up, you can collect the card data. If you haven't set up the card form yet, follow the steps in the Collecting Card Information documentation.

Here's how you can collect the card data using the collect() method:

import Foundation

class CardFormViewModel: ObservableObject {
    @Published var cardForm: CardForm? // Assume this is set up in the linked documentation

    func collectCardData() async throws -> VaultData? {
        do {
            return try await cardForm?.collect()
        } catch {
            print("Error collecting card data: \(error)")
            throw error
        }
    }
}

The collect() method asynchronously gathers the entered card information from the form, returning the collected data as a VaultData object.


Step 3: Create a Card Token

Once the card data is collected, you can create a card token for future use by calling the createCardToken() method:

class CardFormViewModel: ObservableObject {
    @Published var cardForm: CardForm? // Assume this is set up in the linked documentation

    func createCardToken(cardIntentId: String, cardData: VaultData) async {
        do {
            let intentStateDetails = try await cardForm?.createCardToken(
                cardIntentId: cardIntentId,
                cardData: cardData
            )

            switch intentStateDetails {
            case .cardIntentSuccess:
                print("Card token created successfully.")
            case .urlToRender(let url):
                print("Card token creation pending. Open the URL to complete the flow: \(url)")
            default:
                print("Card token creation failed.")
            }
        } catch {
            print("Error creating card token: \(error)")
        }
    }
}

📘

You can implement any additional business logic between collecting card data and tokenizing it.


Complete Example

Here is a complete example of how to integrate all the steps into your iOS app using Swift:

import SwiftUI
import MoneyHashSDK

class CardFormViewModel: ObservableObject {
    @Published var cardForm: CardForm?
    
    init() {
        setupCardForm()
    }
    
    private func setupCardForm() {
        self.cardForm = CardFormBuilder()
            .setCardNumberField { [weak self] state in
                print("Card Number Field State: \(state)")
            }
            .setCVVField { [weak self] state in
                print("CVV Field State: \(state)")
            }
            .setCardHolderNameField { [weak self] state in
                print("Card Holder Name Field State: \(state)")
            }
            .setExpireMonthField { [weak self] state in
                print("Expire Month Field State: \(state)")
            }
            .setExpireYearField { [weak self] state in
                print("Expire Year Field State: \(state)")
            }
            .setCardBrandChangeHandler { [weak self] brand in
                print("Card Brand Changed: \(brand)")
            }.build()
    }
    func saveCard(cardIntentId: String) async {
        do {
            guard let cardData = try await cardForm?.collect() else {
                print("Failed to collect card data.")
                return
            }

            let intentStateDetails = try await cardForm?.createCardToken(
                cardIntentId: cardIntentId,
                cardData: cardData
            )

            switch intentStateDetails {
            case .cardIntentSuccess:
                print("Card token created successfully.")
            case .urlToRender(let url):
                print("Card token creation pending. Open the URL to complete the flow: \(url)")
            default:
                print("Card token creation failed.")
            }
        } catch {
            print("Error during card token creation: \(error)")
        }
    }
}

@available(iOS 15.0, *)
struct CardFormView: View {
    @StateObject var viewModel = CardFormViewModel()

    var body: some View {
        VStack(spacing: 16) {
            SecureTextField(cardFormCollector: viewModel.cardForm!, type: .cardHolderName) {
                Text("Cardholder Name")
                    .foregroundColor(.gray)
            }
            .textFieldStyle()

            SecureTextField(cardFormCollector: viewModel.cardForm!, type: .cardNumber) {
                Text("Card Number")
                    .foregroundColor(.gray)
            }
            .textFieldStyle()

            HStack(spacing: 8) {
                SecureTextField(cardFormCollector: viewModel.cardForm!, type: .expireMonth) {
                    Text("MM")
                        .foregroundColor(.gray)
                }
                .textFieldStyle()

                SecureTextField(cardFormCollector: viewModel.cardForm!, type: .expireYear) {
                    Text("YY")
                        .foregroundColor(.gray)
                }
                .textFieldStyle()

                SecureTextField(cardFormCollector: viewModel.cardForm!, type: .cvv) {
                    Text("CVV")
                        .foregroundColor(.gray)
                }
                .textFieldStyle()
            }

            Button("Save Card") {
                Task {
                    await viewModel.saveCard(cardIntentId: "<card_intent_id>")
                }
            }
        }
        .padding()
    }
}

@available(iOS 15.0, *)
extension View {
    func textFieldStyle() -> some View {
        self
            .padding()
            .background(Color(UIColor.systemGray6))
            .cornerRadius(8)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(Color(UIColor.separator), lineWidth: 1)
            )
    }
}