Incluir Botón de Google pay en mi App

Integración del SDK de Google Pay (Mobile)

Esta guía cubre la integración de Google Pay en apps móviles para Android e iOS.

Para integrar Google Pay en sitios web, consulta la Integración Web.


Android (Google Pay SDK nativo)

1. Agregar dependencias

En el archivo build.gradle del módulo de la app:

dependencies {
    implementation 'com.google.android.gms:play-services-wallet:19.3.0'
}

En AndroidManifest.xml, agregar el meta-data de Google Pay:

<application>
    <!-- ... -->
    <meta-data
        android:name="com.google.android.gms.wallet.api.enabled"
        android:value="true" />
</application>

2. Configurar el PaymentsClient

import com.google.android.gms.wallet.*

val paymentsClient: PaymentsClient = Wallet.getPaymentsClient(
    this, // Activity o Context
    Wallet.WalletOptions.Builder()
        .setEnvironment(WalletConstants.ENVIRONMENT_TEST) // Cambiar a ENVIRONMENT_PRODUCTION en producción
        .build()
)

3. Verificar disponibilidad de Google Pay

val isReadyToPayRequest = IsReadyToPayRequest.newBuilder()
    .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
    .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
    .build()

paymentsClient.isReadyToPay(isReadyToPayRequest)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            // Mostrar botón de Google Pay
            showGooglePayButton()
        }
    }

4. Construir el PaymentDataRequest

val tokenizationSpec = PaymentMethodTokenizationParameters.newBuilder()
    .setPaymentMethodTokenizationType(WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY)
    .addParameter("gateway", "conektagpay")
    .addParameter("gatewayMerchantId", "conekta")
    .build()

val paymentDataRequest = PaymentDataRequest.newBuilder()
    .setTransactionInfo(
        TransactionInfo.newBuilder()
            .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
            .setTotalPrice("100.00")       // Monto del cobro
            .setCurrencyCode("MXN")
            .build()
    )
    .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
    .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
    .setCardRequirements(
        CardRequirements.newBuilder()
            .addAllowedCardNetworks(
                listOf(
                    WalletConstants.CARD_NETWORK_VISA,
                    WalletConstants.CARD_NETWORK_MASTERCARD,
                    WalletConstants.CARD_NETWORK_AMEX
                )
            )
            .build()
    )
    .setPaymentMethodTokenizationParameters(tokenizationSpec)
    .build()

Importante: Los parámetros del gateway deben ser exactamente gateway: "conektagpay" y gatewayMerchantId: "conekta", igual que en la integración web.

5. Lanzar el flujo de pago y capturar el token

private val GOOGLE_PAY_REQUEST_CODE = 991

fun requestPayment() {
    AutoResolveHelper.resolveTask(
        paymentsClient.loadPaymentData(paymentDataRequest),
        this, // Activity
        GOOGLE_PAY_REQUEST_CODE
    )
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == GOOGLE_PAY_REQUEST_CODE) {
        when (resultCode) {
            Activity.RESULT_OK -> {
                val paymentData = PaymentData.getFromIntent(data!!)
                val token = paymentData?.paymentMethodToken?.token ?: return

                // Enviar al backend para crear el cargo en Conekta
                sendTokenToBackend(token, paymentData)
            }
            Activity.RESULT_CANCELED -> {
                // Usuario canceló el pago
            }
            AutoResolveHelper.RESULT_ERROR -> {
                val status = AutoResolveHelper.getStatusFromIntent(data)
                Log.e("GooglePay", "Error: ${status?.statusMessage}")
            }
        }
    }
}

6. Enviar el token al backend

fun sendTokenToBackend(token: String, paymentData: PaymentData) {
    // El token debe codificarse en base64 antes de enviarlo a Conekta
    val tokenBase64 = Base64.encodeToString(token.toByteArray(), Base64.NO_WRAP)

    val payload = JSONObject().apply {
        put("token", tokenBase64)
        put("card_network", "VISA")     // Extraer de paymentData
        put("card_details", "1234")     // Extraer de paymentData
        put("card_funding_source", "credit")
        put("description", "Visa •••• 1234")
    }

    // Enviar payload al backend del merchant,
    // que a su vez crea el cargo en Conekta (ver api-conekta.md)
}

El payload que el backend envía a Conekta es idéntico al de la integración web. Consulta API de Conekta para el detalle completo.


iOS (Google Pay SDK para iOS)

