@cimplify/sdk 0.1.0 → 0.2.1

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
@@ -871,27 +871,32 @@ var SchedulingService = class {
871
871
  async getServices() {
872
872
  return this.client.query("scheduling.services");
873
873
  }
874
+ /**
875
+ * Get a specific service by ID
876
+ * Note: Filters from all services client-side (no single-service endpoint)
877
+ */
874
878
  async getService(serviceId) {
875
- return this.client.query(`scheduling.services.${serviceId}`);
879
+ const services = await this.getServices();
880
+ return services.find((s) => s.id === serviceId) || null;
876
881
  }
877
882
  // --------------------------------------------------------------------------
878
883
  // AVAILABILITY
879
884
  // --------------------------------------------------------------------------
880
885
  async getAvailableSlots(input) {
881
886
  return this.client.query(
882
- "scheduling.available_slots",
887
+ "scheduling.slots",
883
888
  input
884
889
  );
885
890
  }
886
891
  async checkSlotAvailability(input) {
887
892
  return this.client.query(
888
- "scheduling.check_slot",
893
+ "scheduling.check_availability",
889
894
  input
890
895
  );
891
896
  }
892
897
  async getServiceAvailability(params) {
893
898
  return this.client.query(
894
- "scheduling.service_availability",
899
+ "scheduling.availability",
895
900
  params
896
901
  );
897
902
  }
@@ -899,19 +904,19 @@ var SchedulingService = class {
899
904
  // BOOKINGS
900
905
  // --------------------------------------------------------------------------
901
906
  async getBooking(bookingId) {
902
- return this.client.query(`scheduling.bookings.${bookingId}`);
907
+ return this.client.query(`scheduling.${bookingId}`);
903
908
  }
904
909
  async getCustomerBookings() {
905
- return this.client.query("scheduling.customer_bookings");
910
+ return this.client.query("scheduling");
906
911
  }
907
912
  async getUpcomingBookings() {
908
913
  return this.client.query(
909
- "scheduling.customer_bookings[?(@.status!='completed' && @.status!='cancelled')]#sort(start_time,asc)"
914
+ "scheduling[?(@.status!='completed' && @.status!='cancelled')]#sort(start_time,asc)"
910
915
  );
911
916
  }
912
917
  async getPastBookings(limit = 10) {
913
918
  return this.client.query(
914
- `scheduling.customer_bookings[?(@.status=='completed')]#sort(start_time,desc)#limit(${limit})`
919
+ `scheduling[?(@.status=='completed')]#sort(start_time,desc)#limit(${limit})`
915
920
  );
916
921
  }
917
922
  // --------------------------------------------------------------------------
@@ -1304,23 +1309,354 @@ function query(entity) {
1304
1309
  return new QueryBuilder(entity);
1305
1310
  }
1306
1311
 
1307
- // src/utils/payment.ts
1312
+ // src/utils/price.ts
1308
1313
  var CURRENCY_SYMBOLS = {
1309
- GHS: "GH\u20B5",
1314
+ // Major world currencies
1310
1315
  USD: "$",
1311
1316
  EUR: "\u20AC",
1312
1317
  GBP: "\xA3",
1318
+ JPY: "\xA5",
1319
+ CNY: "\xA5",
1320
+ CHF: "CHF",
1321
+ CAD: "C$",
1322
+ AUD: "A$",
1323
+ NZD: "NZ$",
1324
+ HKD: "HK$",
1325
+ SGD: "S$",
1326
+ INR: "\u20B9",
1327
+ BRL: "R$",
1328
+ MXN: "MX$",
1329
+ KRW: "\u20A9",
1330
+ RUB: "\u20BD",
1331
+ TRY: "\u20BA",
1332
+ THB: "\u0E3F",
1333
+ PLN: "z\u0142",
1334
+ SEK: "kr",
1335
+ NOK: "kr",
1336
+ DKK: "kr",
1337
+ CZK: "K\u010D",
1338
+ HUF: "Ft",
1339
+ ILS: "\u20AA",
1340
+ AED: "\u062F.\u0625",
1341
+ SAR: "\uFDFC",
1342
+ MYR: "RM",
1343
+ PHP: "\u20B1",
1344
+ IDR: "Rp",
1345
+ VND: "\u20AB",
1346
+ TWD: "NT$",
1347
+ // African currencies
1348
+ GHS: "GH\u20B5",
1313
1349
  NGN: "\u20A6",
1314
1350
  KES: "KSh",
1315
1351
  ZAR: "R",
1316
1352
  XOF: "CFA",
1317
- XAF: "FCFA"
1353
+ XAF: "FCFA",
1354
+ EGP: "E\xA3",
1355
+ MAD: "MAD",
1356
+ TZS: "TSh",
1357
+ UGX: "USh",
1358
+ RWF: "FRw",
1359
+ ETB: "Br",
1360
+ ZMW: "ZK",
1361
+ BWP: "P",
1362
+ MUR: "\u20A8",
1363
+ SCR: "\u20A8",
1364
+ NAD: "N$",
1365
+ SZL: "E",
1366
+ LSL: "L",
1367
+ MWK: "MK",
1368
+ AOA: "Kz",
1369
+ CDF: "FC",
1370
+ GMD: "D",
1371
+ GNF: "FG",
1372
+ LRD: "L$",
1373
+ SLL: "Le",
1374
+ MZN: "MT",
1375
+ SDG: "SDG",
1376
+ SSP: "SSP",
1377
+ SOS: "Sh.So.",
1378
+ DJF: "Fdj",
1379
+ ERN: "Nfk",
1380
+ CVE: "$",
1381
+ STN: "Db",
1382
+ KMF: "CF",
1383
+ BIF: "FBu"
1318
1384
  };
1385
+ function getCurrencySymbol(currencyCode) {
1386
+ return CURRENCY_SYMBOLS[currencyCode.toUpperCase()] || currencyCode;
1387
+ }
1388
+ function formatNumberCompact(value, decimals = 1) {
1389
+ const absValue = Math.abs(value);
1390
+ const sign = value < 0 ? "-" : "";
1391
+ if (absValue >= 1e9) {
1392
+ return `${sign}${(absValue / 1e9).toFixed(decimals)}B`;
1393
+ }
1394
+ if (absValue >= 1e6) {
1395
+ return `${sign}${(absValue / 1e6).toFixed(decimals)}M`;
1396
+ }
1397
+ if (absValue >= 1e3) {
1398
+ return `${sign}${(absValue / 1e3).toFixed(decimals)}K`;
1399
+ }
1400
+ return `${sign}${absValue.toFixed(decimals)}`;
1401
+ }
1402
+ function formatPrice(amount, currency = "GHS", locale = "en-US") {
1403
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
1404
+ if (isNaN(numAmount)) {
1405
+ return `${getCurrencySymbol(currency)}0.00`;
1406
+ }
1407
+ try {
1408
+ return new Intl.NumberFormat(locale, {
1409
+ style: "currency",
1410
+ currency: currency.toUpperCase(),
1411
+ minimumFractionDigits: 2,
1412
+ maximumFractionDigits: 2
1413
+ }).format(numAmount);
1414
+ } catch {
1415
+ return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
1416
+ }
1417
+ }
1418
+ function formatPriceAdjustment(amount, currency = "GHS", locale = "en-US") {
1419
+ const formatted = formatPrice(Math.abs(amount), currency, locale);
1420
+ if (amount > 0) {
1421
+ return `+${formatted}`;
1422
+ } else if (amount < 0) {
1423
+ return `-${formatted}`;
1424
+ }
1425
+ return formatted;
1426
+ }
1427
+ function formatPriceCompact(amount, currency = "GHS", decimals = 1) {
1428
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
1429
+ if (isNaN(numAmount)) {
1430
+ return `${getCurrencySymbol(currency)}0`;
1431
+ }
1432
+ const symbol = getCurrencySymbol(currency);
1433
+ if (Math.abs(numAmount) < 1e3) {
1434
+ return `${symbol}${numAmount.toFixed(2)}`;
1435
+ }
1436
+ return `${symbol}${formatNumberCompact(numAmount, decimals)}`;
1437
+ }
1319
1438
  function formatMoney(amount, currency = "GHS") {
1320
- const symbol = CURRENCY_SYMBOLS[currency.toUpperCase()] || currency;
1439
+ const symbol = getCurrencySymbol(currency);
1321
1440
  const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
1441
+ if (isNaN(numAmount)) {
1442
+ return `${symbol}0.00`;
1443
+ }
1322
1444
  return `${symbol}${numAmount.toFixed(2)}`;
1323
1445
  }
1446
+ function parsePrice(value) {
1447
+ if (value === void 0 || value === null) {
1448
+ return 0;
1449
+ }
1450
+ if (typeof value === "number") {
1451
+ return isNaN(value) ? 0 : value;
1452
+ }
1453
+ const cleaned = value.replace(/[^\d.-]/g, "");
1454
+ const parsed = parseFloat(cleaned);
1455
+ return isNaN(parsed) ? 0 : parsed;
1456
+ }
1457
+ function parseTaxInfo(taxInfoStr) {
1458
+ try {
1459
+ const parts = taxInfoStr.split(":");
1460
+ if (parts.length < 4 || parts[0] !== "T") {
1461
+ return void 0;
1462
+ }
1463
+ const taxRate = parseFloat(parts[1]);
1464
+ const taxAmount = parseFloat(parts[2]);
1465
+ const isInclusive = parts[3] === "I";
1466
+ const components = [];
1467
+ if (parts.length > 4 && parts[4]) {
1468
+ const componentPairs = parts[4].split(",");
1469
+ for (const pair of componentPairs) {
1470
+ const [name, rateStr] = pair.split(":");
1471
+ if (name && rateStr) {
1472
+ components.push({
1473
+ name: name.trim(),
1474
+ rate: parseFloat(rateStr)
1475
+ });
1476
+ }
1477
+ }
1478
+ }
1479
+ return {
1480
+ taxRate,
1481
+ taxAmount,
1482
+ isInclusive,
1483
+ components
1484
+ };
1485
+ } catch {
1486
+ return void 0;
1487
+ }
1488
+ }
1489
+ function parsePricePath(signedPricePath) {
1490
+ if (!signedPricePath) {
1491
+ return {
1492
+ basePrice: 0,
1493
+ finalPrice: 0,
1494
+ currency: "GHS",
1495
+ decisionPath: "",
1496
+ isValid: false,
1497
+ isExpired: false
1498
+ };
1499
+ }
1500
+ try {
1501
+ const parts = signedPricePath.split("\xA7");
1502
+ if (parts.length !== 3) {
1503
+ return {
1504
+ basePrice: 0,
1505
+ finalPrice: 0,
1506
+ currency: "GHS",
1507
+ decisionPath: "",
1508
+ isValid: false,
1509
+ isExpired: false
1510
+ };
1511
+ }
1512
+ const [pricePath, expiryStr, signature] = parts;
1513
+ const expiryTimestamp = parseInt(expiryStr, 10);
1514
+ const now = Math.floor(Date.now() / 1e3);
1515
+ const isExpired = now > expiryTimestamp;
1516
+ const priceComponents = pricePath.split("|");
1517
+ if (priceComponents.length < 5) {
1518
+ return {
1519
+ basePrice: 0,
1520
+ finalPrice: 0,
1521
+ currency: "GHS",
1522
+ decisionPath: "",
1523
+ isValid: false,
1524
+ isExpired
1525
+ };
1526
+ }
1527
+ const basePrice = parseFloat(priceComponents[0]);
1528
+ const finalPrice = parseFloat(priceComponents[1]);
1529
+ const currency = priceComponents[2];
1530
+ const taxInfoStr = priceComponents[3];
1531
+ const decisionPath = priceComponents.slice(4).join("|");
1532
+ let taxInfo;
1533
+ if (taxInfoStr && taxInfoStr.startsWith("T:")) {
1534
+ taxInfo = parseTaxInfo(taxInfoStr);
1535
+ } else if (taxInfoStr === "0" || !taxInfoStr) {
1536
+ taxInfo = {
1537
+ taxRate: 0,
1538
+ taxAmount: 0,
1539
+ isInclusive: false,
1540
+ components: []
1541
+ };
1542
+ }
1543
+ let markupPercentage;
1544
+ let markupAmount;
1545
+ let discountPercentage;
1546
+ if (decisionPath) {
1547
+ const pathParts = decisionPath.split(":");
1548
+ if (pathParts.length > 1 && pathParts[1]) {
1549
+ const adjustments = pathParts[1].split(",");
1550
+ for (const adj of adjustments) {
1551
+ const adjParts = adj.split("|");
1552
+ if (adjParts[0] && adjParts[0].startsWith("ch")) {
1553
+ markupAmount = parseFloat(adjParts[1]) || 0;
1554
+ markupPercentage = parseFloat(adjParts[2]) || 0;
1555
+ break;
1556
+ }
1557
+ }
1558
+ }
1559
+ }
1560
+ if (markupAmount === void 0 && finalPrice > basePrice) {
1561
+ markupAmount = finalPrice - basePrice;
1562
+ markupPercentage = basePrice > 0 ? markupAmount / basePrice * 100 : 0;
1563
+ } else if (basePrice > finalPrice) {
1564
+ discountPercentage = basePrice > 0 ? (basePrice - finalPrice) / basePrice * 100 : 0;
1565
+ }
1566
+ return {
1567
+ basePrice,
1568
+ finalPrice,
1569
+ currency,
1570
+ decisionPath,
1571
+ discountPercentage,
1572
+ markupPercentage,
1573
+ markupAmount,
1574
+ taxInfo,
1575
+ isValid: !isExpired && signature.length > 0,
1576
+ isExpired,
1577
+ expiryTimestamp
1578
+ };
1579
+ } catch {
1580
+ return {
1581
+ basePrice: 0,
1582
+ finalPrice: 0,
1583
+ currency: "GHS",
1584
+ decisionPath: "",
1585
+ isValid: false,
1586
+ isExpired: false
1587
+ };
1588
+ }
1589
+ }
1590
+ function parsedPriceToPriceInfo(parsedPrice) {
1591
+ return {
1592
+ base_price: parsedPrice.basePrice,
1593
+ final_price: parsedPrice.finalPrice,
1594
+ markup_percentage: parsedPrice.markupPercentage,
1595
+ markup_amount: parsedPrice.markupAmount,
1596
+ currency: parsedPrice.currency,
1597
+ tax_info: parsedPrice.taxInfo,
1598
+ decision_path: parsedPrice.decisionPath
1599
+ };
1600
+ }
1601
+ function extractPriceInfo(signedPricePath) {
1602
+ const parsed = parsePricePath(signedPricePath);
1603
+ return parsedPriceToPriceInfo(parsed);
1604
+ }
1605
+ function getDisplayPrice(product) {
1606
+ if (product.price_path) {
1607
+ const priceInfo = extractPriceInfo(product.price_path);
1608
+ return priceInfo.final_price;
1609
+ } else if (product.price_info) {
1610
+ return product.price_info.final_price;
1611
+ }
1612
+ return 0;
1613
+ }
1614
+ function getBasePrice(product) {
1615
+ if (product.price_path) {
1616
+ const priceInfo = extractPriceInfo(product.price_path);
1617
+ return priceInfo.base_price;
1618
+ } else if (product.price_info) {
1619
+ return product.price_info.base_price;
1620
+ }
1621
+ return 0;
1622
+ }
1623
+ function isOnSale(product) {
1624
+ const basePrice = getBasePrice(product);
1625
+ const finalPrice = getDisplayPrice(product);
1626
+ return basePrice > finalPrice && basePrice > 0;
1627
+ }
1628
+ function getDiscountPercentage(product) {
1629
+ const basePrice = getBasePrice(product);
1630
+ const finalPrice = getDisplayPrice(product);
1631
+ if (basePrice > finalPrice && basePrice > 0) {
1632
+ return Math.round((basePrice - finalPrice) / basePrice * 100);
1633
+ }
1634
+ return 0;
1635
+ }
1636
+ function getMarkupPercentage(product) {
1637
+ const basePrice = getBasePrice(product);
1638
+ const finalPrice = getDisplayPrice(product);
1639
+ if (finalPrice > basePrice && basePrice > 0) {
1640
+ return Math.round((finalPrice - basePrice) / basePrice * 100);
1641
+ }
1642
+ return 0;
1643
+ }
1644
+ function getProductCurrency(product) {
1645
+ if (product.price_path) {
1646
+ const parsed = parsePricePath(product.price_path);
1647
+ return parsed.currency || "GHS";
1648
+ } else if (product.price_info?.currency) {
1649
+ return product.price_info.currency;
1650
+ }
1651
+ return "GHS";
1652
+ }
1653
+ function formatProductPrice(product, locale = "en-US") {
1654
+ const price = getDisplayPrice(product);
1655
+ const currency = getProductCurrency(product);
1656
+ return formatPrice(price, currency, locale);
1657
+ }
1658
+
1659
+ // src/utils/payment.ts
1324
1660
  function categorizePaymentError(error, errorCode) {
1325
1661
  let message = "An unexpected error occurred during payment processing. Please try again or contact support.";
1326
1662
  let recoverable = true;
@@ -1494,7 +1830,23 @@ exports.SchedulingService = SchedulingService;
1494
1830
  exports.categorizePaymentError = categorizePaymentError;
1495
1831
  exports.createCimplifyClient = createCimplifyClient;
1496
1832
  exports.detectMobileMoneyProvider = detectMobileMoneyProvider;
1833
+ exports.extractPriceInfo = extractPriceInfo;
1497
1834
  exports.formatMoney = formatMoney;
1835
+ exports.formatNumberCompact = formatNumberCompact;
1836
+ exports.formatPrice = formatPrice;
1837
+ exports.formatPriceAdjustment = formatPriceAdjustment;
1838
+ exports.formatPriceCompact = formatPriceCompact;
1839
+ exports.formatProductPrice = formatProductPrice;
1840
+ exports.getBasePrice = getBasePrice;
1841
+ exports.getCurrencySymbol = getCurrencySymbol;
1842
+ exports.getDiscountPercentage = getDiscountPercentage;
1843
+ exports.getDisplayPrice = getDisplayPrice;
1844
+ exports.getMarkupPercentage = getMarkupPercentage;
1845
+ exports.getProductCurrency = getProductCurrency;
1846
+ exports.isOnSale = isOnSale;
1498
1847
  exports.normalizePaymentResponse = normalizePaymentResponse;
1499
1848
  exports.normalizeStatusResponse = normalizeStatusResponse;
1849
+ exports.parsePrice = parsePrice;
1850
+ exports.parsePricePath = parsePricePath;
1851
+ exports.parsedPriceToPriceInfo = parsedPriceToPriceInfo;
1500
1852
  exports.query = query;