cabloy 5.1.123 → 5.1.125
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/.cabloy-version +1 -1
- package/CHANGELOG.md +12 -0
- package/cabloy-docs/.vitepress/config.mjs +1 -0
- package/cabloy-docs/ai/playbook-technical-blog-authoring.md +158 -0
- package/package.json +1 -1
- package/vona/pnpm-lock.yaml +176 -16
- package/vona/pnpm-workspace.yaml +1 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/package.json +5 -5
- package/zova/pnpm-lock.yaml +25 -25
- package/vona/src/suite-vendor/a-pay/modules/a-pay/test/outbox.test.ts +0 -209
- package/vona/src/suite-vendor/a-pay/modules/a-pay/test/paymentSession.test.ts +0 -153
- package/vona/src/suite-vendor/a-pay/modules/a-pay/test/webhook.test.ts +0 -278
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/test/payMock.test.ts +0 -141
- /package/vona/patches/{zova-core@5.1.79.patch → zova-core@5.1.81.patch} +0 -0
package/zova/pnpm-lock.yaml
CHANGED
|
@@ -77,7 +77,7 @@ importers:
|
|
|
77
77
|
version: 10.5.2(postcss@8.5.15)
|
|
78
78
|
axios:
|
|
79
79
|
specifier: ^1.18.1
|
|
80
|
-
version: 1.18.1
|
|
80
|
+
version: 1.18.1
|
|
81
81
|
compression:
|
|
82
82
|
specifier: ^1.8.1
|
|
83
83
|
version: 1.8.1
|
|
@@ -136,7 +136,7 @@ importers:
|
|
|
136
136
|
specifier: workspace:^
|
|
137
137
|
version: link:packages-zova/zova
|
|
138
138
|
zova-jsx:
|
|
139
|
-
specifier: ^1.1.
|
|
139
|
+
specifier: ^1.1.81
|
|
140
140
|
version: link:packages-utils/zova-jsx
|
|
141
141
|
zova-module-a-api:
|
|
142
142
|
specifier: workspace:^
|
|
@@ -354,7 +354,7 @@ importers:
|
|
|
354
354
|
devDependencies:
|
|
355
355
|
'@cabloy/cli':
|
|
356
356
|
specifier: ^3.1.28
|
|
357
|
-
version: 3.1.28(
|
|
357
|
+
version: 3.1.28(vue@3.5.38(typescript@5.9.3))
|
|
358
358
|
'@cabloy/lint':
|
|
359
359
|
specifier: ^5.1.27
|
|
360
360
|
version: 5.1.30(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/rule-tester@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.62.1(typescript@5.9.3))(@typescript-eslint/utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.6.0(jiti@2.7.0))(oxfmt@0.45.0)(oxlint@1.72.0)(supports-color@10.2.2)(typescript@5.9.3)(vue-eslint-parser@10.4.1(eslint@10.6.0(jiti@2.7.0)))
|
|
@@ -429,7 +429,7 @@ importers:
|
|
|
429
429
|
dependencies:
|
|
430
430
|
'@cabloy/cli':
|
|
431
431
|
specifier: ^3.1.28
|
|
432
|
-
version: 3.1.28(
|
|
432
|
+
version: 3.1.28(vue@3.5.38(typescript@5.9.3))
|
|
433
433
|
'@cabloy/process-helper':
|
|
434
434
|
specifier: ^3.1.8
|
|
435
435
|
version: 3.1.8
|
|
@@ -440,7 +440,7 @@ importers:
|
|
|
440
440
|
specifier: ^7.6.2
|
|
441
441
|
version: 7.8.5
|
|
442
442
|
zova-cli-set-front:
|
|
443
|
-
specifier: ^1.2.
|
|
443
|
+
specifier: ^1.2.99
|
|
444
444
|
version: link:../cli-set-front
|
|
445
445
|
devDependencies:
|
|
446
446
|
clean-package:
|
|
@@ -463,7 +463,7 @@ importers:
|
|
|
463
463
|
version: 7.29.7(@babel/core@7.29.7)
|
|
464
464
|
'@cabloy/cli':
|
|
465
465
|
specifier: ^3.1.28
|
|
466
|
-
version: 3.1.28(
|
|
466
|
+
version: 3.1.28(vue@3.5.38(typescript@5.9.3))
|
|
467
467
|
'@cabloy/extend':
|
|
468
468
|
specifier: ^3.2.8
|
|
469
469
|
version: 3.2.8
|
|
@@ -717,7 +717,7 @@ importers:
|
|
|
717
717
|
specifier: ^3.5.38
|
|
718
718
|
version: 3.5.38(typescript@5.9.3)
|
|
719
719
|
zova-core:
|
|
720
|
-
specifier: ^5.1.
|
|
720
|
+
specifier: ^5.1.81
|
|
721
721
|
version: link:../../packages-zova/zova-core
|
|
722
722
|
devDependencies:
|
|
723
723
|
clean-package:
|
|
@@ -858,10 +858,10 @@ importers:
|
|
|
858
858
|
packages-zova/zova:
|
|
859
859
|
dependencies:
|
|
860
860
|
zova-core:
|
|
861
|
-
specifier: ^5.1.
|
|
861
|
+
specifier: ^5.1.81
|
|
862
862
|
version: link:../zova-core
|
|
863
863
|
zova-suite-a-zova:
|
|
864
|
-
specifier: ^5.1.
|
|
864
|
+
specifier: ^5.1.143
|
|
865
865
|
version: link:../../src/suite-vendor/a-zova
|
|
866
866
|
devDependencies:
|
|
867
867
|
clean-package:
|
|
@@ -974,16 +974,16 @@ importers:
|
|
|
974
974
|
src/suite-vendor/a-pay:
|
|
975
975
|
dependencies:
|
|
976
976
|
zova-module-a-pay:
|
|
977
|
-
specifier: ^5.0.
|
|
977
|
+
specifier: ^5.0.2
|
|
978
978
|
version: link:modules/a-pay
|
|
979
979
|
zova-module-pay-mock:
|
|
980
|
-
specifier: ^5.0.
|
|
980
|
+
specifier: ^5.0.2
|
|
981
981
|
version: link:modules/pay-mock
|
|
982
982
|
zova-module-pay-paypal:
|
|
983
|
-
specifier: ^5.0.
|
|
983
|
+
specifier: ^5.0.1
|
|
984
984
|
version: link:modules/pay-paypal
|
|
985
985
|
zova-module-pay-stripe:
|
|
986
|
-
specifier: ^5.0.
|
|
986
|
+
specifier: ^5.0.1
|
|
987
987
|
version: link:modules/pay-stripe
|
|
988
988
|
|
|
989
989
|
src/suite-vendor/a-pay/modules/a-pay:
|
|
@@ -1097,7 +1097,7 @@ importers:
|
|
|
1097
1097
|
specifier: ^5.1.36
|
|
1098
1098
|
version: link:modules/a-zod
|
|
1099
1099
|
zova-module-a-zova:
|
|
1100
|
-
specifier: ^5.1.
|
|
1100
|
+
specifier: ^5.1.96
|
|
1101
1101
|
version: link:modules/a-zova
|
|
1102
1102
|
|
|
1103
1103
|
src/suite-vendor/a-zova/modules/a-api:
|
|
@@ -1171,7 +1171,7 @@ importers:
|
|
|
1171
1171
|
dependencies:
|
|
1172
1172
|
axios:
|
|
1173
1173
|
specifier: ^1.18.1
|
|
1174
|
-
version: 1.18.1
|
|
1174
|
+
version: 1.18.1
|
|
1175
1175
|
devDependencies:
|
|
1176
1176
|
clean-package:
|
|
1177
1177
|
specifier: ^2.2.0
|
|
@@ -1439,12 +1439,12 @@ importers:
|
|
|
1439
1439
|
specifier: ^3.7.2
|
|
1440
1440
|
version: 3.7.2
|
|
1441
1441
|
zova-jsx:
|
|
1442
|
-
specifier: ^1.1.
|
|
1442
|
+
specifier: ^1.1.81
|
|
1443
1443
|
version: link:../../../../../packages-utils/zova-jsx
|
|
1444
1444
|
devDependencies:
|
|
1445
1445
|
'@cabloy/cli':
|
|
1446
1446
|
specifier: ^3.1.28
|
|
1447
|
-
version: 3.1.28(
|
|
1447
|
+
version: 3.1.28(vue@3.5.38(typescript@5.9.3))
|
|
1448
1448
|
'@types/luxon':
|
|
1449
1449
|
specifier: ^3.7.1
|
|
1450
1450
|
version: 3.7.2
|
|
@@ -8419,7 +8419,7 @@ snapshots:
|
|
|
8419
8419
|
|
|
8420
8420
|
'@bufbuild/protobuf@2.12.1': {}
|
|
8421
8421
|
|
|
8422
|
-
'@cabloy/cli@3.1.28(
|
|
8422
|
+
'@cabloy/cli@3.1.28(vue@3.5.38(typescript@5.9.3))':
|
|
8423
8423
|
dependencies:
|
|
8424
8424
|
'@babel/parser': 7.29.7
|
|
8425
8425
|
'@cabloy/module-glob': 5.3.17
|
|
@@ -8428,7 +8428,7 @@ snapshots:
|
|
|
8428
8428
|
'@cabloy/utils': 2.1.26
|
|
8429
8429
|
'@cabloy/word-utils': link:packages-utils/word-utils
|
|
8430
8430
|
'@npmcli/config': 10.11.0
|
|
8431
|
-
'@zhennann/common-bin': 4.0.1
|
|
8431
|
+
'@zhennann/common-bin': 4.0.1
|
|
8432
8432
|
'@zhennann/ejs': 3.0.1
|
|
8433
8433
|
boxen: 4.2.0
|
|
8434
8434
|
chalk: 3.0.0
|
|
@@ -10454,7 +10454,7 @@ snapshots:
|
|
|
10454
10454
|
|
|
10455
10455
|
'@vue/shared@3.5.13': {}
|
|
10456
10456
|
|
|
10457
|
-
'@zhennann/common-bin@4.0.1
|
|
10457
|
+
'@zhennann/common-bin@4.0.1':
|
|
10458
10458
|
dependencies:
|
|
10459
10459
|
chalk: 4.1.2
|
|
10460
10460
|
change-case: 4.1.2
|
|
@@ -10488,7 +10488,7 @@ snapshots:
|
|
|
10488
10488
|
|
|
10489
10489
|
acorn@8.17.0: {}
|
|
10490
10490
|
|
|
10491
|
-
agent-base@6.0.2
|
|
10491
|
+
agent-base@6.0.2:
|
|
10492
10492
|
dependencies:
|
|
10493
10493
|
debug: 4.4.3(supports-color@10.2.2)
|
|
10494
10494
|
transitivePeerDependencies:
|
|
@@ -10600,11 +10600,11 @@ snapshots:
|
|
|
10600
10600
|
dependencies:
|
|
10601
10601
|
possible-typed-array-names: 1.1.0
|
|
10602
10602
|
|
|
10603
|
-
axios@1.18.1
|
|
10603
|
+
axios@1.18.1:
|
|
10604
10604
|
dependencies:
|
|
10605
10605
|
follow-redirects: 1.16.0
|
|
10606
10606
|
form-data: 4.0.6
|
|
10607
|
-
https-proxy-agent: 5.0.1
|
|
10607
|
+
https-proxy-agent: 5.0.1
|
|
10608
10608
|
proxy-from-env: 2.1.0
|
|
10609
10609
|
transitivePeerDependencies:
|
|
10610
10610
|
- debug
|
|
@@ -12015,9 +12015,9 @@ snapshots:
|
|
|
12015
12015
|
statuses: 2.0.2
|
|
12016
12016
|
toidentifier: 1.0.1
|
|
12017
12017
|
|
|
12018
|
-
https-proxy-agent@5.0.1
|
|
12018
|
+
https-proxy-agent@5.0.1:
|
|
12019
12019
|
dependencies:
|
|
12020
|
-
agent-base: 6.0.2
|
|
12020
|
+
agent-base: 6.0.2
|
|
12021
12021
|
debug: 4.4.3(supports-color@10.2.2)
|
|
12022
12022
|
transitivePeerDependencies:
|
|
12023
12023
|
- supports-color
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { describe, it } from 'node:test';
|
|
4
|
-
import { app } from 'vona-mock';
|
|
5
|
-
|
|
6
|
-
import type { IPaymentOutcomeEvent } from '../src/types/payment.ts';
|
|
7
|
-
|
|
8
|
-
interface IFixture {
|
|
9
|
-
userId?: number;
|
|
10
|
-
paymentSessionId?: number;
|
|
11
|
-
outboxEventIds: number[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function createFixture(suffix: string): Promise<IFixture> {
|
|
15
|
-
const scope = app.scope('a-pay');
|
|
16
|
-
const user = await app.bean.user.register({ name: `pay-outbox-${suffix}` }, true);
|
|
17
|
-
const session = await scope.service.paymentSession.create({
|
|
18
|
-
userId: user.id,
|
|
19
|
-
payScene: 'commerce-payment:commerceOrder',
|
|
20
|
-
businessReference: `business-${suffix}`,
|
|
21
|
-
amountMinor: 1299,
|
|
22
|
-
currency: 'USD',
|
|
23
|
-
correlationId: `payment-${suffix}`,
|
|
24
|
-
});
|
|
25
|
-
return { userId: user.id as number, paymentSessionId: session.id as number, outboxEventIds: [] };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function createOutcomePayload(fixture: IFixture): IPaymentOutcomeEvent {
|
|
29
|
-
return {
|
|
30
|
-
eventId: randomUUID(),
|
|
31
|
-
paymentSessionId: fixture.paymentSessionId!,
|
|
32
|
-
businessReference: 'outbox-test',
|
|
33
|
-
providerName: 'test-provider',
|
|
34
|
-
state: 'succeeded',
|
|
35
|
-
amountMinor: 1299,
|
|
36
|
-
currency: 'USD',
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function insertOutbox(
|
|
41
|
-
fixture: IFixture,
|
|
42
|
-
overrides?: Partial<{
|
|
43
|
-
eventType: 'payment.outcome.v1';
|
|
44
|
-
state: 'pending' | 'claimed';
|
|
45
|
-
attemptCount: number;
|
|
46
|
-
nextAttemptAt: Date;
|
|
47
|
-
claimToken: string;
|
|
48
|
-
claimedAt: Date;
|
|
49
|
-
claimExpiresAt: Date;
|
|
50
|
-
}>,
|
|
51
|
-
) {
|
|
52
|
-
const event = await app.scope('a-pay').model.outboxEvent.insert({
|
|
53
|
-
eventType: overrides?.eventType ?? 'payment.outcome.v1',
|
|
54
|
-
paymentSessionId: fixture.paymentSessionId!,
|
|
55
|
-
payload: createOutcomePayload(fixture),
|
|
56
|
-
state: overrides?.state ?? 'pending',
|
|
57
|
-
attemptCount: overrides?.attemptCount ?? 0,
|
|
58
|
-
nextAttemptAt: overrides?.nextAttemptAt ?? new Date(Date.now() - 1_000),
|
|
59
|
-
claimToken: overrides?.claimToken,
|
|
60
|
-
claimedAt: overrides?.claimedAt,
|
|
61
|
-
claimExpiresAt: overrides?.claimExpiresAt,
|
|
62
|
-
});
|
|
63
|
-
fixture.outboxEventIds.push(event.id as number);
|
|
64
|
-
return event;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function cleanup(fixture: IFixture) {
|
|
68
|
-
const scope = app.scope('a-pay');
|
|
69
|
-
for (const id of fixture.outboxEventIds) await scope.model.outboxEvent.delete({ id });
|
|
70
|
-
if (fixture.paymentSessionId !== undefined) {
|
|
71
|
-
await scope.model.paymentAudit.delete({ paymentSessionId: fixture.paymentSessionId });
|
|
72
|
-
await scope.model.webhookInbox.delete({ paymentSessionId: fixture.paymentSessionId });
|
|
73
|
-
await scope.model.paymentSession.delete({ id: fixture.paymentSessionId });
|
|
74
|
-
}
|
|
75
|
-
if (fixture.userId !== undefined) {
|
|
76
|
-
await app.scope('home-user').model.roleUser.delete({ userId: fixture.userId });
|
|
77
|
-
await app.bean.user.removeById(fixture.userId);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
describe('outbox.test.ts', { concurrency: false }, () => {
|
|
82
|
-
it('claims, backs off, and exhausts durable delivery attempts', async () => {
|
|
83
|
-
await app.bean.executor.mockCtx(async () => {
|
|
84
|
-
const fixture = await createFixture(randomUUID().slice(0, 12));
|
|
85
|
-
try {
|
|
86
|
-
const event = await insertOutbox(fixture);
|
|
87
|
-
const scope = app.scope('a-pay');
|
|
88
|
-
const first = await scope.service.outbox.claim(event.id);
|
|
89
|
-
assert.equal(first?.attemptCount, 1);
|
|
90
|
-
assert.ok(first?.claimToken);
|
|
91
|
-
const releaseStart = Date.now();
|
|
92
|
-
await scope.service.outbox.release(
|
|
93
|
-
event.id,
|
|
94
|
-
first!.claimToken!,
|
|
95
|
-
new Error('first failure'),
|
|
96
|
-
);
|
|
97
|
-
let persisted = await scope.model.outboxEvent.getById(event.id);
|
|
98
|
-
assert.equal(persisted?.state, 'pending');
|
|
99
|
-
assert.equal(persisted?.errorSummary, 'first failure');
|
|
100
|
-
assert.ok((persisted?.nextAttemptAt?.getTime() ?? 0) >= releaseStart + 900);
|
|
101
|
-
assert.equal(await scope.service.outbox.claim(event.id), undefined);
|
|
102
|
-
await scope.model.outboxEvent.updateById(event.id, {
|
|
103
|
-
nextAttemptAt: new Date(Date.now() - 1),
|
|
104
|
-
});
|
|
105
|
-
const second = await scope.service.outbox.claim(event.id);
|
|
106
|
-
assert.equal(second?.attemptCount, 2);
|
|
107
|
-
const secondReleaseStart = Date.now();
|
|
108
|
-
await scope.service.outbox.release(
|
|
109
|
-
event.id,
|
|
110
|
-
second!.claimToken!,
|
|
111
|
-
new Error('second failure'),
|
|
112
|
-
);
|
|
113
|
-
persisted = await scope.model.outboxEvent.getById(event.id);
|
|
114
|
-
assert.ok((persisted?.nextAttemptAt?.getTime() ?? 0) >= secondReleaseStart + 1_900);
|
|
115
|
-
|
|
116
|
-
const exhausted = await insertOutbox(fixture, { attemptCount: 10 });
|
|
117
|
-
assert.equal(await scope.service.outbox.claim(exhausted.id), undefined);
|
|
118
|
-
assert.equal((await scope.model.outboxEvent.getById(exhausted.id))?.state, 'failed');
|
|
119
|
-
} finally {
|
|
120
|
-
await cleanup(fixture);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('retries a dispatch when the persisted payment scene is unavailable', async () => {
|
|
126
|
-
await app.bean.executor.mockCtx(async () => {
|
|
127
|
-
const fixture = await createFixture(randomUUID().slice(0, 12));
|
|
128
|
-
try {
|
|
129
|
-
const scope = app.scope('a-pay');
|
|
130
|
-
await scope.model.paymentSession.updateById(fixture.paymentSessionId!, {
|
|
131
|
-
payScene: 'test-payment',
|
|
132
|
-
});
|
|
133
|
-
const event = await insertOutbox(fixture);
|
|
134
|
-
await scope.queue.outboxDispatch.pushAsync({ outboxEventId: event.id });
|
|
135
|
-
const persisted = await scope.model.outboxEvent.getById(event.id);
|
|
136
|
-
assert.equal(persisted?.state, 'pending');
|
|
137
|
-
assert.equal(persisted?.attemptCount, 1);
|
|
138
|
-
assert.match(persisted?.errorSummary ?? '', /payment scene not found/);
|
|
139
|
-
} finally {
|
|
140
|
-
await cleanup(fixture);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('reclaims expired leases and fences stale claim tokens', async () => {
|
|
146
|
-
await app.bean.executor.mockCtx(async () => {
|
|
147
|
-
const fixture = await createFixture(randomUUID().slice(0, 12));
|
|
148
|
-
try {
|
|
149
|
-
const event = await insertOutbox(fixture);
|
|
150
|
-
const scope = app.scope('a-pay');
|
|
151
|
-
const first = await scope.service.outbox.claim(event.id);
|
|
152
|
-
await scope.model.outboxEvent.updateById(event.id, {
|
|
153
|
-
claimExpiresAt: new Date(Date.now() - 1),
|
|
154
|
-
});
|
|
155
|
-
const second = await scope.service.outbox.claim(event.id);
|
|
156
|
-
assert.ok(second?.claimToken);
|
|
157
|
-
assert.notEqual(second?.claimToken, first?.claimToken);
|
|
158
|
-
assert.equal(second?.attemptCount, 2);
|
|
159
|
-
assert.equal(
|
|
160
|
-
await scope.service.outbox.markDispatched(event.id, first!.claimToken!),
|
|
161
|
-
undefined,
|
|
162
|
-
);
|
|
163
|
-
assert.equal(
|
|
164
|
-
await scope.service.outbox.release(event.id, first!.claimToken!, new Error('stale')),
|
|
165
|
-
undefined,
|
|
166
|
-
);
|
|
167
|
-
assert.equal(
|
|
168
|
-
(await scope.model.outboxEvent.getById(event.id))?.claimToken,
|
|
169
|
-
second?.claimToken,
|
|
170
|
-
);
|
|
171
|
-
await scope.service.outbox.markDispatched(event.id, second!.claimToken!);
|
|
172
|
-
const persisted = await scope.model.outboxEvent.getById(event.id);
|
|
173
|
-
assert.equal(persisted?.state, 'dispatched');
|
|
174
|
-
assert.equal(persisted?.claimToken, undefined);
|
|
175
|
-
} finally {
|
|
176
|
-
await cleanup(fixture);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('rolls back enqueued durable rows with the enclosing transaction', async () => {
|
|
182
|
-
await app.bean.executor.mockCtx(async () => {
|
|
183
|
-
const fixture = await createFixture(randomUUID().slice(0, 12));
|
|
184
|
-
try {
|
|
185
|
-
const scope = app.scope('a-pay');
|
|
186
|
-
const eventId = await assert.rejects(
|
|
187
|
-
app.ctx.db.transaction.begin(async () => {
|
|
188
|
-
const event = await scope.service.outbox.enqueue(
|
|
189
|
-
fixture.paymentSessionId!,
|
|
190
|
-
'payment.outcome.v1',
|
|
191
|
-
createOutcomePayload(fixture),
|
|
192
|
-
);
|
|
193
|
-
fixture.outboxEventIds.push(event.id as number);
|
|
194
|
-
throw new Error(String(event.id));
|
|
195
|
-
}),
|
|
196
|
-
/\d+/,
|
|
197
|
-
);
|
|
198
|
-
assert.equal(eventId, undefined);
|
|
199
|
-
const events = await scope.model.outboxEvent.select({
|
|
200
|
-
where: { paymentSessionId: fixture.paymentSessionId },
|
|
201
|
-
});
|
|
202
|
-
assert.deepEqual(events, []);
|
|
203
|
-
fixture.outboxEventIds = [];
|
|
204
|
-
} finally {
|
|
205
|
-
await cleanup(fixture);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
});
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { describe, it } from 'node:test';
|
|
4
|
-
import { app } from 'vona-mock';
|
|
5
|
-
|
|
6
|
-
interface IFixture {
|
|
7
|
-
userId?: number;
|
|
8
|
-
paymentSessionId?: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function createFixture(expiresAt: Date): Promise<IFixture> {
|
|
12
|
-
const scope = app.scope('a-pay');
|
|
13
|
-
const suffix = randomUUID().slice(0, 12);
|
|
14
|
-
const user = await app.bean.user.register({ name: `payment-session-${suffix}` }, true);
|
|
15
|
-
const session = await scope.service.paymentSession.create({
|
|
16
|
-
userId: user.id,
|
|
17
|
-
payScene: 'commerce-payment:commerceOrder',
|
|
18
|
-
businessReference: `business-${suffix}`,
|
|
19
|
-
amountMinor: 1299,
|
|
20
|
-
currency: 'USD',
|
|
21
|
-
correlationId: `payment-${suffix}`,
|
|
22
|
-
});
|
|
23
|
-
await scope.model.paymentSession.updateById(session.id, { expiresAt });
|
|
24
|
-
return { userId: user.id as number, paymentSessionId: session.id as number };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function cleanup(fixture: IFixture) {
|
|
28
|
-
const scope = app.scope('a-pay');
|
|
29
|
-
if (fixture.paymentSessionId !== undefined) {
|
|
30
|
-
await scope.model.paymentAudit.delete({ paymentSessionId: fixture.paymentSessionId });
|
|
31
|
-
await scope.model.paymentSession.delete({ id: fixture.paymentSessionId });
|
|
32
|
-
}
|
|
33
|
-
if (fixture.userId !== undefined) {
|
|
34
|
-
await app.scope('home-user').model.roleUser.delete({ userId: fixture.userId });
|
|
35
|
-
await app.bean.user.removeById(fixture.userId);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe('paymentSession.test.ts', { concurrency: false }, () => {
|
|
40
|
-
it('derives provider facts and expiry from the payment scene', async () => {
|
|
41
|
-
await app.bean.executor.mockCtx(async () => {
|
|
42
|
-
const scope = app.scope('a-pay');
|
|
43
|
-
const suffix = randomUUID().slice(0, 12);
|
|
44
|
-
const user = await app.bean.user.register({ name: `payment-session-${suffix}` }, true);
|
|
45
|
-
const fixture: IFixture = { userId: user.id as number };
|
|
46
|
-
try {
|
|
47
|
-
const createdAt = Date.now();
|
|
48
|
-
const session = await scope.service.paymentSession.create({
|
|
49
|
-
userId: user.id,
|
|
50
|
-
payScene: 'commerce-payment:commerceOrder',
|
|
51
|
-
businessReference: `business-${suffix}`,
|
|
52
|
-
amountMinor: 1299,
|
|
53
|
-
currency: 'USD',
|
|
54
|
-
correlationId: `payment-${suffix}`,
|
|
55
|
-
});
|
|
56
|
-
fixture.paymentSessionId = session.id as number;
|
|
57
|
-
assert.equal(session.providerName, 'pay-mock:mock');
|
|
58
|
-
assert.equal(session.clientName, 'default');
|
|
59
|
-
assert.equal(session.environment, 'sandbox');
|
|
60
|
-
assert.ok(session.expiresAt.getTime() >= createdAt + 30 * 60 * 1000);
|
|
61
|
-
assert.ok(session.expiresAt.getTime() <= Date.now() + 30 * 60 * 1000);
|
|
62
|
-
} finally {
|
|
63
|
-
await cleanup(fixture);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('rejects a currency outside the payment scene policy', async () => {
|
|
69
|
-
await app.bean.executor.mockCtx(async () => {
|
|
70
|
-
const scope = app.scope('a-pay');
|
|
71
|
-
const suffix = randomUUID().slice(0, 12);
|
|
72
|
-
const user = await app.bean.user.register({ name: `payment-session-${suffix}` }, true);
|
|
73
|
-
const fixture: IFixture = { userId: user.id as number };
|
|
74
|
-
try {
|
|
75
|
-
await assert.rejects(
|
|
76
|
-
scope.service.paymentSession.create({
|
|
77
|
-
userId: user.id,
|
|
78
|
-
payScene: 'commerce-payment:commerceOrder',
|
|
79
|
-
businessReference: `business-${suffix}`,
|
|
80
|
-
amountMinor: 1299,
|
|
81
|
-
currency: 'EUR',
|
|
82
|
-
correlationId: `payment-${suffix}`,
|
|
83
|
-
}),
|
|
84
|
-
{ status: 422 },
|
|
85
|
-
);
|
|
86
|
-
assert.deepEqual(
|
|
87
|
-
await scope.model.paymentSession.select({
|
|
88
|
-
where: { correlationId: `payment-${suffix}` },
|
|
89
|
-
}),
|
|
90
|
-
[],
|
|
91
|
-
);
|
|
92
|
-
} finally {
|
|
93
|
-
await cleanup(fixture);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('starts only an unexpired created payment session', async () => {
|
|
99
|
-
await app.bean.executor.mockCtx(async () => {
|
|
100
|
-
const fixture = await createFixture(new Date(Date.now() + 60_000));
|
|
101
|
-
try {
|
|
102
|
-
const scope = app.scope('a-pay');
|
|
103
|
-
const session = await scope.service.paymentSession.start(fixture.paymentSessionId!);
|
|
104
|
-
assert.equal(session.state, 'requires_action');
|
|
105
|
-
await assert.rejects(scope.service.paymentSession.start(fixture.paymentSessionId!), {
|
|
106
|
-
status: 409,
|
|
107
|
-
});
|
|
108
|
-
} finally {
|
|
109
|
-
await cleanup(fixture);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('does not start an expired payment session', async () => {
|
|
115
|
-
await app.bean.executor.mockCtx(async () => {
|
|
116
|
-
const fixture = await createFixture(new Date(Date.now() - 1));
|
|
117
|
-
try {
|
|
118
|
-
const scope = app.scope('a-pay');
|
|
119
|
-
await assert.rejects(scope.service.paymentSession.start(fixture.paymentSessionId!), {
|
|
120
|
-
status: 409,
|
|
121
|
-
});
|
|
122
|
-
assert.equal(
|
|
123
|
-
(await scope.model.paymentSession.getById(fixture.paymentSessionId!))?.state,
|
|
124
|
-
'created',
|
|
125
|
-
);
|
|
126
|
-
} finally {
|
|
127
|
-
await cleanup(fixture);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('allows only one competing start to call the provider', async () => {
|
|
133
|
-
const fixture = await app.bean.executor.mockCtx(async () => {
|
|
134
|
-
return await createFixture(new Date(Date.now() + 60_000));
|
|
135
|
-
});
|
|
136
|
-
try {
|
|
137
|
-
const results = await Promise.allSettled([
|
|
138
|
-
app.bean.executor.mockCtx(async () => {
|
|
139
|
-
return await app.scope('a-pay').service.paymentSession.start(fixture.paymentSessionId!);
|
|
140
|
-
}),
|
|
141
|
-
app.bean.executor.mockCtx(async () => {
|
|
142
|
-
return await app.scope('a-pay').service.paymentSession.start(fixture.paymentSessionId!);
|
|
143
|
-
}),
|
|
144
|
-
]);
|
|
145
|
-
assert.equal(results.filter(result => result.status === 'fulfilled').length, 1);
|
|
146
|
-
assert.equal(results.filter(result => result.status === 'rejected').length, 1);
|
|
147
|
-
} finally {
|
|
148
|
-
await app.bean.executor.mockCtx(async () => {
|
|
149
|
-
await cleanup(fixture);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
});
|