Configura la confirmación de la compra

El proceso de confirmación de la compra se invoca cuando un usuario crea un carrito. El contenido del carrito del usuario y los detalles del pedido se envían a tu servicio web de pedidos de extremo a extremo. Tu servicio web valida esta información y, luego, puedes continuar o realizar ajustes en el carrito según sea necesario.

El controlador de confirmación de la compra de tu servicio web debe responder a las solicitudes POST. Cuando un cliente elige confirmar la compra, Google envía al servicio web de pedidos de extremo a extremo un cuerpo de solicitud JSON en forma de CheckoutRequestMessage, que contiene los detalles del Cart de un cliente. Luego, tu servicio web responde con un CheckoutResponseMessage. En el siguiente diagrama, se ilustra el proceso.

CheckoutResponseMessage muestra el carrito del cliente sin modificar o un
error.

Cuando recibas una solicitud de confirmación de la compra, tu servicio web de extremo a extremo de pedidos debe hacer lo siguiente:

  • Verifica la validez del carrito en función de los precios, la disponibilidad y el servicio del proveedor actuales de los artículos.
  • Calcula el precio total (incluidos los descuentos, los impuestos y las tarifas de entrega).
  • Si se realiza correctamente, responde con un carrito sin modificar.
  • Si no se realiza correctamente, responde con un mensaje de error y un nuevo pedido propuesto.

Antes de comenzar a implementar la confirmación de la compra, te recomendamos que revises la documentación de descripción general de la entrega.

Mensaje de solicitud de confirmación de la compra

Para validar el carrito del cliente, cuando este elige finalizar la compra, Google envía una solicitud a tu servicio web con un cuerpo JSON en forma de CheckoutRequestMessage. El pedido del cliente no se envía hasta más adelante en el flujo de pedidos de extremo a extremo.

Los datos contenidos en un CheckoutRequestMessage incluyen lo siguiente:

  • Intento: El campo inputs[0].intent de cada cuerpo de solicitud de confirmación de la compra contiene el valor de cadena actions.foodordering.intent.CHECKOUT.
  • Carrito: El campo inputs[0].arguments[0].extension de una solicitud de confirmación de la compra contiene un objeto Cart que representa el carrito del cliente.
  • Para llevar o entrega a domicilio: El campo de extensión del objeto Cart contiene un objeto FoodCartExtension que especifica propiedades para la entrega a domicilio o para llevar:
    • En el caso de los pedidos de entrega, el objeto FoodCartExtension incluye la dirección de entrega.
    • En el caso de los pedidos para llevar o para retirar, el objeto FoodCartExtension no contiene información de ubicación.
  • Zona de pruebas: El campo isInSandbox de una solicitud de confirmación de la compra contiene un valor booleano que indica si la transacción usa pagos de zona de pruebas.

Ejemplo de solicitud de confirmación de la compra

A continuación, se muestra un ejemplo de un CheckoutRequestMessage:

{
    "user": {},
    "conversation": {
        "conversationId": "CTZbZfUlHCybEdcz_5PB3Ttf"
    },
    "inputs": [
        {
            "intent": "actions.foodordering.intent.CHECKOUT",
            "arguments": [
                {
                    "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.Cart",
                        "merchant": {
                            "id": "restaurant/Restaurant/QWERTY",
                            "name": "Tep Tep Chicken Club"
                        },
                        "lineItems": [
                            {
                                "name": "Spicy Fried Chicken",
                                "type": "REGULAR",
                                "id": "299977679",
                                "quantity": 2,
                                "price": {
                                    "type": "ESTIMATE",
                                    "amount": {
                                        "currencyCode": "AUD",
                                        "units": "39",
                                        "nanos": 600000000
                                    }
                                },
                                "offerId": "MenuItemOffer/QWERTY/scheduleId/496/itemId/143",
                                "extension": {
                                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                                }
                            }
                        ],
                        "extension": {
                            "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                            "fulfillmentPreference": {
                                "fulfillmentInfo": {
                                    "delivery": {
                                        "deliveryTimeIso8601": "P0M"
                                    }
                                }
                            },
                            "location": {
                                "coordinates": {
                                    "latitude": -33.8376441,
                                    "longitude": 151.0868736
                                },
                                "formattedAddress": "Killoola St, 1, Concord West NSW 2138",
                                "zipCode": "2138",
                                "city": "Concord West",
                                "postalAddress": {
                                    "regionCode": "AU",
                                    "postalCode": "2138",
                                    "administrativeArea": "NSW",
                                    "locality": "Concord West",
                                    "addressLines": [
                                        "Killoola St",
                                        "1"
                                    ]
                                }
                            }
                        }
                    }
                }
            ]
        }
    ],
    "directActionOnly": true,
    "isInSandbox": true
}

