google-pay / google-pay-button

Google Pay button - React, Angular, and custom element
Apache License 2.0
255 stars 62 forks source link

[Braintree] This merchant is not enabled for Google Pay #308

Closed WilliamWelsh closed 1 month ago

WilliamWelsh commented 1 month ago

Describe the bug I am setting up Google Pay via Braintree, and no matter what I do I always get the "This merchant is not enabled for Google Pay" error in production mode. I'm passing all the Braintree-specific params according to Google's docs, my merchant has been set up for Google Pay and I added my website as a web integration on the Google Pay console.

Relevant package.json

  "dependencies": {
    "@google-pay/button-react": "^3.1.0",
    "braintree-web": "^3.106.0"
  },

My Google Pay Checkout Component (NextJs, TypeScript, Braintree)

import GooglePayButton from "@google-pay/button-react";
import * as braintree from "braintree-web";
import { env } from "~/env";
import { api } from "~/trpc/react";
import { type RouterInputs } from "~/trpc/shared";
import { type CartItem } from "~/types/CartItem";

const GOOGLE_PAY_MERCHANT_ID = env.NEXT_PUBLIC_GPAYMERCHANTID

const BRAINTREE_MERCHANT_ID = env.NEXT_PUBLIC_BRAINTREEMARCHANTID

export default function GooglePayCheckout({
  theme,
  eventID,
  ccsEventID,
  participantSlug,
  businessName,
  cart,
  submitOrder,
  total
}: {
  theme: string;
  eventID: string;
  ccsEventID: string;
  participantSlug: string | null;
  businessName: string;
  cart: CartItem[];
  submitOrder: (data: RouterInputs["main"]["submitOrder"]) => Promise<void>;
    total: string;
}) {

  const getTotals = api.main.getCartTotalsForExpressCheckout.useMutation();

  const setUpGooglePay = async () => {
    // if (typeof window === "undefined") return;
    //
    // if ((window as any)?.location.protocol !== "https:") return;

    console.log("Initializing Google Pay");

    total.current = cart
      .reduce((acc, item) => acc + item.product.price * item.quantity, 0)
      .toFixed(2);

    const paymentsClient = new google.payments.api.PaymentsClient({
      environment: "PRODUCTION",
      merchantInfo: { merchantId: GOOGLE_PAY_MERCHANT_ID },
      paymentDataCallbacks: {
        // Update the total after we get the shipping address
        onPaymentDataChanged: async (paymentData) => {
          const shippingAddress = paymentData.shippingAddress;

          const data = await getTotals.mutateAsync({
            eventID,
            city: shippingAddress?.locality ?? "",
            state: shippingAddress?.administrativeArea ?? "",
            zip: shippingAddress?.postalCode ?? "",
            participantSlug,
          });

          total.current = data.total.toString();

          return {
            newTransactionInfo: {
              totalPrice: total.current,
              totalPriceStatus: "FINAL",
              currencyCode: "USD",
            },
          };
        },
      },
    });

    const client = await braintree.client.create({
      authorization: env.NEXT_PUBLIC_BRAINTREE_TOKENIZATION_KEY,
    });

    const googleClient = await braintree.googlePayment.create({
      client,
      googlePayVersion: 2,
      googleMerchantId: GOOGLE_PAY_MERCHANT_ID,
    });

    const request = await googleClient.createPaymentDataRequest({
      emailRequired: true,
      callbackIntents: ["SHIPPING_ADDRESS"],
      transactionInfo: {
        totalPrice: total.current,
        currencyCode: "USD",
        totalPriceStatus: "ESTIMATED",
      },
    } as any);

    const cardPaymentMethod = request.allowedPaymentMethods[0];

    if (!cardPaymentMethod) return;

    request.shippingAddressRequired = true;

    cardPaymentMethod.parameters.allowPrepaidCards = true;
    cardPaymentMethod.parameters.allowedCardNetworks = [
      "MASTERCARD",
      "VISA",
      "DISCOVER",
      "AMEX",
    ];
    cardPaymentMethod.parameters.billingAddressRequired = true;
    cardPaymentMethod.parameters.billingAddressParameters = {
      format: "FULL",
      phoneNumberRequired: true,
    };

    const result = await paymentsClient.loadPaymentData(request);

    console.log({ result });
  };

  return (
    <div onClick={setUpGooglePay} className="h-10 w-full cursor-pointer">
      <GooglePayButton
        className="pointer-events-none w-full"
        buttonSizeMode="fill"
        environment="PRODUCTION"
        buttonRadius={6}
        buttonColor={
          theme === "white" || BLACK_BUTTON_THEMES.includes(theme)
            ? "black"
            : "white"
        }
        paymentRequest={{
          apiVersion: 2,
          apiVersionMinor: 0,
          emailRequired: true,
          allowedPaymentMethods: [
            {
              type: "CARD",
              parameters: {
                allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
                allowedCardNetworks: ["MASTERCARD", "VISA", "DISCOVER", "AMEX"],
                billingAddressRequired: true,
                allowPrepaidCards: true,
              },
              tokenizationSpecification: {
                type: "PAYMENT_GATEWAY",
                parameters: {
                  gateway: "braintree",
                  "braintree:apiVersion": "v1",
                  "braintree:sdkVersion": "braintree.client.3.106.0",
                  "braintree:merchantId": BRAINTREE_MERCHANT_ID,
                  "braintree:clientKey":
                    env.NEXT_PUBLIC_BRAINTREE_TOKENIZATION_KEY,
                },
              },
            },
          ],
          merchantInfo: {
            merchantId: GOOGLE_PAY_MERCHANT_ID,
            // merchantName: businessName,
          },
          transactionInfo: {
            totalPriceStatus: "ESTIMATED",
            totalPriceLabel: "Total",
            totalPrice: total.current,
            currencyCode: "USD",
            countryCode: "US",
          },
        }}
      />
    </div>
  );
}

Environment: Website only (Arc/Chrome)

Additional context Add any other context about the problem here.

dmengelt commented 1 month ago

@WilliamWelsh what is the domain name of your integration?

WilliamWelsh commented 1 month ago

@WilliamWelsh what is the domain name of your integration?

https://quic.pics

dmengelt commented 1 month ago

thanks. is there a way for me to test the Google Pay integration on quic.pics?

WilliamWelsh commented 1 month ago

You can try here (only visible on this specific event/page) https://www.quic.pics/pupper-party-02-2024

Click an image, click the first product (Digital Download), add to cart, checkout, and the Google Pay button should show

dmengelt commented 1 month ago

@WilliamWelsh you need to register www.quic.pics and not only quic.pics - Drop a comment here once done (just add a new web integration and upload the same screenshots). I will approve you then.

WilliamWelsh commented 1 month ago

@WilliamWelsh you need to register www.quic.pics and not only quic.pics - Drop a comment here once done (just add a new web integration and upload the same screenshots). I will approve you then.

Done

dmengelt commented 1 month ago

Approved. Works now.

WilliamWelsh commented 1 month ago

Approved. Works now.

Thank you!