payment-kit 1.25.7 → 1.25.10
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/api/src/crons/index.ts +24 -0
- package/api/src/libs/archive/config.ts +254 -0
- package/api/src/libs/archive/executor.ts +729 -0
- package/api/src/libs/archive/index.ts +7 -0
- package/api/src/libs/archive/lock.ts +50 -0
- package/api/src/libs/archive/policy.ts +55 -0
- package/api/src/libs/archive/query.ts +136 -0
- package/api/src/libs/archive/snapshot.ts +291 -0
- package/api/src/libs/archive/store.ts +200 -0
- package/api/src/libs/credit-grant.ts +133 -0
- package/api/src/queues/archive.ts +32 -0
- package/api/src/routes/archive.ts +176 -0
- package/api/src/routes/credit-grants.ts +57 -4
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/payment-stats.ts +167 -20
- package/api/src/store/migrations/20260129-add-grantor-did-index.ts +52 -0
- package/api/src/store/migrations/20260203-archive.ts +12 -0
- package/api/src/store/migrations/20260204-revenue-snapshot.ts +19 -0
- package/api/src/store/models/archive-lock.ts +55 -0
- package/api/src/store/models/archive-metadata.ts +132 -0
- package/api/src/store/models/index.ts +9 -0
- package/api/src/store/models/revenue-snapshot.ts +110 -0
- package/api/tests/libs/archive-config.spec.ts +185 -0
- package/api/tests/libs/archive-executor.spec.ts +678 -0
- package/api/tests/libs/archive-lock.spec.ts +130 -0
- package/api/tests/libs/archive-policy.spec.ts +255 -0
- package/api/tests/libs/archive-query.spec.ts +267 -0
- package/api/tests/libs/archive-store.spec.ts +159 -0
- package/api/tests/libs/credit-grant.spec.ts +184 -0
- package/blocklet.prefs.json +187 -0
- package/blocklet.yml +1 -1
- package/package.json +10 -10
- package/src/locales/en.tsx +4 -0
- package/src/locales/zh.tsx +4 -0
- package/src/pages/admin/overview.tsx +2 -0
- package/vite.config.ts +1 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getArchiveDir,
|
|
5
|
+
getArchiveFilePath,
|
|
6
|
+
listArchiveFiles,
|
|
7
|
+
getFileSize,
|
|
8
|
+
cleanupOldArchiveFiles,
|
|
9
|
+
} from '../../src/libs/archive/store';
|
|
10
|
+
|
|
11
|
+
jest.mock('@blocklet/sdk/lib/config', () => ({
|
|
12
|
+
__esModule: true,
|
|
13
|
+
default: {
|
|
14
|
+
env: {
|
|
15
|
+
dataDir: '/tmp/test-payment-kit',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock('fs');
|
|
21
|
+
jest.mock('../../src/libs/logger', () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
default: {
|
|
24
|
+
info: jest.fn(),
|
|
25
|
+
warn: jest.fn(),
|
|
26
|
+
error: jest.fn(),
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
describe('archive/store', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
jest.clearAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('getArchiveDir', () => {
|
|
36
|
+
it('should create archive directory if it does not exist', () => {
|
|
37
|
+
(fs.existsSync as jest.Mock).mockReturnValue(false);
|
|
38
|
+
(fs.mkdirSync as jest.Mock).mockReturnValue(undefined);
|
|
39
|
+
|
|
40
|
+
const result = getArchiveDir();
|
|
41
|
+
|
|
42
|
+
expect(result).toContain('archive');
|
|
43
|
+
expect(fs.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('archive'), { recursive: true });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should not create directory if it already exists', () => {
|
|
47
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
48
|
+
|
|
49
|
+
getArchiveDir();
|
|
50
|
+
|
|
51
|
+
expect(fs.mkdirSync).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('getArchiveFilePath', () => {
|
|
56
|
+
it('should return full path for archive file', () => {
|
|
57
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
58
|
+
|
|
59
|
+
const result = getArchiveFilePath('archive-2024.db');
|
|
60
|
+
|
|
61
|
+
expect(result).toContain('archive-2024.db');
|
|
62
|
+
expect(result).toContain('archive');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('listArchiveFiles', () => {
|
|
67
|
+
it('should return sorted list of .db files', () => {
|
|
68
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
69
|
+
(fs.readdirSync as jest.Mock).mockReturnValue([
|
|
70
|
+
'archive-2025.db',
|
|
71
|
+
'archive-2023.db',
|
|
72
|
+
'archive-2024.db',
|
|
73
|
+
'some-other-file.txt',
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const result = listArchiveFiles();
|
|
77
|
+
|
|
78
|
+
expect(result).toHaveLength(3);
|
|
79
|
+
expect(result[0]).toContain('archive-2023.db');
|
|
80
|
+
expect(result[1]).toContain('archive-2024.db');
|
|
81
|
+
expect(result[2]).toContain('archive-2025.db');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return empty array when no .db files', () => {
|
|
85
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
86
|
+
(fs.readdirSync as jest.Mock).mockReturnValue(['readme.txt', 'config.json']);
|
|
87
|
+
|
|
88
|
+
const result = listArchiveFiles();
|
|
89
|
+
|
|
90
|
+
expect(result).toHaveLength(0);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('getFileSize', () => {
|
|
95
|
+
it('should return file size in bytes', () => {
|
|
96
|
+
(fs.statSync as jest.Mock).mockReturnValue({ size: 1024 });
|
|
97
|
+
|
|
98
|
+
const result = getFileSize('/tmp/archive.db');
|
|
99
|
+
|
|
100
|
+
expect(result).toBe(1024);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should return 0 when file does not exist', () => {
|
|
104
|
+
(fs.statSync as jest.Mock).mockImplementation(() => {
|
|
105
|
+
throw new Error('ENOENT');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const result = getFileSize('/tmp/nonexistent.db');
|
|
109
|
+
|
|
110
|
+
expect(result).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('cleanupOldArchiveFiles', () => {
|
|
115
|
+
it('should not remove files when under max limit', () => {
|
|
116
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
117
|
+
(fs.readdirSync as jest.Mock).mockReturnValue(['archive-2023.db', 'archive-2024.db']);
|
|
118
|
+
|
|
119
|
+
const result = cleanupOldArchiveFiles(10);
|
|
120
|
+
|
|
121
|
+
expect(result).toHaveLength(0);
|
|
122
|
+
expect(fs.unlinkSync).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should remove oldest files when exceeding max limit', () => {
|
|
126
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
127
|
+
(fs.readdirSync as jest.Mock).mockReturnValue([
|
|
128
|
+
'archive-2021.db',
|
|
129
|
+
'archive-2022.db',
|
|
130
|
+
'archive-2023.db',
|
|
131
|
+
'archive-2024.db',
|
|
132
|
+
]);
|
|
133
|
+
(fs.unlinkSync as jest.Mock).mockReturnValue(undefined);
|
|
134
|
+
|
|
135
|
+
const result = cleanupOldArchiveFiles(2);
|
|
136
|
+
|
|
137
|
+
expect(result).toHaveLength(2);
|
|
138
|
+
expect(result).toContain('archive-2021.db');
|
|
139
|
+
expect(result).toContain('archive-2022.db');
|
|
140
|
+
expect(fs.unlinkSync).toHaveBeenCalledTimes(2);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should continue cleanup even if one file fails to delete', () => {
|
|
144
|
+
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
|
145
|
+
(fs.readdirSync as jest.Mock).mockReturnValue(['archive-2022.db', 'archive-2023.db', 'archive-2024.db']);
|
|
146
|
+
(fs.unlinkSync as jest.Mock)
|
|
147
|
+
.mockImplementationOnce(() => {
|
|
148
|
+
throw new Error('Permission denied');
|
|
149
|
+
})
|
|
150
|
+
.mockReturnValue(undefined);
|
|
151
|
+
|
|
152
|
+
const result = cleanupOldArchiveFiles(1);
|
|
153
|
+
|
|
154
|
+
expect(fs.unlinkSync).toHaveBeenCalledTimes(2);
|
|
155
|
+
expect(result).toHaveLength(1);
|
|
156
|
+
expect(result).toContain('archive-2023.db');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Op } from 'sequelize';
|
|
2
|
+
|
|
3
|
+
import { getCreditGrantStats } from '../../src/libs/credit-grant';
|
|
4
|
+
import { CreditGrant, PaymentCurrency } from '../../src/store/models';
|
|
5
|
+
|
|
6
|
+
jest.mock('../../src/libs/logger', () => ({
|
|
7
|
+
__esModule: true,
|
|
8
|
+
default: {
|
|
9
|
+
info: jest.fn(),
|
|
10
|
+
warn: jest.fn(),
|
|
11
|
+
error: jest.fn(),
|
|
12
|
+
debug: jest.fn(),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('../../src/libs/subscription', () => ({
|
|
17
|
+
getMeterPriceIdsFromSubscription: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock('../../src/store/models', () => ({
|
|
21
|
+
CreditGrant: {
|
|
22
|
+
findAll: jest.fn(),
|
|
23
|
+
},
|
|
24
|
+
PaymentCurrency: {
|
|
25
|
+
findByPk: jest.fn(),
|
|
26
|
+
},
|
|
27
|
+
Customer: {
|
|
28
|
+
findByPk: jest.fn(),
|
|
29
|
+
},
|
|
30
|
+
Subscription: {
|
|
31
|
+
findByPk: jest.fn(),
|
|
32
|
+
},
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
describe('libs/credit-grant.ts', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
jest.clearAllMocks();
|
|
38
|
+
jest.restoreAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('aggregates daily stats and totals', async () => {
|
|
42
|
+
const currencyJson = { id: 'cur_1', name: 'USD', symbol: '$', decimal: 2 };
|
|
43
|
+
(PaymentCurrency.findByPk as jest.Mock).mockResolvedValue({
|
|
44
|
+
...currencyJson,
|
|
45
|
+
toJSON: () => currencyJson,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
(CreditGrant.findAll as jest.Mock).mockResolvedValue([
|
|
49
|
+
{
|
|
50
|
+
amount: '100',
|
|
51
|
+
remaining_amount: '40',
|
|
52
|
+
created_at: new Date('2024-01-01T01:00:00Z'),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
amount: '200',
|
|
56
|
+
remaining_amount: '200',
|
|
57
|
+
created_at: new Date('2024-01-01T10:00:00Z'),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
amount: '50',
|
|
61
|
+
remaining_amount: '10',
|
|
62
|
+
created_at: new Date('2024-01-02T05:00:00Z'),
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const result = await getCreditGrantStats({
|
|
67
|
+
currencyId: 'cur_1',
|
|
68
|
+
startDate: 1704067200,
|
|
69
|
+
endDate: 1704240000,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(PaymentCurrency.findByPk).toHaveBeenCalledWith('cur_1', {
|
|
73
|
+
attributes: ['id', 'name', 'symbol', 'decimal'],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const callArg = (CreditGrant.findAll as jest.Mock).mock.calls[0][0];
|
|
77
|
+
expect(callArg.attributes).toEqual(['amount', 'remaining_amount', 'created_at']);
|
|
78
|
+
expect(callArg.raw).toBe(true);
|
|
79
|
+
|
|
80
|
+
expect(result.stats).toEqual({
|
|
81
|
+
currency_id: 'cur_1',
|
|
82
|
+
currency: currencyJson,
|
|
83
|
+
grant_count: 3,
|
|
84
|
+
total_granted: '350',
|
|
85
|
+
total_remaining: '250',
|
|
86
|
+
total_consumed: '100',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result.daily_stats).toEqual([
|
|
90
|
+
{
|
|
91
|
+
date: '2024-01-01',
|
|
92
|
+
currency_id: 'cur_1',
|
|
93
|
+
grant_count: 2,
|
|
94
|
+
total_granted: '300',
|
|
95
|
+
total_remaining: '240',
|
|
96
|
+
total_consumed: '60',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
date: '2024-01-02',
|
|
100
|
+
currency_id: 'cur_1',
|
|
101
|
+
grant_count: 1,
|
|
102
|
+
total_granted: '50',
|
|
103
|
+
total_remaining: '10',
|
|
104
|
+
total_consumed: '40',
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('groups grants by day using timezone offset', async () => {
|
|
110
|
+
const currencyJson = { id: 'cur_2', name: 'USD', symbol: '$', decimal: 2 };
|
|
111
|
+
(PaymentCurrency.findByPk as jest.Mock).mockResolvedValue({
|
|
112
|
+
...currencyJson,
|
|
113
|
+
toJSON: () => currencyJson,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
(CreditGrant.findAll as jest.Mock).mockResolvedValue([
|
|
117
|
+
{
|
|
118
|
+
amount: '10',
|
|
119
|
+
remaining_amount: '5',
|
|
120
|
+
created_at: new Date('2024-01-01T23:30:00Z'),
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
amount: '20',
|
|
124
|
+
remaining_amount: '0',
|
|
125
|
+
created_at: new Date('2024-01-02T01:00:00Z'),
|
|
126
|
+
},
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
const result = await getCreditGrantStats({
|
|
130
|
+
currencyId: 'cur_2',
|
|
131
|
+
startDate: 1704067200,
|
|
132
|
+
endDate: 1704240000,
|
|
133
|
+
timezoneOffset: 480,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(result.daily_stats).toEqual([
|
|
137
|
+
{
|
|
138
|
+
date: '2024-01-02',
|
|
139
|
+
currency_id: 'cur_2',
|
|
140
|
+
grant_count: 2,
|
|
141
|
+
total_granted: '30',
|
|
142
|
+
total_remaining: '5',
|
|
143
|
+
total_consumed: '25',
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('builds where clause with grantedBy and category filters', async () => {
|
|
149
|
+
const currencyJson = { id: 'cur_3', name: 'USD', symbol: '$', decimal: 2 };
|
|
150
|
+
(PaymentCurrency.findByPk as jest.Mock).mockResolvedValue({
|
|
151
|
+
...currencyJson,
|
|
152
|
+
toJSON: () => currencyJson,
|
|
153
|
+
});
|
|
154
|
+
(CreditGrant.findAll as jest.Mock).mockResolvedValue([]);
|
|
155
|
+
|
|
156
|
+
const startDate = 1704067200;
|
|
157
|
+
const endDate = 1704153600;
|
|
158
|
+
|
|
159
|
+
const result = await getCreditGrantStats({
|
|
160
|
+
currencyId: 'cur_3',
|
|
161
|
+
startDate,
|
|
162
|
+
endDate,
|
|
163
|
+
grantedBy: 'did:example:123',
|
|
164
|
+
category: 'paid',
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const callArg = (CreditGrant.findAll as jest.Mock).mock.calls[0][0];
|
|
168
|
+
expect(callArg.where.currency_id).toBe('cur_3');
|
|
169
|
+
expect(callArg.where['metadata.granted_by']).toBe('did:example:123');
|
|
170
|
+
expect(callArg.where.category).toBe('paid');
|
|
171
|
+
expect(callArg.where.created_at[Op.gte]).toEqual(new Date(startDate * 1000));
|
|
172
|
+
expect(callArg.where.created_at[Op.lte]).toEqual(new Date(endDate * 1000));
|
|
173
|
+
|
|
174
|
+
expect(result.stats).toEqual({
|
|
175
|
+
currency_id: 'cur_3',
|
|
176
|
+
currency: currencyJson,
|
|
177
|
+
grant_count: 0,
|
|
178
|
+
total_granted: '0',
|
|
179
|
+
total_remaining: '0',
|
|
180
|
+
total_consumed: '0',
|
|
181
|
+
});
|
|
182
|
+
expect(result.daily_stats).toEqual([]);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{
|
|
2
|
+
"form": {
|
|
3
|
+
"labelCol": 6,
|
|
4
|
+
"wrapperCol": 12
|
|
5
|
+
},
|
|
6
|
+
"schema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"8ezxt923qyy": {
|
|
10
|
+
"type": "void",
|
|
11
|
+
"x-component": "Card",
|
|
12
|
+
"x-component-props": {
|
|
13
|
+
"title": "Config"
|
|
14
|
+
},
|
|
15
|
+
"x-designable-id": "8ezxt923qyy",
|
|
16
|
+
"properties": {
|
|
17
|
+
"wvny47lybcc": {
|
|
18
|
+
"type": "void",
|
|
19
|
+
"x-component": "FormCollapse",
|
|
20
|
+
"x-component-props": {},
|
|
21
|
+
"name": "Data Retention",
|
|
22
|
+
"x-designable-id": "wvny47lybcc",
|
|
23
|
+
"properties": {
|
|
24
|
+
"5tiydb79qp2": {
|
|
25
|
+
"type": "void",
|
|
26
|
+
"x-component": "FormCollapse.CollapsePanel",
|
|
27
|
+
"x-component-props": {
|
|
28
|
+
"header": "Data Retention"
|
|
29
|
+
},
|
|
30
|
+
"x-designable-id": "5tiydb79qp2",
|
|
31
|
+
"properties": {
|
|
32
|
+
"retentionEnabled": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"title": "Enable Data Retention ",
|
|
35
|
+
"x-decorator": "FormItem",
|
|
36
|
+
"x-component": "Switch",
|
|
37
|
+
"x-validator": [],
|
|
38
|
+
"x-component-props": {},
|
|
39
|
+
"x-decorator-props": {},
|
|
40
|
+
"name": "retentionEnabled",
|
|
41
|
+
"default": false,
|
|
42
|
+
"description": "Archive expired data to reduce database size",
|
|
43
|
+
"x-designable-id": "886b4pe1o1y",
|
|
44
|
+
"x-index": 0
|
|
45
|
+
},
|
|
46
|
+
"scheduleEnabled": {
|
|
47
|
+
"type": "boolean",
|
|
48
|
+
"title": "Enable Scheduled Archive",
|
|
49
|
+
"x-decorator": "FormItem",
|
|
50
|
+
"x-component": "Switch",
|
|
51
|
+
"x-validator": [],
|
|
52
|
+
"x-component-props": {},
|
|
53
|
+
"x-decorator-props": {},
|
|
54
|
+
"description": "Run archive job automatically every day",
|
|
55
|
+
"name": "scheduleEnabled",
|
|
56
|
+
"default": true,
|
|
57
|
+
"x-reactions": {
|
|
58
|
+
"dependencies": [
|
|
59
|
+
{
|
|
60
|
+
"property": "value",
|
|
61
|
+
"type": "boolean",
|
|
62
|
+
"source": "8ezxt923qyy.wvny47lybcc.5tiydb79qp2.retentionEnabled",
|
|
63
|
+
"name": "retentionEnabled"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"fulfill": {
|
|
67
|
+
"state": {
|
|
68
|
+
"visible": "{{$deps.retentionEnabled}}"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"x-designable-id": "53cmu3qg9j3",
|
|
73
|
+
"x-index": 1
|
|
74
|
+
},
|
|
75
|
+
"scheduleHour": {
|
|
76
|
+
"type": "number",
|
|
77
|
+
"title": " Archive Hour (0-23) ",
|
|
78
|
+
"x-decorator": "FormItem",
|
|
79
|
+
"x-component": "NumberPicker",
|
|
80
|
+
"x-validator": "number",
|
|
81
|
+
"x-component-props": {
|
|
82
|
+
"min": 0,
|
|
83
|
+
"max": 23
|
|
84
|
+
},
|
|
85
|
+
"x-decorator-props": {},
|
|
86
|
+
"name": "scheduleHour",
|
|
87
|
+
"default": 2,
|
|
88
|
+
"description": "Hour of day to\nrun the archive job (server Local\ntime)",
|
|
89
|
+
"x-reactions": {
|
|
90
|
+
"dependencies": [
|
|
91
|
+
{
|
|
92
|
+
"property": "value",
|
|
93
|
+
"type": "boolean",
|
|
94
|
+
"source": "8ezxt923qyy.wvny47lybcc.5tiydb79qp2.retentionEnabled",
|
|
95
|
+
"name": "retentionEnabled"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"property": "value",
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"source": "8ezxt923qyy.wvny47lybcc.5tiydb79qp2.scheduleEnabled",
|
|
101
|
+
"name": "scheduleEnabled"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"fulfill": {
|
|
105
|
+
"state": {
|
|
106
|
+
"visible": "{{$deps.retentionEnabled === true && $deps.scheduleEnabled === true}}"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"x-designable-id": "dvdqp2aaszw",
|
|
111
|
+
"x-index": 2
|
|
112
|
+
},
|
|
113
|
+
"batchSize": {
|
|
114
|
+
"type": "number",
|
|
115
|
+
"title": "Batch Size ",
|
|
116
|
+
"x-decorator": "FormItem",
|
|
117
|
+
"x-component": "NumberPicker",
|
|
118
|
+
"x-validator": [],
|
|
119
|
+
"x-component-props": {
|
|
120
|
+
"min": 100
|
|
121
|
+
},
|
|
122
|
+
"x-decorator-props": {},
|
|
123
|
+
"name": "batchSize",
|
|
124
|
+
"default": 500,
|
|
125
|
+
"description": "Number of records to archive per batch. Smaller\nvalues reduce database load",
|
|
126
|
+
"x-reactions": {
|
|
127
|
+
"dependencies": [
|
|
128
|
+
{
|
|
129
|
+
"property": "value",
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"source": "8ezxt923qyy.wvny47lybcc.5tiydb79qp2.retentionEnabled",
|
|
132
|
+
"name": "retentionEnabled"
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
"fulfill": {
|
|
136
|
+
"state": {
|
|
137
|
+
"visible": "{{$deps.retentionEnabled}}"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"x-designable-id": "5mdfl84f6xw",
|
|
142
|
+
"x-index": 3
|
|
143
|
+
},
|
|
144
|
+
"minFreeDiskMB": {
|
|
145
|
+
"type": "number",
|
|
146
|
+
"title": "Min Free Disk Space (MB)",
|
|
147
|
+
"x-decorator": "FormItem",
|
|
148
|
+
"x-component": "NumberPicker",
|
|
149
|
+
"x-validator": [],
|
|
150
|
+
"x-component-props": {
|
|
151
|
+
"min": 100
|
|
152
|
+
},
|
|
153
|
+
"x-decorator-props": {},
|
|
154
|
+
"name": "minFreeDiskMB",
|
|
155
|
+
"default": 1000,
|
|
156
|
+
"description": "Stop archiving when free disk space falls below this threshold (default 1GB)",
|
|
157
|
+
"x-reactions": {
|
|
158
|
+
"dependencies": [
|
|
159
|
+
{
|
|
160
|
+
"property": "value",
|
|
161
|
+
"type": "boolean",
|
|
162
|
+
"source": "8ezxt923qyy.wvny47lybcc.5tiydb79qp2.retentionEnabled",
|
|
163
|
+
"name": "retentionEnabled"
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"fulfill": {
|
|
167
|
+
"state": {
|
|
168
|
+
"visible": "{{$deps.retentionEnabled}}"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"x-designable-id": "ud3govolh1e",
|
|
173
|
+
"x-index": 4
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"x-index": 0
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"x-index": 0
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"x-index": 0
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"x-designable-id": "yfw4ta3wi0l"
|
|
186
|
+
}
|
|
187
|
+
}
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.10",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"prelint": "npm run types",
|
|
@@ -47,23 +47,23 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@abtnode/cron": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
49
49
|
"@arcblock/did": "^1.28.5",
|
|
50
|
-
"@arcblock/did-connect-react": "^3.
|
|
50
|
+
"@arcblock/did-connect-react": "^3.5.1",
|
|
51
51
|
"@arcblock/did-connect-storage-nedb": "^1.8.0",
|
|
52
52
|
"@arcblock/did-util": "^1.28.5",
|
|
53
53
|
"@arcblock/jwt": "^1.28.5",
|
|
54
|
-
"@arcblock/react-hooks": "^3.
|
|
55
|
-
"@arcblock/ux": "^3.
|
|
54
|
+
"@arcblock/react-hooks": "^3.5.1",
|
|
55
|
+
"@arcblock/ux": "^3.5.1",
|
|
56
56
|
"@arcblock/validator": "^1.28.5",
|
|
57
57
|
"@arcblock/vc": "^1.28.5",
|
|
58
58
|
"@blocklet/did-space-js": "^1.2.15",
|
|
59
59
|
"@blocklet/error": "^0.3.5",
|
|
60
60
|
"@blocklet/js-sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
61
61
|
"@blocklet/logger": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
62
|
-
"@blocklet/payment-broker-client": "1.25.
|
|
63
|
-
"@blocklet/payment-react": "1.25.
|
|
64
|
-
"@blocklet/payment-vendor": "1.25.
|
|
62
|
+
"@blocklet/payment-broker-client": "1.25.10",
|
|
63
|
+
"@blocklet/payment-react": "1.25.10",
|
|
64
|
+
"@blocklet/payment-vendor": "1.25.10",
|
|
65
65
|
"@blocklet/sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
66
|
-
"@blocklet/ui-react": "^3.
|
|
66
|
+
"@blocklet/ui-react": "^3.5.1",
|
|
67
67
|
"@blocklet/uploader": "^0.3.19",
|
|
68
68
|
"@blocklet/xss": "^0.3.16",
|
|
69
69
|
"@mui/icons-material": "^7.1.2",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"devDependencies": {
|
|
133
133
|
"@abtnode/types": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
134
134
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
135
|
-
"@blocklet/payment-types": "1.25.
|
|
135
|
+
"@blocklet/payment-types": "1.25.10",
|
|
136
136
|
"@types/cookie-parser": "^1.4.9",
|
|
137
137
|
"@types/cors": "^2.8.19",
|
|
138
138
|
"@types/debug": "^4.1.12",
|
|
@@ -179,5 +179,5 @@
|
|
|
179
179
|
"parser": "typescript"
|
|
180
180
|
}
|
|
181
181
|
},
|
|
182
|
-
"gitHead": "
|
|
182
|
+
"gitHead": "36417122ec9a0cfd1e289b4ca21d33f1b5d87a9a"
|
|
183
183
|
}
|
package/src/locales/en.tsx
CHANGED
|
@@ -164,6 +164,10 @@ export default flat({
|
|
|
164
164
|
title: 'Total Income',
|
|
165
165
|
subtitle: "User's actual payment",
|
|
166
166
|
},
|
|
167
|
+
refundAmount: {
|
|
168
|
+
title: 'Refund Amount',
|
|
169
|
+
subtitle: 'Total refunds issued',
|
|
170
|
+
},
|
|
167
171
|
costOfGoods: {
|
|
168
172
|
title: 'Cost of Goods',
|
|
169
173
|
subtitle: 'Supplier share',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -47,7 +47,9 @@ import DateRangePicker from '../../components/date-range-picker';
|
|
|
47
47
|
|
|
48
48
|
type TRevenueStat = {
|
|
49
49
|
totalRevenue: string;
|
|
50
|
+
refundAmount: string;
|
|
50
51
|
promotionCost: string;
|
|
52
|
+
creditGrantCost: string;
|
|
51
53
|
vendorCost: string;
|
|
52
54
|
taxedRevenue: string;
|
|
53
55
|
netRevenue: string;
|
package/vite.config.ts
CHANGED
|
@@ -54,6 +54,7 @@ export default defineConfig(({ mode }) => {
|
|
|
54
54
|
react({ babel: { plugins: isProduction ? [['lodash']] : [] } }),
|
|
55
55
|
createBlockletPlugin({
|
|
56
56
|
disableDynamicAssetHost: false,
|
|
57
|
+
chunkSizeLimit: 3500,
|
|
57
58
|
}),
|
|
58
59
|
svgr(),
|
|
59
60
|
process.env.ANALYZE && visualizer({ open: true, gzipSize: true, brotliSize: true }),
|