Mensaje de respuesta de confirmación de la compra

Después de recibir una solicitud del servicio de pedidos de extremo a extremo, tu servicio web de confirmación de la compra debe procesarla y responder con un CheckoutResponseMessage. El CheckoutResponseMessage debe cubrir una solicitud exitosa o fallida.

Solicitud correcta

Si una solicitud de salida se realiza correctamente, CheckoutResponseMessage debe incluir ProposedOrder y PaymentOptions:

  • ProposedOrder

    • cart: Es un objeto cart idéntico al carrito proporcionado en CheckoutRequestMessage. Si se debe cambiar alguno de los elementos del carrito, CheckoutResponseMessage debe incluir un FoodErrorExtension con un ProposedOrder corregido.
    • otherItems: Son los elementos que agrega el proveedor, como los cargos de entrega, los impuestos y otras tarifas. También puede contener la propina que agregó el usuario.
    • totalPrice: Es el precio total del pedido.
    • extension: Es un FoodOrderExtension que define la información de entrega del pedido, como el tiempo de entrega.
  • PaymentOptions

    • La configuración del procesamiento de pagos se explica más adelante en Cómo configurar Google Pay. Puedes usar el marcador de posición JSON en tu CheckoutResponseMessage hasta que tengas todo listo para implementar el procesamiento de pagos.
    • Para agregar opciones de pago de marcador de posición en tu CheckoutResponseMessage, consulta el ejemplo que aparece a continuación, en el que se usa una puerta de enlace de pago de ejemplo para PaymentOptions.

Ejemplo de respuesta correcta

