All files / src/utils modal-forms.ts

100% Statements 146/146
96.87% Branches 31/32
100% Functions 3/3
100% Lines 146/146

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 1471x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 25x 25x 25x 25x 25x 34x 34x 34x 2x 2x 34x 3x 3x 32x 3x 3x 8x 2x 8x 6x 6x 6x 6x 6x 6x 3x 29x 26x 26x 26x 26x 26x 26x 26x 34x 25x 1x 1x 1x 1x 1x 1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 1x 1x 9x 9x 11x 11x 11x 11x 11x 11x 11x 2x 2x 2x 2x 2x 2x 2x 11x 1x 1x 1x 1x 1x 1x 1x 9x 9x 9x 9x 9x 9x 9x 9x 9x 1x 1x 8x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 3x 3x 9x 9x  
/**
 * Modal form submission utilities
 */
 
import { SDKError } from '../types';
import { decodeBase64 } from './helpers';
 
/**
 * Creates hidden input fields from a data object
 */
export function createFormFields(
  formElement: HTMLFormElement,
  data: Record<string, unknown>,
  prefix = ''
): void {
  for (const [key, value] of Object.entries(data)) {
    const fieldName = prefix ? `${prefix}[${key}]` : key;
 
    if (value === null || value === undefined) {
      // Skip null/undefined values
      continue;
    } else if (typeof value === 'object' && !Array.isArray(value)) {
      // Recursively handle nested objects
      createFormFields(formElement, value as Record<string, unknown>, fieldName);
    } else if (Array.isArray(value)) {
      // Handle arrays
      value.forEach((item, index) => {
        if (typeof item === 'object' && item !== null) {
          createFormFields(formElement, item as Record<string, unknown>, `${fieldName}[${index}]`);
        } else {
          const input = document.createElement('input');
          input.setAttribute('type', 'hidden');
          input.setAttribute('name', `${fieldName}[${index}]`);
          input.setAttribute('value', String(item));
          formElement.appendChild(input);
        }
      });
    } else {
      // Create hidden input for primitive values
      const input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', fieldName);
      input.setAttribute('value', String(value));
      formElement.appendChild(input);
    }
  }
}
 
/**
 * Submits the form to load 3DS authentication page in iframe
 * Decodes base64 data, parses JSON, and creates hidden form fields
 */
export function submitRedirectForm(
  formElement: HTMLFormElement,
  url: string,
  method: string,
  base64Data: string,
  add3dsSdkFlag = false,
  log?: (message: string, ...args: unknown[]) => void
): void {
  // Set form action and method
  formElement.setAttribute('action', url);
  formElement.setAttribute('method', method.toUpperCase());
 
  // Clear any existing inputs
  formElement.innerHTML = '';
 
  try {
    // Decode base64 data
    log?.('Decoding base64 data');
    const decodedData = decodeBase64(base64Data);
 
    // Parse JSON object
    log?.('Parsing JSON data');
    const parsedData = JSON.parse(decodedData) as Record<string, unknown>;
 
    // Adding flag so that paymentengine know it come from 3ds sdk
    if (add3dsSdkFlag) {
      parsedData.from3dsSdk = true;
    }
 
    // Create hidden input fields from parsed JSON object
    log?.('Creating form fields from parsed data', parsedData);
    createFormFields(formElement, parsedData);
 
    // Submit the form
    log?.('Submitting form to', url, 'with method', method);
    formElement.submit();
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    log?.('Error processing form data', error);
    throw new SDKError(
      `Failed to process form data: ${errorMessage}`,
      'FORM_DATA_PROCESSING_FAILED'
    );
  }
}
 
/**
 * Submits form to iframe for action flow
 * Creates a form that submits to the action URL with the value object as JSON string
 * The form will submit to the modal iframe, similar to redirect flow
 */
export function submitActionForm(
  formElement: HTMLFormElement,
  iframeElement: HTMLIFrameElement,
  actionUrl: string,
  value: Record<string, unknown>,
  log?: (message: string, ...args: unknown[]) => void
): void {
  // Get the iframe name to target it
  const iframeName = iframeElement.getAttribute('name');
  if (!iframeName) {
    throw new SDKError('Iframe name not found', 'IFRAME_NAME_NOT_FOUND');
  }
 
  log?.('Submitting action form to', actionUrl, 'with value object', value);
 
  // Set form action, method, and target (iframe instead of _top)
  formElement.setAttribute('action', actionUrl);
  formElement.setAttribute('method', 'post');
  formElement.setAttribute('target', iframeName);
  formElement.setAttribute('name', 'form1');
 
  // Clear any existing inputs
  formElement.innerHTML = '';
 
  // Convert the entire value object to JSON string
  const valueJsonString = JSON.stringify(value);
  log?.('Converted value object to JSON string', valueJsonString);
 
  // Create hidden input for data with the JSON stringified value
  const dataInput = document.createElement('input');
  dataInput.setAttribute('type', 'hidden');
  dataInput.setAttribute('name', 'data');
  dataInput.setAttribute('value', valueJsonString);
  dataInput.setAttribute('id', 'data');
  formElement.appendChild(dataInput);
 
  // Auto-submit the form after a brief delay to ensure it's in the DOM
  // Using setTimeout to ensure the form is fully rendered
  setTimeout(() => {
    log?.('Auto-submitting form to', actionUrl);
    formElement.submit();
  }, 100);
}