@frigear-nu/payments 0.0.9 → 0.1.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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@frigear-nu/payments",
3
3
  "configKey": "payments",
4
- "version": "0.0.9",
4
+ "version": "0.1.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -38,8 +38,7 @@ const module$1 = defineNuxtModule({
38
38
  nuxt.options.runtimeConfig.payments = {
39
39
  checkoutExpiresInMinutes: options.settings?.checkoutExpiresInMinutes ?? 30
40
40
  };
41
- await registerDatabaseTable(nuxt, {
42
- name: "payments",
41
+ registerDatabaseTable(nuxt, {
43
42
  resolver,
44
43
  schema: (dialect) => `./runtime/server/db/schema.${dialect}`
45
44
  });
@@ -88,7 +87,14 @@ const module$1 = defineNuxtModule({
88
87
  if (hasNuxtModule("@frigear-nu/admin") && hasNuxtModule("@frigear-nu/widget")) {
89
88
  const { extendWidgets } = await import('@frigear-nu/widget/utils');
90
89
  extendWidgets(nuxt, "admin.dashboard.top", { id: "payments-revenue-stat", component: resolver.resolve("./runtime/app/widgets/RevenueStat.vue"), order: 30 });
91
- extendWidgets(nuxt, "admin.dashboard.side", { id: "payments-recent", component: resolver.resolve("./runtime/app/widgets/RecentTransactions.vue"), order: 20 });
90
+ extendWidgets(nuxt, "admin.dashboard.side", {
91
+ id: "payments-recent",
92
+ component: resolver.resolve("./runtime/app/widgets/RecentTransactions.vue"),
93
+ order: 20,
94
+ props: {
95
+ tabId: "payments"
96
+ }
97
+ });
92
98
  }
93
99
  addServerImportsDir(resolver.resolve("./runtime/server/utils"));
94
100
  inlineModuleRuntime(nuxt, resolver);
@@ -1,23 +1,15 @@
1
1
  <script setup>
2
- import { formatMoney as formatMinorAmount } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "UsersAdminPayments" });
4
3
  const props = defineProps({
5
4
  user: { type: Object, required: true }
6
5
  });
7
6
  const t = useModuleT("payments.admin");
8
7
  const status = useModuleT("payments.status");
8
+ const format = useFormat();
9
9
  const { data: payments, status: fetchStatus } = useLazyFetch(
10
10
  () => `/api/admin/users/${props.user.id}/payments`,
11
11
  { key: () => `admin-payments-${props.user.id}` }
12
12
  );
13
- const statusColors = {
14
- paid: "success",
15
- pending: "warning",
16
- failed: "error",
17
- expired: "neutral",
18
- refunded: "neutral",
19
- partially_refunded: "warning"
20
- };
21
13
  const statusLabel = (value) => status(value, value);
22
14
  const columns = computed(() => [
23
15
  { accessorKey: "createdAt", header: t("date", "Date") },
@@ -27,48 +19,50 @@ const columns = computed(() => [
27
19
  { accessorKey: "refundedAmount", header: t("refunded", "Refunded") },
28
20
  { accessorKey: "referenceType", header: t("reference", "Reference") }
29
21
  ]);
30
- const date = (value) => value ? new Date(value).toLocaleString() : "\u2013";
31
22
  </script>
32
23
 
33
24
  <template>
34
- <AdminSection
35
- :title="t('title', 'Payments')"
36
- >
37
- <template #flush>
38
- <UTable
39
- :data="payments ?? []"
40
- :columns="columns"
41
- :loading="fetchStatus === 'pending'"
42
- >
43
- <template #createdAt-cell="{ row }">
44
- <span class="whitespace-nowrap">{{ date(row.original.paidAt ?? row.original.createdAt) }}</span>
45
- </template>
46
- <template #description-cell="{ row }">
47
- {{ row.original.description ?? t("defaultDescription", "Payment") }}
48
- </template>
49
- <template #amount-cell="{ row }">
50
- <span class="whitespace-nowrap">{{ formatMinorAmount(row.original.amount, row.original.currency) }}</span>
51
- </template>
52
- <template #status-cell="{ row }">
53
- <UBadge
54
- variant="subtle"
55
- :color="statusColors[row.original.status] ?? 'neutral'"
56
- :label="statusLabel(row.original.status)"
57
- />
58
- </template>
59
- <template #refundedAmount-cell="{ row }">
60
- <span class="whitespace-nowrap">{{ row.original.refundedAmount ? formatMinorAmount(row.original.refundedAmount, row.original.currency) : "\u2013" }}</span>
61
- </template>
62
- <template #referenceType-cell="{ row }">
63
- <span class="text-muted">{{ row.original.referenceType ? `${row.original.referenceType} ${row.original.referenceId ?? ""}`.trim() : "\u2013" }}</span>
64
- </template>
65
- <template #empty>
66
- <AdminEmpty
67
- icon="i-lucide-credit-card"
68
- :title="t('empty', 'No payments yet.')"
69
- />
70
- </template>
71
- </UTable>
25
+ <TableCard>
26
+ <template #header>
27
+ <h2 class="text-highlighted text-sm font-semibold">
28
+ {{ t("title", "Payments") }}
29
+ </h2>
72
30
  </template>
73
- </AdminSection>
31
+ <UTable
32
+ :data="payments ?? []"
33
+ :columns="columns"
34
+ :loading="fetchStatus === 'pending'"
35
+ :ui="tableUi({ tr: '' })"
36
+ >
37
+ <template #createdAt-cell="{ row }">
38
+ <span class="whitespace-nowrap">{{ format.dateTime(row.original.paidAt ?? row.original.createdAt) || "\u2013" }}</span>
39
+ </template>
40
+ <template #description-cell="{ row }">
41
+ {{ row.original.description ?? t("defaultDescription", "Payment") }}
42
+ </template>
43
+ <template #amount-cell="{ row }">
44
+ <span class="whitespace-nowrap">{{ format.money(row.original.amount, row.original.currency) }}</span>
45
+ </template>
46
+ <template #status-cell="{ row }">
47
+ <UBadge
48
+ variant="subtle"
49
+ :color="statusColor(row.original.status, { partially_refunded: 'warning' })"
50
+ :label="statusLabel(row.original.status)"
51
+ />
52
+ </template>
53
+ <template #refundedAmount-cell="{ row }">
54
+ <span class="whitespace-nowrap">{{ row.original.refundedAmount ? format.money(row.original.refundedAmount, row.original.currency) : "\u2013" }}</span>
55
+ </template>
56
+ <template #referenceType-cell="{ row }">
57
+ <span class="text-muted">{{ row.original.referenceType ? `${row.original.referenceType} ${row.original.referenceId ?? ""}`.trim() : "\u2013" }}</span>
58
+ </template>
59
+ <template #empty>
60
+ <UEmpty
61
+ icon="i-lucide-credit-card"
62
+ variant="naked"
63
+ :title="t('empty', 'No payments yet')"
64
+ />
65
+ </template>
66
+ </UTable>
67
+ </TableCard>
74
68
  </template>
@@ -1,33 +1,45 @@
1
1
  <script setup>
2
- import { formatMoney as formatMinorAmount } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "UsersAdminLastPayment" });
4
3
  const props = defineProps({
5
4
  user: { type: Object, required: true }
6
5
  });
7
6
  const t = useModuleT("payments.admin");
8
7
  const status = useModuleT("payments.status");
8
+ const format = useFormat();
9
9
  const { data: payments, status: fetchStatus } = useLazyFetch(
10
10
  () => `/api/admin/users/${props.user.id}/payments`,
11
11
  { key: () => `admin-last-payment-${props.user.id}`, query: { limit: 1 } }
12
12
  );
13
13
  const last = computed(() => payments.value?.[0]);
14
- const date = (value) => value ? new Date(value).toLocaleDateString() : "\u2013";
14
+ const facts = computed(() => last.value ? [
15
+ { key: "amount", label: t("amount", "Amount"), value: format.money(last.value.amount, last.value.currency) },
16
+ { key: "status", label: t("statusColumn", "Status") },
17
+ { key: "date", label: t("date", "Date"), value: format.date(last.value.paidAt ?? last.value.createdAt) }
18
+ ] : []);
15
19
  </script>
16
20
 
17
21
  <template>
18
- <AdminStatCard
19
- :label="t('lastPayment', 'Last payment')"
20
- :empty="t('empty', 'No payments yet.')"
22
+ <SectionCard
23
+ :title="t('lastPayment', 'Last payment')"
21
24
  :loading="fetchStatus === 'pending' && !payments"
22
25
  >
23
- <template v-if="last">
24
- <span class="text-highlighted font-medium">{{ formatMinorAmount(last.amount, last.currency) }}</span>
25
- <UBadge
26
- variant="subtle"
27
- :color="last.status === 'paid' ? 'success' : 'neutral'"
28
- :label="status(last.status, last.status)"
29
- />
30
- <span class="text-muted">{{ date(last.paidAt ?? last.createdAt) }}</span>
31
- </template>
32
- </AdminStatCard>
26
+ <DescriptionList
27
+ v-if="last"
28
+ :items="facts"
29
+ >
30
+ <template #status>
31
+ <UBadge
32
+ variant="subtle"
33
+ :color="statusColor(last.status, { partially_refunded: 'warning' })"
34
+ :label="status(last.status, last.status)"
35
+ />
36
+ </template>
37
+ </DescriptionList>
38
+ <UEmpty
39
+ v-else
40
+ icon="i-lucide-credit-card"
41
+ variant="naked"
42
+ :title="t('empty', 'No payments yet')"
43
+ />
44
+ </SectionCard>
33
45
  </template>
@@ -1,3 +1,6 @@
1
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ type __VLS_Props = {
2
+ tabId?: string;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
5
  declare const _default: typeof __VLS_export;
3
6
  export default _default;
@@ -1,21 +1,26 @@
1
1
  <script setup>
2
- import { formatMoney } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "PaymentsRecentTransactions" });
3
+ const props = defineProps({
4
+ tabId: { type: String, required: false, default: "payments" }
5
+ });
4
6
  const t = useModuleT("payments.admin");
5
7
  const status = useModuleT("payments.status");
8
+ const format = useFormat();
9
+ const { localize } = useLocaleLink();
6
10
  const usersPage = useRuntimeConfig().public.users?.pages?.admin;
11
+ const usersPath = usersPage ? localize(usersPage.replace(/\/+$/, "")) : void 0;
7
12
  const { data, status: fetchStatus } = useLazyFetch("/api/payments/admin/recent", { key: "admin-payments-recent" });
8
- const date = (value) => value ? new Date(value).toLocaleDateString() : "\u2013";
9
- const color = (value) => value === "paid" ? "success" : value === "failed" ? "error" : "neutral";
10
- const userTo = (id) => usersPage ? `${usersPage.replace(/\/+$/, "")}/${id}` : void 0;
13
+ const userTo = (id) => usersPath ? { path: `${usersPath}/${id}`, query: { tab: props.tabId } } : void 0;
11
14
  </script>
12
15
 
13
16
  <template>
14
- <AdminListCard
17
+ <ListCard
15
18
  :title="t('recent', 'Recent transactions')"
16
19
  :loading="fetchStatus === 'pending' && !data"
17
20
  :is-empty="!data?.items.length"
18
- :empty="t('empty', 'No payments yet.')"
21
+ :empty="t('empty', 'No payments yet')"
22
+ :to="usersPath"
23
+ :to-label="t('viewUsers', 'View users')"
19
24
  >
20
25
  <li
21
26
  v-for="payment in data?.items"
@@ -35,20 +40,20 @@ const userTo = (id) => usersPage ? `${usersPage.replace(/\/+$/, "")}/${id}` : vo
35
40
  class="text-highlighted block truncate font-medium"
36
41
  >{{ payment.userName ?? payment.description ?? t("defaultDescription", "Payment") }}</span>
37
42
  <p class="text-muted truncate text-xs">
38
- {{ date(payment.paidAt ?? payment.createdAt) }}<template v-if="payment.description && payment.userName">
43
+ {{ format.date(payment.paidAt ?? payment.createdAt) }}<template v-if="payment.description && payment.userName">
39
44
  · {{ payment.description }}
40
45
  </template>
41
46
  </p>
42
47
  </div>
43
48
  <div class="flex shrink-0 flex-col items-end gap-1">
44
- <span class="text-highlighted font-medium">{{ formatMoney(payment.amount, payment.currency) }}</span>
49
+ <span class="text-highlighted font-medium">{{ format.money(payment.amount, payment.currency) }}</span>
45
50
  <UBadge
46
51
  size="sm"
47
52
  variant="subtle"
48
- :color="color(payment.status)"
53
+ :color="statusColor(payment.status, { partially_refunded: 'warning' })"
49
54
  :label="status(payment.status, payment.status)"
50
55
  />
51
56
  </div>
52
57
  </li>
53
- </AdminListCard>
58
+ </ListCard>
54
59
  </template>
@@ -1,3 +1,6 @@
1
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ type __VLS_Props = {
2
+ tabId?: string;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
5
  declare const _default: typeof __VLS_export;
3
6
  export default _default;
@@ -1,21 +1,18 @@
1
1
  <script setup>
2
- import { formatMoney } from "@frigear-nu/ui/money";
3
2
  defineOptions({ name: "PaymentsRevenueStat" });
4
3
  const t = useModuleT("payments.admin");
4
+ const format = useFormat();
5
5
  const { data, status } = useLazyFetch("/api/payments/admin/recent", { key: "admin-payments-recent" });
6
6
  const main = computed(() => data.value?.revenue[0]);
7
7
  </script>
8
8
 
9
9
  <template>
10
- <AdminStatCard
10
+ <StatCard
11
11
  :label="t('revenue', 'Revenue, 30 days')"
12
12
  icon="i-lucide-circle-dollar-sign"
13
+ :value="main ? format.money(main.amount, main.currency) : void 0"
14
+ :hint="main ? t('transactions', '{count} transactions', { count: main.count }) : void 0"
13
15
  :loading="status === 'pending' && !data"
14
- :empty="t('noRevenue', 'No payments yet.')"
15
- >
16
- <template v-if="main">
17
- <span>{{ formatMoney(main.amount, main.currency) }}</span>
18
- <span class="text-muted text-sm font-normal">{{ t("transactions", "{count} transactions").replace("{count}", String(main.count)) }}</span>
19
- </template>
20
- </AdminStatCard>
16
+ :empty="t('noRevenue', 'No payments yet')"
17
+ />
21
18
  </template>
@@ -13,7 +13,7 @@
13
13
  "tab": "Betalinger",
14
14
  "title": "Betalinger",
15
15
  "lastPayment": "Seneste betaling",
16
- "empty": "Ingen betalinger endnu.",
16
+ "empty": "Ingen betalinger endnu",
17
17
  "date": "Dato",
18
18
  "description": "Beskrivelse",
19
19
  "amount": "Beløb",
@@ -22,9 +22,10 @@
22
22
  "reference": "Reference",
23
23
  "defaultDescription": "Betaling",
24
24
  "revenue": "Omsætning, 30 dage",
25
- "noRevenue": "Ingen betalinger endnu.",
25
+ "noRevenue": "Ingen betalinger endnu",
26
26
  "transactions": "{count} transaktioner",
27
- "recent": "Seneste transaktioner"
27
+ "recent": "Seneste transaktioner",
28
+ "viewUsers": "Se brugere"
28
29
  }
29
30
  }
