@zendfi/sdk 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -200,12 +200,12 @@ var ConfigLoader = class {
200
200
  const env = this.detectEnvironment();
201
201
  if (mode === "live" && env === "development") {
202
202
  console.warn(
203
- "\u26A0\uFE0F Warning: Using a live API key (zfi_live_) in development environment. This will create real mainnet transactions. Use a test key (zfi_test_) for devnet testing."
203
+ "Warning: Using a live API key (zfi_live_) in development environment. This will create real mainnet transactions. Use a test key (zfi_test_) for devnet testing."
204
204
  );
205
205
  }
206
206
  if (mode === "test" && env === "production") {
207
207
  console.warn(
208
- "\u26A0\uFE0F Warning: Using a test API key (zfi_test_) in production environment. This will create devnet transactions only. Use a live key (zfi_live_) for mainnet."
208
+ "Warning: Using a test API key (zfi_test_) in production environment. This will create devnet transactions only. Use a live key (zfi_live_) for mainnet."
209
209
  );
210
210
  }
211
211
  }
@@ -307,10 +307,10 @@ var ZendFiError2 = class _ZendFiError extends Error {
307
307
  let message = `[${this.code}] ${this.message}`;
308
308
  if (this.suggestion) {
309
309
  message += `
310
- \u{1F4A1} Suggestion: ${this.suggestion}`;
310
+ Suggestion: ${this.suggestion}`;
311
311
  }
312
312
  message += `
313
- \u{1F4DA} Docs: ${this.docs_url}`;
313
+ Docs: ${this.docs_url}`;
314
314
  return message;
315
315
  }
316
316
  /**
@@ -334,7 +334,7 @@ var AuthenticationError2 = class extends ZendFiError2 {
334
334
  code,
335
335
  message,
336
336
  type: "authentication_error",
337
- suggestion: suggestion || "Check your API key in the dashboard at https://app.zendfi.com/settings/api-keys",
337
+ suggestion: suggestion || "Check your API key in the dashboard at https://dashboard.zendfi.tech",
338
338
  statusCode: 401
339
339
  });
340
340
  this.name = "AuthenticationError";
@@ -442,25 +442,19 @@ function createZendFiError(statusCode, responseBody, message) {
442
442
  return new ApiError(errorMessage, errorCode, statusCode, responseBody);
443
443
  }
444
444
  var ERROR_CODES = {
445
- // Authentication
446
445
  INVALID_API_KEY: "invalid_api_key",
447
446
  API_KEY_EXPIRED: "api_key_expired",
448
447
  API_KEY_REVOKED: "api_key_revoked",
449
- // Payment
450
448
  INSUFFICIENT_BALANCE: "insufficient_balance",
451
449
  PAYMENT_DECLINED: "payment_declined",
452
450
  PAYMENT_EXPIRED: "payment_expired",
453
451
  INVALID_AMOUNT: "invalid_amount",
454
452
  INVALID_CURRENCY: "invalid_currency",
455
- // Validation
456
453
  MISSING_REQUIRED_FIELD: "missing_required_field",
457
454
  INVALID_PARAMETER: "invalid_parameter",
458
- // Network
459
455
  NETWORK_ERROR: "network_error",
460
456
  TIMEOUT: "timeout",
461
- // Rate limiting
462
457
  RATE_LIMIT_EXCEEDED: "rate_limit_exceeded",
463
- // Webhook
464
458
  WEBHOOK_SIGNATURE_INVALID: "webhook_signature_invalid",
465
459
  WEBHOOK_TIMESTAMP_TOO_OLD: "webhook_timestamp_too_old"
466
460
  };
@@ -987,6 +981,18 @@ function verifyWebhookSignature(payload, signature, secret) {
987
981
  }
988
982
 
989
983
  // src/embedded-checkout.ts
984
+ var Icons = {
985
+ clipboard: `<svg class="icon" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3.375 3.375 0 00-3.375-3.375H9.75" /></svg>`,
986
+ checkCircle: `<svg class="icon icon-lg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>`,
987
+ xCircle: `<svg class="icon icon-lg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>`,
988
+ check: `<svg class="icon icon-sm" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" /></svg>`,
989
+ refresh: `<svg class="icon icon-sm" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /></svg>`,
990
+ clock: `<svg class="icon icon-sm" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>`,
991
+ loader: `<svg class="icon icon-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>`,
992
+ bank: `<svg class="icon" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21v-8.25M15.75 21v-8.25M8.25 21v-8.25M3 9l9-6 9 6m-1.5 12V10.332A48.36 48.36 0 0012 9.75c-2.551 0-5.056.2-7.5.582V21M3 21h18M12 6.75h.008v.008H12V6.75z" /></svg>`,
993
+ clocklg: `<svg class="icon icon-lg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>`,
994
+ hammer: `<svg class="icon" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" /></svg>`
995
+ };
990
996
  var ZendFiEmbeddedCheckout = class {
991
997
  config;
992
998
  container = null;
@@ -994,8 +1000,6 @@ var ZendFiEmbeddedCheckout = class {
994
1000
  pollInterval = null;
995
1001
  mounted = false;
996
1002
  paymentProcessed = false;
997
- // Prevent duplicate success callbacks
998
- // Bank payment state
999
1003
  bankPaymentState = {};
1000
1004
  constructor(config) {
1001
1005
  this.config = {
@@ -1209,13 +1213,28 @@ var ZendFiEmbeddedCheckout = class {
1209
1213
  */
1210
1214
  renderHeader() {
1211
1215
  if (!this.checkoutData) return "";
1216
+ const testnetBadge = this.checkoutData.solana_network === "devnet" || this.checkoutData.solana_network === "testnet" ? `<span style="font-size: 9px; font-weight: 500; color: #d97706; background: #fef3c7; padding: 2px 6px; border-radius: 4px; margin-left: 6px;">Testnet</span>` : "";
1212
1217
  return `
1213
- <div class="zendfi-checkout-header" style="padding: 24px; border-bottom: 1px solid #e5e7eb;">
1214
- <h2 style="margin: 0; font-size: 24px; font-weight: 600; color: ${this.config.theme.textColor || "#1f2937"};">
1215
- Pay ${this.checkoutData.merchant_name}
1216
- </h2>
1218
+ <div class="zendfi-checkout-header" style="padding: 24px; text-align: center; border-bottom: 1px solid #e5e7eb;">
1219
+ <!-- Merchant Badge -->
1220
+ <div style="display: inline-flex; items-center; gap: 8px; padding: 6px 12px; background: white; border: 1px solid #e5e7eb; border-radius: 20px; margin-bottom: 16px;">
1221
+ <div style="width: 20px; height: 20px; border-radius: 50%; background: #1f2937; display: flex; align-items: center; justify-content: center;">
1222
+ <span style="font-size: 10px; font-weight: 600; color: white;">
1223
+ ${this.checkoutData.merchant_name.charAt(0).toUpperCase()}
1224
+ </span>
1225
+ </div>
1226
+ <span style="font-size: 12px; font-weight: 500; color: #374151;">${this.checkoutData.merchant_name}</span>
1227
+ ${testnetBadge}
1228
+ </div>
1229
+
1230
+ <!-- Amount -->
1231
+ <div style="font-size: 40px; font-weight: 600; color: #1f2937; margin-bottom: 4px;">
1232
+ $${this.checkoutData.amount_usd.toFixed(2)}
1233
+ </div>
1234
+ <p style="margin: 0; font-size: 13px; color: #6b7280;">${this.checkoutData.token} on Solana</p>
1235
+
1217
1236
  ${this.checkoutData.description ? `
1218
- <p style="margin: 8px 0 0 0; color: #6b7280; font-size: 14px;">
1237
+ <p style="margin: 12px auto 0; color: #9ca3af; font-size: 13px; max-width: 280px;">
1219
1238
  ${this.checkoutData.description}
1220
1239
  </p>
1221
1240
  ` : ""}
@@ -1299,21 +1318,22 @@ var ZendFiEmbeddedCheckout = class {
1299
1318
  renderQRCodeMethod() {
1300
1319
  if (!this.checkoutData) return "";
1301
1320
  return `
1302
- <div class="zendfi-payment-method" style="padding: 16px; border: 1px solid #e5e7eb; border-radius: 12px; margin-bottom: 16px;">
1321
+ <div class="zendfi-payment-method" style="padding: 20px; border: 1px solid #e5e7eb; border-radius: 12px; margin-bottom: 16px; background: white; transition: all 0.2s;" onmouseover="this.style.borderColor='#d1d5db'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.05)';" onmouseout="this.style.borderColor='#e5e7eb'; this.style.boxShadow='none';">
1303
1322
  <div style="text-align: center;">
1304
- <p style="font-size: 14px; color: #6b7280; margin-bottom: 16px;">
1305
- Scan with Solana wallet
1323
+ <h4 style="margin: 0 0 12px 0; font-size: 15px; font-weight: 600; color: #374151;">Scan QR Code</h4>
1324
+ <p style="font-size: 13px; color: #6b7280; margin-bottom: 16px;">
1325
+ Use any Solana wallet app
1306
1326
  </p>
1307
- <div id="zendfi-qr-container" style="display: flex; justify-content: center; margin-bottom: 16px;">
1327
+ <div id="zendfi-qr-container" style="display: flex; justify-content: center; margin-bottom: 16px; padding: 12px; background: #fafbfc; border-radius: 8px;">
1308
1328
  <canvas id="zendfi-qr-code"></canvas>
1309
1329
  </div>
1310
1330
  <button
1311
1331
  id="zendfi-copy-address"
1312
- style="padding: 10px 20px; background: #f3f4f6; border: 1px solid #d1d5db; border-radius: 8px; cursor: pointer; font-size: 14px; color: #374151; transition: all 0.2s;"
1332
+ style="padding: 10px 20px; background: #f3f4f6; border: 1px solid #d1d5db; border-radius: 8px; cursor: pointer; font-size: 14px; color: #374151; font-weight: 500; transition: all 0.2s; display: inline-flex; align-items: center; gap: 6px;"
1313
1333
  onmouseover="this.style.background='#e5e7eb'"
1314
1334
  onmouseout="this.style.background='#f3f4f6'"
1315
1335
  >
1316
- \u{1F4CB} Copy Address
1336
+ ${Icons.clipboard} Copy Address
1317
1337
  </button>
1318
1338
  </div>
1319
1339
  </div>
@@ -1323,15 +1343,16 @@ var ZendFiEmbeddedCheckout = class {
1323
1343
  * Render browser wallet method
1324
1344
  */
1325
1345
  renderWalletMethod() {
1346
+ const walletIcon = `<svg style="width: 20px; height: 20px; display: inline-block; vertical-align: middle; margin-right: 8px;" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a2.25 2.25 0 00-2.25-2.25H15a3 3 0 11-6 0H5.25A2.25 2.25 0 003 12m18 0v6a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 18v-6m18 0V9M3 12V9m18 0a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 9m18 0V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v3" /></svg>`;
1326
1347
  return `
1327
1348
  <div class="zendfi-payment-method" style="margin-bottom: 16px;">
1328
1349
  <button
1329
1350
  id="zendfi-connect-wallet"
1330
- style="width: 100%; padding: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);"
1331
- onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 12px rgba(0, 0, 0, 0.15)';"
1332
- onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 6px rgba(0, 0, 0, 0.1)';"
1351
+ style="width: 100%; padding: 16px; background: #1f2937; color: white; border: none; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); display: flex; align-items: center; justify-content: center;"
1352
+ onmouseover="this.style.transform='translateY(-1px)'; this.style.boxShadow='0 4px 12px rgba(0, 0, 0, 0.15)'; this.style.background='#111827';"
1353
+ onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 6px rgba(0, 0, 0, 0.1)'; this.style.background='#1f2937';"
1333
1354
  >
1334
- \u{1F517} Connect Wallet
1355
+ ${walletIcon} Connect Wallet
1335
1356
  </button>
1336
1357
  </div>
1337
1358
  `;
@@ -1340,15 +1361,16 @@ var ZendFiEmbeddedCheckout = class {
1340
1361
  * Render WalletConnect method
1341
1362
  */
1342
1363
  renderWalletConnectMethod() {
1364
+ const qrIcon = `<svg style="width: 20px; height: 20px; display: inline-block; vertical-align: middle; margin-right: 8px;" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 013.75 9.375v-4.5zM3.75 14.625c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5a1.125 1.125 0 01-1.125-1.125v-4.5zM13.5 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0113.5 9.375v-4.5z" /><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 6.75h.75v.75h-.75v-.75zM6.75 16.5h.75v.75h-.75v-.75zM16.5 6.75h.75v.75h-.75v-.75zM13.5 13.5h.75v.75h-.75v-.75zM13.5 19.5h.75v.75h-.75v-.75zM19.5 13.5h.75v.75h-.75v-.75zM19.5 19.5h.75v.75h-.75v-.75zM16.5 16.5h.75v.75h-.75v-.75z" /></svg>`;
1343
1365
  return `
1344
- <div class="zendfi-payment-method">
1366
+ <div class="zendfi-payment-method" style="margin-bottom: 16px;">
1345
1367
  <button
1346
1368
  id="zendfi-wallet-connect"
1347
- style="width: 100%; padding: 16px; background: white; color: #1f2937; border: 2px solid #e5e7eb; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s;"
1348
- onmouseover="this.style.borderColor='#667eea'; this.style.background='#f9fafb';"
1349
- onmouseout="this.style.borderColor='#e5e7eb'; this.style.background='white';"
1369
+ style="width: 100%; padding: 16px; background: white; color: #1f2937; border: 2px solid #d1d5db; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center;"
1370
+ onmouseover="this.style.borderColor='#9ca3af'; this.style.background='#f9fafb';"
1371
+ onmouseout="this.style.borderColor='#d1d5db'; this.style.background='white';"
1350
1372
  >
1351
- \u{1F4F1} WalletConnect
1373
+ ${qrIcon} WalletConnect
1352
1374
  </button>
1353
1375
  </div>
1354
1376
  `;
@@ -1361,13 +1383,13 @@ var ZendFiEmbeddedCheckout = class {
1361
1383
  const isEmailStep = !this.bankPaymentState.orderId;
1362
1384
  const isBankDetailsStep = !!this.bankPaymentState.bankDetails;
1363
1385
  return `
1364
- <div class="zendfi-payment-method" style="padding: 16px; border: 2px solid #10b981; border-radius: 12px; margin-bottom: 16px; background: #f0fdf4;">
1386
+ <div class="zendfi-payment-method" style="padding: 20px; border: 2px solid #10b981; border-radius: 12px; margin-bottom: 16px; background: #f0fdf4; transition: all 0.2s;" onmouseover="this.style.boxShadow='0 2px 8px rgba(16,185,129,0.15)';" onmouseout="this.style.boxShadow='none';">
1365
1387
  <div style="margin-bottom: 12px;">
1366
- <h4 style="margin: 0 0 4px 0; font-size: 16px; font-weight: 600; color: #065f46; display: flex; align-items: center;">
1367
- \u{1F3E6} Pay with Bank Transfer (Nigeria)
1388
+ <h4 style="margin: 0 0 4px 0; font-size: 16px; font-weight: 600; color: #065f46; display: flex; align-items: center; gap: 8px;">
1389
+ ${Icons.bank} Pay with Bank Transfer
1368
1390
  </h4>
1369
- <p style="margin: 0; font-size: 12px; color: #047857;">
1370
- Pay with your Nigerian bank account \u2022 Instant confirmation
1391
+ <p style="margin: 0; font-size: 13px; color: #047857;">
1392
+ Nigerian bank account \u2022 Instant confirmation
1371
1393
  </p>
1372
1394
  </div>
1373
1395
 
@@ -1432,8 +1454,8 @@ var ZendFiEmbeddedCheckout = class {
1432
1454
  return `
1433
1455
  <div id="zendfi-bank-details-step">
1434
1456
  <div style="background: white; border: 1px solid #d1d5db; border-radius: 8px; padding: 16px; margin-bottom: 16px;">
1435
- <h5 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: #374151;">
1436
- \u{1F4CB} Transfer to this account:
1457
+ <h5 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: #374151; display: flex; align-items: center; gap: 6px;">
1458
+ ${Icons.bank} Transfer to this account:
1437
1459
  </h5>
1438
1460
 
1439
1461
  <div style="margin-bottom: 8px;">
@@ -1476,14 +1498,14 @@ var ZendFiEmbeddedCheckout = class {
1476
1498
  <div style="color: #6b7280; font-size: 14px;" id="zendfi-bank-status-text">
1477
1499
  Waiting for bank transfer... (<span id="zendfi-bank-timer">0:00</span>)
1478
1500
  </div>
1479
- <div style="font-size: 12px; color: #9ca3af; margin-top: 4px;">
1480
- \u23F1\uFE0F Usually takes 10-30 seconds
1501
+ <div style="font-size: 12px; color: #9ca3af; margin-top: 4px; display: inline-flex; align-items: center; gap: 4px;">
1502
+ ${Icons.clock} Usually takes 10-30 seconds
1481
1503
  </div>
1482
1504
  <button
1483
1505
  id="zendfi-refresh-status"
1484
- style="margin-top: 12px; padding: 8px 16px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; cursor: pointer; color: #6b7280; font-size: 14px;"
1506
+ style="margin-top: 12px; padding: 8px 16px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; cursor: pointer; color: #6b7280; font-size: 14px; display: inline-flex; align-items: center; gap: 6px;"
1485
1507
  >
1486
- \u{1F504} Refresh Status
1508
+ ${Icons.refresh} Refresh Status
1487
1509
  </button>
1488
1510
  </div>
1489
1511
  </div>
@@ -1493,10 +1515,13 @@ var ZendFiEmbeddedCheckout = class {
1493
1515
  * Render footer
1494
1516
  */
1495
1517
  renderFooter() {
1518
+ const lockIcon = `<svg style="width: 14px; height: 14px; display: inline-block; vertical-align: middle; margin-right: 4px;" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" /></svg>`;
1496
1519
  return `
1497
1520
  <div class="zendfi-checkout-footer" style="padding: 16px 24px; border-top: 1px solid #e5e7eb; text-align: center;">
1498
- <p style="margin: 0; font-size: 12px; color: #9ca3af;">
1499
- Powered by <a href="https://zendfi.tech" target="_blank" style="color: #667eea; text-decoration: none; font-weight: 600;">ZendFi</a>
1521
+ <p style="margin: 0; font-size: 12px; color: #9ca3af; display: flex; align-items: center; justify-content: center; gap: 4px;">
1522
+ ${lockIcon}
1523
+ <span>Secured by</span>
1524
+ <a href="https://zendfi.tech" target="_blank" style="color: #667eea; text-decoration: none; font-weight: 600; transition: opacity 0.2s;" onmouseover="this.style.opacity='0.7'" onmouseout="this.style.opacity='1'">ZendFi</a>
1500
1525
  </p>
1501
1526
  </div>
1502
1527
  `;
@@ -1508,7 +1533,7 @@ var ZendFiEmbeddedCheckout = class {
1508
1533
  if (!this.container) return;
1509
1534
  this.container.innerHTML = `
1510
1535
  <div class="zendfi-checkout-success" style="${this.getSuccessStyles()}">
1511
- <div style="font-size: 64px; margin-bottom: 16px;">\u2705</div>
1536
+ <div style="color: #10b981; margin-bottom: 16px;">${Icons.checkCircle}</div>
1512
1537
  <h3 style="margin: 0 0 8px 0; font-size: 24px; font-weight: 600; color: #059669;">
1513
1538
  Payment Successful!
1514
1539
  </h3>
@@ -1518,6 +1543,91 @@ var ZendFiEmbeddedCheckout = class {
1518
1543
  </div>
1519
1544
  `;
1520
1545
  }
1546
+ /**
1547
+ * Render payment under review state
1548
+ */
1549
+ renderUnderReview() {
1550
+ if (!this.container) return;
1551
+ const customerEmail = this.bankPaymentState.customerEmail || "unknown";
1552
+ const orderId = this.bankPaymentState.orderId || "N/A";
1553
+ this.container.innerHTML = `
1554
+ <div class="zendfi-checkout-under-review" style="padding: 32px 24px; text-align: center;">
1555
+ <div style="width: 64px; height: 64px; background: #fef3c7; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; color: #d97706;">
1556
+ ${Icons.clock}
1557
+ </div>
1558
+
1559
+ <h3 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #1f2937;">
1560
+ Payment Under Review
1561
+ </h3>
1562
+ <p style="margin: 0 0 24px 0; color: #6b7280; font-size: 14px; line-height: 1.5;">
1563
+ We're still processing your bank transfer. This is taking longer than usual.
1564
+ </p>
1565
+
1566
+ <!-- What's Happening -->
1567
+ <div style="background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 12px; padding: 16px; margin-bottom: 16px; text-align: left;">
1568
+ <h4 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: #1e3a8a;">What's happening?</h4>
1569
+ <ul style="margin: 0; padding: 0 0 0 20px; font-size: 13px; color: #1e40af; line-height: 1.6;">
1570
+ <li style="margin-bottom: 8px;">Our team has been notified and is investigating</li>
1571
+ <li style="margin-bottom: 8px;">We're coordinating with our payment partner</li>
1572
+ <li>You'll receive an update via email within 24 hours</li>
1573
+ </ul>
1574
+ </div>
1575
+
1576
+ <!-- Guarantee -->
1577
+ <div style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 12px; padding: 16px; margin-bottom: 16px; text-align: left;">
1578
+ <div style="display: flex; align-items: start; gap: 12px;">
1579
+ <div style="color: #16a34a; flex-shrink: 0;">${Icons.checkCircle}</div>
1580
+ <div>
1581
+ <h4 style="margin: 0 0 4px 0; font-size: 14px; font-weight: 600; color: #15803d;">You're Protected</h4>
1582
+ <p style="margin: 0; font-size: 13px; color: #166534; line-height: 1.5;">
1583
+ If we can't deliver your USDC, you'll receive a <strong>full refund</strong> to your bank account within 3-5 business days.
1584
+ </p>
1585
+ </div>
1586
+ </div>
1587
+ </div>
1588
+
1589
+ <!-- Contact Support -->
1590
+ <div style="background: #f9fafb; border-radius: 12px; padding: 16px; text-align: left;">
1591
+ <h4 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 600; color: #1f2937;">Need help?</h4>
1592
+ <p style="margin: 0 0 12px 0; font-size: 13px; color: #6b7280;">
1593
+ Contact our support team if you have questions:
1594
+ </p>
1595
+ <a
1596
+ href="mailto:tosinoyinboblessed@gmail.com?subject=Payment%20Review%20-%20${orderId}&body=Order%20ID:%20${orderId}%0AEmail:%20${customerEmail}"
1597
+ style="display: block; width: 100%; padding: 12px; background: #1f2937; color: white; text-decoration: none; border-radius: 8px; font-size: 14px; font-weight: 600; text-align: center; transition: background 0.2s;"
1598
+ onmouseover="this.style.background='#111827'"
1599
+ onmouseout="this.style.background='#1f2937'"
1600
+ >
1601
+ Email Support
1602
+ </a>
1603
+ <p style="margin: 12px 0 0 0; font-size: 12px; color: #9ca3af; text-align: center;">
1604
+ Reference: ${orderId.slice(0, 8)}
1605
+ </p>
1606
+ </div>
1607
+ </div>
1608
+ `;
1609
+ }
1610
+ /**
1611
+ * Handle payment timeout (15 minutes)
1612
+ */
1613
+ handlePaymentTimeout() {
1614
+ if (this.bankPaymentState.pollingInterval) {
1615
+ clearInterval(this.bankPaymentState.pollingInterval);
1616
+ this.bankPaymentState.pollingInterval = void 0;
1617
+ }
1618
+ if (this.bankPaymentState.timerInterval) {
1619
+ clearInterval(this.bankPaymentState.timerInterval);
1620
+ }
1621
+ this.renderUnderReview();
1622
+ this.config.onError({
1623
+ code: "PAYMENT_TIMEOUT",
1624
+ message: "Payment is taking longer than expected and is now under review",
1625
+ details: {
1626
+ orderId: this.bankPaymentState.orderId,
1627
+ customerEmail: this.bankPaymentState.customerEmail
1628
+ }
1629
+ });
1630
+ }
1521
1631
  /**
1522
1632
  * Render error state
1523
1633
  */
@@ -1525,7 +1635,7 @@ var ZendFiEmbeddedCheckout = class {
1525
1635
  if (!this.container) return;
1526
1636
  this.container.innerHTML = `
1527
1637
  <div class="zendfi-checkout-error" style="${this.getErrorStyles()}">
1528
- <div style="font-size: 64px; margin-bottom: 16px;">\u274C</div>
1638
+ <div style="color: #ef4444; margin-bottom: 16px;">${Icons.xCircle}</div>
1529
1639
  <h3 style="margin: 0 0 8px 0; font-size: 24px; font-weight: 600; color: #dc2626;">
1530
1640
  Payment Failed
1531
1641
  </h3>
@@ -1547,9 +1657,9 @@ var ZendFiEmbeddedCheckout = class {
1547
1657
  if (copyBtn && this.checkoutData) {
1548
1658
  copyBtn.addEventListener("click", () => {
1549
1659
  navigator.clipboard.writeText(this.checkoutData.wallet_address);
1550
- copyBtn.textContent = "\u2713 Copied!";
1660
+ copyBtn.innerHTML = `${Icons.check} Copied!`;
1551
1661
  setTimeout(() => {
1552
- copyBtn.textContent = "\u{1F4CB} Copy Address";
1662
+ copyBtn.innerHTML = `${Icons.clipboard} Copy Address`;
1553
1663
  }, 2e3);
1554
1664
  });
1555
1665
  }
@@ -1597,13 +1707,13 @@ var ZendFiEmbeddedCheckout = class {
1597
1707
  }
1598
1708
  const connectBtn = document.getElementById("zendfi-connect-wallet");
1599
1709
  if (connectBtn) {
1600
- connectBtn.textContent = "\u{1F504} Connecting...";
1710
+ connectBtn.innerHTML = `${Icons.loader} Connecting...`;
1601
1711
  connectBtn.disabled = true;
1602
1712
  }
1603
1713
  await provider.connect();
1604
1714
  const publicKey = provider.publicKey.toString();
1605
1715
  if (connectBtn) {
1606
- connectBtn.textContent = "\u{1F528} Building transaction...";
1716
+ connectBtn.innerHTML = `${Icons.hammer} Building transaction...`;
1607
1717
  }
1608
1718
  const response = await fetch(
1609
1719
  `${this.config.apiUrl}/api/v1/payments/${this.checkoutData.payment_id}/build-transaction`,
@@ -1621,14 +1731,14 @@ var ZendFiEmbeddedCheckout = class {
1621
1731
  }
1622
1732
  const { transaction: transactionBase64, is_gasless } = await response.json();
1623
1733
  if (connectBtn) {
1624
- connectBtn.textContent = "\u270D\uFE0F Sign transaction...";
1734
+ connectBtn.innerHTML = `${Icons.loader} Signing...`;
1625
1735
  }
1626
1736
  const solanaWeb3 = window.solanaWeb3;
1627
1737
  const transactionBuffer = Uint8Array.from(atob(transactionBase64), (c) => c.charCodeAt(0));
1628
1738
  const transaction = solanaWeb3.Transaction.from(transactionBuffer);
1629
1739
  const signedTransaction = await provider.signTransaction(transaction);
1630
1740
  if (connectBtn) {
1631
- connectBtn.textContent = "\u{1F4E1} Submitting...";
1741
+ connectBtn.innerHTML = `${Icons.loader} Submitting...`;
1632
1742
  }
1633
1743
  if (is_gasless) {
1634
1744
  const serialized = signedTransaction.serialize();
@@ -1668,13 +1778,13 @@ var ZendFiEmbeddedCheckout = class {
1668
1778
  }
1669
1779
  }
1670
1780
  if (connectBtn) {
1671
- connectBtn.textContent = "\u23F3 Confirming...";
1781
+ connectBtn.innerHTML = `${Icons.loader} Confirming...`;
1672
1782
  }
1673
1783
  } catch (error) {
1674
1784
  console.error("Wallet connection error:", error);
1675
1785
  const connectBtn = document.getElementById("zendfi-connect-wallet");
1676
1786
  if (connectBtn) {
1677
- connectBtn.textContent = "\u{1F517} Connect Wallet";
1787
+ connectBtn.textContent = "Connect Wallet";
1678
1788
  connectBtn.disabled = false;
1679
1789
  }
1680
1790
  this.config.onError({
@@ -1737,6 +1847,38 @@ var ZendFiEmbeddedCheckout = class {
1737
1847
  @keyframes zendfi-spin {
1738
1848
  to { transform: rotate(360deg); }
1739
1849
  }
1850
+
1851
+ .zendfi-embedded-checkout .icon {
1852
+ width: 20px;
1853
+ height: 20px;
1854
+ display: inline-block;
1855
+ vertical-align: middle;
1856
+ }
1857
+
1858
+ .zendfi-embedded-checkout .icon-sm {
1859
+ width: 16px;
1860
+ height: 16px;
1861
+ }
1862
+
1863
+ .zendfi-embedded-checkout .icon-lg {
1864
+ width: 64px;
1865
+ height: 64px;
1866
+ }
1867
+
1868
+ .zendfi-embedded-checkout .icon-spin {
1869
+ animation: zendfi-spin 0.8s linear infinite;
1870
+ width: 40px;
1871
+ height: 40px;
1872
+ }
1873
+
1874
+ .zendfi-spinner {
1875
+ width: 40px;
1876
+ height: 40px;
1877
+ border: 4px solid #f3f4f6;
1878
+ border-top-color: #667eea;
1879
+ border-radius: 50%;
1880
+ animation: zendfi-spin 0.8s linear infinite;
1881
+ }
1740
1882
  `;
1741
1883
  document.head.appendChild(style);
1742
1884
  }
@@ -1788,9 +1930,10 @@ var ZendFiEmbeddedCheckout = class {
1788
1930
  font-family: ${theme.fontFamily};
1789
1931
  background: ${theme.backgroundColor};
1790
1932
  border-radius: ${theme.borderRadius};
1791
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
1933
+ border: 1px solid #e5e7eb;
1934
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
1792
1935
  overflow: hidden;
1793
- max-width: 500px;
1936
+ max-width: 480px;
1794
1937
  margin: 0 auto;
1795
1938
  `.replace(/\s+/g, " ").trim();
1796
1939
  }
@@ -1847,7 +1990,9 @@ var ZendFiEmbeddedCheckout = class {
1847
1990
  otp,
1848
1991
  fiat_amount: this.checkoutData.amount_usd,
1849
1992
  currency: this.checkoutData.currency,
1850
- payment_link_id: this.checkoutData.payment_link_id
1993
+ payment_link_id: this.checkoutData.payment_link_id,
1994
+ amount_ngn: this.checkoutData.amount_ngn,
1995
+ payer_service_charge: this.checkoutData.payer_service_charge
1851
1996
  })
1852
1997
  });
1853
1998
  if (!response.ok) {
@@ -1877,7 +2022,8 @@ var ZendFiEmbeddedCheckout = class {
1877
2022
  body: JSON.stringify({
1878
2023
  customer_email: email,
1879
2024
  fiat_amount: this.checkoutData.amount_usd,
1880
- payment_link_id: this.checkoutData.payment_link_id
2025
+ payment_link_id: this.checkoutData.payment_link_id,
2026
+ amount_ngn: this.checkoutData.amount_ngn
1881
2027
  })
1882
2028
  });
1883
2029
  if (!response.ok) {
@@ -1903,6 +2049,7 @@ var ZendFiEmbeddedCheckout = class {
1903
2049
  startBankStatusPolling() {
1904
2050
  if (!this.bankPaymentState.orderId) return;
1905
2051
  let startTime = Date.now();
2052
+ const TIMEOUT_MS = 15 * 60 * 1e3;
1906
2053
  const timerInterval = setInterval(() => {
1907
2054
  const elapsed = Math.floor((Date.now() - startTime) / 1e3);
1908
2055
  const minutes = Math.floor(elapsed / 60);
@@ -1911,6 +2058,9 @@ var ZendFiEmbeddedCheckout = class {
1911
2058
  if (timerEl) {
1912
2059
  timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, "0")}`;
1913
2060
  }
2061
+ if (Date.now() - startTime > TIMEOUT_MS) {
2062
+ this.handlePaymentTimeout();
2063
+ }
1914
2064
  }, 1e3);
1915
2065
  const pollInterval = setInterval(async () => {
1916
2066
  await this.checkBankPaymentStatus();
@@ -2102,7 +2252,6 @@ async function processWebhook(a, b, c) {
2102
2252
  isProcessed: cfg.isProcessed,
2103
2253
  onProcessed: cfg.onProcessed,
2104
2254
  onError: cfg.onError,
2105
- // Forward compatibility for alternate names and flags
2106
2255
  enableDeduplication: cfg.enableDeduplication,
2107
2256
  checkDuplicate: cfg.checkDuplicate,
2108
2257
  markProcessed: cfg.markProcessed
@@ -2509,7 +2658,6 @@ var TransactionPoller = class {
2509
2658
  switch (commitment) {
2510
2659
  case "processed":
2511
2660
  return true;
2512
- // Any status means processed
2513
2661
  case "confirmed":
2514
2662
  return status.confirmationStatus === "confirmed" || status.confirmationStatus === "finalized";
2515
2663
  case "finalized":
@@ -2630,7 +2778,6 @@ var TransactionMonitor = class {
2630
2778
  const status = await TransactionPoller.waitForConfirmation(signature, {
2631
2779
  ...options,
2632
2780
  maxAttempts: 1
2633
- // Single check per interval
2634
2781
  });
2635
2782
  if (status.confirmed) {
2636
2783
  this.stopMonitoring(signature);
@@ -2758,7 +2905,6 @@ var DevTools = class {
2758
2905
  sessionWallet: keypair.publicKey.toString(),
2759
2906
  privateKey: keypair.secretKey,
2760
2907
  budget: 10
2761
- // $10 test budget
2762
2908
  };
2763
2909
  }
2764
2910
  /**
@@ -2770,14 +2916,13 @@ var DevTools = class {
2770
2916
  address: mockAddress,
2771
2917
  publicKey: { toString: () => mockAddress },
2772
2918
  signTransaction: async (tx) => {
2773
- console.log("\u{1F527} Mock wallet: Signing transaction");
2919
+ console.log("Mock wallet: Signing transaction");
2774
2920
  return tx;
2775
2921
  },
2776
2922
  signMessage: async (_msg) => {
2777
- console.log("\u{1F527} Mock wallet: Signing message");
2923
+ console.log("Mock wallet: Signing message");
2778
2924
  return {
2779
2925
  signature: new Uint8Array(64)
2780
- // Mock signature
2781
2926
  };
2782
2927
  },
2783
2928
  isConnected: () => true,
@@ -2792,26 +2937,26 @@ var DevTools = class {
2792
2937
  static logTransactionFlow(paymentId) {
2793
2938
  console.log(`
2794
2939
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2795
- \u2551 TRANSACTION FLOW \u2551
2940
+ \u2551 TRANSACTION FLOW \u2551
2796
2941
  \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
2797
- \u2551 \u2551
2798
- \u2551 Payment ID: ${paymentId} \u2551
2799
- \u2551 \u2551
2800
- \u2551 1. \u{1F3D7}\uFE0F Create Payment Intent \u2551
2942
+ \u2551 \u2551
2943
+ \u2551 Payment ID: ${paymentId} \u2551
2944
+ \u2551 \u2551
2945
+ \u2551 1. Create Payment Intent \u2551
2801
2946
  \u2551 \u2514\u2500> POST /api/v1/ai/smart-payment \u2551
2802
- \u2551 \u2551
2803
- \u2551 2. \u{1F510} Sign Transaction (Device-Bound) \u2551
2804
- \u2551 \u2514\u2500> Client-side signing with cached keypair \u2551
2805
- \u2551 \u2551
2806
- \u2551 3. \u{1F4E4} Submit Signed Transaction \u2551
2807
- \u2551 \u2514\u2500> POST /api/v1/ai/payments/{id}/submit-signed \u2551
2808
- \u2551 \u2551
2809
- \u2551 4. \u23F3 Wait for Blockchain Confirmation \u2551
2810
- \u2551 \u2514\u2500> Poll Solana RPC (~30-60 seconds) \u2551
2811
- \u2551 \u2551
2812
- \u2551 5. \u2705 Payment Confirmed \u2551
2813
- \u2551 \u2514\u2500> Webhook fired: payment.confirmed \u2551
2814
- \u2551 \u2551
2947
+ \u2551 \u2551
2948
+ \u2551 2. Sign Transaction (Device-Bound) \u2551
2949
+ \u2551 \u2514\u2500> Client-side signing with cached keypair \u2551
2950
+ \u2551 \u2551
2951
+ \u2551 3. Submit Signed Transaction \u2551
2952
+ \u2551 \u2514\u2500> POST /api/v1/ai/payments/{id}/submit-signed \u2551
2953
+ \u2551 \u2551
2954
+ \u2551 4. Wait for Blockchain Confirmation \u2551
2955
+ \u2551 \u2514\u2500> Poll Solana RPC (~30-60 seconds) \u2551
2956
+ \u2551 \u2551
2957
+ \u2551 5. Payment Confirmed \u2551
2958
+ \u2551 \u2514\u2500> Webhook fired: payment.confirmed \u2551
2959
+ \u2551 \u2551
2815
2960
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2816
2961
  `);
2817
2962
  }