chromium/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_unittest.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.h"

#include <memory>

#include "base/base64.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/values.h"
#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_details.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace payments::facilitated {

using FacilitatedPaymentsInitiatePaymentRequestTest = testing::Test;

TEST_F(FacilitatedPaymentsInitiatePaymentRequestTest,
       RequestContents_WithAllDetails) {
  auto request_details_full =
      std::make_unique<FacilitatedPaymentsInitiatePaymentRequestDetails>();
  request_details_full->risk_data_ = "seems pretty risky";
  // The client token will be base64 encoded as "dG9rZW4=" in the request
  // content.
  request_details_full->client_token_ =
      std::vector<uint8_t>{'t', 'o', 'k', 'e', 'n'};
  request_details_full->billing_customer_number_ = 11;
  request_details_full->merchant_payment_page_hostname_ = "foo.com";
  request_details_full->instrument_id_ = 13;
  request_details_full->pix_code_ = "a valid code";

  auto request = std::make_unique<FacilitatedPaymentsInitiatePaymentRequest>(
      std::move(request_details_full), /*response_callback=*/base::DoNothing(),
      /*app_locale=*/"US", /*full_sync_enabled=*/true);

  EXPECT_EQ(request->GetRequestUrlPath(),
            "payments/apis/chromepaymentsservice/initiatepayment");
  EXPECT_EQ(request->GetRequestContentType(), "application/json");
  //   Verify that all the data is added to the request content.
  EXPECT_EQ(
      request->GetRequestContent(),
      "{\"chrome_user_context\":{\"full_sync_enabled\":true},\"client_token\":"
      "\"dG9rZW4=\",\"context\":{\"billable_service\":70154,\"customer_"
      "context\":{"
      "\"external_customer_id\":\"11\"},\"language_code\":\"US\"},\"merchant_"
      "info\":{\"merchant_checkout_page_url\":\"foo.com\"},\"payment_details\":"
      "{\"payment_rail\":\"PIX\",\"qr_code\":\"a "
      "valid "
      "code\"},\"risk_data_encoded\":{\"encoding_type\":\"BASE_64\",\"message_"
      "type\":\"BROWSER_NATIVE_FINGERPRINTING\",\"value\":\"seems pretty "
      "risky\"},\"sender_instrument_id\":\"13\"}");
}

TEST_F(FacilitatedPaymentsInitiatePaymentRequestTest,
       RequestContents_WithoutOptionalDetails) {
  auto request_details_without_optional_data =
      std::make_unique<FacilitatedPaymentsInitiatePaymentRequestDetails>();
  // `billing_customer_number_` and `merchant_payment_page_hostname_` optional
  // fields are not set.
  request_details_without_optional_data->risk_data_ = "seems pretty risky";
  // The client token will be base64 encoded as "dG9rZW4=" in the request
  // content.
  request_details_without_optional_data->client_token_ =
      std::vector<uint8_t>{'t', 'o', 'k', 'e', 'n'};
  request_details_without_optional_data->instrument_id_ = 13;
  request_details_without_optional_data->pix_code_ = "a valid code";

  auto request = std::make_unique<FacilitatedPaymentsInitiatePaymentRequest>(
      std::move(request_details_without_optional_data),
      /*response_callback=*/base::DoNothing(),
      /*app_locale=*/"US", /*full_sync_enabled=*/true);

  EXPECT_EQ(request->GetRequestUrlPath(),
            "payments/apis/chromepaymentsservice/initiatepayment");
  EXPECT_EQ(request->GetRequestContentType(), "application/json");
  // Verify that all data except the optional fields are added to the request
  // content. Excluded field `billing_customer_number_` maps to
  // `external_customer_id`, and `merchant_payment_page_hostname_` maps to
  // `merchant_checkout_page_url` in the request content. Both these data should
  // be absent.
  EXPECT_EQ(request->GetRequestContent(),
            "{\"chrome_user_context\":{\"full_sync_enabled\":true},\"client_"
            "token\":\"dG9rZW4=\",\"context\":{\"billable_service\":70154,"
            "\"language_code\":\"US\"},\"payment_details\":{\"payment_rail\":"
            "\"PIX\",\"qr_code\":\"a valid "
            "code\"},\"risk_data_encoded\":{\"encoding_type\":\"BASE_64\","
            "\"message_type\":\"BROWSER_NATIVE_FINGERPRINTING\",\"value\":"
            "\"seems pretty risky\"},\"sender_instrument_id\":\"13\"}");
}

TEST_F(FacilitatedPaymentsInitiatePaymentRequestTest,
       ParseResponse_WithActionToken) {
  auto request = std::make_unique<FacilitatedPaymentsInitiatePaymentRequest>(
      std::make_unique<FacilitatedPaymentsInitiatePaymentRequestDetails>(),
      /*response_callback=*/base::DoNothing(),
      /*app_locale=*/"US", /*full_sync_enabled=*/true);
  // The action token "token" is base64 encoded as "dG9rZW4=" in the response
  // content.
  std::optional<base::Value> response = base::JSONReader::Read(
      "{\"trigger_purchase_manager\":{\"o2_action_token\":\"dG9rZW4=\"}}");
  request->ParseResponse(response->GetDict());

  std::vector<uint8_t> expected_action_token = {'t', 'o', 'k', 'e', 'n'};
  EXPECT_EQ(expected_action_token, request->response_details_->action_token_);

  // Verify that the response is considered complete.
  EXPECT_TRUE(request->IsResponseComplete());
}

TEST_F(FacilitatedPaymentsInitiatePaymentRequestTest,
       ParseResponse_WithCorruptActionToken) {
  auto request = std::make_unique<FacilitatedPaymentsInitiatePaymentRequest>(
      std::make_unique<FacilitatedPaymentsInitiatePaymentRequestDetails>(),
      /*response_callback=*/base::DoNothing(),
      /*app_locale=*/"US", /*full_sync_enabled=*/true);
  // Set a corrupt base64 action token to simulate Base64Decode to return an
  // empty vector.
  std::optional<base::Value> response = base::JSONReader::Read(
      "{\"trigger_purchase_manager\":{\"o2_action_token\":\"dG9r00ZW4=\"}}");
  request->ParseResponse(response->GetDict());

  EXPECT_TRUE(request->response_details_->action_token_.empty());
  // Verify that the response is considered incomplete.
  EXPECT_FALSE(request->IsResponseComplete());
}

TEST_F(FacilitatedPaymentsInitiatePaymentRequestTest,
       ParseResponse_WithErrorMessage) {
  auto request = std::make_unique<FacilitatedPaymentsInitiatePaymentRequest>(
      std::make_unique<FacilitatedPaymentsInitiatePaymentRequestDetails>(),
      /*response_callback=*/base::DoNothing(),
      /*app_locale=*/"US", /*full_sync_enabled=*/true);
  std::optional<base::Value> response = base::JSONReader::Read(
      "{\"error\":{\"user_error_message\":\"Something went wrong!\"}}");
  request->ParseResponse(response->GetDict());

  EXPECT_EQ("Something went wrong!",
            request->response_details_->error_message_.value());

  // Verify that the response is considered incomplete.
  EXPECT_FALSE(request->IsResponseComplete());
}

}  // namespace payments::facilitated