> ## Documentation Index
> Fetch the complete documentation index at: https://flashapi.phs.vn/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Đăng ký OAPI

> Vui lòng điền vào mẫu đăng ký dưới đây để yêu cầu quyền truy cập PHS OpenAPI (Flash API). Khi gửi đăng ký này, Quý khách xác nhận và đồng ý rằng quyền truy cập PHS OpenAPI sẽ được cấp dựa trên thông tin Quý khách đã cung cấp. Quý khách cũng đồng ý tuân thủ các Điều khoản và Điều kiện, Thông báo về Rủi ro, Tài liệu hướng dẫn API của PHS OpenAPI, cùng tất cả các chính sách và quy định hiện hành của PHS (bao gồm cả các sửa đổi, bổ sung tùy từng thời điểm).

<form
  noValidate
  id="oapi-registration-form"
  onSubmit={async (e) => {
  e.preventDefault();

  const formElement = e.currentTarget;
  const btn = e.currentTarget.querySelector('button[type="submit"]');
  const msgBox = document.getElementById("form-status-msg");

  const showError = (message, step = null, fieldName = null) => {
      const banner = document.getElementById("form-error-banner");
      const bannerText = document.getElementById("form-error-banner-text");

      if (banner && bannerText) {
          bannerText.innerText = message;
          banner.style.display = "flex";
          document.getElementById("oapi-registration-form")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
      }

      if (step) {
          const tabBtn =
              document.getElementById(`desktop-tab-btn-${step}`) ||
              document.getElementById(`mobile-tab-btn-${step}`);
          if (tabBtn) tabBtn.click();
      }

      if (fieldName) {
          setTimeout(() => {
              const input = formElement[fieldName];
              if (input) {
                  const scrollBox = input.closest('.step-scroll-box');
                  if (scrollBox) {
                      const scrollBoxRect = scrollBox.getBoundingClientRect();
                      const inputRect = input.getBoundingClientRect();
                      if (inputRect.top < scrollBoxRect.top || inputRect.bottom > scrollBoxRect.bottom) {
                          scrollBox.scrollBy({ top: inputRect.top - scrollBoxRect.top - 20, behavior: "smooth" });
                      }
                  }
                  input.focus({ preventScroll: true });
              }
          }, 100);
      }
  };

  const hideError = () => {
      const errorBanner = document.getElementById("form-error-banner");
      if (errorBanner) errorBanner.style.display = "none";
  };

  const getValue = (name) => (formElement[name]?.value ?? "").trim();

  const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);

  const isValidPhone = (value) => /^\d{9,15}$/.test(value);

  hideError();

  const customerName = getValue("customerName");
  const identityNumber = getValue("identityNumber");
  const phsCustCode = getValue("phsCustCode");
  const accountBasic = getValue("accountBasic");
  const notificationEmail = getValue("notificationEmail");
  const alertPhoneNumber = getValue("alertPhoneNumber");
  const technicalContactName = getValue("technicalContactName");
  const technicalContactInfo = getValue("technicalContactInfo");

  const ipWhitelist = getValue("ipWhitelist");
  const appName = getValue("appName");
  const techStack = getValue("techStack");
  const keySecurityMechanism = getValue("keySecurityMechanism");
  const rateLimitMechanism = getValue("rateLimitMechanism");
  const testingPlan = getValue("testingPlan");
  const goLiveDate = getValue("goLiveDate");
  const devUnit = getValue("devUnit");
  const thirdPartyName = getValue("thirdPartyName");

  // STEP 1 validation
  if (customerName.length < 2) {
      showError("Tên khách hàng / tổ chức phải có ít nhất 2 ký tự.", 1, "customerName");
      return;
  }

  if (!phsCustCode) {
      showError("Yêu cầu cung cấp số tài khoản / mã khách hàng tại PHS.", 1, "phsCustCode");
      return;
  }

  if (!accountBasic) {
      showError("Vui lòng nhập số tài khoản/tiểu khoản chứng khoán cơ sở.", 1, "accountBasic");
      return;
  }

  if (!isValidEmail(notificationEmail)) {
      showError("Email nhận thông báo API không hợp lệ.", 1, "notificationEmail");
      return;
  }

  if (!isValidPhone(alertPhoneNumber)) {
      showError("Số điện thoại nhận OTP/cảnh báo phải chứa số và có độ dài từ 9–15 ký tự.", 1, "alertPhoneNumber");
      return;
  }

  // STEP 2 validation
  const apiScopesCheck = e.currentTarget.querySelectorAll('input[name="apiScope"]:checked');
  if (apiScopesCheck.length < 1) {
      showError("Vui lòng chọn ít nhất 1 phạm vi API.", 2);
      return;
  }

  // STEP 3 validation
  if (!ipWhitelist) {
      showError("Vui lòng nhập IP WAN / danh sách IP whitelist.", 3, "ipWhitelist");
      return;
  }

  // STEP 4 validation
  const terms = e.currentTarget.querySelectorAll('input[name="commitments"]:checked');
  if (terms.length < 5) {
      showError("Vui lòng tích chọn đồng ý đầy đủ 5 điều khoản cam kết!", 4);
      return;
  }

  if (btn) {
      btn.disabled = true;
      btn.innerText = "Đang gửi hồ sơ đăng ký...";
  }

  const apiScopes = Array.from(
      e.currentTarget.querySelectorAll('input[name="apiScope"]:checked'),
  ).map((cb) => cb.value);

  const formData = {
      customerName: getValue("customerName"),
      identityNumber: getValue("identityNumber"),
      phsCustCode: getValue("phsCustCode"),
      accountBasic: getValue("accountBasic"),
      accountDerivatives: getValue("accountDerivatives"),
      marginProfile: getValue("marginProfile"),
      notificationEmail: getValue("notificationEmail"),
      alertPhoneNumber: getValue("alertPhoneNumber"),
      technicalContactName: getValue("technicalContactName"),
      technicalContactInfo: getValue("technicalContactInfo"),
      apiScopes: apiScopes,
      apiScopeNote: getValue("apiScopeNote"),
      environment: "Production",
      ipWhitelist: getValue("ipWhitelist"),
      appName: getValue("appName"),
      devUnit:
          getValue("devUnit") === "third_party"
              ? `Thuê bên thứ ba: ${getValue("thirdPartyName")}`
              : "Tự phát triển",
      techStack: getValue("techStack"),
      keySecurityMechanism: getValue("keySecurityMechanism"),
      rateLimitMechanism: getValue("rateLimitMechanism"),
      testingPlan: getValue("testingPlan"),
      goLiveDate: getValue("goLiveDate"),
  };

  // Bỏ các trường trống để không bị lỗi API (ví dụ: goLiveDate không hợp lệ)
  Object.keys(formData).forEach(key => {
      if (formData[key] === "") {
          delete formData[key];
      }
  });

  try {
      const response = await fetch('/api/api-onboarding', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json',
              'Authorization': 'Bearer LILQYb7bfKfF05auHyYEiCM9b9z-KtV4tcqrU0VzT_eqkX0aXMVV-J84q1Dw98Ot'
          },
          body: JSON.stringify(formData)
      });

      if (response.ok) {
          if (msgBox) {
              msgBox.style.display = "block";
              msgBox.style.backgroundColor = "#e6fffa";
              msgBox.style.color = "#234e52";
              msgBox.style.border = "1px solid #319795";
              msgBox.innerText =
                  "Đăng ký thành công! PHS sẽ xem xét hồ sơ của bạn và liên hệ để kích hoạt API trong thời gian sớm nhất.";
              msgBox.scrollIntoView({ behavior: "smooth", block: "center" });
          }
          formElement.reset();
      } else {
          const errorData = await response.json().catch(() => null);
          console.error("API Error details:", errorData);
          throw new Error("Đã có lỗi xảy ra khi gửi hồ sơ đăng ký");
      }
  } catch (err) {
      showError("Đã có lỗi xảy ra từ hệ thống hoặc không thể kết nối. Vui lòng kiểm tra lại thông tin!");
      if (msgBox) msgBox.style.display = "none";
  } finally {
      if (btn) {
          btn.disabled = false;
          btn.innerText = "Gửi hồ sơ đăng ký";
      }
  }
}}
  style={{
  display: "flex",
  flexDirection: "column",
  gap: "20px",
  marginTop: "20px",
  maxWidth: "750px",
  width: "100%",
  height: "65vh",
  minHeight: "450px",
  boxSizing: "border-box",
  border: "1px solid rgba(0,0,0,0.1)",
  borderRadius: "8px",
  padding: "20px",
  overflow: "hidden",
}}
>
  <div
    id="form-error-banner"
    style={{
      display: 'none',
      padding: '14px 18px',
      borderRadius: '8px',
      backgroundColor: 'rgba(239, 68, 68, 0.1)',
      border: '1px solid #ef4444',
      alignItems: 'center',
      justifyContent: 'space-between',
      gap: '12px',
      boxSizing: 'border-box',
      marginBottom: '16px'
  }}
  >
    <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
      <span style={{ fontSize: '18px', color: '#ef4444', flexShrink: 0 }}>⚠️</span>

      <span id="form-error-banner-text" style={{ fontSize: '14px', color: '#ef4444', fontWeight: '500' }}>
        Error message
      </span>
    </div>

    <button
      type="button"
      onClick={() => {
          const banner = document.getElementById('form-error-banner');
          if (banner) banner.style.display = 'none';
      }}
      style={{
          background: 'transparent',
          border: 'none',
          color: '#ef4444',
          fontSize: '18px',
          cursor: 'pointer',
          padding: '0 4px',
          lineHeight: 1
      }}
    >
      ✕
    </button>
  </div>

  <div className="block sm:hidden" style={{ marginBottom: "12px" }}>
    <div
      style={{
          display: "flex",
          flexWrap: "wrap",
          gap: "8px",
          width: "100%",
          borderBottom: "1px solid currentColor",
          opacity: 0.2,
          paddingBottom: "12px",
      }}
    >
      {[1, 2, 3, 4].map((num) => {
                                const titles = [
                                    "1. Thông tin Khách Hàng",
                                    "2. Phạm vi API đề nghị cấp",
                                    "3. Thông tin kỹ thuật đăng ký",
                                    "4. Cam kết của Khách Hàng"
                                ];
                                return (
                                    <button
                                        key={`mobile-${num}`}
                                        type="button"
                                        id={`mobile-tab-btn-${num}`}
                                        onClick={() => {
                                            [1, 2, 3, 4].forEach((n) => {
                                                if (document.getElementById(`step-${n}`))
                                                    document.getElementById(`step-${n}`).style.display = "none";
                                                const mBtn = document.getElementById(`mobile-tab-btn-${n}`);
                                                const dBtn = document.getElementById(`desktop-tab-btn-${n}`);
                                                if (mBtn) {
                                                    mBtn.style.backgroundColor = "transparent";
                                                    mBtn.style.color = "inherit";
                                                    mBtn.style.borderColor = "currentColor";
                                                }
                                                if (dBtn) {
                                                    dBtn.style.backgroundColor = "transparent";
                                                    dBtn.style.color = "inherit";
                                                    dBtn.style.borderColor = "currentColor";
                                                }
                                            });

                                            document.getElementById(`step-${num}`).style.display = "flex";
                                            const curM = document.getElementById(`mobile-tab-btn-${num}`);
                                            const curD = document.getElementById(`desktop-tab-btn-${num}`);
                                            if (curM) {
                                                curM.style.backgroundColor = "#15803d";
                                                curM.style.color = "#ffffff";
                                                curM.style.borderColor = "#15803d";
                                            }
                                            if (curD) {
                                                curD.style.backgroundColor = "#15803d";
                                                curD.style.color = "#ffffff";
                                                curD.style.borderColor = "#15803d";
                                            }

                                            document.getElementById('oapi-registration-form')?.scrollIntoView({
                                                behavior: 'smooth',
                                                block: 'start'
                                            });

                                            if (num === 1) {
                                                setTimeout(() => document.querySelector('input[name="customerName"]')?.focus(), 50);
                                            } else if (num === 3) {
                                                setTimeout(() => document.querySelector('input[name="ipWhitelist"]')?.focus(), 50);
                                            }
                                        }}
                                        style={{
                                            flex: "1 1 45%",
                                            minWidth: "140px",
                                            padding: "10px 8px",
                                            borderRadius: "6px",
                                            fontSize: "13px",
                                            fontWeight: "bold",
                                            border: num === 1 ? "1px solid #15803d" : "1px solid currentColor",
                                            cursor: "pointer",
                                            backgroundColor: num === 1 ? "#15803d" : "transparent",
                                            color: num === 1 ? "#ffffff" : "inherit",
                                            textAlign: "center",
                                            boxSizing: "border-box",
                                        }}
                                    >
                                        {titles[num - 1]}
                                    </button>
                                );
                            })}
    </div>
  </div>

  <div
    className="hidden sm:flex"
    style={{
      borderBottom: "1px solid currentColor",
      opacity: 0.8,
      gap: "8px",
      paddingBottom: "10px",
      width: "100%",
  }}
  >
    {[1, 2, 3, 4].map((num) => {
                      const titles = [
                          "1. Thông tin Khách Hàng",
                          "2. Phạm vi API đề nghị cấp",
                          "3. Thông tin kỹ thuật đăng ký",
                          "4. Cam kết của Khách Hàng"
                      ];
                      return (
                          <button
                              key={`desktop-${num}`}
                              type="button"
                              id={`desktop-tab-btn-${num}`}
                              onClick={() => {
                                  [1, 2, 3, 4].forEach((n) => {
                                      if (document.getElementById(`step-${n}`))
                                          document.getElementById(`step-${n}`).style.display = "none";
                                      const mBtn = document.getElementById(`mobile-tab-btn-${n}`);
                                      const dBtn = document.getElementById(`desktop-tab-btn-${n}`);
                                      if (mBtn) {
                                          mBtn.style.backgroundColor = "transparent";
                                          mBtn.style.color = "inherit";
                                          mBtn.style.borderColor = "currentColor";
                                      }
                                      if (dBtn) {
                                          dBtn.style.backgroundColor = "transparent";
                                          dBtn.style.color = "inherit";
                                          dBtn.style.borderColor = "currentColor";
                                      }
                                  });

                                  document.getElementById(`step-${num}`).style.display = "flex";
                                  const curM = document.getElementById(`mobile-tab-btn-${num}`);
                                  const curD = document.getElementById(`desktop-tab-btn-${num}`);
                                  if (curM) {
                                      curM.style.backgroundColor = "#15803d";
                                      curM.style.color = "#ffffff";
                                      curM.style.borderColor = "#15803d";
                                  }
                                  if (curD) {
                                      curD.style.backgroundColor = "#15803d";
                                      curD.style.color = "#ffffff";
                                      curD.style.borderColor = "#15803d";
                                  }

                                  document.getElementById('oapi-registration-form')?.scrollIntoView({
                                      behavior: 'smooth',
                                      block: 'start'
                                  });

                                  if (num === 1) {
                                      setTimeout(() => document.querySelector('input[name="customerName"]')?.focus(), 50);
                                  } else if (num === 3) {
                                      setTimeout(() => document.querySelector('input[name="ipWhitelist"]')?.focus(), 50);
                                  }
                              }}
                              style={{
                                  flex: 1,
                                  padding: "4px 8px",
                                  borderRadius: "5px",
                                  fontSize: "12px",
                                  fontWeight: "bold",
                                  border: num === 1 ? "1px solid #15803d" : "1px solid currentColor",
                                  cursor: "pointer",
                                  backgroundColor: num === 1 ? "#15803d" : "transparent",
                                  color: num === 1 ? "#ffffff" : "inherit",
                                  textAlign: "center",
                                  transition: "all 0.2s",
                                  whiteSpace: "nowrap",
                              }}
                          >
                              {titles[num - 1]}
                          </button>
                      );
                  })}
  </div>

  <div id="step-1" style={{ display: "flex", flexDirection: "column", flex: 1, overflow: "hidden" }}>
    <div style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px", flexShrink: 0 }}>
      Điều 1. Thông tin Khách Hàng
    </div>

    <div className="step-scroll-box" style={{ flex: 1, overflowY: "auto", paddingRight: "10px" }}>
      <div className="grid grid-cols-1 sm:grid-cols-2" style={{ gap: "16px", alignItems: "end" }}>
        <div className="col-span-1 sm:col-span-2" style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Tên Khách Hàng / Tổ chức
          </label>

          <input
            autoFocus
            type="text"
            name="customerName"
            required
            minLength={2}
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Số giấy tờ định danh / ĐKKD
          </label>

          <input
            type="text"
            name="identityNumber"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Số lưu ký / Mã KH tại PHS
          </label>

          <input
            type="text"
            name="phsCustCode"
            required
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Tài khoản/tiểu khoản chứng khoán cơ sở đăng ký API
          </label>

          <input
            type="text"
            name="accountBasic"
            required
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Tài khoản/tiểu khoản phái sinh đăng ký API (nếu có)
          </label>

          <input
            type="text"
            name="accountDerivatives"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div className="col-span-1 sm:col-span-2" style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Hồ sơ ký quỹ áp dụng (nếu có)
          </label>

          <input
            type="text"
            name="marginProfile"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Email nhận thông báo API
          </label>

          <input
            type="email"
            name="notificationEmail"
            required
            placeholder="example@email.com"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            SĐT nhận OTP / Cảnh báo
          </label>

          <input
            type="tel"
            name="alertPhoneNumber"
            required
            inputMode="numeric"
            pattern="\d{9,15}"
            title="Digits only, 9 to 15 digits."
            onInput={(e) => {
                      e.currentTarget.value = e.currentTarget.value.replace(/\D/g, "");
                  }}
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Người phụ trách kỹ thuật / Chức vụ
          </label>

          <input
            type="text"
            name="technicalContactName"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div style={{ display: "flex", flexDirection: "column" }}>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Email / SĐT người phụ trách kỹ thuật
          </label>

          <input
            type="text"
            name="technicalContactInfo"
            placeholder="example@email.com or 0912345678"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>
      </div>
    </div>

    <div className="flex flex-col-reverse sm:flex-row justify-end gap-3" style={{ marginTop: "20px", paddingTop: "10px", borderTop: "1px solid currentColor", opacity: 0.9, flexShrink: 0 }}>
      <button
        type="button"
        onClick={() => {
              const form = document.getElementById("oapi-registration-form");
              const getValue = (name) => (form[name]?.value ?? "").trim();
              const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
              const isValidPhone = (value) => /^\d{9,15}$/.test(value);

              const showError = (message, fieldName) => {
                  const banner = document.getElementById("form-error-banner");
                  const bannerText = document.getElementById("form-error-banner-text");
                  if (banner && bannerText) {
                      bannerText.innerText = message;
                      banner.style.display = "flex";
                      document.getElementById("oapi-registration-form")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
                  }
                  if (fieldName) {
                      setTimeout(() => {
                          const input = form[fieldName];
                          if (input) {
                              const scrollBox = input.closest('.step-scroll-box');
                              if (scrollBox) {
                                  const scrollBoxRect = scrollBox.getBoundingClientRect();
                                  const inputRect = input.getBoundingClientRect();
                                  if (inputRect.top < scrollBoxRect.top || inputRect.bottom > scrollBoxRect.bottom) {
                                      scrollBox.scrollBy({ top: inputRect.top - scrollBoxRect.top - 20, behavior: "smooth" });
                                  }
                              }
                              input.focus({ preventScroll: true });
                          }
                      }, 100);
                  }
              };

              const errorBanner = document.getElementById("form-error-banner");
              if (errorBanner) errorBanner.style.display = "none";

              const customerName = getValue("customerName");
              const identityNumber = getValue("identityNumber");
              const phsCustCode = getValue("phsCustCode");
              const accountBasic = getValue("accountBasic");
              const notificationEmail = getValue("notificationEmail");
              const alertPhoneNumber = getValue("alertPhoneNumber");
              const technicalContactName = getValue("technicalContactName");
              const technicalContactInfo = getValue("technicalContactInfo");

              if (customerName.length < 2) {
                  showError("Tên khách hàng / tổ chức phải có ít nhất 2 ký tự.", "customerName");
                  return;
              }
              if (!phsCustCode) {
                  showError("Số lưu ký / Mã KH tại PHS là bắt buộc.", "phsCustCode");
                  return;
              }
              if (!accountBasic) {
                  showError("Yêu cầu phải có account / sub-account chứng khoán cơ sở.", "accountBasic");
                  return;
              }
              if (!isValidEmail(notificationEmail)) {
                  showError("Email nhận thông báo về API phải là địa chỉ email hợp lệ (phải chứa ký tự @, ví dụ: name@example.com).", "notificationEmail");
                  return;
              }
              if (!isValidPhone(alertPhoneNumber)) {
                  showError("Số điện thoại nhận OTP và thông báo phải chỉ chứa các chữ số và có độ dài từ 9–15 ký tự.", "alertPhoneNumber");
                  return;
              }

              const btn =
                  document.getElementById("desktop-tab-btn-2") ||
                  document.getElementById("mobile-tab-btn-2");
              if (btn) btn.click();
          }}
        className="w-full sm:w-auto"
        style={{
              padding: "10px 20px",
              backgroundColor: "#15803d",
              color: "#fff",
              border: "none",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
          }}
      >
        Tiếp theo: Phạm vi API đề nghị cấp ➔
      </button>
    </div>
  </div>

  <div id="step-2" style={{ display: "none", flexDirection: "column", flex: 1, overflow: "hidden" }}>
    <div style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "6px", flexShrink: 0 }}>
      Điều 2. Phạm vi API đề nghị cấp (Production)
    </div>

    <div className="step-scroll-box" style={{ flex: 1, overflowY: "auto", paddingRight: "10px" }}>
      <p style={{ fontSize: "14px", marginBottom: "16px", opacity: 0.8 }}>
        Chọn các nhóm API bạn muốn đăng ký sử dụng:
      </p>

      <div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
        {[
                                          {
                                              val: "auth_security",
                                              title: "Authentication & Security",
                                              sub: "Cấp API Key, Secret Key, Access Token",
                                              checked: true,
                                          },
                                          {
                                              val: "trading_equity",
                                              title: "Trading API – underlying securities",
                                              sub: "Đặt, sửa, hủy lệnh, tra cứu lệnh cơ sở",
                                          },
                                          {
                                              val: "trading_derivatives",
                                              title: "Trading API – derivatives",
                                              sub: "Đặt, sửa, hủy lệnh, xác thực OTP, tra cứu lệnh phái sinh",
                                          },
                                          {
                                              val: "account_api",
                                              title: "Account API",
                                              sub: "Xem số dư, danh mục, sức mua, nghĩa vụ ký quỹ",
                                          },
                                          {
                                              val: "market_data",
                                              title: "Market Data API / Historical Candles",
                                              sub: "Truy xuất dữ liệu thị trường và nến lịch sử",
                                          },
                                          {
                                              val: "websocket_streaming",
                                              title: "Streaming / WebSocket",
                                              sub: "Đăng ký nhận stream realtime cho Account / Instrument / Trade",
                                          },
                                          {
                                              val: "developer_portal",
                                              title: "API Playground / Developer Portal",
                                              sub: "Quyền truy cập cổng thử nghiệm API",
                                          },
                                      ].map((scope) => (
                                          <label
                                              key={scope.val}
                                              style={{
                                                  display: "flex",
                                                  gap: "12px",
                                                  alignItems: "center",
                                                  padding: "12px 16px",
                                                  border: "1px solid currentColor",
                                                  borderRadius: "8px",
                                                  cursor: "pointer",
                                              }}
                                          >
                                              <input
                                                  type="checkbox"
                                                  name="apiScope"
                                                  value={scope.val}
                                                  defaultChecked={scope.checked}
                                                  style={{
                                                      width: "18px",
                                                      height: "18px",
                                                      cursor: "pointer",
                                                      accentColor: "#15803d",
                                                  }}
                                              />
                                              <div style={{ display: "flex", flexDirection: "column", gap: "2px" }}>
                                                  <strong style={{ fontSize: "14px" }}>{scope.title}</strong>
                                                  <span style={{ fontSize: "13px", opacity: 0.7 }}>{scope.sub}</span>
                                              </div>
                                          </label>
                                      ))}

        <div
          style={{
                  marginTop: "8px",
                  display: "flex",
                  flexDirection: "column",
                  gap: "6px",
              }}
        >
          <label style={{ fontWeight: "bold", display: "block", fontSize: "14px" }}>
            Ghi chú / Phạm vi tài khoản chi tiết
          </label>

          <input
            type="text"
            name="apiScopeNote"
            placeholder="Additional notes on the scope of API-enabled accounts..."
            style={{
                      width: "100%",
                      padding: "10px 12px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      backgroundColor: "transparent",
                      color: "inherit",
                      fontSize: "14px",
                      boxSizing: "border-box",
                  }}
          />
        </div>
      </div>
    </div>

    <div className="flex flex-col-reverse sm:flex-row justify-between gap-3" style={{ marginTop: "20px", paddingTop: "10px", borderTop: "1px solid currentColor", opacity: 0.9, flexShrink: 0 }}>
      <button
        type="button"
        onClick={() => {
              const btn =
                  document.getElementById("desktop-tab-btn-1") ||
                  document.getElementById("mobile-tab-btn-1");
              if (btn) btn.click();
          }}
        className="w-full sm:w-auto"
        style={{
              padding: "10px 20px",
              border: "1px solid currentColor",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
              backgroundColor: "transparent",
          }}
      >
        Quay lại Bước 1
      </button>

      <button
        type="button"
        onClick={(e) => {
              const showError = (message) => {
                  const banner = document.getElementById("form-error-banner");
                  const bannerText = document.getElementById("form-error-banner-text");
                  if (banner && bannerText) {
                      bannerText.innerText = message;
                      banner.style.display = "flex";
                      banner.scrollIntoView({ behavior: "smooth", block: "center" });
                  }
              };

              const errorBanner = document.getElementById("form-error-banner");
              if (errorBanner) errorBanner.style.display = "none";

              const checkedScopes = document.querySelectorAll('input[name="apiScope"]:checked');
              if (checkedScopes.length < 1) {
                  showError("Please select at least one API scope before continuing.");
                  return;
              }

              const btn =
                  document.getElementById("desktop-tab-btn-3") ||
                  document.getElementById("mobile-tab-btn-3");
              if (btn) btn.click();
          }}
        className="w-full sm:w-auto"
        style={{
              padding: "10px 20px",
              backgroundColor: "#15803d",
              color: "#fff",
              border: "none",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
          }}
      >
        Tiếp theo: Thông tin kỹ thuật ➔
      </button>
    </div>
  </div>

  <div id="step-3" style={{ display: "none", flexDirection: "column", flex: 1, overflow: "hidden" }}>
    <div style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px", flexShrink: 0 }}>
      Điều 3. Thông tin kỹ thuật đăng ký
    </div>

    <div className="step-scroll-box" style={{ flex: 1, overflowY: "auto", paddingRight: "10px" }}>
      <div style={{ display: "flex", flexDirection: "column", gap: "14px" }}>
        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            IP WAN / IP Whitelist đăng ký
          </label>

          <input
            type="text"
            name="ipWhitelist"
            required
            placeholder="Example: 1.2.3.4"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Tên hệ thống / Ứng dụng tích hợp API
          </label>

          <input
            type="text"
            name="appName"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Đơn vị phát triển / vận hành ứng dụng
          </label>

          <div
            style={{
                      display: "flex",
                      gap: "20px",
                      alignItems: "center",
                      marginTop: "6px",
                  }}
          >
            <label className="cursor-pointer flex items-center gap-1.5">
              <input type="radio" name="devUnit" value="self" defaultChecked style={{ accentColor: "#15803d" }} />

              {" "}

              Tự phát triển
            </label>

            <label className="cursor-pointer flex items-center gap-1.5">
              <input type="radio" name="devUnit" value="third_party" style={{ accentColor: "#15803d" }} />

              {" "}

              Thuê bên thứ ba
            </label>
          </div>

          <input
            type="text"
            name="thirdPartyName"
            placeholder="Name of third-party entity (if contracted)..."
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      marginTop: "6px",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Ngôn ngữ / Nền tảng kỹ thuật chính
          </label>

          <input
            type="text"
            name="techStack"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Cơ chế lưu trữ và bảo vệ API Key / Secret Key
          </label>

          <input
            type="text"
            name="keySecurityMechanism"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Cơ chế Kill Switch / Rate Limit nội bộ của KH
          </label>

          <input
            type="text"
            name="rateLimitMechanism"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Kế hoạch kiểm thử trước khi go-live
          </label>

          <input
            type="text"
            name="testingPlan"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>

        <div>
          <label
            style={{
                      fontWeight: "bold",
                      display: "block",
                      marginBottom: "4px",
                      fontSize: "14px",
                  }}
          >
            Ngày dự kiến Go-live Production
          </label>

          <input
            type="date"
            name="goLiveDate"
            style={{
                      width: "100%",
                      padding: "8px",
                      borderRadius: "6px",
                      border: "1px solid currentColor",
                      boxSizing: "border-box",
                  }}
          />
        </div>
      </div>
    </div>

    <div className="flex flex-col-reverse sm:flex-row justify-between gap-3" style={{ marginTop: "20px", paddingTop: "10px", borderTop: "1px solid currentColor", opacity: 0.9, flexShrink: 0 }}>
      <button
        type="button"
        onClick={() => {
              const btn =
                  document.getElementById("desktop-tab-btn-2") ||
                  document.getElementById("mobile-tab-btn-2");
              if (btn) btn.click();
          }}
        className="w-full sm:w-auto"
        style={{
              padding: "10px 20px",
              border: "1px solid currentColor",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
              backgroundColor: "transparent",
          }}
      >
        Quay lại Bước 2
      </button>

      <button
        type="button"
        onClick={() => {
              const form = document.getElementById("oapi-registration-form");
              const getValue = (name) => (form[name]?.value ?? "").trim();

              const showError = (message, fieldName) => {
                  const banner = document.getElementById("form-error-banner");
                  const bannerText = document.getElementById("form-error-banner-text");
                  if (banner && bannerText) {
                      bannerText.innerText = message;
                      banner.style.display = "flex";
                      document.getElementById("oapi-registration-form")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
                  }
                  if (fieldName) {
                      setTimeout(() => {
                          const input = form[fieldName];
                          if (input) {
                              const scrollBox = input.closest('.step-scroll-box');
                              if (scrollBox) {
                                  const scrollBoxRect = scrollBox.getBoundingClientRect();
                                  const inputRect = input.getBoundingClientRect();
                                  if (inputRect.top < scrollBoxRect.top || inputRect.bottom > scrollBoxRect.bottom) {
                                      scrollBox.scrollBy({ top: inputRect.top - scrollBoxRect.top - 20, behavior: "smooth" });
                                  }
                              }
                              input.focus({ preventScroll: true });
                          }
                      }, 100);
                  }
              };

              const errorBanner = document.getElementById("form-error-banner");
              if (errorBanner) errorBanner.style.display = "none";

              const ipWhitelist = getValue("ipWhitelist");
              const appName = getValue("appName");
              const techStack = getValue("techStack");
              const keySecurityMechanism = getValue("keySecurityMechanism");
              const rateLimitMechanism = getValue("rateLimitMechanism");
              const testingPlan = getValue("testingPlan");
              const goLiveDate = getValue("goLiveDate");
              const devUnit = getValue("devUnit");
              const thirdPartyName = getValue("thirdPartyName");

              if (!ipWhitelist) {
                  showError("Registered WAN IP / IP whitelist is required.", "ipWhitelist");
                  return;
              }

              const btn =
                  document.getElementById("desktop-tab-btn-4") ||
                  document.getElementById("mobile-tab-btn-4");
              if (btn) btn.click();
          }}
        className="w-full sm:w-auto"
        style={{
              padding: "10px 20px",
              backgroundColor: "#15803d",
              color: "#fff",
              border: "none",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
          }}
      >
        Tiếp theo: Cam kết của Khách Hàng ➔
      </button>
    </div>
  </div>

  <div id="step-4" style={{ display: "none", flexDirection: "column", flex: 1, overflow: "hidden" }}>
    <div style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "6px", flexShrink: 0 }}>
      Điều 4. Cam kết của Khách Hàng
    </div>

    <div className="step-scroll-box" style={{ flex: 1, overflowY: "auto", paddingRight: "10px" }}>
      <p
        style={{
              fontSize: "14px",
              color: "#e53e3e",
              fontWeight: "bold",
              marginBottom: "12px",
          }}
      >
        Vui lòng tích chọn xác nhận đủ tất cả các điều khoản bên dưới:
      </p>

      <div
        style={{
              display: "flex",
              flexDirection: "column",
              gap: "12px",
              fontSize: "14px",
          }}
      >
        <label
          style={{
                  display: "flex",
                  gap: "10px",
                  alignItems: "center",
                  cursor: "pointer",
                  paddingBottom: "8px",
                  borderBottom: "1px solid currentColor",
                  opacity: 0.9,
                  fontWeight: "bold",
              }}
        >
          <input
            type="checkbox"
            id="toggle-all-commitments"
            onChange={(e) => {
                      const isChecked = e.target.checked;
                      const checkboxes = document.querySelectorAll('input[name="commitments"]');
                      checkboxes.forEach((cb) => {
                          cb.checked = isChecked;
                      });
                  }}
            style={{
                      width: "18px",
                      height: "18px",
                      flexShrink: 0,
                      cursor: "pointer",
                      accentColor: "#15803d",
                  }}
          />

          <span>Chọn tất cả</span>
        </label>

        {[
                                          "Khách Hàng đã mở và duy trì tài khoản giao dịch chứng khoán tại PHS; đối với từng loại API, Khách Hàng đã hoặc sẽ hoàn tất hồ sơ sản phẩm/tài khoản tương ứng trước khi sử dụng production.",
                                          "Khách Hàng đã đọc, hiểu và đồng ý với Điều khoản và Điều kiện sử dụng dịch vụ PHS OpenAPI, Bản công bố rủi ro, Tài Liệu API, quy định giao dịch trực tuyến và các hợp đồng/hồ sơ tài khoản có liên quan tại PHS.",
                                          "Khách Hàng chịu trách nhiệm bảo mật Thông Tin Xác Thực API, quản lý Ứng Dụng Tích Hợp API và mọi yêu cầu/lệnh phát sinh qua API.",
                                          "Khách Hàng cam kết không sử dụng API để thao túng thị trường, lạm dụng thị trường, gửi lệnh gây nhiễu, thử lỗ hổng, vượt giới hạn API, phân phối lại dữ liệu trái phép hoặc thực hiện hành vi bị cấm theo pháp luật.",
                                          "Khách Hàng đồng ý để PHS giám sát, ghi nhận log, áp dụng giới hạn, tạm ngừng hoặc thu hồi quyền truy cập API khi cần thiết để bảo vệ hệ thống, thị trường, nhà đầu tư hoặc để tuân thủ pháp luật/yêu cầu cơ quan có thẩm quyền."
                                      ].map((text, idx) => (
                                          <label
                                              key={idx}
                                              style={{
                                                  display: "flex",
                                                  gap: "10px",
                                                  alignItems: "center",
                                                  cursor: "pointer",
                                              }}
                                          >
                                              <input
                                                  type="checkbox"
                                                  name="commitments"
                                                  required
                                                  onChange={() => {
                                                      const all = document.querySelectorAll('input[name="commitments"]');
                                                      const checked = document.querySelectorAll('input[name="commitments"]:checked');
                                                      const toggleAll = document.getElementById("toggle-all-commitments");
                                                      if (toggleAll) toggleAll.checked = all.length === checked.length;
                                                  }}
                                                  style={{
                                                      width: "18px",
                                                      height: "18px",
                                                      flexShrink: 0,
                                                      cursor: "pointer",
                                                      accentColor: "#15803d",
                                                  }}
                                              />
                                              <span>{text}</span>
                                          </label>
                                      ))}
      </div>

      <div
        id="form-status-msg"
        style={{
              display: "none",
              padding: "12px",
              borderRadius: "6px",
              fontSize: "14px",
              marginTop: "16px",
          }}
      />
    </div>

    <div className="flex flex-col-reverse sm:flex-row justify-between gap-3" style={{ marginTop: "20px", paddingTop: "10px", borderTop: "1px solid currentColor", opacity: 0.9, flexShrink: 0 }}>
      <button
        type="button"
        onClick={() => {
              const btn =
                  document.getElementById("desktop-tab-btn-3") ||
                  document.getElementById("mobile-tab-btn-3");
              if (btn) btn.click();
          }}
        className="w-full sm:w-auto"
        style={{
              padding: "10px 20px",
              border: "1px solid currentColor",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
              backgroundColor: "transparent",
          }}
      >
        Quay lại Bước 3
      </button>

      <button
        type="submit"
        className="w-full sm:w-auto"
        style={{
              padding: "12px 24px",
              backgroundColor: "#15803d",
              color: "#fff",
              border: "none",
              borderRadius: "6px",
              fontWeight: "bold",
              cursor: "pointer",
              fontSize: "15px",
          }}
      >
        Gửi Đơn Đăng Ký
      </button>
    </div>
  </div>
</form>