{
    "finalResponse": {
        "richResponse": {
            "items": [
                {
                    "structuredResponse": {
                        "checkoutResponse": {
                            "proposedOrder": {
                                "cart": {
                                    "merchant": {
                                        "id": "restaurant/Restaurant/QWERTY",
                                        "name": "Tep Tep Chicken Club"
                                    },
                                    "lineItems": [
                                        {
                                            "name": "Spicy Fried Chicken",
                                            "type": "REGULAR",
                                            "id": "299977679",
                                            "quantity": 2,
                                            "price": {
                                                "type": "ESTIMATE",
                                                "amount": {
                                                    "currencyCode": "AUD",
                                                    "units": "39",
                                                    "nanos": 600000000
                                                }
                                            },
                                            "offerId": "MenuItemOffer/QWERTY/scheduleId/496/itemId/143",
                                            "extension": {
                                                "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                                            }
                                        }
                                    ],
                                    "extension": {
                                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                                        "fulfillmentPreference": {
                                            "fulfillmentInfo": {
                                                "delivery": {
                                                    "deliveryTimeIso8601": "P0M"
                                                }
                                            }
                                        },
                                        "location": {
                                            "coordinates": {
                                                "latitude": -33.8376441,
                                                "longitude": 151.0868736
                                            },
                                            "formattedAddress": "Killoola St, 1, Concord West NSW 2138",
                                            "zipCode": "2138",
                                            "city": "Concord West",
                                            "postalAddress": {
                                                "regionCode": "AU",
                                                "postalCode": "2138",
                                                "administrativeArea": "NSW",
                                                "locality": "Concord West",
                                                "addressLines": [
                                                    "Killoola St",
                                                    "1"
                                                ]
                                            }
                                        }
                                    }
                                },
                                "totalPrice": {
                                    "type": "ESTIMATE",
                                    "amount": {
                                        "currencyCode": "AUD",
                                        "units": "43",
                                        "nanos": 100000000
                                    }
                                },
                                "extension": {
                                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                                    "availableFulfillmentOptions": [
                                        {
                                            "fulfillmentInfo": {
                                                "delivery": {
                                                    "deliveryTimeIso8601": "P0M"
                                                }
                                            }
                                        }
                                    ]
                                },
                                "otherItems": [
                                    {
                                        "name": "Delivery fee",
                                        "price": {
                                            "type": "ESTIMATE",
                                            "amount": {
                                                "currencyCode": "AUD",
                                                "units": "3",
                                                "nanos": 500000000
                                            }
                                        },
                                        "type": "DELIVERY"
                                    }
                                ]
                            },
                            "paymentOptions": {
                                "googleProvidedOptions": {
                                    "facilitationSpecification": "{\"apiVersion\":2,\"apiVersionMinor\":0,\"merchantInfo\":{\"merchantName\":\"merchantName\"},\"allowedPaymentMethods\":[{\"type\":\"CARD\",\"parameters\":{\"allowedAuthMethods\":[\"PAN_ONLY\"],\"allowedCardNetworks\":[\"VISA\",\"MASTERCARD\"],\"billingAddressRequired\":true,\"cvcRequired\":false},\"tokenizationSpecification\":{\"type\":\"PAYMENT_GATEWAY\",\"parameters\":{\"gatewayMerchantId\":\"YOUR_MERCHANT_ID\",\"gateway\":\"cybersource\"}}}],\"transactionInfo\":{\"currencyCode\":\"AUD\",\"totalPriceStatus\":\"ESTIMATED\",\"totalPrice\":\"43.1\"}} "
                                }
                            },
                            "additionalPaymentOptions": [
                                {
                                    "actionProvidedOptions": {
                                        "paymentType": "ON_FULFILLMENT",
                                        "displayName": "Pay when you get your food.",
                                        "onFulfillmentPaymentData": {
                                            "supportedPaymentOptions": []
                                        }
                                    }
                                }
                            ]
                        }
                    }
                }
            ]
        }
    }
}

Solicitud no exitosa

Si una solicitud de confirmación de la compra no se realiza correctamente, CheckoutResponseMessage debe incluir FoodErrorExtension, que contiene una lista de elementos FoodOrderError que describen los errores que se produjeron. Si hay errores recuperables en el pedido, como un cambio de precio de un artículo en el carrito, FoodErrorExtension debe incluir correctedProposedOrder.

Ejemplo de una respuesta incorrecta

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "CLOSED",
                  "description": "The restaurant is closed."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Implementación de la confirmación de la compra

Cuando implementes la confirmación de la compra, debes seguir los siguientes pasos:

Valida el servicio

Muestra un FoodOrderError para la primera condición de error de servicio que se encuentre. Estos errores no se pueden recuperar, por lo que se debe mostrar el primer error que se encuentre. Consulta Cómo manejar errores para obtener una descripción de los errores recuperables.

  1. Lee la propiedad FulfillmentOptionInfo en la solicitud para determinar si el tipo de entrega es para delivery o pickup.
  2. Muestra los siguientes tipos de errores si es necesario:

    Tipo de error Caso de uso
    INVALID El tipo de entrega no es válido.
    No se ha encontrado. No se encontró el tipo de entrega.
    CERRADA
    • No hay ventanas de OperationHours para el pedido.
    • El pedido es para lo antes posible y no hay ServiceHours disponibles para la hora actual.
    • Hay un cierre de emergencia o el servicio isDisabled es verdadero.
    Ten en cuenta que las ventanas especiales tienen prioridad sobre las normales. Consulta ejemplos en validación del período de pedido y quita entidades de servicio temporalmente.
    UNAVAILABLE_SLOT No se puede entregar el pedido con anticipación.
    NO_CAPACITY El restaurante está ocupado y no está tomando pedidos en este momento.
    OUT_OF_SERVICE_AREA El pedido no se puede entregar a la dirección del usuario. Consulta Validación de la dirección de entrega para ver un ejemplo.
    NO_COURIER_AVAILABLE No se puede entregar el pedido debido a la falta de personal de entrega.

Valida y asigna un precio al carrito

  1. Busca cada Cart.lineItems y valida con los datos actuales de tu sistema o del sistema del comercio. El valor MenuItemOffer.sku de la entidad del feed se incluye como LineItem.offerId. Si es necesario, crea un FoodOrderError para cada línea de pedido. Crea un máximo de un error para cada elemento. Muestra los siguientes tipos de errores si es necesario:

    Tipo de error Caso de uso Recuperable
    INVALID Los datos del artículo o cualquiera de los datos de las opciones no son válidos. No
    No se ha encontrado. No se encuentra el artículo ni ninguna de las opciones. No
    PRICE_CHANGED Cambió el precio de un artículo o una combinación de complementos. Este error se puede tratar como recuperable.
    AVAILABILITY_CHANGED El importe solicitado para las líneas de pedido o cualquiera de las opciones no está disponible.
    REQUIREMENTS_NOT_MET No se cumple el importe mínimo ni máximo del pedido. Para determinarlo, verifica si el precio del carrito es inferior a Tarifa.eligibleTransactionVolumeMin o superior a Tarifa.eligibleTransactionVolumeMax. Consulta el ejemplo en Validación del valor mínimo del pedido. No
  2. Muestra la lista validada de lineItems con LineItemType REGULAR. La suma de todos los precios de las líneas de pedido del carrito es el precio del carrito o SUBTOTAL.

Consulta ejemplos en la validación de artículos del carrito.

Calcula los cargos del servicio

  1. Busca la entidad Fee correcta para el servicio según eligibleRegion, validFrom, validThrough y priority.
  2. Calcula el importe de la tarifa según si la entidad se definió con una propiedad price, percentageOfCart o pricePerMeter.
  3. Muestra la tarifa del servicio de entrega o para llevar como LineItem con LineItemType DELIVERY o FEE, respectivamente. Agrega la tarifa a la lista Carrito.otherItems.

Aplica promociones

  1. Busca la entidad Deal según la coincidencia del valor de Promotion.coupon con el de Deal.dealCode.
  2. Valida la oferta y muestra un FoodOrderError si es necesario. Estos errores se pueden tratar como recuperables. Muestra los siguientes tipos de error si es necesario:

    Tipo de error Caso de uso
    PROMO_NOT_RECOGNIZED No se reconoció el código del cupón.
    PROMO_EXPIRED La validez de la oferta venció.
    PROMO_ORDER_INELIGIBLE El pedido no es apto para el cupón.
    PROMO_NOT_APPLICABLE Cualquier otro motivo
  3. Calcula el importe del precio de la oferta según Deal.discount o Deal.discountPercentage.

  4. Aplica el importe del precio de la oferta con el total del carrito o el total de las tarifas según la Oferta.dealType.

  5. Muestra el objeto Cart.promotions con la promoción aplicada.

  6. Muestra la promoción como una LineItem con LineItemType DISCOUNT. Agrega el descuento a la lista Cart.otherItems con un precio negativo.

Devuelve la respuesta

  1. Crea el ProposedOrder.cart, el carrito de respuesta es el mismo que el carrito de solicitud si no se encuentran errores durante la validación.
  2. Muestra la lista ProposedOrder.otherItems, incluidos los impuestos, las tarifas, las propinas y los descuentos, si se aplican. Consulta Propinas para obtener más detalles sobre cómo configurar el elemento propina.
  3. Para incluir el ProposedOrder.totalPrice, agrega el precio del carrito, las tarifas, el descuento, los impuestos y la propina.
  4. Muestra FoodOrderExtension.availableFulfillmentOptions con la FulfillmentOption correspondiente. Actualiza el tiempo estimado de retiro o entrega al tiempo esperado.
  5. Si hay FoodOrderErrors generados a partir de las verificaciones de validación anteriores, haz lo siguiente:
    • Incluye StructuredResponse.error y la lista de errores en FoodErrorExtension.foodOrderErrors.
    • Muestra ProposedOrder en el campo correctedProposedOrder si todos los errores se pueden recuperar.
    • Muestra PaymentOptions en el campo paymentOptions si todos los errores se pueden recuperar.
    • De manera opcional, incluye el additionalPaymentOptions si hay otras opciones de pago disponibles y todos los errores se pueden recuperar.
  6. Si no hay errores de validación, muestra proposedOrder y paymentOptions en el objeto CheckoutResponse. De manera opcional, incluye additionalPaymentOptions si hay otras opciones de pago disponibles.