Pay Using Card Information

Here's how to implement the exact same sections for Android, converting the iOS-based steps into Android using Kotlin and Jetpack Compose:

🚧

Ensure your card method connection is set to Server to Server in the MoneyHash dashboard.

Using your account Public API Key on the MoneyHashSDK class instance, you can access the functionality to collect card data and process payments.


Initialize MoneyHashSDK

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

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

Replace "<account_public_api_key>" with your actual Public API Key from MoneyHash.


Collect Card Data

Once the card form is set up (you can follow the steps here), you can collect card data using the collect() method from the CardForm instance:

class CardFormViewModel {

    var cardForm: CardForm // Assume this is set up as per the linked documentation

    suspend fun collectCardData(): VaultData? {
        return try {
                cardForm?.collect()
            } catch (throwable: Throwable) {
                println("Error collecting card data: ${throwable.message}")
                throw e
            }
        }
}

Note: Make sure the card form is properly set up and rendered before attempting to collect card data.


Pay

Once you've collected the card data, you can process the payment using the pay() method:

class CardFormViewModel {

    var cardForm: CardForm? = null // Assume this is set up as per the linked documentation

    suspend fun processPayment(intentId: String, saveCard: Boolean, billingData: Map<String, String>?, shippingData: Map<String, String>?) {
            try {
                val cardData = collectCardData() ?: return@withContext

                val intentDetails: IntentDetails? = cardForm?.pay(
                    intentId = intentId,
                    cardData = cardData,
                    saveCard = saveCard,
                    billingData = billingData,
                    shippingData = shippingData
                )

                println("Payment successful: $intentDetails")
            } catch (throwable: Throwable) {
                println("Payment failed: ${throwable.message}")
         }
    }
}

Here, the pay() method processes the payment using the provided card data, intent ID, and optional billing/shipping data for reference. Make sure to check out the basics docs.

📘

Note: saveCard

Passing saveCard: true will allow MoneyHash to tokenize and save the card for future use. Make sure your intent is created with "allow_tokenize_card": true for tokenization to work.


Complete Example

class CardFormViewModel : ViewModel() {
    var cardForm: CardForm? = null

    init {
        setupCardForm()
    }

    private fun setupCardForm() {
        cardForm = CardFormBuilder()
            .setCardNumberField { state ->
                println("Card Number Field State: $state")
            }
            .setCVVField { state ->
                println("CVV Field State: $state")
            }
            .setCardHolderNameField { state ->
                println("Card Holder Name Field State: $state")
            }
            .setExpireMonthField { state ->
                println("Expire Month Field State: $state")
            }
            .setExpireYearField { state ->
                println("Expire Year Field State: $state")
            }
            .setCardBrandChangeListener { brand ->
                println("Card Brand Changed: $brand")
            }
            .build()
    }

    fun handlePayment(intentId: String) {
        viewModelScope.launch {
            try {
                val cardData = cardForm?.collect()
                if (cardData == null) {
                    println("Failed to collect card data.")
                    return@launch
                }

                val intentDetails = cardForm?.pay(
                    intentId = intentId,
                    cardData = cardData,
                    saveCard = true, // Set to true to save the card (optional)
                    billingData = mapOf("address" to "123 Main St", "city" to "New York"), // Optional
                    shippingData = mapOf("address" to "456 Elm St", "city" to "Boston")   // Optional
                )

                println("Payment Successful: $intentDetails")
            } catch (e: Exception) {
                println("Payment Error: ${e.message}")
            }
        }
    }
}

class CardFormActivity : ComponentActivity() {
    private val viewModel: CardFormViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CardFormScreen(viewModel = viewModel)
        }
    }
}

@Composable
fun CardFormScreen(viewModel: CardFormViewModel) {
    val coroutineScope = rememberCoroutineScope()

    Scaffold(
        topBar = {
            TopAppBar(title = { Text("Payment Form") })
        },
        content = {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp),
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                SecureTextField(
                    cardForm = viewModel.cardForm!!,
                    type = FieldType.CARD_HOLDER_NAME,
                    placeholder = { Text("Cardholder Name") }
                )

                SecureTextField(
                    cardForm = viewModel.cardForm!!,
                    type = FieldType.CARD_NUMBER,
                    placeholder = { Text("Card Number") }
                )

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    SecureTextField(
                        cardForm = viewModel.cardForm!!,
                        type = FieldType.EXPIRE_MONTH,
                        placeholder = { Text("MM") },
                        modifier = Modifier.weight(1f)
                    )

                    SecureTextField(
                        cardForm = viewModel.cardForm!!,
                        type = FieldType.EXPIRE_YEAR,
                        placeholder = { Text("YY") },
                        modifier = Modifier.weight(1f)
                    )

                    SecureTextField(
                        cardForm = viewModel.cardForm!!,
                        type = FieldType.CVV,
                        placeholder = { Text("CVV") },
                        modifier = Modifier.weight(1f)
                    )
                }

                Button(
                    onClick = {
                        viewModel.handlePayment("<intent_id>")
                    },
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Text("Pay")
                }
            }
        }
    )
}

Explanation:

  • ViewModel (CardFormViewModel):

    • Purpose: Manages the CardForm instance, ensuring it is initialized and can be shared across the UI.

    • Functions:

      • setupCardForm(): Initializes the card form with field listeners.
      • handlePayment(intentId: String): Collects the card data and processes the payment.
  • Activity (CardFormActivity):

    • Purpose: Hosts the UI and integrates the CardForm with Jetpack Compose.

    • setContent: Sets the UI content to the CardFormScreen composable, passing the viewModel.

  • Composable Function (CardFormScreen):

    • Purpose: Displays the form fields using SecureTextField for cardholder name, card number, expiry month, expiry year, and CVV.

    • Button: Triggers the handlePayment() function in the ViewModel to collect and process the payment when clicked.

  • SecureTextField Components:

    • Used for: Capturing user input securely for card details.

    • Fields:

      • Cardholder Name
      • Card Number
      • Expiry Month
      • Expiry Year
      • CVV
  • Payment Processing:

    • handlePayment() Function:
      • Collects Card Data: Calls cardForm.collect() to gather card data.
      • Processes Payment: Calls cardForm.pay() with the collected card data and intent ID.
      • Error Handling: Catches and logs exceptions during payment processing.

Notes:

  • Replace "<intent_id>" with your actual payment intent ID.
  • Ensure that the cardForm is not null before using it in the composable (cardForm = viewModel.cardForm!!).
  • The viewModelScope.launch is used to handle coroutines within the ViewModel.