getHtml static method

String getHtml({
  1. required String publicKey,
  2. required int amount,
  3. required bool sandbox,
  4. String currency = 'xof',
  5. required String companyName,
  6. String? color,
  7. String? logoUrl,
  8. String? callbackUrl,
})

Implementation

static String getHtml({
  required String publicKey,
  required int amount,
  required bool sandbox,
  String currency = 'xof',
  required String companyName,
  String? color,
  String? logoUrl,
  String? callbackUrl,
}) {
  final cleanColor = color?.replaceAll('#', '') ?? 'green';
  final safeCompanyName = _escapeHtml(companyName);
  final safeLogoUrl = _escapeHtml(logoUrl ?? '');
  final safeCallbackUrl = _escapeHtml(callbackUrl ?? '');

  return '''
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>Paiement Intram</title>
  <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body {
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          background: #f5f5f5;
          min-height: 100vh;
          display: flex;
          align-items: center;
          justify-content: center;
          padding: 20px;
      }
      #loading {
          text-align: center;
      }
      .spinner {
          border: 4px solid rgba(0, 0, 0, 0.1);
          border-left-color: #29b3a6;
          border-radius: 50%;
          width: 50px;
          height: 50px;
          animation: spin 1s linear infinite;
          margin: 0 auto 20px;
      }
      @keyframes spin {
          to { transform: rotate(360deg); }
      }
      .loading-text {
          color: #666;
          font-size: 16px;
      }
      #error-container {
          display: none;
          background: #fff;
          border-radius: 12px;
          padding: 30px;
          box-shadow: 0 4px 20px rgba(0,0,0,0.1);
          text-align: center;
          max-width: 400px;
      }
      .error-icon { font-size: 50px; margin-bottom: 15px; }
      .error-title { font-size: 20px; font-weight: 600; color: #e74c3c; margin-bottom: 10px; }
      .error-message { color: #666; line-height: 1.6; margin-bottom: 20px; font-size: 15px; }
      .retry-button {
          background: #29b3a6;
          color: white;
          border: none;
          padding: 12px 30px;
          border-radius: 8px;
          font-size: 16px;
          cursor: pointer;
      }
      .sandbox-badge {
          position: fixed;
          top: 10px;
          left: 10px;
          background: #ff9800;
          color: white;
          padding: 6px 12px;
          border-radius: 20px;
          font-size: 12px;
          font-weight: 600;
          z-index: 1000;
      }
      #success-container {
          display: none;
          background: #fff;
          border-radius: 12px;
          padding: 40px 30px;
          box-shadow: 0 4px 20px rgba(0,0,0,0.1);
          text-align: center;
          max-width: 400px;
          animation: slideUp 0.3s ease-out;
      }
      @keyframes slideUp {
          from { transform: translateY(20px); opacity: 0; }
          to { transform: translateY(0); opacity: 1; }
      }
      .success-icon {
          font-size: 70px;
          margin-bottom: 20px;
          animation: scaleIn 0.5s ease-out;
      }
      @keyframes scaleIn {
          from { transform: scale(0); }
          to { transform: scale(1); }
      }
      .success-title {
          font-size: 24px;
          font-weight: 700;
          color: #27ae60;
          margin-bottom: 15px;
      }
      .success-message {
          color: #666;
          line-height: 1.6;
          margin-bottom: 20px;
          font-size: 15px;
      }
      .transaction-id {
          background: #f8f9fa;
          padding: 12px;
          border-radius: 8px;
          font-family: monospace;
          font-size: 13px;
          color: #333;
          margin-bottom: 20px;
          word-break: break-all;
      }
      .close-button {
          background: #27ae60;
          color: white;
          border: none;
          padding: 12px 30px;
          border-radius: 8px;
          font-size: 16px;
          cursor: pointer;
          margin-top: 10px;
      }
  </style>
</head>
<body>
  ${sandbox ? '<div class="sandbox-badge">đź§Ş MODE TEST</div>' : ''}

  <div id="loading">
      <div class="spinner"></div>
      <div class="loading-text">Initialisation du paiement...</div>
  </div>

  <div id="error-container">
      <div class="error-icon">⚠️</div>
      <div class="error-title">Erreur de paiement</div>
      <div class="error-message" id="error-message"></div>
      <button class="retry-button" onclick="retryPayment()">🔄 Réessayer</button>
  </div>

  <div id="success-container">
      <div class="success-icon">âś…</div>
      <div class="success-title">Paiement réussi !</div>
      <div class="success-message">
          Votre transaction a été effectuée avec succès.
      </div>
      <div class="transaction-id" id="transaction-display"></div>
      <button class="close-button" onclick="closeAfterSuccess()">Terminer</button>
  </div>

  <div id="widget-container"></div>

  <script>
      var config = {
          public_key: '$publicKey',
          amount: $amount,
          sandbox: $sandbox,
          currency: '$currency',
          callback_url: '$safeCallbackUrl',
          company: {
              name: '$safeCompanyName',
              template: 'default+',
              color: '$cleanColor',
              logo_url: '$safeLogoUrl'
          }
      };

      var sdkLoaded = false;
      var paymentInitialized = false;
      var paymentSuccessData = null;

      function showLoading() {
          document.getElementById('loading').style.display = 'block';
          document.getElementById('error-container').style.display = 'none';
          document.getElementById('success-container').style.display = 'none';
          document.getElementById('widget-container').style.display = 'none';
      }

      function hideLoading() {
          document.getElementById('loading').style.display = 'none';
          document.getElementById('widget-container').style.display = 'block';
      }

      function showError(message) {
          document.getElementById('loading').style.display = 'none';
          document.getElementById('widget-container').style.display = 'none';
          document.getElementById('success-container').style.display = 'none';
          document.getElementById('error-container').style.display = 'block';
          document.getElementById('error-message').textContent = message || 'Une erreur est survenue';
      }

      function showSuccess(transactionId, amount) {
          document.getElementById('loading').style.display = 'none';
          document.getElementById('widget-container').style.display = 'none';
          document.getElementById('error-container').style.display = 'none';
          document.getElementById('success-container').style.display = 'block';

          var displayText = 'Transaction ID: ' + transactionId;
          if (amount) {
              displayText += '\\nMontant: ' + amount + ' XOF';
          }
          document.getElementById('transaction-display').textContent = displayText;
      }

      function closeAfterSuccess() {
          if (paymentSuccessData) {
              sendMessageToFlutter({
                  type: 'close_success',
                  data: paymentSuccessData.data,
                  transaction_id: paymentSuccessData.transaction_id,
                  timestamp: paymentSuccessData.timestamp,
                  message: 'User confirmed payment success'
              });
          } else {
              sendMessageToFlutter({
                  type: 'close_success',
                  message: 'User confirmed payment success'
              });
          }
      }

      function sendMessageToFlutter(message) {
          try {
              var messageStr = JSON.stringify(message);

              if (window.FlutterChannel && window.FlutterChannel.postMessage) {
                  window.FlutterChannel.postMessage(messageStr);
              }

              if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
                  window.flutter_inappwebview.callHandler('paymentHandler', message);
              }

              if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.FlutterChannel) {
                  window.webkit.messageHandlers.FlutterChannel.postMessage(message);
              }

              console.log('📤 Message to Flutter:', message);
          } catch (error) {
              console.error('❌ Error sending message:', error);
          }
      }

      function initIntramPayment() {
          if (paymentInitialized) {
              console.warn('⚠️ Payment already initialized');
              return;
          }

          try {
              console.log('🚀 Initializing payment...', config);

              if (typeof intramOpenWidget === 'undefined') {
                  throw new Error('SDK Intram non chargé. Vérifiez votre connexion Internet.');
              }

              paymentInitialized = true;

              sendMessageToFlutter({
                  type: 'loading',
                  message: 'Initialisation du widget...'
              });

              intramOpenWidget.init(config)
                  .then(function(response) {
                      console.log('âś… Payment response:', response);

                      var transactionId = response.transactionId || response.transaction_id || response.id || 'N/A';
                      var amount = response.amount || config.amount;

                      paymentSuccessData = {
                          type: 'success',
                          data: response,
                          transaction_id: transactionId,
                          timestamp: new Date().toISOString()
                      };

                      showSuccess(transactionId, amount);

                      sendMessageToFlutter(paymentSuccessData);
                  })
                  .catch(function(error) {
                      console.error('❌ Payment error:', error);
                      paymentInitialized = false;
                      showError(error.message || 'Erreur lors du paiement');

                      sendMessageToFlutter({
                          type: 'error',
                          message: error.message || 'Unknown error',
                          code: 'PAYMENT_ERROR',
                          timestamp: new Date().toISOString()
                      });
                  });

          } catch (error) {
              console.error('❌ Init error:', error);
              paymentInitialized = false;
              showError(error.message);

              sendMessageToFlutter({
                  type: 'error',
                  message: error.message,
                  code: 'INIT_ERROR',
                  timestamp: new Date().toISOString()
              });
          }
      }

      function retryPayment() {
          console.log('🔄 Retrying payment...');
          paymentInitialized = false;
          document.getElementById('error-container').style.display = 'none';
          showLoading();
          setTimeout(initIntramPayment, 500);
      }

      window.onerror = function(message, source, lineno, colno, error) {
          console.error('đź’Ą Global error:', { message, source, lineno, colno, error });

          sendMessageToFlutter({
              type: 'error',
              message: message.toString(),
              code: 'GLOBAL_ERROR',
              timestamp: new Date().toISOString()
          });

          return false;
      };

      function loadIntramSDK() {
          var script = document.createElement('script');
          script.src = 'https://cdn.intram.org/sdk-javascript.js';
          script.async = true;

          script.onload = function() {
              console.log('âś… Intram SDK loaded');
              sdkLoaded = true;

              sendMessageToFlutter({
                  type: 'ready',
                  message: 'SDK loaded successfully',
                  timestamp: new Date().toISOString()
              });

              setTimeout(initIntramPayment, 500);
          };

          script.onerror = function() {
              console.error('❌ Failed to load Intram SDK');
              showError('Impossible de charger le SDK Intram. Vérifiez votre connexion Internet.');

              sendMessageToFlutter({
                  type: 'error',
                  message: 'Failed to load Intram SDK from CDN',
                  code: 'SDK_LOAD_ERROR',
                  timestamp: new Date().toISOString()
              });
          };

          document.body.appendChild(script);
      }

      if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', function() {
              loadIntramSDK();
          });
      } else {
          loadIntramSDK();
      }
  </script>
</body>
</html>
  ''';
}