# MD for: https://www.mercadopago.com.mx/developers/pt/docs/checkout-api-orders/additional-settings/mobile/behavior-customizations.md \# Card Payment behavior customizations The \*\*Mercado Pago SDK Checkout\*\* offers additional resources for the mobile integration of the Card Payment Brick for card payments in iOS and Android applications. In this section, you will see how to tokenize a card without making a charge, handle user cancellation, and restrict the accepted payment methods. See below how to configure these resources. :::::::AccordionComponent{title="Restrict payment methods and limit installments" pill="client-side"} If your operation does not accept certain card types or brands, or works with a specific installment range, you can apply these rules directly in the checkout through the \`setPaymentMethodConfiguration\` method of the SDK \`Builder\`. Validation happens client-side, before tokenization: cards outside the rule are rejected in the form itself, without any token being generated, and only the installments within the configured range are shown to the buyer. The configuration is done by \*\*exclusion\*\*, meaning you specify what you do not accept. ::::::TabsComponent :::::TabComponent{title="Android"} \`\`\`kotlin MercadoPagoCheckout.Builder( context = this, checkoutType = MPCheckoutType.CardTransaction( order = MPOrder( orderId = "order-id", clientToken = "order-client-token" ) ) ).setPaymentMethodConfiguration( listOf( MPPaymentMethodConfig.Card( excludedPaymentTypes = listOf(MPCardType.DEBIT, MPCardType.PREPAID), excludedPaymentMethods = listOf(MPCardBrand.AMEX), installment = MPInstallment(minInstallments = 1, maxInstallments = 6) ) ) ).build() \`\`\` | Parameter | Type | Description | |-----------|------|-------------| | \`excludedPaymentTypes\` | \`MPCardType\` | Excluded card types, which can be: \`DEBIT\`, \`PREPAID\` or \`CREDIT\`. | | \`excludedPaymentMethods\` | \`MPCardBrand\` | Excluded brands, such as \`MPCardBrand.Visa\`, \`MPCardBrand.Mastercard\`, or \`MPCardBrand.AMEX\`. Use \`MPCardBrand.Custom(...)\` for brands outside the default list. | | \`installment\` | \`MPInstallment\` | Minimum and maximum accepted installments. | ::::: :::::TabComponent{title="iOS"} \`\`\`swift MercadoPagoCheckout.Builder( checkoutType: .cardTransaction( order: MPOrder( orderId: "order-id", clientToken: "order-client-token" ) ) ).setPaymentMethodConfiguration(\[ .card( excludedTypes: \[.debit, .prepaid\], excludedBrands: \[.amex\], installment: MPInstallment(minInstallments: 1, maxInstallments: 6) ) \]).build() \`\`\` | Parameter | Type | Description | |-----------|------|-------------| | \`excludedTypes\` | \`\[CardType\]\` | Excluded card types, which can be: \`.debit\`, \`.prepaid\` or \`.credit\`. | | \`excludedBrands\` | \`\[CardBrand\]\` | Excluded brands, such as \`.visa\`, \`.master\`, or \`.amex\`. Use \`.custom(...)\` for brands outside the default list. | | \`installment\` | \`MPInstallment\` | Minimum and maximum accepted installments. | ::::: :::::: ::::::: :::::::AccordionComponent{title="Tokenize card without making a payment" pill="client-side"} > WARNING > > If your integration needs to restrict a card type, the accepted brands, or the installment range, this configuration must be applied \*\*before\*\* starting the tokenization. The rules are evaluated at the moment the form is displayed, so a card outside the restrictions is rejected without any token being generated. For more information, see the \[Restrict payment methods and limit installments\](https://www.mercadopago.com.mx/developers/en/docs/checkout-api-orders/additional-settings/mobile/behavior-customizations#bookmark\_restrict\_payment\_methods\_and\_limit\_installments) section. In subscription, delivery, or recurring service applications, it is common to capture the card data at one moment and make the charge at another. For these cases, the SDK offers a flow that displays the card form, validates the entered data, and generates the token \*\*without creating an order or moving money\*\*. Upon completion, the SDK returns a single-use token along with \`paymentMethodId\`, \`paymentTypeId\`, and \`issuerId\`. To do this, follow the steps below according to the chosen operating system. > NOTE > > \`CardSave\` does not associate the card with a customer nor make a charge. To store the card, send the token to your backend following the \[Save cards\](https://www.mercadopago.com.mx/developers/en/docs/checkout-api-orders/saved-cards) flow. ::::::TabsComponent :::::TabComponent{title="Android"} For Android applications, the flow responsible for this tokenization is \`CardSave\`, defined in the \`checkoutType\` when building the checkout. \`\`\`kotlin val checkout = MercadoPagoCheckout.Builder( context = this, checkoutType = MPCheckoutType.CardSave ).build() checkout.show { result -> when (result) { is MercadoPagoCheckoutResult.Success -> { val data = result.paymentData // MPPaymentData.CardSave // Send data.token to your backend to continue the storage flow } is MercadoPagoCheckoutResult.Error -> { // Show an error message or offer retry } is MercadoPagoCheckoutResult.UserCancelled -> { // Return to the cart or to the previous step } } } \`\`\` On success, the SDK will return the following information in \`MPPaymentData.CardSave\`: | Parameter | Type | Description | Required | |---|---|---|---| | \`token\` | \`String\` | Payment token generated for the transaction. | Required | | \`paymentMethodId\` | \`String\` | Identifier of the selected payment method. | Required | | \`paymentTypeId\` | \`String\` | Identifier of the selected payment type. | Required | | \`payer\` | \`Payer?\` | Payer information (\`documentType\` and \`documentNumber\`). | Optional | | \`issuerId\` | \`String?\` | Card issuer identifier. | Optional | ::::: :::::TabComponent{title="iOS"} For iOS applications, the flow responsible for this tokenization is \`saveCard\`, defined in the \`checkoutType\` when building the checkout. \`\`\`swift let checkout = MercadoPagoCheckout.Builder( checkoutType: .saveCard ).build() \`\`\` ### Display the checkout After building the instance, you need to present it in the application interface. The display method varies according to the UI framework and the navigation type of your project. In all cases, the result is delivered in the same closure, which must handle the three possible scenarios: success, error, and cancellation by the user. Choose below the option corresponding to your application architecture. ::::TabsComponent :::TabComponent{title="SwiftUI"} To open the checkout in interfaces built with SwiftUI, use \`checkout.show\`. \`\`\`swift .fullScreenCover(isPresented: $showCheckout) { checkout.show { result in switch result { case .success(let paymentData): // Send paymentData.token to your backend to continue the storage flow case .error(let error): // Show an error message or offer retry case .userCancelled(let context): // Return to the cart or to the previous step } } } \`\`\` ::: :::TabComponent{title="UIKit — modal"} To open the checkout modally in interfaces built with UIKit, use \`checkout.present\`. \`\`\`swift checkout.present(from: self) { result in switch result { case .success(let paymentData): // Send paymentData.token to your backend to continue the storage flow case .error(let error): // Show an error message or offer retry case .userCancelled(let context): // Return to the cart or to the previous step } } \`\`\` ::: :::TabComponent{title="UIKit — push"} To stack the checkout on the existing navigation stack in interfaces built with UIKit, use \`checkout.push\`. \`\`\`swift checkout.push(to: navigationController) { result in switch result { case .success(let paymentData): // Send paymentData.token to your backend to continue the storage flow case .error(let error): // Show an error message or offer retry case .userCancelled(let context): // Return to the cart or to the previous step } } \`\`\` ::: :::: On success, the SDK will return the following information in \`MPPaymentData.saveCard\`: | Parameter | Type | Description | Required | |---|---|---|---| | \`token\` | \`String\` | Payment token generated for the transaction. | Required | | \`paymentMethodId\` | \`String\` | Identifier of the selected payment method. | Required | | \`paymentTypeId\` | \`String\` | Identifier of the selected payment type. | Required | | \`issuerId\` | \`String?\` | Card issuer identifier. | Optional | | \`payer\` | \`Payer?\` | Payer information (\`documentType\` and \`documentNumber\`). | Optional | ::::: :::::: ::::::: :::::::AccordionComponent{title="Define behavior after user cancellation" pill="client-side"} The buyer can abandon the checkout before completing the operation, whether by closing the screen, returning to the previous navigation, or interrupting the form completion. In these cases, the SDK \*\*does not return an error\*\*, but rather returns a specific cancellation result (\`MercadoPagoCheckoutResult.UserCancelled\`) containing the state of each field at the moment the flow was closed. Use this information to define the application behavior after the abandonment, such as resuming the completion from where the buyer stopped or identifying at which stage of the form the abandonment occurred. See below the data returned in each operating system. ::::::TabsComponent :::::TabComponent{title="Android"} On Android, the cancellation data is available in the \`cancelledData\` property, with the concrete type defined by the \`checkoutType\` configured in the \`Builder\`. \`\`\`kotlin checkout.show { result -> when (result) { is MercadoPagoCheckoutResult.UserCancelled -> { // Iterate over the fields to know what had already been filled in result.cancelledData.fields.forEach { fieldState -> when (fieldState.state) { is State.Valid -> { /\* Valid field: reuse it on the next attempt \*/ } is State.Empty -> { /\* Field not filled in \*/ } is State.Incomplete -> { /\* Field partially filled in \*/ } is State.Invalid -> { /\* Field with an invalid value \*/ } is State.CardBrandNotAccepted -> { /\* Brand not accepted \*/ } is State.CardTypeNotAccepted -> { /\* Card type not accepted \*/ } } } } is MercadoPagoCheckoutResult.Success -> { /\* Handle the success \*/ } is MercadoPagoCheckoutResult.Error -> { /\* Handle the error \*/ } } } \`\`\` The object received in \`cancelledData\` is an \`MPUserCancelledContext\` and has the properties below. | Property | Type | Description | |---|---|---| | \`fields\` | \`List\` | State of each form field at the moment of cancellation. | | \`screens\` | \`List\` | Screens visited by the buyer before cancelling, in the order they were accessed. Available only in the \`CardTransaction\` flow. Possible values: \`CARD\_FORM\` and \`INSTALLMENTS\`. | Each item in \`fields\` is an \`MPCancelledFieldState\`, composed of: | Property | Type | Description | |---|---|---| | \`field\` | \`Field\` | Form field, which can be: \`CARD\_NUMBER\`, \`CARD\_HOLDER\`, \`EXPIRATION\_DATE\`, \`SECURITY\_CODE\` and \`DOCUMENT\`. | | \`state\` | \`State\` | Field state, which can be: \`Valid\`, \`Empty\`, \`Incomplete\`, \`Invalid\`, \`CardBrandNotAccepted(brand)\` and \`CardTypeNotAccepted(cardType)\`. | ::::: :::::TabComponent{title="iOS"} On iOS, the cancellation context is delivered as a parameter of the \`.userCancelled\` case, with the concrete type defined by the \`checkoutType\` configured in the \`Builder\`. \`\`\`swift checkout.show { result in switch result { case let .userCancelled(context): // Iterate over the fields to know what had already been filled in for fieldState in context.cardForm.fields { switch fieldState.state { case .valid: break // Valid field: reuse it on the next attempt case .empty: break // Field not filled in case .incomplete: break // Field partially filled in case .invalid: break // Field with an invalid value case .cardBrandNotAccepted: break // Brand not accepted case .cardTypeNotAccepted: break // Card type not accepted } } case let .success(paymentData): break // Handle the success case let .error(error): break // Handle the error } } \`\`\` The object received is an \`MPUserCancelledContext\` and has the properties below. | Property | Type | Description | |---|---|---| | \`cardForm\` | \`MPCardFormUserCancelledContext\` | State of the card form at the moment of cancellation. | | \`screens\` | \`\[MPScreen\]\` | Screens visited by the buyer before cancelling, in the order they were accessed. Available in the \`cardTransaction\` and \`payment\` flows. Possible values: \`.paymentMethodSelector\`, \`.installments\` and \`.securityCode\`. | The \`cardForm\` exposes the \`fields\` property, an array of \`FieldState\` composed of: | Property | Type | Description | |---|---|---| | \`field\` | \`CardFormField\` | Form field, which can be: \`.cardNumber\`, \`.cardHolder\`, \`.expirationDate\`, \`.securityCode\` and \`.document\`. | | \`state\` | \`State\` | Field state, which can be: \`.valid\`, \`.empty\`, \`.incomplete\`, \`.invalid\`, \`.cardBrandNotAccepted\` and \`.cardTypeNotAccepted\`. | ::::: :::::: :::::::