cabloy 5.1.124 → 5.1.126
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/.claude/skills/cabloy-backend-scaffold/SKILL.md +1 -1
- package/.claude/skills/cabloy-backend-scaffold/evals/evals.json +6 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +13 -4
- package/.claude/skills/cabloy-workflow/SKILL.md +4 -2
- package/.claude/skills/cabloy-workflow/evals/evals.json +14 -2
- package/.claude/skills/cabloy-worktree-environment/SKILL.md +206 -0
- package/.claude/skills/cabloy-worktree-environment/evals/evals.json +77 -0
- package/.github/workflows/vona-test-pg.yml +3 -0
- package/.github/workflows/vona-test-sqlite3.yml +3 -0
- package/CHANGELOG.md +21 -0
- package/CLAUDE.md +1 -1
- package/cabloy-docs/.vitepress/config.mjs +2 -0
- package/cabloy-docs/ai/playbook-technical-blog-authoring.md +158 -0
- package/cabloy-docs/backend/backend-contract-emission-specimen.md +2 -2
- package/cabloy-docs/backend/dto-guide.md +10 -7
- package/cabloy-docs/backend/dto-infer-generation.md +127 -7
- package/cabloy-docs/backend/foundation.md +1 -1
- package/cabloy-docs/backend/introduction.md +1 -0
- package/cabloy-docs/backend/module-dependencies.md +104 -0
- package/cabloy-docs/backend/serialization-guide.md +6 -0
- package/cabloy-docs/fullstack/parallel-worktree-environment.md +72 -34
- package/cabloy-docs/fullstack/suites-and-modules.md +1 -0
- package/cabloy-docs/reference/package-map.md +1 -0
- package/e2e/specs/a-commerce/commerce.spec.ts +89 -0
- package/package.json +2 -1
- package/scripts/init.ts +7 -12
- package/scripts/initAppName.test.ts +72 -0
- package/scripts/initAppName.ts +50 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +48 -146
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineCreate.tsx +6 -27
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineItem.tsx +6 -31
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineUpdate.tsx +6 -2
- package/vona/src/suite/a-commerce/modules/commerce-member/src/lib/addressMine.ts +16 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/service/address.ts +4 -17
- package/vona/src/suite/a-commerce/modules/commerce-member/test/addressOwnership.test.ts +63 -19
- package/vona/src/suite/a-commerce/modules/commerce-payment/package.json +2 -1
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/payScene.commerceOrder.ts +11 -2
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +26 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentAttempt.test.ts +19 -2
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/config/locale/en-us.ts +5 -2
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/config/locale/zh-cn.ts +5 -2
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateCreate.tsx +26 -35
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateUpdate.tsx +6 -1
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/entity/couponTemplate.tsx +40 -4
- package/vona/src/suite/a-commerce/modules/commerce-promotion/test/couponTemplateFormLayout.test.ts +85 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +1 -1
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/order.ts +3 -5
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSummary.tsx +7 -40
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/shipmentView.tsx +6 -15
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/lib/order.ts +9 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +121 -11
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/order.test.ts +25 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +19 -2
- package/vona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectResItem.tsx +6 -1
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +3 -1
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +4 -1
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +45 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordResItem.tsx +6 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSummary.tsx +5 -23
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +37 -5
- package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +29 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.index.ts +3 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.redlock.ts +3 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.version.ts +7 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/queue.outboxDispatch.ts +9 -4
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/schedule.providerOperationDispatch.ts +11 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/outboxEvent.tsx +11 -4
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperation.tsx +12 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/webhookInbox.tsx +6 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/outbox.ts +12 -5
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +1 -37
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +355 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +226 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/webhook.ts +148 -50
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payScene.ts +2 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/index.ts +39 -22
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +42 -13
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/controller/mockPayment.ts +11 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/dto/mockRefundComplete.tsx +13 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/dto/mockRefundReceipt.tsx +16 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/service/payMock.ts +56 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +2 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +117 -8
- 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/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGet.ts +5 -5
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +54 -12
- package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +625 -64
- package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/types.ts +55 -15
- package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +1 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeOrder.ts +15 -17
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +12 -8
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +111 -26
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeOrder.ts +3 -3
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +1 -8
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +5 -9
- package/zova/src/suite/a-training/modules/training-student/src/.metadata/index.ts +1 -1
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +301 -17
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +5461 -1788
- 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
|
@@ -16,7 +16,7 @@ import { BeanBase, useApp } from 'vona';
|
|
|
16
16
|
import { PayProvider } from 'vona-module-a-pay';
|
|
17
17
|
import { z } from 'zod';
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const PaymentWebhookBodySchema = z.object({
|
|
20
20
|
eventId: z.string().min(1).max(255),
|
|
21
21
|
eventType: z.enum(['payment.succeeded', 'payment.failed', 'payment.cancelled']),
|
|
22
22
|
paymentSessionId: z.string().min(1),
|
|
@@ -27,6 +27,16 @@ const WebhookBodySchema = z.object({
|
|
|
27
27
|
providerCaptureId: z.string().min(1).max(255).optional(),
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
const RefundWebhookBodySchema = z.object({
|
|
31
|
+
eventId: z.string().min(1).max(255),
|
|
32
|
+
eventType: z.enum(['refund.pending', 'refund.succeeded', 'refund.failed', 'refund.cancelled']),
|
|
33
|
+
refundOperationId: z.string().min(1),
|
|
34
|
+
state: z.enum(['pending', 'succeeded', 'failed', 'cancelled']),
|
|
35
|
+
amountMinor: z.number().int().positive(),
|
|
36
|
+
currency: z.string().length(3),
|
|
37
|
+
providerRefundId: z.string().min(1).max(255).optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
30
40
|
export interface IPayProviderMockClientRecord extends IPayProviderClientRecord {
|
|
31
41
|
secondary: never;
|
|
32
42
|
}
|
|
@@ -118,23 +128,42 @@ export class PayProviderMock
|
|
|
118
128
|
if (!expected || !actual || !safeEqual(actual, expected)) {
|
|
119
129
|
this.app.throw(401, 'mock webhook signature is invalid');
|
|
120
130
|
}
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
131
|
+
const payment = PaymentWebhookBodySchema.safeParse(input.body);
|
|
132
|
+
if (payment.success) {
|
|
133
|
+
const eventType = `payment.${payment.data.state}`;
|
|
134
|
+
if (payment.data.eventType !== eventType)
|
|
135
|
+
this.app.throw(400, 'mock webhook event type is invalid');
|
|
136
|
+
return {
|
|
137
|
+
eventId: payment.data.eventId,
|
|
138
|
+
eventType,
|
|
139
|
+
paymentSessionId: payment.data.paymentSessionId,
|
|
140
|
+
payment: {
|
|
141
|
+
state: payment.data.state,
|
|
142
|
+
providerPaymentId: payment.data.providerPaymentId,
|
|
143
|
+
providerCaptureId: payment.data.providerCaptureId,
|
|
144
|
+
},
|
|
145
|
+
summary: {
|
|
146
|
+
amountMinor: payment.data.amountMinor,
|
|
147
|
+
currency: payment.data.currency,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const refund = RefundWebhookBodySchema.safeParse(input.body);
|
|
152
|
+
if (!refund.success) this.app.throw(400, 'mock webhook is invalid');
|
|
153
|
+
const eventType = `refund.${refund.data.state}`;
|
|
154
|
+
if (refund.data.eventType !== eventType)
|
|
125
155
|
this.app.throw(400, 'mock webhook event type is invalid');
|
|
126
156
|
return {
|
|
127
|
-
eventId:
|
|
157
|
+
eventId: refund.data.eventId,
|
|
128
158
|
eventType,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
state:
|
|
132
|
-
|
|
133
|
-
providerCaptureId: body.data.providerCaptureId,
|
|
159
|
+
refundOperationId: refund.data.refundOperationId,
|
|
160
|
+
refund: {
|
|
161
|
+
state: refund.data.state,
|
|
162
|
+
providerRefundId: refund.data.providerRefundId,
|
|
134
163
|
},
|
|
135
164
|
summary: {
|
|
136
|
-
amountMinor:
|
|
137
|
-
currency:
|
|
165
|
+
amountMinor: refund.data.amountMinor,
|
|
166
|
+
currency: refund.data.currency,
|
|
138
167
|
},
|
|
139
168
|
};
|
|
140
169
|
}
|
|
@@ -7,6 +7,8 @@ import { Arg, Controller, Web } from 'vona-module-a-web';
|
|
|
7
7
|
|
|
8
8
|
import { DtoMockPaymentComplete } from '../dto/mockPaymentComplete.tsx';
|
|
9
9
|
import { DtoMockPaymentReceipt } from '../dto/mockPaymentReceipt.tsx';
|
|
10
|
+
import { DtoMockRefundComplete } from '../dto/mockRefundComplete.tsx';
|
|
11
|
+
import { DtoMockRefundReceipt } from '../dto/mockRefundReceipt.tsx';
|
|
10
12
|
|
|
11
13
|
export interface IControllerOptionsMockPayment extends IDecoratorControllerOptions {}
|
|
12
14
|
|
|
@@ -22,4 +24,13 @@ export class ControllerMockPayment extends BeanBase {
|
|
|
22
24
|
): Promise<DtoMockPaymentReceipt> {
|
|
23
25
|
return await this.scope.service.payMock.completePaymentSession(id, command.outcome);
|
|
24
26
|
}
|
|
27
|
+
|
|
28
|
+
@Web.post('refund-operation/:id/complete')
|
|
29
|
+
@Api.body(DtoMockRefundReceipt)
|
|
30
|
+
async completeRefund(
|
|
31
|
+
@Arg.param('id', v.tableIdentity()) id: TableIdentity,
|
|
32
|
+
@Arg.body() command: DtoMockRefundComplete,
|
|
33
|
+
): Promise<DtoMockRefundReceipt> {
|
|
34
|
+
return await this.scope.service.payMock.completeRefundOperation(id, command.outcome);
|
|
35
|
+
}
|
|
25
36
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
|
|
3
|
+
import { Api } from 'vona-module-a-openapiutils';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
export interface IDtoOptionsMockRefundComplete extends IDecoratorDtoOptions {}
|
|
8
|
+
|
|
9
|
+
@Dto<IDtoOptionsMockRefundComplete>()
|
|
10
|
+
export class DtoMockRefundComplete {
|
|
11
|
+
@Api.field(z.enum(['succeeded', 'failed', 'cancelled']))
|
|
12
|
+
outcome: 'succeeded' | 'failed' | 'cancelled';
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TableIdentity } from 'table-identity';
|
|
2
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
3
|
+
|
|
4
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
5
|
+
import { Dto } from 'vona-module-a-web';
|
|
6
|
+
|
|
7
|
+
export interface IDtoOptionsMockRefundReceipt extends IDecoratorDtoOptions {}
|
|
8
|
+
|
|
9
|
+
@Dto<IDtoOptionsMockRefundReceipt>()
|
|
10
|
+
export class DtoMockRefundReceipt {
|
|
11
|
+
@Api.field(v.tableIdentity())
|
|
12
|
+
refundOperationId: TableIdentity;
|
|
13
|
+
|
|
14
|
+
@Api.field(v.required())
|
|
15
|
+
accepted: true;
|
|
16
|
+
}
|
|
@@ -5,6 +5,7 @@ import { BeanBase } from 'vona';
|
|
|
5
5
|
import { Service } from 'vona-module-a-bean';
|
|
6
6
|
|
|
7
7
|
export type TypeMockPaymentOutcome = 'succeeded' | 'failed' | 'cancelled';
|
|
8
|
+
export type TypeMockRefundOutcome = 'succeeded' | 'failed' | 'cancelled';
|
|
8
9
|
|
|
9
10
|
@Service()
|
|
10
11
|
export class ServicePayMock extends BeanBase {
|
|
@@ -70,6 +71,61 @@ export class ServicePayMock extends BeanBase {
|
|
|
70
71
|
return { paymentSessionId: session.id, accepted: true as const };
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
async completeRefundOperation(refundOperationId: TableIdentity, outcome: TypeMockRefundOutcome) {
|
|
75
|
+
this._assertSimulatorEnabled();
|
|
76
|
+
const refund = await this.$scope.pay.model.refundOperation.getById(refundOperationId);
|
|
77
|
+
if (!refund) this.app.throw(404, 'mock refund operation not found');
|
|
78
|
+
const session = await this.$scope.pay.model.paymentSession.getById(refund.paymentSessionId);
|
|
79
|
+
if (!session) this.app.throw(404, 'mock refund operation not found');
|
|
80
|
+
if (!(await this.bean.passport.isSystemAdmin())) {
|
|
81
|
+
this.app.throw(403, 'mock refund completion requires system administrator authority');
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
session.providerName !== 'pay-mock:mock' ||
|
|
85
|
+
session.clientName !== 'default' ||
|
|
86
|
+
session.environment !== 'sandbox' ||
|
|
87
|
+
refund.state !== 'pending'
|
|
88
|
+
) {
|
|
89
|
+
this.app.throw(409, 'mock refund operation is not actionable');
|
|
90
|
+
}
|
|
91
|
+
const { clientOptions } = this.bean.payProvider.resolveByName(
|
|
92
|
+
session.providerName,
|
|
93
|
+
session.clientName,
|
|
94
|
+
);
|
|
95
|
+
const secret = clientOptions.secretWebhook;
|
|
96
|
+
if (typeof secret !== 'string' || !secret) {
|
|
97
|
+
this.app.throw(500, 'mock webhook secret is not configured');
|
|
98
|
+
}
|
|
99
|
+
const rawBody = JSON.stringify({
|
|
100
|
+
eventId: `mock-refund-${randomUUID()}`,
|
|
101
|
+
eventType: `refund.${outcome}`,
|
|
102
|
+
refundOperationId: String(refund.id),
|
|
103
|
+
state: outcome,
|
|
104
|
+
amountMinor: refund.amountMinor,
|
|
105
|
+
currency: refund.currency,
|
|
106
|
+
providerRefundId: refund.providerRefundId ?? `mock-refund-${refund.id}`,
|
|
107
|
+
});
|
|
108
|
+
const signature = createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
109
|
+
const instanceName = this.ctx.instanceName;
|
|
110
|
+
const instanceHeaderField = this.app.config.instance.headerField;
|
|
111
|
+
const response = await fetch(
|
|
112
|
+
this.app.util.getAbsoluteUrlByApiPath(
|
|
113
|
+
`/pay/webhook/${session.providerName}/${session.clientName}`,
|
|
114
|
+
),
|
|
115
|
+
{
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: {
|
|
118
|
+
'content-type': 'application/json',
|
|
119
|
+
'x-pay-mock-signature': signature,
|
|
120
|
+
...(instanceName && instanceHeaderField ? { [instanceHeaderField]: instanceName } : {}),
|
|
121
|
+
},
|
|
122
|
+
body: rawBody,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
if (!response.ok) this.app.throw(502, 'mock refund webhook was rejected');
|
|
126
|
+
return { refundOperationId: refund.id, accepted: true as const };
|
|
127
|
+
}
|
|
128
|
+
|
|
73
129
|
private _assertSimulatorEnabled() {
|
|
74
130
|
if (this.app.meta.env.META_MODE === 'prod') {
|
|
75
131
|
this.app.throw(404, 'mock payment simulator is unavailable');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-module-pay-paypal",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Vona Module"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"postpack": "clean-package restore"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@cabloy/paypal-server-sdk": "2.2.0",
|
|
34
35
|
"vona-module-a-pay": "workspace:*"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
@@ -11,6 +11,14 @@ import type {
|
|
|
11
11
|
IPayProviderWebhookInput,
|
|
12
12
|
} from 'vona-module-a-pay';
|
|
13
13
|
|
|
14
|
+
import {
|
|
15
|
+
CheckoutPaymentIntent,
|
|
16
|
+
Client,
|
|
17
|
+
Environment,
|
|
18
|
+
OrdersController,
|
|
19
|
+
OrderStatus,
|
|
20
|
+
PaymentsController,
|
|
21
|
+
} from '@cabloy/paypal-server-sdk';
|
|
14
22
|
import { BeanBase, useApp } from 'vona';
|
|
15
23
|
import { PayProvider } from 'vona-module-a-pay';
|
|
16
24
|
|
|
@@ -56,32 +64,133 @@ export class PayProviderPaypal
|
|
|
56
64
|
implements IPayProviderExecute<IPayProviderPaypalClientOptions>
|
|
57
65
|
{
|
|
58
66
|
async startPayment(
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
input: IPayProviderPaymentInput,
|
|
68
|
+
clientOptions: IPayProviderPaypalClientOptions,
|
|
61
69
|
): Promise<IPayProviderPaymentSnapshot> {
|
|
62
|
-
this.
|
|
70
|
+
if (input.providerOrderId) return await this.queryPayment(input, clientOptions);
|
|
71
|
+
if (!input.returnUrl || !input.cancelUrl) {
|
|
72
|
+
this.app.throw(422, 'PayPal payment requires return and cancel URLs');
|
|
73
|
+
}
|
|
74
|
+
const orders = new OrdersController(this._createClient(clientOptions));
|
|
75
|
+
const response = await orders.createOrder({
|
|
76
|
+
paypalRequestId: input.idempotencyKey,
|
|
77
|
+
body: {
|
|
78
|
+
intent: CheckoutPaymentIntent.Capture,
|
|
79
|
+
applicationContext: { returnUrl: input.returnUrl, cancelUrl: input.cancelUrl },
|
|
80
|
+
purchaseUnits: [
|
|
81
|
+
{
|
|
82
|
+
customId: String(input.paymentSessionId),
|
|
83
|
+
invoiceId: input.businessReference,
|
|
84
|
+
amount: {
|
|
85
|
+
currencyCode: input.currency,
|
|
86
|
+
value: formatMinorAmount(input.amountMinor, input.currency),
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
} as never);
|
|
92
|
+
const order = response.result;
|
|
93
|
+
const approvalUrl = order?.links?.find(item => item.rel === 'approve')?.href;
|
|
94
|
+
if (!order?.id || !approvalUrl) this.app.throw(502, 'PayPal did not return an approval order');
|
|
95
|
+
return {
|
|
96
|
+
state: 'requires_action',
|
|
97
|
+
providerPaymentId: order.id,
|
|
98
|
+
providerOrderId: order.id,
|
|
99
|
+
nextAction: { kind: 'redirect', url: approvalUrl },
|
|
100
|
+
};
|
|
63
101
|
}
|
|
64
102
|
|
|
65
103
|
async queryPayment(
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
input: IPayProviderPaymentInput,
|
|
105
|
+
clientOptions: IPayProviderPaypalClientOptions,
|
|
68
106
|
): Promise<IPayProviderPaymentSnapshot> {
|
|
69
|
-
|
|
107
|
+
if (!input.providerOrderId) return { state: 'requires_action' };
|
|
108
|
+
const orders = new OrdersController(this._createClient(clientOptions));
|
|
109
|
+
const response = await orders.getOrder({ id: input.providerOrderId });
|
|
110
|
+
const order = response.result;
|
|
111
|
+
if (!order?.id) this.app.throw(502, 'PayPal order query returned no order');
|
|
112
|
+
const capture = order.purchaseUnits?.[0]?.payments?.captures?.[0];
|
|
113
|
+
if (capture?.status === 'COMPLETED') {
|
|
114
|
+
return {
|
|
115
|
+
state: 'succeeded',
|
|
116
|
+
providerPaymentId: order.id,
|
|
117
|
+
providerOrderId: order.id,
|
|
118
|
+
providerCaptureId: capture.id,
|
|
119
|
+
nextAction: { kind: 'completed' },
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (capture?.status === 'FAILED' || capture?.status === 'DECLINED') {
|
|
123
|
+
return { state: 'failed', providerPaymentId: order.id, providerOrderId: order.id };
|
|
124
|
+
}
|
|
125
|
+
if (order.status === OrderStatus.Voided) {
|
|
126
|
+
return { state: 'cancelled', providerPaymentId: order.id, providerOrderId: order.id };
|
|
127
|
+
}
|
|
128
|
+
if (capture?.status === 'PENDING') {
|
|
129
|
+
return { state: 'processing', providerPaymentId: order.id, providerOrderId: order.id };
|
|
130
|
+
}
|
|
131
|
+
return { state: 'requires_action', providerPaymentId: order.id, providerOrderId: order.id };
|
|
70
132
|
}
|
|
71
133
|
|
|
72
134
|
async createRefund(
|
|
135
|
+
input: IPayProviderRefundInput,
|
|
136
|
+
clientOptions: IPayProviderPaypalClientOptions,
|
|
137
|
+
): Promise<IPayProviderRefundSnapshot> {
|
|
138
|
+
const payments = new PaymentsController(this._createClient(clientOptions));
|
|
139
|
+
const response = await payments.refundCapturedPayment({
|
|
140
|
+
captureId: input.providerCaptureId,
|
|
141
|
+
paypalRequestId: input.idempotencyKey,
|
|
142
|
+
body: {
|
|
143
|
+
amount: {
|
|
144
|
+
currencyCode: input.currency,
|
|
145
|
+
value: formatMinorAmount(input.amountMinor, input.currency),
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
} as never);
|
|
149
|
+
return mapRefund(response.result?.status, response.result?.id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async queryRefund(
|
|
73
153
|
_input: IPayProviderRefundInput,
|
|
74
154
|
_clientOptions: IPayProviderPaypalClientOptions,
|
|
75
155
|
): Promise<IPayProviderRefundSnapshot> {
|
|
76
|
-
this.app.throw(501, 'PayPal provider
|
|
156
|
+
this.app.throw(501, 'PayPal refund query requires a persisted provider refund identifier');
|
|
77
157
|
}
|
|
78
158
|
|
|
79
159
|
async verifyWebhook(
|
|
80
160
|
_input: IPayProviderWebhookInput,
|
|
81
161
|
_clientOptions: IPayProviderPaypalClientOptions,
|
|
82
162
|
): Promise<IPayProviderVerifiedWebhook> {
|
|
83
|
-
this.app.throw(501, 'PayPal webhook verification
|
|
163
|
+
this.app.throw(501, 'PayPal webhook verification requires configured webhook verification');
|
|
84
164
|
}
|
|
165
|
+
|
|
166
|
+
private _createClient(clientOptions: IPayProviderPaypalClientOptions) {
|
|
167
|
+
const { clientId, clientSecret } = clientOptions.secretCredential;
|
|
168
|
+
if (!clientId || !clientSecret)
|
|
169
|
+
this.app.throw(503, 'PayPal client credentials are not configured');
|
|
170
|
+
return new Client({
|
|
171
|
+
clientCredentialsAuthCredentials: {
|
|
172
|
+
oAuthClientId: clientId,
|
|
173
|
+
oAuthClientSecret: clientSecret,
|
|
174
|
+
},
|
|
175
|
+
timeout: 0,
|
|
176
|
+
environment:
|
|
177
|
+
clientOptions.environment === 'live' ? Environment.Production : Environment.Sandbox,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function formatMinorAmount(amountMinor: number, currency: string) {
|
|
183
|
+
if (!Number.isSafeInteger(amountMinor) || amountMinor < 0 || currency !== 'USD') {
|
|
184
|
+
throw new Error('PayPal supports only nonnegative USD minor-unit amounts');
|
|
185
|
+
}
|
|
186
|
+
return `${Math.floor(amountMinor / 100)}.${String(amountMinor % 100).padStart(2, '0')}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function mapRefund(status?: string, providerRefundId?: string): IPayProviderRefundSnapshot {
|
|
190
|
+
if (status === 'COMPLETED') return { state: 'succeeded', providerRefundId };
|
|
191
|
+
if (status === 'FAILED') return { state: 'failed', providerRefundId };
|
|
192
|
+
if (status === 'CANCELLED') return { state: 'cancelled', providerRefundId };
|
|
193
|
+
return { state: 'pending', providerRefundId };
|
|
85
194
|
}
|
|
86
195
|
|
|
87
196
|
declare module 'vona-module-a-pay' {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-suite-a-pay",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "",
|
|
6
6
|
"files": [],
|
|
7
7
|
"type": "module",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"vona-module-a-pay": "^5.0.
|
|
10
|
-
"vona-module-pay-mock": "^5.0.
|
|
11
|
-
"vona-module-pay-paypal": "^5.0.
|
|
12
|
-
"vona-module-pay-stripe": "^5.0.
|
|
9
|
+
"vona-module-a-pay": "^5.0.3",
|
|
10
|
+
"vona-module-pay-mock": "^5.0.3",
|
|
11
|
+
"vona-module-pay-paypal": "^5.0.3",
|
|
12
|
+
"vona-module-pay-stripe": "^5.0.3"
|
|
13
13
|
},
|
|
14
14
|
"title": "a-pay"
|
|
15
15
|
}
|
|
@@ -82,7 +82,6 @@ function _DtoGet_relation_handle<TRecord extends {}>(
|
|
|
82
82
|
return;
|
|
83
83
|
}
|
|
84
84
|
const { type, model, options } = relationReal;
|
|
85
|
-
const modelTarget = prepareClassModel(model);
|
|
86
85
|
const optionsReal = Object.assign({}, options, { include: includeReal, with: withReal });
|
|
87
86
|
if (mutateTypeTopLevel) {
|
|
88
87
|
if (type === 'belongsTo') {
|
|
@@ -94,7 +93,7 @@ function _DtoGet_relation_handle<TRecord extends {}>(
|
|
|
94
93
|
schema = v.array(z.object({ id: v.tableIdentity()(), deleted: z.boolean().optional() }));
|
|
95
94
|
} else if (type === 'hasOne') {
|
|
96
95
|
const schemaLazy = _DtoGet_relation_handle_schemaLazy(
|
|
97
|
-
|
|
96
|
+
model,
|
|
98
97
|
optionsReal,
|
|
99
98
|
autoload,
|
|
100
99
|
mutateTypeTopLevel,
|
|
@@ -105,7 +104,7 @@ function _DtoGet_relation_handle<TRecord extends {}>(
|
|
|
105
104
|
} else {
|
|
106
105
|
// hasMany
|
|
107
106
|
const schemaLazy = _DtoGet_relation_handle_schemaLazy(
|
|
108
|
-
|
|
107
|
+
model,
|
|
109
108
|
optionsReal,
|
|
110
109
|
autoload,
|
|
111
110
|
mutateTypeTopLevel,
|
|
@@ -116,7 +115,7 @@ function _DtoGet_relation_handle<TRecord extends {}>(
|
|
|
116
115
|
Api.field(v.optional(), schema)(entityClass.prototype, relationName);
|
|
117
116
|
} else {
|
|
118
117
|
const schemaLazy = _DtoGet_relation_handle_schemaLazy(
|
|
119
|
-
|
|
118
|
+
model,
|
|
120
119
|
optionsReal,
|
|
121
120
|
autoload,
|
|
122
121
|
mutateTypeTopLevel,
|
|
@@ -146,13 +145,14 @@ function _DtoGet_relation_handle<TRecord extends {}>(
|
|
|
146
145
|
}
|
|
147
146
|
|
|
148
147
|
function _DtoGet_relation_handle_schemaLazy(
|
|
149
|
-
|
|
148
|
+
modelLike,
|
|
150
149
|
optionsReal,
|
|
151
150
|
autoload,
|
|
152
151
|
mutateTypeTopLevel?: TypeDtoMutateType,
|
|
153
152
|
relation?: IRelationItem,
|
|
154
153
|
) {
|
|
155
154
|
return () => {
|
|
155
|
+
const modelTarget = prepareClassModel(modelLike);
|
|
156
156
|
if (!autoload) {
|
|
157
157
|
return _DtoGet_relation_handle_schemaLazy_raw(
|
|
158
158
|
modelTarget,
|
|
@@ -8,6 +8,9 @@ export type ApiSchemaTestVonaEntityProduct = components['schemas']['test-vona.en
|
|
|
8
8
|
export type ApiSchemaTestVonaEntityProductPartial = Partial<ApiSchemaTestVonaEntityProduct>;
|
|
9
9
|
export type ApiSchemaACaptchaDtoCaptchaData = components['schemas']['a-captcha.dto.captchaData'];
|
|
10
10
|
export type ApiSchemaACaptchaDtoCaptchaDataPartial = Partial<ApiSchemaACaptchaDtoCaptchaData>;
|
|
11
|
+
export type ApiSchemaAPayDtoPaymentSessionView =
|
|
12
|
+
components['schemas']['a-pay.dto.paymentSessionView'];
|
|
13
|
+
export type ApiSchemaAPayDtoPaymentSessionViewPartial = Partial<ApiSchemaAPayDtoPaymentSessionView>;
|
|
11
14
|
export type ApiSchemaCommerceMemberDtoAddressMineRes =
|
|
12
15
|
components['schemas']['commerce-member.dto.addressMineRes'];
|
|
13
16
|
export type ApiSchemaCommerceMemberDtoAddressMineResPartial =
|
|
@@ -92,10 +95,10 @@ export type ApiSchemaCommercePromotionDtoCouponTemplateSelectResItem =
|
|
|
92
95
|
components['schemas']['commerce-promotion.dto.couponTemplateSelectResItem'];
|
|
93
96
|
export type ApiSchemaCommercePromotionDtoCouponTemplateSelectResItemPartial =
|
|
94
97
|
Partial<ApiSchemaCommercePromotionDtoCouponTemplateSelectResItem>;
|
|
95
|
-
export type
|
|
96
|
-
components['schemas']['commerce-promotion.dto.
|
|
97
|
-
export type
|
|
98
|
-
Partial<
|
|
98
|
+
export type ApiSchemaCommercePromotionDtoCouponTemplateView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_91abd8df366c0c4d47021b38af2a356349ee178b =
|
|
99
|
+
components['schemas']['commerce-promotion.dto.couponTemplateView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_91abd8df366c0c4d47021b38af2a356349ee178b'];
|
|
100
|
+
export type ApiSchemaCommercePromotionDtoCouponTemplateView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_91abd8df366c0c4d47021b38af2a356349ee178bPartial =
|
|
101
|
+
Partial<ApiSchemaCommercePromotionDtoCouponTemplateView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_91abd8df366c0c4d47021b38af2a356349ee178b>;
|
|
99
102
|
export type ApiSchemaCommercePromotionDtoCouponTemplateUpdate =
|
|
100
103
|
components['schemas']['commerce-promotion.dto.couponTemplateUpdate'];
|
|
101
104
|
export type ApiSchemaCommercePromotionDtoCouponTemplateUpdatePartial =
|
|
@@ -142,10 +145,37 @@ export type ApiSchemaCommerceTradeDtoOrderCouponSnapshot_2d063d28bc7243bed02ebd8
|
|
|
142
145
|
components['schemas']['commerce-trade.dto.orderCouponSnapshot_2d063d28bc7243bed02ebd8bddf1212a93c6305b'];
|
|
143
146
|
export type ApiSchemaCommerceTradeDtoOrderCouponSnapshot_2d063d28bc7243bed02ebd8bddf1212a93c6305bPartial =
|
|
144
147
|
Partial<ApiSchemaCommerceTradeDtoOrderCouponSnapshot_2d063d28bc7243bed02ebd8bddf1212a93c6305b>;
|
|
148
|
+
export type ApiSchemaCommerceTradeDtoShipmentView_2d063d28bc7243bed02ebd8bddf1212a93c6305b =
|
|
149
|
+
components['schemas']['commerce-trade.dto.shipmentView_2d063d28bc7243bed02ebd8bddf1212a93c6305b'];
|
|
150
|
+
export type ApiSchemaCommerceTradeDtoShipmentView_2d063d28bc7243bed02ebd8bddf1212a93c6305bPartial =
|
|
151
|
+
Partial<ApiSchemaCommerceTradeDtoShipmentView_2d063d28bc7243bed02ebd8bddf1212a93c6305b>;
|
|
145
152
|
export type ApiSchemaCommerceTradeDtoOrderLineSkuAttributeSnapshot =
|
|
146
153
|
components['schemas']['commerce-trade.dto.orderLineSkuAttributeSnapshot'];
|
|
147
154
|
export type ApiSchemaCommerceTradeDtoOrderLineSkuAttributeSnapshotPartial =
|
|
148
155
|
Partial<ApiSchemaCommerceTradeDtoOrderLineSkuAttributeSnapshot>;
|
|
156
|
+
export type ApiSchemaCommerceTradeDtoRefundResult =
|
|
157
|
+
components['schemas']['commerce-trade.dto.refundResult'];
|
|
158
|
+
export type ApiSchemaCommerceTradeDtoRefundResultPartial =
|
|
159
|
+
Partial<ApiSchemaCommerceTradeDtoRefundResult>;
|
|
160
|
+
export type ApiSchemaCommerceTradeDtoRefundRequestCreate =
|
|
161
|
+
components['schemas']['commerce-trade.dto.refundRequestCreate'];
|
|
162
|
+
export type ApiSchemaCommerceTradeDtoRefundRequestCreatePartial =
|
|
163
|
+
Partial<ApiSchemaCommerceTradeDtoRefundRequestCreate>;
|
|
164
|
+
export type ApiSchemaCommerceTradeDtoRefundReview =
|
|
165
|
+
components['schemas']['commerce-trade.dto.refundReview'];
|
|
166
|
+
export type ApiSchemaCommerceTradeDtoRefundReviewPartial =
|
|
167
|
+
Partial<ApiSchemaCommerceTradeDtoRefundReview>;
|
|
168
|
+
export type ApiSchemaCommerceTradeDtoRefundOutcomeCreate =
|
|
169
|
+
components['schemas']['commerce-trade.dto.refundOutcomeCreate'];
|
|
170
|
+
export type ApiSchemaCommerceTradeDtoRefundOutcomeCreatePartial =
|
|
171
|
+
Partial<ApiSchemaCommerceTradeDtoRefundOutcomeCreate>;
|
|
172
|
+
export type ApiSchemaCommerceTradeDtoShipmentView =
|
|
173
|
+
components['schemas']['commerce-trade.dto.shipmentView'];
|
|
174
|
+
export type ApiSchemaCommerceTradeDtoShipmentViewPartial =
|
|
175
|
+
Partial<ApiSchemaCommerceTradeDtoShipmentView>;
|
|
176
|
+
export type ApiSchemaCommerceTradeDtoOrderShip =
|
|
177
|
+
components['schemas']['commerce-trade.dto.orderShip'];
|
|
178
|
+
export type ApiSchemaCommerceTradeDtoOrderShipPartial = Partial<ApiSchemaCommerceTradeDtoOrderShip>;
|
|
149
179
|
export type ApiSchemaCommerceTradeDtoOrderSelectRes =
|
|
150
180
|
components['schemas']['commerce-trade.dto.orderSelectRes'];
|
|
151
181
|
export type ApiSchemaCommerceTradeDtoOrderSelectResPartial =
|
|
@@ -158,14 +188,6 @@ export type ApiSchemaCommerceTradeDtoOrderView_2d063d28bc7243bed02ebd8bddf1212a9
|
|
|
158
188
|
components['schemas']['commerce-trade.dto.orderView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_425dbecccd52e19e24888f99e1b1670233afa875'];
|
|
159
189
|
export type ApiSchemaCommerceTradeDtoOrderView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_425dbecccd52e19e24888f99e1b1670233afa875Partial =
|
|
160
190
|
Partial<ApiSchemaCommerceTradeDtoOrderView_2d063d28bc7243bed02ebd8bddf1212a93c6305b_425dbecccd52e19e24888f99e1b1670233afa875>;
|
|
161
|
-
export type ApiSchemaCommerceTradeDtoPaymentOutcomeResult =
|
|
162
|
-
components['schemas']['commerce-trade.dto.paymentOutcomeResult'];
|
|
163
|
-
export type ApiSchemaCommerceTradeDtoPaymentOutcomeResultPartial =
|
|
164
|
-
Partial<ApiSchemaCommerceTradeDtoPaymentOutcomeResult>;
|
|
165
|
-
export type ApiSchemaCommerceTradeDtoPaymentOutcomeCreate =
|
|
166
|
-
components['schemas']['commerce-trade.dto.paymentOutcomeCreate'];
|
|
167
|
-
export type ApiSchemaCommerceTradeDtoPaymentOutcomeCreatePartial =
|
|
168
|
-
Partial<ApiSchemaCommerceTradeDtoPaymentOutcomeCreate>;
|
|
169
191
|
export type ApiSchemaCommerceTradeDtoStockAuditSelectRes =
|
|
170
192
|
components['schemas']['commerce-trade.dto.stockAuditSelectRes'];
|
|
171
193
|
export type ApiSchemaCommerceTradeDtoStockAuditSelectResPartial =
|
|
@@ -351,6 +373,18 @@ export type ApiSchemaTrainingStudentDtoStudentSummary_2d063d28bc7243bed02ebd8bdd
|
|
|
351
373
|
components['schemas']['training-student.dto.studentSummary_2d063d28bc7243bed02ebd8bddf1212a93c6305b'];
|
|
352
374
|
export type ApiSchemaTrainingStudentDtoStudentSummary_2d063d28bc7243bed02ebd8bddf1212a93c6305bPartial =
|
|
353
375
|
Partial<ApiSchemaTrainingStudentDtoStudentSummary_2d063d28bc7243bed02ebd8bddf1212a93c6305b>;
|
|
376
|
+
export type ApiSchemaBasicMetricsDtoMetricsSnapshot =
|
|
377
|
+
components['schemas']['basic-metrics.dto.metricsSnapshot'];
|
|
378
|
+
export type ApiSchemaBasicMetricsDtoMetricsSnapshotPartial =
|
|
379
|
+
Partial<ApiSchemaBasicMetricsDtoMetricsSnapshot>;
|
|
380
|
+
export type ApiSchemaBasicMetricsDtoMetricsRuntime =
|
|
381
|
+
components['schemas']['basic-metrics.dto.metricsRuntime'];
|
|
382
|
+
export type ApiSchemaBasicMetricsDtoMetricsRuntimePartial =
|
|
383
|
+
Partial<ApiSchemaBasicMetricsDtoMetricsRuntime>;
|
|
384
|
+
export type ApiSchemaBasicMetricsDtoMetricsQueue =
|
|
385
|
+
components['schemas']['basic-metrics.dto.metricsQueue'];
|
|
386
|
+
export type ApiSchemaBasicMetricsDtoMetricsQueuePartial =
|
|
387
|
+
Partial<ApiSchemaBasicMetricsDtoMetricsQueue>;
|
|
354
388
|
export type ApiSchemaAFileDtoFileUploadPolicyResponse =
|
|
355
389
|
components['schemas']['a-file.dto.fileUploadPolicyResponse'];
|
|
356
390
|
export type ApiSchemaAFileDtoFileUploadPolicyResponsePartial =
|
|
@@ -419,6 +453,14 @@ export type ApiSchemaAImageDtoImageTransformOptions_2d063d28bc7243bed02ebd8bddf1
|
|
|
419
453
|
components['schemas']['a-image.dto.imageTransformOptions_2d063d28bc7243bed02ebd8bddf1212a93c6305b'];
|
|
420
454
|
export type ApiSchemaAImageDtoImageTransformOptions_2d063d28bc7243bed02ebd8bddf1212a93c6305bPartial =
|
|
421
455
|
Partial<ApiSchemaAImageDtoImageTransformOptions_2d063d28bc7243bed02ebd8bddf1212a93c6305b>;
|
|
456
|
+
export type ApiSchemaPayMockDtoMockPaymentReceipt =
|
|
457
|
+
components['schemas']['pay-mock.dto.mockPaymentReceipt'];
|
|
458
|
+
export type ApiSchemaPayMockDtoMockPaymentReceiptPartial =
|
|
459
|
+
Partial<ApiSchemaPayMockDtoMockPaymentReceipt>;
|
|
460
|
+
export type ApiSchemaPayMockDtoMockPaymentComplete =
|
|
461
|
+
components['schemas']['pay-mock.dto.mockPaymentComplete'];
|
|
462
|
+
export type ApiSchemaPayMockDtoMockPaymentCompletePartial =
|
|
463
|
+
Partial<ApiSchemaPayMockDtoMockPaymentComplete>;
|
|
422
464
|
export type ApiSchemaAPaypalEntityPaypalRecord =
|
|
423
465
|
components['schemas']['a-paypal.entity.paypalRecord'];
|
|
424
466
|
export type ApiSchemaAPaypalEntityPaypalRecordPartial = Partial<ApiSchemaAPaypalEntityPaypalRecord>;
|