30
31
  }
@@ -13,7 +13,7 @@
13
13
  "tab": "Payments",
14
14
  "title": "Payments",
15
15
  "lastPayment": "Last payment",
16
- "empty": "No payments yet.",
16
+ "empty": "No payments yet",
17
17
  "date": "Date",
18
18
  "description": "Description",
19
19
  "amount": "Amount",
@@ -22,9 +22,10 @@
22
22
  "reference": "Reference",
23
23
  "defaultDescription": "Payment",
24
24
  "revenue": "Revenue, 30 days",
25
- "noRevenue": "No payments yet.",
25
+ "noRevenue": "No payments yet",
26
26
  "transactions": "{count} transactions",
27
- "recent": "Recent transactions"
27
+ "recent": "Recent transactions",
28
+ "viewUsers": "View users"
28
29
  }
29
30
  }
30
31
  }
@@ -0,0 +1,301 @@
1
+ import type { PaymentItem } from '../../../types.js';
2
+ export declare const payments: import("drizzle-orm/pg-core").PgTableWithColumns<{
3
+ name: "payments";
4
+ schema: undefined;
5
+ columns: {
6
+ id: import("drizzle-orm/pg-core").PgColumn<{
7
+ name: "id";
8
+ tableName: "payments";
9
+ dataType: "string";
10
+ columnType: "PgText";
11
+ data: string;
12
+ driverParam: string;
13
+ notNull: true;
14
+ hasDefault: false;
15
+ isPrimaryKey: true;
16
+ isAutoincrement: false;
17
+ hasRuntimeDefault: false;
18
+ enumValues: [string, ...string[]];
19
+ baseColumn: never;
20
+ identity: undefined;
21
+ generated: undefined;
22
+ }, {}, {}>;
23
+ userId: import("drizzle-orm/pg-core").PgColumn<{
24
+ name: "userId";
25
+ tableName: "payments";
26
+ dataType: "number";
27
+ columnType: "PgInteger";
28
+ data: number;
29
+ driverParam: string | number;
30
+ notNull: true;
31
+ hasDefault: false;
32
+ isPrimaryKey: false;
33
+ isAutoincrement: false;
34
+ hasRuntimeDefault: false;
35
+ enumValues: undefined;
36
+ baseColumn: never;
37
+ identity: undefined;
38
+ generated: undefined;
39
+ }, {}, {}>;
40
+ status: import("drizzle-orm/pg-core").PgColumn<{
41
+ name: "status";
42
+ tableName: "payments";
43
+ dataType: "string";
44
+ columnType: "PgText";
45
+ data: string;
46
+ driverParam: string;
47
+ notNull: true;
48
+ hasDefault: true;
49
+ isPrimaryKey: false;
50
+ isAutoincrement: false;
51
+ hasRuntimeDefault: false;
52
+ enumValues: [string, ...string[]];
53
+ baseColumn: never;
54
+ identity: undefined;
55
+ generated: undefined;
56
+ }, {}, {}>;
57
+ amount: import("drizzle-orm/pg-core").PgColumn<{
58
+ name: "amount";
59
+ tableName: "payments";
60
+ dataType: "number";
61
+ columnType: "PgInteger";
62
+ data: number;
63
+ driverParam: string | number;
64
+ notNull: true;
65
+ hasDefault: false;
66
+ isPrimaryKey: false;
67
+ isAutoincrement: false;
68
+ hasRuntimeDefault: false;
69
+ enumValues: undefined;
70
+ baseColumn: never;
71
+ identity: undefined;
72
+ generated: undefined;
73
+ }, {}, {}>;
74
+ currency: import("drizzle-orm/pg-core").PgColumn<{
75
+ name: "currency";
76
+ tableName: "payments";
77
+ dataType: "string";
78
+ columnType: "PgText";
79
+ data: string;
80
+ driverParam: string;
81
+ notNull: true;
82
+ hasDefault: false;
83
+ isPrimaryKey: false;
84
+ isAutoincrement: false;
85
+ hasRuntimeDefault: false;
86
+ enumValues: [string, ...string[]];
87
+ baseColumn: never;
88
+ identity: undefined;
89
+ generated: undefined;
90
+ }, {}, {}>;
91
+ description: import("drizzle-orm/pg-core").PgColumn<{
92
+ name: "description";
93
+ tableName: "payments";
94
+ dataType: "string";
95
+ columnType: "PgText";
96
+ data: string;
97
+ driverParam: string;
98
+ notNull: false;
99
+ hasDefault: false;
100
+ isPrimaryKey: false;
101
+ isAutoincrement: false;
102
+ hasRuntimeDefault: false;
103
+ enumValues: [string, ...string[]];
104
+ baseColumn: never;
105
+ identity: undefined;
106
+ generated: undefined;
107
+ }, {}, {}>;
108
+ referenceType: import("drizzle-orm/pg-core").PgColumn<{
109
+ name: "referenceType";
110
+ tableName: "payments";
111
+ dataType: "string";
112
+ columnType: "PgText";
113
+ data: string;
114
+ driverParam: string;
115
+ notNull: false;
116
+ hasDefault: false;
117
+ isPrimaryKey: false;
118
+ isAutoincrement: false;
119
+ hasRuntimeDefault: false;
120
+ enumValues: [string, ...string[]];
121
+ baseColumn: never;
122
+ identity: undefined;
123
+ generated: undefined;
124
+ }, {}, {}>;
125
+ referenceId: import("drizzle-orm/pg-core").PgColumn<{
126
+ name: "referenceId";
127
+ tableName: "payments";
128
+ dataType: "string";
129
+ columnType: "PgText";
130
+ data: string;
131
+ driverParam: string;
132
+ notNull: false;
133
+ hasDefault: false;
134
+ isPrimaryKey: false;
135
+ isAutoincrement: false;
136
+ hasRuntimeDefault: false;
137
+ enumValues: [string, ...string[]];
138
+ baseColumn: never;
139
+ identity: undefined;
140
+ generated: undefined;
141
+ }, {}, {}>;
142
+ checkoutSessionId: import("drizzle-orm/pg-core").PgColumn<{
143
+ name: "checkoutSessionId";
144
+ tableName: "payments";
145
+ dataType: "string";
146
+ columnType: "PgText";
147
+ data: string;
148
+ driverParam: string;
149
+ notNull: false;
150
+ hasDefault: false;
151
+ isPrimaryKey: false;
152
+ isAutoincrement: false;
153
+ hasRuntimeDefault: false;
154
+ enumValues: [string, ...string[]];
155
+ baseColumn: never;
156
+ identity: undefined;
157
+ generated: undefined;
158
+ }, {}, {}>;
159
+ invoiceId: import("drizzle-orm/pg-core").PgColumn<{
160
+ name: "invoiceId";
161
+ tableName: "payments";
162
+ dataType: "string";
163
+ columnType: "PgText";
164
+ data: string;
165
+ driverParam: string;
166
+ notNull: false;
167
+ hasDefault: false;
168
+ isPrimaryKey: false;
169
+ isAutoincrement: false;
170
+ hasRuntimeDefault: false;
171
+ enumValues: [string, ...string[]];
172
+ baseColumn: never;
173
+ identity: undefined;
174
+ generated: undefined;
175
+ }, {}, {}>;
176
+ paymentIntentId: import("drizzle-orm/pg-core").PgColumn<{
177
+ name: "paymentIntentId";
178
+ tableName: "payments";
179
+ dataType: "string";
180
+ columnType: "PgText";
181
+ data: string;
182
+ driverParam: string;
183
+ notNull: false;
184
+ hasDefault: false;
185
+ isPrimaryKey: false;
186
+ isAutoincrement: false;
187
+ hasRuntimeDefault: false;
188
+ enumValues: [string, ...string[]];
189
+ baseColumn: never;
190
+ identity: undefined;
191
+ generated: undefined;
192
+ }, {}, {}>;
193
+ refundedAmount: import("drizzle-orm/pg-core").PgColumn<{
194
+ name: "refundedAmount";
195
+ tableName: "payments";
196
+ dataType: "number";
197
+ columnType: "PgInteger";
198
+ data: number;
199
+ driverParam: string | number;
200
+ notNull: true;
201
+ hasDefault: true;
202
+ isPrimaryKey: false;
203
+ isAutoincrement: false;
204
+ hasRuntimeDefault: false;
205
+ enumValues: undefined;
206
+ baseColumn: never;
207
+ identity: undefined;
208
+ generated: undefined;
209
+ }, {}, {}>;
210
+ metadata: import("drizzle-orm/pg-core").PgColumn<{
211
+ name: "metadata";
212
+ tableName: "payments";
213
+ dataType: "json";
214
+ columnType: "PgJsonb";
215
+ data: Record<string, string>;
216
+ driverParam: unknown;
217
+ notNull: false;
218
+ hasDefault: false;
219
+ isPrimaryKey: false;
220
+ isAutoincrement: false;
221
+ hasRuntimeDefault: false;
222
+ enumValues: undefined;
223
+ baseColumn: never;
224
+ identity: undefined;
225
+ generated: undefined;
226
+ }, {}, {
227
+ $type: Record<string, string>;
228
+ }>;
229
+ items: import("drizzle-orm/pg-core").PgColumn<{
230
+ name: "items";
231
+ tableName: "payments";
232
+ dataType: "json";
233
+ columnType: "PgJsonb";
234
+ data: PaymentItem[];
235
+ driverParam: unknown;
236
+ notNull: false;
237
+ hasDefault: false;
238
+ isPrimaryKey: false;
239
+ isAutoincrement: false;
240
+ hasRuntimeDefault: false;
241
+ enumValues: undefined;
242
+ baseColumn: never;
243
+ identity: undefined;
244
+ generated: undefined;
245
+ }, {}, {
246
+ $type: PaymentItem[];
247
+ }>;
248
+ paidAt: import("drizzle-orm/pg-core").PgColumn<{
249
+ name: "paidAt";
250
+ tableName: "payments";
251
+ dataType: "date";
252
+ columnType: "PgTimestamp";
253
+ data: Date;
254
+ driverParam: string;
255
+ notNull: false;
256
+ hasDefault: false;
257
+ isPrimaryKey: false;
258
+ isAutoincrement: false;
259
+ hasRuntimeDefault: false;
260
+ enumValues: undefined;
261
+ baseColumn: never;
262
+ identity: undefined;
263
+ generated: undefined;
264
+ }, {}, {}>;
265
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
266
+ name: "createdAt";
267
+ tableName: "payments";
268
+ dataType: "date";
269
+ columnType: "PgTimestamp";
270
+ data: Date;
271
+ driverParam: string;
272
+ notNull: true;
273
+ hasDefault: true;
274
+ isPrimaryKey: false;
275
+ isAutoincrement: false;
276
+ hasRuntimeDefault: true;
277
+ enumValues: undefined;
278
+ baseColumn: never;
279
+ identity: undefined;
280
+ generated: undefined;
281
+ }, {}, {}>;
282
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
283
+ name: "updatedAt";
284
+ tableName: "payments";
285
+ dataType: "date";
286
+ columnType: "PgTimestamp";
287
+ data: Date;
288
+ driverParam: string;
289
+ notNull: true;
290
+ hasDefault: true;
291
+ isPrimaryKey: false;
292
+ isAutoincrement: false;
293
+ hasRuntimeDefault: true;
294
+ enumValues: undefined;
295
+ baseColumn: never;
296
+ identity: undefined;
297
+ generated: undefined;
298
+ }, {}, {}>;
299
+ };
300
+ dialect: "pg";
301
+ }>;
@@ -0,0 +1,31 @@
1
+ import { pgTable, text, integer, jsonb, timestamp, index } from "drizzle-orm/pg-core";
2
+ export const payments = pgTable("payments", {
3
+ /** `pay_` + random id. */
4
+ id: text().primaryKey(),
5
+ userId: integer().notNull(),
6
+ /** `pending | paid | failed | expired | refunded | partially_refunded`. */
7
+ status: text().notNull().default("pending"),
8
+ /** Total in the currency's minor unit. */
9
+ amount: integer().notNull(),
10
+ currency: text().notNull(),
11
+ description: text(),
12
+ /** What the payment is for (e.g. `event_ticket`) and the id of that thing. */
13
+ referenceType: text(),
14
+ referenceId: text(),
15
+ /** `null` for payments recorded from a Stripe invoice (e.g. a membership renewal), which have no Checkout session. */
16
+ checkoutSessionId: text().unique(),
17
+ /** Stripe invoice id for payments recorded from an invoice (see `recordInvoicePayment`). */
18
+ invoiceId: text().unique(),
19
+ paymentIntentId: text(),
20
+ refundedAmount: integer().notNull().default(0),
21
+ metadata: jsonb().$type(),
22
+ /** The line items the session was created from, so an expired session can be rebuilt. `null` for rows created before this column existed. */
23
+ items: jsonb().$type(),
24
+ paidAt: timestamp({ withTimezone: true }),
25
+ createdAt: timestamp({ withTimezone: true }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
26
+ updatedAt: timestamp({ withTimezone: true }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()).$onUpdateFn(() => /* @__PURE__ */ new Date())
27
+ }, (t) => [
28
+ index("payment_user_idx").on(t.userId),
29
+ index("payment_reference_idx").on(t.referenceType, t.referenceId),
30
+ index("payment_intent_idx").on(t.paymentIntentId)
31
+ ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/payments",
3
- "version": "0.0.9",
3
+ "version": "0.1.0",
4
4
  "description": "One-time payments (Stripe Checkout, refunds and payment lifecycle hooks) for the frigear ecosystem, built on @frigear-nu/stripe.",
5
5
  "homepage": "https://docs.frigear.dev/modules/payments",
6
6
  "repository": {
@@ -30,14 +30,14 @@
30
30
  "playground"
31
31
  ],
32
32
  "dependencies": {
33
- "@frigear-nu/auth": "0.0.4",
34
- "@frigear-nu/mcp": "0.0.4",
35
- "@frigear-nu/module-kit": "1.0.0",
36
- "@frigear-nu/stripe": "0.0.9",
37
- "@frigear-nu/ui": "0.0.8",
38
- "@frigear-nu/users": "1.0.10",
33
+ "@frigear-nu/auth": "0.1.0",
34
+ "@frigear-nu/mcp": "0.1.0",
35
+ "@frigear-nu/module-kit": "1.1.0",
36
+ "@frigear-nu/stripe": "0.1.0",
37
+ "@frigear-nu/ui": "0.1.0",
38
+ "@frigear-nu/users": "1.1.0",
39
39
  "@libsql/client": "^0.18.0",
40
- "@nuxt/kit": "^4.6.0",
40
+ "@nuxt/kit": "^4.6.1",
41
41
  "drizzle-kit": "^0.31.11",
42
42
  "drizzle-orm": "^0.45.3",
43
43
  "h3": "^1.15.11",
@@ -46,8 +46,8 @@
46
46
  "zod": "^4.6.5"
47
47
  },
48
48
  "peerDependencies": {
49
- "@frigear-nu/widget": "0.0.3",
50
- "@nuxt/ui": "^4.10.0",
49
+ "@frigear-nu/widget": "0.1.0",
50
+ "@nuxt/ui": "^4.11.2",
51
51
  "@nuxthub/core": "^0.10.8"
52
52
  },
53
53
  "peerDependenciesMeta": {
@@ -59,20 +59,20 @@
59
59
  }
60
60
  },
61
61
  "devDependencies": {
62
- "@frigear-nu/widget": "0.0.3",
62
+ "@frigear-nu/widget": "0.1.0",
63
63
  "@nuxt/devtools": "^3.4.2",
64
64
  "@nuxt/eslint-config": "^1.17.0",
65
65
  "@nuxt/module-builder": "^1.0.3",
66
- "@nuxt/schema": "^4.6.0",
66
+ "@nuxt/schema": "^4.6.1",
67
67
  "@nuxt/test-utils": "^4.3.2",
68
- "@nuxt/ui": "^4.11.0",
68
+ "@nuxt/ui": "^4.11.2",
69
69
  "@nuxthub/core": "^0.10.8",
70
- "@types/node": "latest",
70
+ "@types/node": "^26.6.5",
71
71
  "changelogen": "^0.6.2",
72
- "eslint": "^10.10.0",
72
+ "eslint": "^10.12.0",
73
73
  "nuxt": "^4.6.1",
74
74
  "typescript": "^6.0.3",
75
- "vitest": "^5.0.1",
75
+ "vitest": "^5.0.2",
76
76
  "vue-tsc": "^3.3.11"
77
77
  },
78
78
  "scripts": {