Google Pay en iOS se integra a través del SDK GooglePayButton disponible via Swift Package Manager o CocoaPods.

1. Agregar dependencia

Swift Package Manager:

En Xcode, agregar el paquete: https://github.com/nicklama/google-pay-button-ios

CocoaPods:

pod 'GooglePayButton'

2. Configurar el botón de Google Pay

import GooglePayButton

class CheckoutViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let googlePayButton = GooglePayButton(
            configuration: GooglePayButton.Configuration(
                buttonType: .pay,
                buttonStyle: .black,
                cornerRadius: 8
            )
        )

        googlePayButton.addTarget(self, action: #selector(googlePayTapped), for: .touchUpInside)
        view.addSubview(googlePayButton)
    }
}

3. Construir el PaymentDataRequest

let paymentDataRequest: [String: Any] = [
    "apiVersion": 2,
    "apiVersionMinor": 0,
    "allowedPaymentMethods": [[
        "type": "CARD",
        "parameters": [
            "allowedAuthMethods": ["CRYPTOGRAM_3DS"],
            "allowedCardNetworks": ["VISA", "MASTERCARD", "AMEX"],
            "assuranceDetailsRequired": true
        ],
        "tokenizationSpecification": [
            "type": "PAYMENT_GATEWAY",
            "parameters": [
                "gateway": "conektagpay",
                "gatewayMerchantId": "conekta"
            ]
        ]
    ]],
    "transactionInfo": [
        "totalPriceStatus": "FINAL",
        "totalPrice": "100.00",
        "currencyCode": "MXN",
        "countryCode": "MX"
    ],
    "merchantInfo": [
        "merchantId": "<GOOGLE_MERCHANT_ID>",  // De Google Pay & Wallet Console (producción)
        "merchantName": "Nombre del Merchant"
    ]
]

4. Lanzar el flujo y capturar el token

@objc func googlePayTapped() {
    // Presentar el flujo de Google Pay con el paymentDataRequest
    // El SDK retorna un PaymentData con la misma estructura que en web/Android

    // Al recibir el paymentData:
    handlePaymentData(paymentData)
}

func handlePaymentData(_ paymentData: [String: Any]) {
    guard let paymentMethodData = paymentData["paymentMethodData"] as? [String: Any],
          let tokenizationData = paymentMethodData["tokenizationData"] as? [String: Any],
          let token = tokenizationData["token"] as? String,
          let info = paymentMethodData["info"] as? [String: Any] else {
        return
    }

    // El token debe codificarse en base64 antes de enviarlo a Conekta
    let tokenData = Data(token.utf8)
    let tokenBase64 = tokenData.base64EncodedString()

    let payload: [String: Any] = [
        "token": tokenBase64,
        "card_network": info["cardNetwork"] ?? "",
        "card_details": info["cardDetails"] ?? "",
        "card_funding_source": ((info["cardFundingSource"] as? String) ?? "CREDIT").lowercased(),
        "description": paymentMethodData["description"] ?? ""
    ]

    // Enviar payload al backend del merchant,
    // que a su vez crea el cargo en Conekta (ver api-conekta.md)
    sendToBackend(payload)
}

Notas Importantes para Ambas Plataformas

Parámetros del gateway

Los parámetros de tokenización son los mismos en todas las plataformas:

ParámetroValorDescripción
gatewayconektagpayIdentificador de Conekta en Google Pay
gatewayMerchantIdconektaValor fijo

Método de autenticación

Solo se soporta CRYPTOGRAM_3DS. No usar PAN_ONLY.

Codificación del token

En todas las plataformas, el token retornado por Google Pay debe codificarse en base64 antes de enviarlo a Conekta:

PlataformaCodificación
Android (Kotlin)Base64.encodeToString(token.toByteArray(), Base64.NO_WRAP)
iOS (Swift)Data(token.utf8).base64EncodedString()

Payload a Conekta

El payload que el backend del merchant envía a la API de Conekta es idéntico sin importar si el token proviene de web, Android o iOS. Consulta API de Conekta para el formato completo.

Ambientes

AmbienteAndroidiOS
PruebasWalletConstants.ENVIRONMENT_TESTenvironment: .test
ProducciónWalletConstants.ENVIRONMENT_PRODUCTIONenvironment: .production

En ambiente de pruebas, Google Pay muestra tarjetas de prueba automáticamente sin necesidad de registro en Google Pay & Wallet Console.