chromium/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.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 <string>
#include <vector>

#include "base/base64.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "components/autofill/core/browser/payments/payments_autofill_client.h"

namespace payments::facilitated {

namespace {
const char kInitiatePaymentRequestPath[] =
    "payments/apis/chromepaymentsservice/initiatepayment";
}  // namespace

FacilitatedPaymentsInitiatePaymentRequest::
    FacilitatedPaymentsInitiatePaymentRequest(
        std::unique_ptr<FacilitatedPaymentsInitiatePaymentRequestDetails>
            request_details,
        FacilitatedPaymentsNetworkInterface::InitiatePaymentResponseCallback
            response_callback,
        const std::string& app_locale,
        const bool full_sync_enabled)
    : request_details_(std::move(request_details)),
      response_details_(std::make_unique<
                        FacilitatedPaymentsInitiatePaymentResponseDetails>()),
      response_callback_(std::move(response_callback)),
      app_locale_(app_locale),
      full_sync_enabled_(full_sync_enabled) {}

FacilitatedPaymentsInitiatePaymentRequest::
    ~FacilitatedPaymentsInitiatePaymentRequest() = default;

std::string FacilitatedPaymentsInitiatePaymentRequest::GetRequestUrlPath() {
  return kInitiatePaymentRequestPath;
}

std::string FacilitatedPaymentsInitiatePaymentRequest::GetRequestContentType() {
  return "application/json";
}

std::string FacilitatedPaymentsInitiatePaymentRequest::GetRequestContent() {
  base::Value::Dict request_dict;

  base::Value::Dict chrome_user_context;
  chrome_user_context.Set("full_sync_enabled", full_sync_enabled_);
  request_dict.Set("chrome_user_context", std::move(chrome_user_context));

  base::Value::Dict risk_data;
  risk_data.Set("message_type", "BROWSER_NATIVE_FINGERPRINTING");
  risk_data.Set("encoding_type", "BASE_64");
  risk_data.Set("value", request_details_->risk_data_);
  request_dict.Set("risk_data_encoded", std::move(risk_data));

  request_dict.Set(
      "client_token",
      base::Value(base::Base64Encode(request_details_->client_token_)));

  base::Value::Dict context;
  context.Set("language_code", app_locale_);
  context.Set("billable_service", kFacilitatedPaymentsBillableServiceNumber);
  if (request_details_->billing_customer_number_.has_value()) {
    base::Value::Dict customer_context;
    customer_context.Set(
        "external_customer_id",
        base::NumberToString(
            request_details_->billing_customer_number_.value()));
    context.Set("customer_context", std::move(customer_context));
  }
  request_dict.Set("context", std::move(context));

  if (request_details_->merchant_payment_page_hostname_.has_value()) {
    base::Value::Dict merchant_info;
    merchant_info.Set(
        "merchant_checkout_page_url",
        request_details_->merchant_payment_page_hostname_.value());
    request_dict.Set("merchant_info", std::move(merchant_info));
  }

  request_dict.Set(
      "sender_instrument_id",
      base::NumberToString(request_details_->instrument_id_.value()));

  base::Value::Dict payment_details;
  if (request_details_->pix_code_.has_value()) {
    // TODO(b/332602034): Pass the payment rail to be used for payment as an
    // enum. In future, some other payment rail could also support PIX codes.
    payment_details.Set("payment_rail", "PIX");
    payment_details.Set("qr_code", request_details_->pix_code_.value());
  }
  // The request should have a payment rail.
  DCHECK(payment_details.FindString("payment_rail"));
  request_dict.Set("payment_details", std::move(payment_details));

  std::string request_content;
  base::JSONWriter::Write(request_dict, &request_content);
  return request_content;
}

void FacilitatedPaymentsInitiatePaymentRequest::ParseResponse(
    const base::Value::Dict& response) {
  // The `error` is only set in the absence of the action token.
  if (const base::Value::Dict* error = response.FindDict("error")) {
    if (const std::string* error_message =
            error->FindString("user_error_message")) {
      response_details_->error_message_ = *error_message;
    }
    return;
  }

  if (const base::Value::Dict* trigger_purchase_manager =
          response.FindDict("trigger_purchase_manager")) {
    if (const std::string* action_token =
            trigger_purchase_manager->FindString("o2_action_token")) {
      std::optional<std::vector<uint8_t>> decoded_bytes =
          base::Base64Decode(*action_token);
      if (decoded_bytes.has_value()) {
        response_details_->action_token_ = std::move(*decoded_bytes);
      }
    }
  }
}

bool FacilitatedPaymentsInitiatePaymentRequest::IsResponseComplete() {
  return !response_details_->action_token_.empty();
}

void FacilitatedPaymentsInitiatePaymentRequest::RespondToDelegate(
    autofill::payments::PaymentsAutofillClient::PaymentsRpcResult result) {
  std::move(response_callback_).Run(result, std::move(response_details_));
}

}  // namespace payments::facilitated