vonpay-medusa 1.1.1 → 1.2.0
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/.medusa/server/src/admin/index.js +92 -92
- package/.medusa/server/src/admin/index.mjs +94 -94
- package/.medusa/server/src/lib/money.js +4 -0
- package/.medusa/server/src/providers/vonpay/admin-holds.js +13 -0
- package/.medusa/server/src/providers/vonpay/service.js +70 -13
- package/.medusa/server/src/providers/vonpay/session-data.js +5 -0
- package/.medusa/server/src/providers/vonpay/setup-check.js +9 -0
- package/README.md +30 -0
- package/bin/vonpay-medusa.cjs +12 -4
- package/package.json +2 -2
- package/storefront/src/card-form-keep.ts +13 -0
- package/storefront/src/copy.ts +7 -0
- package/storefront/src/outcome.ts +12 -0
- package/storefront/src/reload-once.ts +21 -0
- package/storefront/src/shared.ts +1 -0
- package/storefront/src/shown-total.ts +21 -0
- package/storefront/src/vonpay-card-payment.tsx +191 -13
- package/storefront/src/vonpay-payment-view.tsx +1 -1
- package/storefront/src/vora.ts +11 -1
|
@@ -626,6 +626,84 @@ const UpdateBanner = () => {
|
|
|
626
626
|
] }) });
|
|
627
627
|
};
|
|
628
628
|
adminSdk.defineWidgetConfig({ zone: "order.list.before" });
|
|
629
|
+
const EVENT_LABEL = {
|
|
630
|
+
"subscription.created": "Signed up",
|
|
631
|
+
"subscription.renewed": "Renewal paid",
|
|
632
|
+
"subscription.payment_failed": "Renewal declined",
|
|
633
|
+
"subscription.cancelled": "Cancelled",
|
|
634
|
+
"subscription.updated": "Changed"
|
|
635
|
+
};
|
|
636
|
+
const eventLabel = (type) => EVENT_LABEL[type] ?? "Other";
|
|
637
|
+
const amountLabel = (major, currency) => major && currency ? `${major} ${currency}` : "—";
|
|
638
|
+
const NOT_ENABLED_NOTE = `Vonpay is not running this store's subscriptions. To let it, set the payment provider's subscriptions option to "vonpay", register the plugin's subscriptions module in medusa-config, run npx medusa db:migrate and restart — the setup check confirms each step.`;
|
|
639
|
+
const ORDERS_NOT_YET_NOTE = "Renewal orders are not created from these notifications yet: this list shows what Vonpay reported, so nothing here changes an order.";
|
|
640
|
+
const SUBSCRIPTION_PAGE_SIZE = 20;
|
|
641
|
+
function useSubscriptionEvents(page) {
|
|
642
|
+
return reactQuery.useQuery({
|
|
643
|
+
queryKey: ["vonpay-subscription-events", page.limit, page.offset],
|
|
644
|
+
queryFn: () => sdk.client.fetch("/admin/vonpay/subscriptions", { query: { limit: page.limit, offset: page.offset } }),
|
|
645
|
+
placeholderData: reactQuery.keepPreviousData
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
const pageCountFor = (count) => {
|
|
649
|
+
const remainder = count % SUBSCRIPTION_PAGE_SIZE;
|
|
650
|
+
return Math.max(1, (count - remainder) / SUBSCRIPTION_PAGE_SIZE + (remainder > 0 ? 1 : 0));
|
|
651
|
+
};
|
|
652
|
+
const when = (iso) => iso ? new Date(iso).toLocaleString() : "—";
|
|
653
|
+
const VonpaySubscriptionsPage = () => {
|
|
654
|
+
const [pageIndex, setPageIndex] = react.useState(0);
|
|
655
|
+
const { data, isLoading, isError } = useSubscriptionEvents({ limit: SUBSCRIPTION_PAGE_SIZE, offset: pageIndex * SUBSCRIPTION_PAGE_SIZE });
|
|
656
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
657
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4", children: [
|
|
658
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Vonpay subscriptions" }),
|
|
659
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Notifications from Vonpay's subscription billing, newest first." })
|
|
660
|
+
] }),
|
|
661
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-8 w-full" }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "The list could not be loaded. Reload the page to try again." }) }) : !data?.enabled ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "info", children: NOT_ENABLED_NOTE }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
662
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: ORDERS_NOT_YET_NOTE }) }),
|
|
663
|
+
data.events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "No subscription notifications yet." }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
664
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
665
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
666
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "What happened" }),
|
|
667
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Subscription" }),
|
|
668
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Period" }),
|
|
669
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Amount" }),
|
|
670
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Next bill" }),
|
|
671
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Received" })
|
|
672
|
+
] }) }),
|
|
673
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: data.events.map((e) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
674
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
675
|
+
eventLabel(e.type),
|
|
676
|
+
e.mode === "test" ? " (test)" : ""
|
|
677
|
+
] }),
|
|
678
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "font-mono text-xs", children: e.subscription_id }),
|
|
679
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: e.billing_cycle ?? "—" }),
|
|
680
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: amountLabel(e.amount_major, e.currency) }),
|
|
681
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: when(e.next_bill_at) }),
|
|
682
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: when(e.received_at) })
|
|
683
|
+
] }, e.id)) })
|
|
684
|
+
] }) }),
|
|
685
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
686
|
+
ui.Table.Pagination,
|
|
687
|
+
{
|
|
688
|
+
count: data.count,
|
|
689
|
+
pageSize: SUBSCRIPTION_PAGE_SIZE,
|
|
690
|
+
pageIndex,
|
|
691
|
+
pageCount: pageCountFor(data.count),
|
|
692
|
+
canPreviousPage: pageIndex > 0,
|
|
693
|
+
canNextPage: (pageIndex + 1) * SUBSCRIPTION_PAGE_SIZE < data.count,
|
|
694
|
+
previousPage: () => setPageIndex((p) => Math.max(0, p - 1)),
|
|
695
|
+
nextPage: () => setPageIndex((p) => p + 1)
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
] })
|
|
699
|
+
] })
|
|
700
|
+
] });
|
|
701
|
+
};
|
|
702
|
+
const config$2 = adminSdk.defineRouteConfig({
|
|
703
|
+
label: "Vonpay subscriptions",
|
|
704
|
+
icon: icons.ArrowPath,
|
|
705
|
+
nested: "/orders"
|
|
706
|
+
});
|
|
629
707
|
function useSetupCheck() {
|
|
630
708
|
return reactQuery.useMutation({
|
|
631
709
|
mutationFn: () => sdk.client.fetch("/admin/vonpay/setup-check", { method: "POST", body: {} }),
|
|
@@ -1050,7 +1128,7 @@ const HeldPaymentsPage = () => {
|
|
|
1050
1128
|
/* @__PURE__ */ jsxRuntime.jsx(SetupCheckPanel, {})
|
|
1051
1129
|
] });
|
|
1052
1130
|
};
|
|
1053
|
-
const config$
|
|
1131
|
+
const config$1 = adminSdk.defineRouteConfig({
|
|
1054
1132
|
label: "Held payments",
|
|
1055
1133
|
icon: icons.ExclamationCircle,
|
|
1056
1134
|
nested: "/orders"
|
|
@@ -1514,89 +1592,11 @@ const VonpayOffersPage = () => {
|
|
|
1514
1592
|
] })
|
|
1515
1593
|
] });
|
|
1516
1594
|
};
|
|
1517
|
-
const config
|
|
1595
|
+
const config = adminSdk.defineRouteConfig({
|
|
1518
1596
|
label: "One-click offers",
|
|
1519
1597
|
icon: icons.Sparkles,
|
|
1520
1598
|
nested: "/orders"
|
|
1521
1599
|
});
|
|
1522
|
-
const EVENT_LABEL = {
|
|
1523
|
-
"subscription.created": "Signed up",
|
|
1524
|
-
"subscription.renewed": "Renewal paid",
|
|
1525
|
-
"subscription.payment_failed": "Renewal declined",
|
|
1526
|
-
"subscription.cancelled": "Cancelled",
|
|
1527
|
-
"subscription.updated": "Changed"
|
|
1528
|
-
};
|
|
1529
|
-
const eventLabel = (type) => EVENT_LABEL[type] ?? "Other";
|
|
1530
|
-
const amountLabel = (major, currency) => major && currency ? `${major} ${currency}` : "—";
|
|
1531
|
-
const NOT_ENABLED_NOTE = `Vonpay is not running this store's subscriptions. To let it, set the payment provider's subscriptions option to "vonpay", register the plugin's subscriptions module in medusa-config, run npx medusa db:migrate and restart — the setup check confirms each step.`;
|
|
1532
|
-
const ORDERS_NOT_YET_NOTE = "Renewal orders are not created from these notifications yet: this list shows what Vonpay reported, so nothing here changes an order.";
|
|
1533
|
-
const SUBSCRIPTION_PAGE_SIZE = 20;
|
|
1534
|
-
function useSubscriptionEvents(page) {
|
|
1535
|
-
return reactQuery.useQuery({
|
|
1536
|
-
queryKey: ["vonpay-subscription-events", page.limit, page.offset],
|
|
1537
|
-
queryFn: () => sdk.client.fetch("/admin/vonpay/subscriptions", { query: { limit: page.limit, offset: page.offset } }),
|
|
1538
|
-
placeholderData: reactQuery.keepPreviousData
|
|
1539
|
-
});
|
|
1540
|
-
}
|
|
1541
|
-
const pageCountFor = (count) => {
|
|
1542
|
-
const remainder = count % SUBSCRIPTION_PAGE_SIZE;
|
|
1543
|
-
return Math.max(1, (count - remainder) / SUBSCRIPTION_PAGE_SIZE + (remainder > 0 ? 1 : 0));
|
|
1544
|
-
};
|
|
1545
|
-
const when = (iso) => iso ? new Date(iso).toLocaleString() : "—";
|
|
1546
|
-
const VonpaySubscriptionsPage = () => {
|
|
1547
|
-
const [pageIndex, setPageIndex] = react.useState(0);
|
|
1548
|
-
const { data, isLoading, isError } = useSubscriptionEvents({ limit: SUBSCRIPTION_PAGE_SIZE, offset: pageIndex * SUBSCRIPTION_PAGE_SIZE });
|
|
1549
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
1550
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4", children: [
|
|
1551
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Vonpay subscriptions" }),
|
|
1552
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Notifications from Vonpay's subscription billing, newest first." })
|
|
1553
|
-
] }),
|
|
1554
|
-
isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-8 w-full" }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "The list could not be loaded. Reload the page to try again." }) }) : !data?.enabled ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "info", children: NOT_ENABLED_NOTE }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1555
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: ORDERS_NOT_YET_NOTE }) }),
|
|
1556
|
-
data.events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "No subscription notifications yet." }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1557
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
1558
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1559
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "What happened" }),
|
|
1560
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Subscription" }),
|
|
1561
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Period" }),
|
|
1562
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Amount" }),
|
|
1563
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Next bill" }),
|
|
1564
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Received" })
|
|
1565
|
-
] }) }),
|
|
1566
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: data.events.map((e) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1567
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
1568
|
-
eventLabel(e.type),
|
|
1569
|
-
e.mode === "test" ? " (test)" : ""
|
|
1570
|
-
] }),
|
|
1571
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "font-mono text-xs", children: e.subscription_id }),
|
|
1572
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: e.billing_cycle ?? "—" }),
|
|
1573
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: amountLabel(e.amount_major, e.currency) }),
|
|
1574
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: when(e.next_bill_at) }),
|
|
1575
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: when(e.received_at) })
|
|
1576
|
-
] }, e.id)) })
|
|
1577
|
-
] }) }),
|
|
1578
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1579
|
-
ui.Table.Pagination,
|
|
1580
|
-
{
|
|
1581
|
-
count: data.count,
|
|
1582
|
-
pageSize: SUBSCRIPTION_PAGE_SIZE,
|
|
1583
|
-
pageIndex,
|
|
1584
|
-
pageCount: pageCountFor(data.count),
|
|
1585
|
-
canPreviousPage: pageIndex > 0,
|
|
1586
|
-
canNextPage: (pageIndex + 1) * SUBSCRIPTION_PAGE_SIZE < data.count,
|
|
1587
|
-
previousPage: () => setPageIndex((p) => Math.max(0, p - 1)),
|
|
1588
|
-
nextPage: () => setPageIndex((p) => p + 1)
|
|
1589
|
-
}
|
|
1590
|
-
)
|
|
1591
|
-
] })
|
|
1592
|
-
] })
|
|
1593
|
-
] });
|
|
1594
|
-
};
|
|
1595
|
-
const config = adminSdk.defineRouteConfig({
|
|
1596
|
-
label: "Vonpay subscriptions",
|
|
1597
|
-
icon: icons.ArrowPath,
|
|
1598
|
-
nested: "/orders"
|
|
1599
|
-
});
|
|
1600
1600
|
const i18nTranslations0 = {};
|
|
1601
1601
|
const widgetModule = { widgets: [
|
|
1602
1602
|
{
|
|
@@ -1618,18 +1618,18 @@ const widgetModule = { widgets: [
|
|
|
1618
1618
|
const routeModule = {
|
|
1619
1619
|
routes: [
|
|
1620
1620
|
{
|
|
1621
|
-
Component:
|
|
1622
|
-
path: "/vonpay-
|
|
1621
|
+
Component: VonpaySubscriptionsPage,
|
|
1622
|
+
path: "/vonpay-subscriptions",
|
|
1623
1623
|
handle: { label: config$2.label, translationNs: config$2.translationNs }
|
|
1624
1624
|
},
|
|
1625
1625
|
{
|
|
1626
|
-
Component:
|
|
1627
|
-
path: "/vonpay-
|
|
1626
|
+
Component: HeldPaymentsPage,
|
|
1627
|
+
path: "/vonpay-holds",
|
|
1628
1628
|
handle: { label: config$1.label, translationNs: config$1.translationNs }
|
|
1629
1629
|
},
|
|
1630
1630
|
{
|
|
1631
|
-
Component:
|
|
1632
|
-
path: "/vonpay-
|
|
1631
|
+
Component: VonpayOffersPage,
|
|
1632
|
+
path: "/vonpay-offers",
|
|
1633
1633
|
handle: { label: config.label, translationNs: config.translationNs }
|
|
1634
1634
|
}
|
|
1635
1635
|
]
|
|
@@ -1637,24 +1637,24 @@ const routeModule = {
|
|
|
1637
1637
|
const menuItemModule = {
|
|
1638
1638
|
menuItems: [
|
|
1639
1639
|
{
|
|
1640
|
-
label: config$
|
|
1641
|
-
icon: config$
|
|
1640
|
+
label: config$1.label,
|
|
1641
|
+
icon: config$1.icon,
|
|
1642
1642
|
path: "/vonpay-holds",
|
|
1643
1643
|
nested: "/orders",
|
|
1644
1644
|
rank: void 0,
|
|
1645
1645
|
translationNs: void 0
|
|
1646
1646
|
},
|
|
1647
1647
|
{
|
|
1648
|
-
label: config
|
|
1649
|
-
icon: config
|
|
1648
|
+
label: config.label,
|
|
1649
|
+
icon: config.icon,
|
|
1650
1650
|
path: "/vonpay-offers",
|
|
1651
1651
|
nested: "/orders",
|
|
1652
1652
|
rank: void 0,
|
|
1653
1653
|
translationNs: void 0
|
|
1654
1654
|
},
|
|
1655
1655
|
{
|
|
1656
|
-
label: config.label,
|
|
1657
|
-
icon: config.icon,
|
|
1656
|
+
label: config$2.label,
|
|
1657
|
+
icon: config$2.icon,
|
|
1658
1658
|
path: "/vonpay-subscriptions",
|
|
1659
1659
|
nested: "/orders",
|
|
1660
1660
|
rank: void 0,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { Alert, Text, Button, Container, Heading,
|
|
3
|
+
import { Alert, Text, Button, Container, Heading, Skeleton, Table, StatusBadge, Select, Tooltip, Copy, Drawer, CodeBlock, Prompt, Label, Input, Textarea, usePrompt, Switch, IconButton } from "@medusajs/ui";
|
|
4
4
|
import { Link } from "react-router-dom";
|
|
5
5
|
import { useQuery, keepPreviousData, useQueryClient, useMutation } from "@tanstack/react-query";
|
|
6
6
|
import Medusa from "@medusajs/js-sdk";
|
|
7
|
-
import { ExclamationCircle, XMarkMini, Sparkles, ArrowUpMini, ArrowDownMini, Trash
|
|
7
|
+
import { ArrowPath, ExclamationCircle, XMarkMini, Sparkles, ArrowUpMini, ArrowDownMini, Trash } from "@medusajs/icons";
|
|
8
8
|
import { useState } from "react";
|
|
9
9
|
const REASON_COPY = {
|
|
10
10
|
deleted_while_charged: {
|
|
@@ -623,6 +623,84 @@ const UpdateBanner = () => {
|
|
|
623
623
|
] }) });
|
|
624
624
|
};
|
|
625
625
|
defineWidgetConfig({ zone: "order.list.before" });
|
|
626
|
+
const EVENT_LABEL = {
|
|
627
|
+
"subscription.created": "Signed up",
|
|
628
|
+
"subscription.renewed": "Renewal paid",
|
|
629
|
+
"subscription.payment_failed": "Renewal declined",
|
|
630
|
+
"subscription.cancelled": "Cancelled",
|
|
631
|
+
"subscription.updated": "Changed"
|
|
632
|
+
};
|
|
633
|
+
const eventLabel = (type) => EVENT_LABEL[type] ?? "Other";
|
|
634
|
+
const amountLabel = (major, currency) => major && currency ? `${major} ${currency}` : "—";
|
|
635
|
+
const NOT_ENABLED_NOTE = `Vonpay is not running this store's subscriptions. To let it, set the payment provider's subscriptions option to "vonpay", register the plugin's subscriptions module in medusa-config, run npx medusa db:migrate and restart — the setup check confirms each step.`;
|
|
636
|
+
const ORDERS_NOT_YET_NOTE = "Renewal orders are not created from these notifications yet: this list shows what Vonpay reported, so nothing here changes an order.";
|
|
637
|
+
const SUBSCRIPTION_PAGE_SIZE = 20;
|
|
638
|
+
function useSubscriptionEvents(page) {
|
|
639
|
+
return useQuery({
|
|
640
|
+
queryKey: ["vonpay-subscription-events", page.limit, page.offset],
|
|
641
|
+
queryFn: () => sdk.client.fetch("/admin/vonpay/subscriptions", { query: { limit: page.limit, offset: page.offset } }),
|
|
642
|
+
placeholderData: keepPreviousData
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const pageCountFor = (count) => {
|
|
646
|
+
const remainder = count % SUBSCRIPTION_PAGE_SIZE;
|
|
647
|
+
return Math.max(1, (count - remainder) / SUBSCRIPTION_PAGE_SIZE + (remainder > 0 ? 1 : 0));
|
|
648
|
+
};
|
|
649
|
+
const when = (iso) => iso ? new Date(iso).toLocaleString() : "—";
|
|
650
|
+
const VonpaySubscriptionsPage = () => {
|
|
651
|
+
const [pageIndex, setPageIndex] = useState(0);
|
|
652
|
+
const { data, isLoading, isError } = useSubscriptionEvents({ limit: SUBSCRIPTION_PAGE_SIZE, offset: pageIndex * SUBSCRIPTION_PAGE_SIZE });
|
|
653
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
654
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4", children: [
|
|
655
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Vonpay subscriptions" }),
|
|
656
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Notifications from Vonpay's subscription billing, newest first." })
|
|
657
|
+
] }),
|
|
658
|
+
isLoading ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-full" }) }) : isError ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Alert, { variant: "error", children: "The list could not be loaded. Reload the page to try again." }) }) : !data?.enabled ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Alert, { variant: "info", children: NOT_ENABLED_NOTE }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
659
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: ORDERS_NOT_YET_NOTE }) }),
|
|
660
|
+
data.events.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { children: "No subscription notifications yet." }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
661
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
662
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
663
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "What happened" }),
|
|
664
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Subscription" }),
|
|
665
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Period" }),
|
|
666
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Amount" }),
|
|
667
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Next bill" }),
|
|
668
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Received" })
|
|
669
|
+
] }) }),
|
|
670
|
+
/* @__PURE__ */ jsx(Table.Body, { children: data.events.map((e) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
671
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
672
|
+
eventLabel(e.type),
|
|
673
|
+
e.mode === "test" ? " (test)" : ""
|
|
674
|
+
] }),
|
|
675
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "font-mono text-xs", children: e.subscription_id }),
|
|
676
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: e.billing_cycle ?? "—" }),
|
|
677
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: amountLabel(e.amount_major, e.currency) }),
|
|
678
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: when(e.next_bill_at) }),
|
|
679
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: when(e.received_at) })
|
|
680
|
+
] }, e.id)) })
|
|
681
|
+
] }) }),
|
|
682
|
+
/* @__PURE__ */ jsx(
|
|
683
|
+
Table.Pagination,
|
|
684
|
+
{
|
|
685
|
+
count: data.count,
|
|
686
|
+
pageSize: SUBSCRIPTION_PAGE_SIZE,
|
|
687
|
+
pageIndex,
|
|
688
|
+
pageCount: pageCountFor(data.count),
|
|
689
|
+
canPreviousPage: pageIndex > 0,
|
|
690
|
+
canNextPage: (pageIndex + 1) * SUBSCRIPTION_PAGE_SIZE < data.count,
|
|
691
|
+
previousPage: () => setPageIndex((p) => Math.max(0, p - 1)),
|
|
692
|
+
nextPage: () => setPageIndex((p) => p + 1)
|
|
693
|
+
}
|
|
694
|
+
)
|
|
695
|
+
] })
|
|
696
|
+
] })
|
|
697
|
+
] });
|
|
698
|
+
};
|
|
699
|
+
const config$2 = defineRouteConfig({
|
|
700
|
+
label: "Vonpay subscriptions",
|
|
701
|
+
icon: ArrowPath,
|
|
702
|
+
nested: "/orders"
|
|
703
|
+
});
|
|
626
704
|
function useSetupCheck() {
|
|
627
705
|
return useMutation({
|
|
628
706
|
mutationFn: () => sdk.client.fetch("/admin/vonpay/setup-check", { method: "POST", body: {} }),
|
|
@@ -1047,7 +1125,7 @@ const HeldPaymentsPage = () => {
|
|
|
1047
1125
|
/* @__PURE__ */ jsx(SetupCheckPanel, {})
|
|
1048
1126
|
] });
|
|
1049
1127
|
};
|
|
1050
|
-
const config$
|
|
1128
|
+
const config$1 = defineRouteConfig({
|
|
1051
1129
|
label: "Held payments",
|
|
1052
1130
|
icon: ExclamationCircle,
|
|
1053
1131
|
nested: "/orders"
|
|
@@ -1511,89 +1589,11 @@ const VonpayOffersPage = () => {
|
|
|
1511
1589
|
] })
|
|
1512
1590
|
] });
|
|
1513
1591
|
};
|
|
1514
|
-
const config
|
|
1592
|
+
const config = defineRouteConfig({
|
|
1515
1593
|
label: "One-click offers",
|
|
1516
1594
|
icon: Sparkles,
|
|
1517
1595
|
nested: "/orders"
|
|
1518
1596
|
});
|
|
1519
|
-
const EVENT_LABEL = {
|
|
1520
|
-
"subscription.created": "Signed up",
|
|
1521
|
-
"subscription.renewed": "Renewal paid",
|
|
1522
|
-
"subscription.payment_failed": "Renewal declined",
|
|
1523
|
-
"subscription.cancelled": "Cancelled",
|
|
1524
|
-
"subscription.updated": "Changed"
|
|
1525
|
-
};
|
|
1526
|
-
const eventLabel = (type) => EVENT_LABEL[type] ?? "Other";
|
|
1527
|
-
const amountLabel = (major, currency) => major && currency ? `${major} ${currency}` : "—";
|
|
1528
|
-
const NOT_ENABLED_NOTE = `Vonpay is not running this store's subscriptions. To let it, set the payment provider's subscriptions option to "vonpay", register the plugin's subscriptions module in medusa-config, run npx medusa db:migrate and restart — the setup check confirms each step.`;
|
|
1529
|
-
const ORDERS_NOT_YET_NOTE = "Renewal orders are not created from these notifications yet: this list shows what Vonpay reported, so nothing here changes an order.";
|
|
1530
|
-
const SUBSCRIPTION_PAGE_SIZE = 20;
|
|
1531
|
-
function useSubscriptionEvents(page) {
|
|
1532
|
-
return useQuery({
|
|
1533
|
-
queryKey: ["vonpay-subscription-events", page.limit, page.offset],
|
|
1534
|
-
queryFn: () => sdk.client.fetch("/admin/vonpay/subscriptions", { query: { limit: page.limit, offset: page.offset } }),
|
|
1535
|
-
placeholderData: keepPreviousData
|
|
1536
|
-
});
|
|
1537
|
-
}
|
|
1538
|
-
const pageCountFor = (count) => {
|
|
1539
|
-
const remainder = count % SUBSCRIPTION_PAGE_SIZE;
|
|
1540
|
-
return Math.max(1, (count - remainder) / SUBSCRIPTION_PAGE_SIZE + (remainder > 0 ? 1 : 0));
|
|
1541
|
-
};
|
|
1542
|
-
const when = (iso) => iso ? new Date(iso).toLocaleString() : "—";
|
|
1543
|
-
const VonpaySubscriptionsPage = () => {
|
|
1544
|
-
const [pageIndex, setPageIndex] = useState(0);
|
|
1545
|
-
const { data, isLoading, isError } = useSubscriptionEvents({ limit: SUBSCRIPTION_PAGE_SIZE, offset: pageIndex * SUBSCRIPTION_PAGE_SIZE });
|
|
1546
|
-
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
1547
|
-
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4", children: [
|
|
1548
|
-
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Vonpay subscriptions" }),
|
|
1549
|
-
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Notifications from Vonpay's subscription billing, newest first." })
|
|
1550
|
-
] }),
|
|
1551
|
-
isLoading ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-full" }) }) : isError ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Alert, { variant: "error", children: "The list could not be loaded. Reload the page to try again." }) }) : !data?.enabled ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Alert, { variant: "info", children: NOT_ENABLED_NOTE }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1552
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: ORDERS_NOT_YET_NOTE }) }),
|
|
1553
|
-
data.events.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { children: "No subscription notifications yet." }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1554
|
-
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
1555
|
-
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1556
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "What happened" }),
|
|
1557
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Subscription" }),
|
|
1558
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Period" }),
|
|
1559
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Amount" }),
|
|
1560
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Next bill" }),
|
|
1561
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Received" })
|
|
1562
|
-
] }) }),
|
|
1563
|
-
/* @__PURE__ */ jsx(Table.Body, { children: data.events.map((e) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1564
|
-
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
1565
|
-
eventLabel(e.type),
|
|
1566
|
-
e.mode === "test" ? " (test)" : ""
|
|
1567
|
-
] }),
|
|
1568
|
-
/* @__PURE__ */ jsx(Table.Cell, { className: "font-mono text-xs", children: e.subscription_id }),
|
|
1569
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: e.billing_cycle ?? "—" }),
|
|
1570
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: amountLabel(e.amount_major, e.currency) }),
|
|
1571
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: when(e.next_bill_at) }),
|
|
1572
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: when(e.received_at) })
|
|
1573
|
-
] }, e.id)) })
|
|
1574
|
-
] }) }),
|
|
1575
|
-
/* @__PURE__ */ jsx(
|
|
1576
|
-
Table.Pagination,
|
|
1577
|
-
{
|
|
1578
|
-
count: data.count,
|
|
1579
|
-
pageSize: SUBSCRIPTION_PAGE_SIZE,
|
|
1580
|
-
pageIndex,
|
|
1581
|
-
pageCount: pageCountFor(data.count),
|
|
1582
|
-
canPreviousPage: pageIndex > 0,
|
|
1583
|
-
canNextPage: (pageIndex + 1) * SUBSCRIPTION_PAGE_SIZE < data.count,
|
|
1584
|
-
previousPage: () => setPageIndex((p) => Math.max(0, p - 1)),
|
|
1585
|
-
nextPage: () => setPageIndex((p) => p + 1)
|
|
1586
|
-
}
|
|
1587
|
-
)
|
|
1588
|
-
] })
|
|
1589
|
-
] })
|
|
1590
|
-
] });
|
|
1591
|
-
};
|
|
1592
|
-
const config = defineRouteConfig({
|
|
1593
|
-
label: "Vonpay subscriptions",
|
|
1594
|
-
icon: ArrowPath,
|
|
1595
|
-
nested: "/orders"
|
|
1596
|
-
});
|
|
1597
1597
|
const i18nTranslations0 = {};
|
|
1598
1598
|
const widgetModule = { widgets: [
|
|
1599
1599
|
{
|
|
@@ -1615,18 +1615,18 @@ const widgetModule = { widgets: [
|
|
|
1615
1615
|
const routeModule = {
|
|
1616
1616
|
routes: [
|
|
1617
1617
|
{
|
|
1618
|
-
Component:
|
|
1619
|
-
path: "/vonpay-
|
|
1618
|
+
Component: VonpaySubscriptionsPage,
|
|
1619
|
+
path: "/vonpay-subscriptions",
|
|
1620
1620
|
handle: { label: config$2.label, translationNs: config$2.translationNs }
|
|
1621
1621
|
},
|
|
1622
1622
|
{
|
|
1623
|
-
Component:
|
|
1624
|
-
path: "/vonpay-
|
|
1623
|
+
Component: HeldPaymentsPage,
|
|
1624
|
+
path: "/vonpay-holds",
|
|
1625
1625
|
handle: { label: config$1.label, translationNs: config$1.translationNs }
|
|
1626
1626
|
},
|
|
1627
1627
|
{
|
|
1628
|
-
Component:
|
|
1629
|
-
path: "/vonpay-
|
|
1628
|
+
Component: VonpayOffersPage,
|
|
1629
|
+
path: "/vonpay-offers",
|
|
1630
1630
|
handle: { label: config.label, translationNs: config.translationNs }
|
|
1631
1631
|
}
|
|
1632
1632
|
]
|
|
@@ -1634,24 +1634,24 @@ const routeModule = {
|
|
|
1634
1634
|
const menuItemModule = {
|
|
1635
1635
|
menuItems: [
|
|
1636
1636
|
{
|
|
1637
|
-
label: config$
|
|
1638
|
-
icon: config$
|
|
1637
|
+
label: config$1.label,
|
|
1638
|
+
icon: config$1.icon,
|
|
1639
1639
|
path: "/vonpay-holds",
|
|
1640
1640
|
nested: "/orders",
|
|
1641
1641
|
rank: void 0,
|
|
1642
1642
|
translationNs: void 0
|
|
1643
1643
|
},
|
|
1644
1644
|
{
|
|
1645
|
-
label: config
|
|
1646
|
-
icon: config
|
|
1645
|
+
label: config.label,
|
|
1646
|
+
icon: config.icon,
|
|
1647
1647
|
path: "/vonpay-offers",
|
|
1648
1648
|
nested: "/orders",
|
|
1649
1649
|
rank: void 0,
|
|
1650
1650
|
translationNs: void 0
|
|
1651
1651
|
},
|
|
1652
1652
|
{
|
|
1653
|
-
label: config.label,
|
|
1654
|
-
icon: config.icon,
|
|
1653
|
+
label: config$2.label,
|
|
1654
|
+
icon: config$2.icon,
|
|
1655
1655
|
path: "/vonpay-subscriptions",
|
|
1656
1656
|
nested: "/orders",
|
|
1657
1657
|
rank: void 0,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.currencyDigits = currencyDigits;
|
|
3
4
|
exports.toMinorUnits = toMinorUnits;
|
|
4
5
|
exports.exactMinorUnits = exactMinorUnits;
|
|
5
6
|
exports.minorTotal = minorTotal;
|
|
@@ -27,6 +28,9 @@ function toDecimal(amount) {
|
|
|
27
28
|
throw (0, errors_1.vonpayError)("amount_not_a_number");
|
|
28
29
|
return parsed;
|
|
29
30
|
}
|
|
31
|
+
function currencyDigits(currency) {
|
|
32
|
+
return (0, checkout_node_1.minorUnitDigits)(currency) ?? null;
|
|
33
|
+
}
|
|
30
34
|
function toMinorUnits(amount, currency) {
|
|
31
35
|
const digits = digitsFor(currency);
|
|
32
36
|
const major = toDecimal(amount);
|
|
@@ -13,6 +13,7 @@ exports.readOrderFacts = readOrderFacts;
|
|
|
13
13
|
exports.collectionHasLiveOrder = collectionHasLiveOrder;
|
|
14
14
|
exports.requireResolveConfirmation = requireResolveConfirmation;
|
|
15
15
|
exports.within = within;
|
|
16
|
+
exports.readCartTotal = readCartTotal;
|
|
16
17
|
exports.isSubscriptionLine = isSubscriptionLine;
|
|
17
18
|
exports.checkoutEmailOf = checkoutEmailOf;
|
|
18
19
|
exports.readCheckoutCart = readCheckoutCart;
|
|
@@ -248,6 +249,18 @@ async function cartIdsFor(query, collectionId, ms) {
|
|
|
248
249
|
const ids = data.map((r) => r?.cart_id).filter((x) => typeof x === "string" && session_data_1.MEDUSA_ID.test(x));
|
|
249
250
|
return [...new Set(ids)].sort();
|
|
250
251
|
}
|
|
252
|
+
async function readCartTotal(query, collectionId, ms) {
|
|
253
|
+
const ids = await cartIdsFor(query, collectionId, ms);
|
|
254
|
+
if (ids.length === 0)
|
|
255
|
+
return "none";
|
|
256
|
+
if (ids.length > 1)
|
|
257
|
+
throw new Error("several_carts");
|
|
258
|
+
const { data } = await within(ms, () => query.graph({ entity: "cart", fields: ["id", "total", "raw_total", "currency_code"], filters: { id: ids } }));
|
|
259
|
+
const c = data[0];
|
|
260
|
+
if (!c || c.id !== ids[0])
|
|
261
|
+
throw new Error("cart_unreadable");
|
|
262
|
+
return { total: c.raw_total ?? c.total, currency: c.currency_code };
|
|
263
|
+
}
|
|
251
264
|
function isSubscriptionLine(metadata) {
|
|
252
265
|
if (!metadata || typeof metadata !== "object")
|
|
253
266
|
return false;
|