cabloy 5.1.127 → 5.1.129

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.
Files changed (88) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/cabloy-docs/backend/i18n-guide.md +86 -7
  4. package/e2e/specs/a-commerce/commerce.spec.ts +93 -8
  5. package/package.json +1 -1
  6. package/vona/env/.env +2 -0
  7. package/vona/env/.env.prod +6 -2
  8. package/vona/packages-cli/cabloy-cli/package.json +1 -1
  9. package/vona/packages-cli/cli/package.json +1 -1
  10. package/vona/packages-cli/cli-set-api/package.json +1 -1
  11. package/vona/packages-utils/module-glob/package.json +1 -1
  12. package/vona/packages-utils/utils/package.json +1 -1
  13. package/vona/packages-utils/utils/src/vona.ts +8 -1
  14. package/vona/packages-vona/vona/package.json +1 -1
  15. package/vona/packages-vona/vona-core/package.json +1 -1
  16. package/vona/packages-vona/vona-mock/package.json +1 -1
  17. package/vona/pnpm-lock.yaml +142 -21
  18. package/vona/pnpm-workspace.yaml +1 -1
  19. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/index.ts +1 -1
  20. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productSelectResItem.tsx +3 -1
  21. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +3 -1
  22. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/entity/product.tsx +23 -3
  23. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/model/product.ts +1 -0
  24. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/service/product.ts +7 -2
  25. package/vona/src/suite/a-commerce/modules/commerce-catalog/test/productPresentation.test.ts +214 -0
  26. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/payScene.commerceOrder.ts +39 -3
  27. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +14 -1
  28. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/paymentAttempt.ts +5 -1
  29. package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +17 -0
  30. package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/checkout.ts +18 -0
  31. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutCreate.tsx +3 -0
  32. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutPaymentMethod.tsx +19 -0
  33. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutPaymentMethods.tsx +17 -0
  34. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +51 -0
  35. package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +2 -1
  36. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  37. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +94 -55
  38. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payScene.ts +65 -1
  39. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.redlock.ts +2 -0
  40. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/paymentCallback.ts +32 -0
  41. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/paymentSession.ts +26 -8
  42. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentCallback.ts +99 -0
  43. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +26 -3
  44. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +214 -82
  45. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +27 -2
  46. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payScene.ts +9 -0
  47. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +5 -0
  48. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  49. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +7 -0
  50. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  51. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +395 -84
  52. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/index.ts +1 -1
  53. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/paypalGateway.ts +129 -0
  54. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/types/index.ts +2 -0
  55. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  56. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/bean/payProvider.stripe.ts +7 -0
  57. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  58. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  59. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  60. package/zova/packages-cli/cli/package.json +3 -3
  61. package/zova/packages-cli/cli-set-front/package.json +4 -4
  62. package/zova/packages-utils/zova-jsx/package.json +3 -3
  63. package/zova/packages-utils/zova-vite/package.json +2 -2
  64. package/zova/packages-zova/zova/package.json +3 -3
  65. package/zova/packages-zova/zova-core/package.json +2 -2
  66. package/zova/pnpm-lock.yaml +41 -41
  67. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +8 -0
  68. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +175 -0
  69. package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +1 -0
  70. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeCheckout.ts +16 -0
  71. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +8 -0
  72. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +177 -2
  73. package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeCheckout.ts +12 -1
  74. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/cart.ts +11 -0
  75. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +25 -1
  76. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +27 -4
  77. package/zova/src/suite-vendor/a-pay/modules/a-pay/cli/openapi.config.ts +1 -1
  78. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  79. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/schemas.ts +20 -8
  80. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/types.ts +292 -32
  81. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/paymentSession.ts +26 -4
  82. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/apiSchema/paymentSession.ts +5 -0
  83. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +2 -1
  84. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +12 -0
  85. package/zova/src/suite-vendor/a-pay/package.json +2 -2
  86. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +4 -4
  87. package/zova/src/suite-vendor/a-zova/package.json +2 -2
  88. /package/vona/patches/{zova-core@5.1.81.patch → zova-core@5.1.82.patch} +0 -0
@@ -17,7 +17,7 @@ import type { IEntityOptionsRefundOperation } from '../entity/refundOperation.ts
17
17
  import type { IEntityOptionsWebhookInbox } from '../entity/webhookInbox.tsx';
18
18
  import 'vona-module-a-orm';
19
19
  declare module 'vona-module-a-orm' {
20
-
20
+
21
21
  export interface IEntityRecord {
22
22
  'a-pay:outboxEvent': IEntityOptionsOutboxEvent;
23
23
  'a-pay:paymentAudit': IEntityOptionsPaymentAudit;
@@ -27,10 +27,10 @@ declare module 'vona-module-a-orm' {
27
27
  'a-pay:webhookInbox': IEntityOptionsWebhookInbox;
28
28
  }
29
29
 
30
-
30
+
31
31
  }
32
32
  declare module 'vona-module-a-pay' {
33
-
33
+
34
34
  }
35
35
  /** entity: end */
36
36
  /** entity: begin */
@@ -73,7 +73,7 @@ declare module 'vona-module-a-orm' {
73
73
  }
74
74
  }
75
75
  declare module 'vona-module-a-pay' {
76
-
76
+
77
77
  export interface IEntityOptionsOutboxEvent {
78
78
  fields?: TypeEntityOptionsFields<EntityOutboxEvent, IEntityOptionsOutboxEvent[TypeSymbolKeyFieldsMore]>;
79
79
  }
@@ -114,7 +114,7 @@ import type { IModelOptionsRefundOperation } from '../model/refundOperation.ts';
114
114
  import type { IModelOptionsWebhookInbox } from '../model/webhookInbox.ts';
115
115
  import 'vona-module-a-orm';
116
116
  declare module 'vona-module-a-orm' {
117
-
117
+
118
118
  export interface IModelRecord {
119
119
  'a-pay:outboxEvent': IModelOptionsOutboxEvent;
120
120
  'a-pay:paymentAudit': IModelOptionsPaymentAudit;
@@ -124,10 +124,10 @@ declare module 'vona-module-a-orm' {
124
124
  'a-pay:webhookInbox': IModelOptionsWebhookInbox;
125
125
  }
126
126
 
127
-
127
+
128
128
  }
129
129
  declare module 'vona-module-a-pay' {
130
-
130
+
131
131
  export interface ModelOutboxEvent {
132
132
  /** @internal */
133
133
  get scope(): ScopeModuleAPay;
@@ -192,7 +192,7 @@ declare module 'vona-module-a-pay' {
192
192
  get $beanFullName(): 'a-pay.model.webhookInbox';
193
193
  get $onionName(): 'a-pay:webhookInbox';
194
194
  get $onionOptions(): IModelOptionsWebhookInbox;
195
- }
195
+ }
196
196
  }
197
197
  /** model: end */
198
198
  /** model: begin */
@@ -229,7 +229,7 @@ declare module 'vona' {
229
229
  import type { IModelGetOptions, IModelMethodOptions, IModelSelectParams, TypeModelSelectAndCount, TypeModelRelationResult, TypeModelWhere, IModelInsertOptions, TypeModelMutateRelationData, IModelDeleteOptions, IModelUpdateOptions, IModelMutateOptions, IModelSelectCountParams, IModelIncrementParams, IModelSelectAggrParams, TypeModelAggrRelationResult, IModelSelectGroupParams, TypeModelGroupRelationResult } from 'vona-module-a-orm';
230
230
  import { SymbolKeyEntity, SymbolKeyEntityMeta, SymbolKeyModelOptions } from 'vona-module-a-orm';
231
231
  declare module 'vona-module-a-pay' {
232
-
232
+
233
233
  export interface ModelOutboxEvent {
234
234
  [SymbolKeyEntity]: EntityOutboxEvent;
235
235
  [SymbolKeyEntityMeta]: EntityOutboxEventMeta;
@@ -470,11 +470,11 @@ export * from '../bean/bean.payScene.ts';
470
470
 
471
471
  import 'vona';
472
472
  declare module 'vona' {
473
-
474
-
473
+
474
+
475
475
  }
476
476
  declare module 'vona-module-a-pay' {
477
-
477
+
478
478
  export interface BeanPayProvider {
479
479
  /** @internal */
480
480
  get scope(): ScopeModuleAPay;
@@ -483,7 +483,7 @@ declare module 'vona-module-a-pay' {
483
483
  export interface BeanPayScene {
484
484
  /** @internal */
485
485
  get scope(): ScopeModuleAPay;
486
- }
486
+ }
487
487
  }
488
488
  /** bean: end */
489
489
  /** bean: begin */
@@ -499,6 +499,7 @@ declare module 'vona' {
499
499
  /** bean: end */
500
500
  /** service: begin */
501
501
  export * from '../service/outbox.ts';
502
+ export * from '../service/paymentCallback.ts';
502
503
  export * from '../service/paymentSession.ts';
503
504
  export * from '../service/providerOperation.ts';
504
505
  export * from '../service/refundOperation.ts';
@@ -506,19 +507,20 @@ export * from '../service/webhook.ts';
506
507
 
507
508
  import 'vona-module-a-bean';
508
509
  declare module 'vona-module-a-bean' {
509
-
510
+
510
511
  export interface IServiceRecord {
511
512
  'a-pay:outbox': never;
513
+ 'a-pay:paymentCallback': never;
512
514
  'a-pay:paymentSession': never;
513
515
  'a-pay:providerOperation': never;
514
516
  'a-pay:refundOperation': never;
515
517
  'a-pay:webhook': never;
516
518
  }
517
519
 
518
-
520
+
519
521
  }
520
522
  declare module 'vona-module-a-pay' {
521
-
523
+
522
524
  export interface ServiceOutbox {
523
525
  /** @internal */
524
526
  get scope(): ScopeModuleAPay;
@@ -527,7 +529,18 @@ declare module 'vona-module-a-pay' {
527
529
  export interface ServiceOutbox {
528
530
  get $beanFullName(): 'a-pay.service.outbox';
529
531
  get $onionName(): 'a-pay:outbox';
530
-
532
+
533
+ }
534
+
535
+ export interface ServicePaymentCallback {
536
+ /** @internal */
537
+ get scope(): ScopeModuleAPay;
538
+ }
539
+
540
+ export interface ServicePaymentCallback {
541
+ get $beanFullName(): 'a-pay.service.paymentCallback';
542
+ get $onionName(): 'a-pay:paymentCallback';
543
+
531
544
  }
532
545
 
533
546
  export interface ServicePaymentSession {
@@ -538,7 +551,7 @@ declare module 'vona-module-a-pay' {
538
551
  export interface ServicePaymentSession {
539
552
  get $beanFullName(): 'a-pay.service.paymentSession';
540
553
  get $onionName(): 'a-pay:paymentSession';
541
-
554
+
542
555
  }
543
556
 
544
557
  export interface ServiceProviderOperation {
@@ -549,7 +562,7 @@ declare module 'vona-module-a-pay' {
549
562
  export interface ServiceProviderOperation {
550
563
  get $beanFullName(): 'a-pay.service.providerOperation';
551
564
  get $onionName(): 'a-pay:providerOperation';
552
-
565
+
553
566
  }
554
567
 
555
568
  export interface ServiceRefundOperation {
@@ -560,7 +573,7 @@ declare module 'vona-module-a-pay' {
560
573
  export interface ServiceRefundOperation {
561
574
  get $beanFullName(): 'a-pay.service.refundOperation';
562
575
  get $onionName(): 'a-pay:refundOperation';
563
-
576
+
564
577
  }
565
578
 
566
579
  export interface ServiceWebhook {
@@ -571,18 +584,20 @@ declare module 'vona-module-a-pay' {
571
584
  export interface ServiceWebhook {
572
585
  get $beanFullName(): 'a-pay.service.webhook';
573
586
  get $onionName(): 'a-pay:webhook';
574
-
575
- }
587
+
588
+ }
576
589
  }
577
590
  /** service: end */
578
591
  /** service: begin */
579
592
  import type { ServiceOutbox } from '../service/outbox.ts';
593
+ import type { ServicePaymentCallback } from '../service/paymentCallback.ts';
580
594
  import type { ServicePaymentSession } from '../service/paymentSession.ts';
581
595
  import type { ServiceProviderOperation } from '../service/providerOperation.ts';
582
596
  import type { ServiceRefundOperation } from '../service/refundOperation.ts';
583
597
  import type { ServiceWebhook } from '../service/webhook.ts';
584
598
  export interface IModuleService {
585
599
  'outbox': ServiceOutbox;
600
+ 'paymentCallback': ServicePaymentCallback;
586
601
  'paymentSession': ServicePaymentSession;
587
602
  'providerOperation': ServiceProviderOperation;
588
603
  'refundOperation': ServiceRefundOperation;
@@ -595,6 +610,7 @@ import 'vona';
595
610
  declare module 'vona' {
596
611
  export interface IBeanRecordGeneral {
597
612
  'a-pay.service.outbox': ServiceOutbox;
613
+ 'a-pay.service.paymentCallback': ServicePaymentCallback;
598
614
  'a-pay.service.paymentSession': ServicePaymentSession;
599
615
  'a-pay.service.providerOperation': ServiceProviderOperation;
600
616
  'a-pay.service.refundOperation': ServiceRefundOperation;
@@ -609,17 +625,17 @@ export * from '../bean/meta.version.ts';
609
625
  import type { IMetaOptionsIndex } from 'vona-module-a-index';
610
626
  import 'vona-module-a-meta';
611
627
  declare module 'vona-module-a-meta' {
612
-
628
+
613
629
  export interface IMetaRecord {
614
630
  'a-pay:index': IMetaOptionsIndex;
615
631
  'a-pay:redlock': never;
616
632
  'a-pay:version': never;
617
633
  }
618
634
 
619
-
635
+
620
636
  }
621
637
  declare module 'vona-module-a-pay' {
622
-
638
+
623
639
  export interface MetaIndex {
624
640
  /** @internal */
625
641
  get scope(): ScopeModuleAPay;
@@ -639,7 +655,7 @@ declare module 'vona-module-a-pay' {
639
655
  export interface MetaRedlock {
640
656
  get $beanFullName(): 'a-pay.meta.redlock';
641
657
  get $onionName(): 'a-pay:redlock';
642
-
658
+
643
659
  }
644
660
 
645
661
  export interface MetaVersion {
@@ -650,8 +666,8 @@ declare module 'vona-module-a-pay' {
650
666
  export interface MetaVersion {
651
667
  get $beanFullName(): 'a-pay.meta.version';
652
668
  get $onionName(): 'a-pay:version';
653
-
654
- }
669
+
670
+ }
655
671
  }
656
672
  /** meta: end */
657
673
  /** meta redlock: begin */
@@ -662,15 +678,15 @@ export * from '../bean/queue.outboxDispatch.ts';
662
678
 
663
679
  import { type IDecoratorQueueOptions } from 'vona-module-a-queue';
664
680
  declare module 'vona-module-a-queue' {
665
-
681
+
666
682
  export interface IQueueRecord {
667
683
  'a-pay:outboxDispatch': IDecoratorQueueOptions;
668
684
  }
669
685
 
670
-
686
+
671
687
  }
672
688
  declare module 'vona-module-a-pay' {
673
-
689
+
674
690
  export interface QueueOutboxDispatch {
675
691
  /** @internal */
676
692
  get scope(): ScopeModuleAPay;
@@ -680,7 +696,7 @@ declare module 'vona-module-a-pay' {
680
696
  get $beanFullName(): 'a-pay.queue.outboxDispatch';
681
697
  get $onionName(): 'a-pay:outboxDispatch';
682
698
  get $onionOptions(): IDecoratorQueueOptions;
683
- }
699
+ }
684
700
  }
685
701
  /** queue: end */
686
702
  /** queue: begin */
@@ -695,16 +711,16 @@ export * from '../bean/schedule.providerOperationDispatch.ts';
695
711
 
696
712
  import { type IDecoratorScheduleOptions } from 'vona-module-a-schedule';
697
713
  declare module 'vona-module-a-schedule' {
698
-
714
+
699
715
  export interface IScheduleRecord {
700
716
  'a-pay:outboxDispatch': IDecoratorScheduleOptions;
701
717
  'a-pay:providerOperationDispatch': IDecoratorScheduleOptions;
702
718
  }
703
719
 
704
-
720
+
705
721
  }
706
722
  declare module 'vona-module-a-pay' {
707
-
723
+
708
724
  export interface ScheduleOutboxDispatch {
709
725
  /** @internal */
710
726
  get scope(): ScopeModuleAPay;
@@ -725,7 +741,7 @@ declare module 'vona-module-a-pay' {
725
741
  get $beanFullName(): 'a-pay.schedule.providerOperationDispatch';
726
742
  get $onionName(): 'a-pay:providerOperationDispatch';
727
743
  get $onionOptions(): IDecoratorScheduleOptions;
728
- }
744
+ }
729
745
  }
730
746
  /** schedule: end */
731
747
  /** dto: begin */
@@ -737,17 +753,17 @@ import type { IDtoOptionsPaymentSessionView } from '../dto/paymentSessionView.ts
737
753
  import type { IDtoOptionsWebhookReceipt } from '../dto/webhookReceipt.tsx';
738
754
  import 'vona-module-a-web';
739
755
  declare module 'vona-module-a-web' {
740
-
756
+
741
757
  export interface IDtoRecord {
742
758
  'a-pay:paymentSessionStart': IDtoOptionsPaymentSessionStart;
743
759
  'a-pay:paymentSessionView': IDtoOptionsPaymentSessionView;
744
760
  'a-pay:webhookReceipt': IDtoOptionsWebhookReceipt;
745
761
  }
746
762
 
747
-
763
+
748
764
  }
749
765
  declare module 'vona-module-a-pay' {
750
-
766
+
751
767
  }
752
768
  /** dto: end */
753
769
  /** dto: begin */
@@ -755,7 +771,7 @@ import type { DtoPaymentSessionStart } from '../dto/paymentSessionStart.tsx';
755
771
  import type { DtoPaymentSessionView } from '../dto/paymentSessionView.tsx';
756
772
  import type { DtoWebhookReceipt } from '../dto/webhookReceipt.tsx';
757
773
  declare module 'vona-module-a-pay' {
758
-
774
+
759
775
  export interface IDtoOptionsPaymentSessionStart {
760
776
  fields?: TypeEntityOptionsFields<DtoPaymentSessionStart, IDtoOptionsPaymentSessionStart[TypeSymbolKeyFieldsMore]>;
761
777
  }
@@ -770,22 +786,36 @@ declare module 'vona-module-a-pay' {
770
786
  }
771
787
  /** dto: end */
772
788
  /** controller: begin */
789
+ export * from '../controller/paymentCallback.ts';
773
790
  export * from '../controller/paymentSession.ts';
774
791
  export * from '../controller/webhook.ts';
792
+ import type { IControllerOptionsPaymentCallback } from '../controller/paymentCallback.ts';
775
793
  import type { IControllerOptionsPaymentSession } from '../controller/paymentSession.ts';
776
794
  import type { IControllerOptionsWebhook } from '../controller/webhook.ts';
777
795
  import 'vona-module-a-web';
778
796
  declare module 'vona-module-a-web' {
779
-
797
+
780
798
  export interface IControllerRecord {
781
- 'a-pay:paymentSession': IControllerOptionsPaymentSession;
799
+ 'a-pay:paymentCallback': IControllerOptionsPaymentCallback;
800
+ 'a-pay:paymentSession': IControllerOptionsPaymentSession;
782
801
  'a-pay:webhook': IControllerOptionsWebhook;
783
802
  }
784
803
 
785
-
804
+
786
805
  }
787
806
  declare module 'vona-module-a-pay' {
788
-
807
+
808
+ export interface ControllerPaymentCallback {
809
+ /** @internal */
810
+ get scope(): ScopeModuleAPay;
811
+ }
812
+
813
+ export interface ControllerPaymentCallback {
814
+ get $beanFullName(): 'a-pay.controller.paymentCallback';
815
+ get $onionName(): 'a-pay:paymentCallback';
816
+ get $onionOptions(): IControllerOptionsPaymentCallback;
817
+ }
818
+
789
819
  export interface ControllerPaymentSession {
790
820
  /** @internal */
791
821
  get scope(): ScopeModuleAPay;
@@ -806,16 +836,22 @@ declare module 'vona-module-a-pay' {
806
836
  get $beanFullName(): 'a-pay.controller.webhook';
807
837
  get $onionName(): 'a-pay:webhook';
808
838
  get $onionOptions(): IControllerOptionsWebhook;
809
- }
839
+ }
810
840
  }
811
841
  /** controller: end */
812
842
  /** controller: begin */
813
843
  // @ts-ignore ignore
844
+ import type { ControllerPaymentCallback } from '../controller/paymentCallback.ts';
845
+ // @ts-ignore ignore
814
846
  import type { ControllerPaymentSession } from '../controller/paymentSession.ts';
815
847
  // @ts-ignore ignore
816
848
  import type { ControllerWebhook } from '../controller/webhook.ts';
817
849
  declare module 'vona-module-a-pay' {
818
-
850
+
851
+ export interface IControllerOptionsPaymentCallback {
852
+ actions?: TypeControllerOptionsActions<ControllerPaymentCallback>;
853
+ }
854
+
819
855
  export interface IControllerOptionsPaymentSession {
820
856
  actions?: TypeControllerOptionsActions<ControllerPaymentSession>;
821
857
  }
@@ -825,12 +861,15 @@ declare module 'vona-module-a-pay' {
825
861
  }
826
862
  }
827
863
  declare module 'vona-module-a-web' {
828
- export interface IApiPathPostRecord{
829
- '/payment-session/:id/start': undefined;
830
- '/pay/webhook/:providerName/:clientName': undefined;
864
+ export interface IApiPathGetRecord{
865
+ '/pay/payment-callback/return': undefined;
866
+ '/pay/payment-callback/cancel': undefined;
867
+ '/pay/payment-session/:id': undefined;
831
868
  }
832
- export interface IApiPathGetRecord{
833
- '/payment-session/:id': undefined;
869
+ export interface IApiPathPostRecord{
870
+ '/pay/payment-session/:id/start': undefined;
871
+ '/pay/payment-session/:id/reconcile': undefined;
872
+ '/pay/webhook/:providerName/:clientName': undefined;
834
873
  }
835
874
 
836
875
  }
@@ -861,11 +900,11 @@ declare module 'vona' {
861
900
  export interface IBeanScopeContainer {
862
901
  pay: ScopeModuleAPay;
863
902
  }
864
-
865
-
866
903
 
867
-
868
904
 
869
-
905
+
906
+
907
+
908
+
870
909
  }
871
910
  /** scope: end */
@@ -24,6 +24,24 @@ export class BeanPayScene extends BeanBase {
24
24
  return onionSlice.beanOptions.options as IDecoratorPaySceneOptions;
25
25
  }
26
26
 
27
+ async getAvailableProviderCandidates<N extends keyof IPaySceneRecord>(
28
+ paySceneName: N,
29
+ input: {
30
+ userId: TableIdentity;
31
+ businessReference: string;
32
+ amountMinor: number;
33
+ currency: string;
34
+ providerCandidateKey?: string;
35
+ },
36
+ ) {
37
+ const options = this.getOptions(paySceneName);
38
+ return await this._getAvailableProviderCandidates(options, {
39
+ payScene: paySceneName,
40
+ ...input,
41
+ providers: this._getProviderCandidates(options),
42
+ });
43
+ }
44
+
27
45
  async resolveProvider<N extends keyof IPaySceneRecord>(
28
46
  paySceneName: N,
29
47
  input: {
@@ -31,10 +49,28 @@ export class BeanPayScene extends BeanBase {
31
49
  businessReference: string;
32
50
  amountMinor: number;
33
51
  currency: string;
52
+ providerCandidateKey?: string;
34
53
  },
35
54
  ) {
36
55
  const options = this.getOptions(paySceneName);
37
- const providers = this._getProviderCandidates(options);
56
+ const allProviders = this._getProviderCandidates(options);
57
+ if (
58
+ input.providerCandidateKey &&
59
+ !allProviders.some(item => item.key === input.providerCandidateKey)
60
+ ) {
61
+ this.app.throw(422, 'payment provider candidate is unavailable');
62
+ }
63
+ const providers = await this._getAvailableProviderCandidates(options, {
64
+ payScene: paySceneName,
65
+ ...input,
66
+ providers: allProviders,
67
+ });
68
+ if (
69
+ input.providerCandidateKey &&
70
+ !providers.some(item => item.key === input.providerCandidateKey)
71
+ ) {
72
+ this.app.throw(422, 'payment provider candidate is unavailable');
73
+ }
38
74
  const candidateKey = await this._resolveProviderCandidateKey(
39
75
  paySceneName,
40
76
  options,
@@ -59,6 +95,27 @@ export class BeanPayScene extends BeanBase {
59
95
  };
60
96
  }
61
97
 
98
+ private async _getAvailableProviderCandidates(
99
+ options: IDecoratorPaySceneOptions,
100
+ input: Omit<
101
+ Parameters<NonNullable<IDecoratorPaySceneOptions['isProviderAvailable']>>[1],
102
+ 'candidate'
103
+ >,
104
+ ) {
105
+ const providers = input.providers;
106
+ const available = await Promise.all(
107
+ providers.map(async candidate => ({
108
+ candidate,
109
+ available: options.isProviderAvailable
110
+ ? await options.isProviderAvailable(this.ctx, { ...input, candidate })
111
+ : true,
112
+ })),
113
+ );
114
+ const result = available.filter(item => item.available).map(item => item.candidate);
115
+ if (!result.length) this.app.throw(409, 'payment scene has no available provider candidates');
116
+ return result;
117
+ }
118
+
62
119
  private _getProviderCandidates(options: IDecoratorPaySceneOptions) {
63
120
  const providers = options.providers;
64
121
  if (!providers?.length) this.app.throw(500, 'payment scene has no provider candidates');
@@ -84,8 +141,15 @@ export class BeanPayScene extends BeanBase {
84
141
  businessReference: string;
85
142
  amountMinor: number;
86
143
  currency: string;
144
+ providerCandidateKey?: string;
87
145
  },
88
146
  ) {
147
+ if (input.providerCandidateKey) {
148
+ if (!providers.some(item => item.key === input.providerCandidateKey)) {
149
+ this.app.throw(422, 'payment provider candidate is unavailable');
150
+ }
151
+ return input.providerCandidateKey;
152
+ }
89
153
  if (providers.length === 1 && !options.resolveProvider) return providers[0]!.key;
90
154
  if (!options.resolveProvider) {
91
155
  this.app.throw(500, 'payment scene requires a provider resolver for multiple candidates');
@@ -3,6 +3,8 @@ import { BeanRedlockBase } from 'vona-module-a-redlock';
3
3
 
4
4
  export type TypeRedlockLockResource =
5
5
  | `pay.providerOperation.start.${string}`
6
+ | `pay.providerOperation.confirm.${string}`
7
+ | `pay.providerOperation.query.${string}`
6
8
  | `pay.webhook.${string}`;
7
9
  export type TypeRedlockLockIsolateResource = never;
8
10
 
@@ -0,0 +1,32 @@
1
+ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
2
+
3
+ import { BeanBase } from 'vona';
4
+ import { Passport } from 'vona-module-a-user';
5
+ import { Arg, Controller, Web } from 'vona-module-a-web';
6
+
7
+ export interface IControllerOptionsPaymentCallback extends IDecoratorControllerOptions {}
8
+
9
+ @Controller<IControllerOptionsPaymentCallback>('payment-callback')
10
+ export class ControllerPaymentCallback extends BeanBase {
11
+ @Web.get('return')
12
+ @Passport.public()
13
+ async returned(@Arg.query('state') state: string) {
14
+ await this._handle('return', state);
15
+ }
16
+
17
+ @Web.get('cancel')
18
+ @Passport.public()
19
+ async cancelled(@Arg.query('state') state: string) {
20
+ await this._handle('cancel', state);
21
+ }
22
+
23
+ private async _handle(purpose: 'return' | 'cancel', token: string) {
24
+ const callback = await this.scope.service.paymentCallback.consume(purpose, token);
25
+ if (purpose === 'return') {
26
+ await this.scope.service.providerOperation.confirm(callback.paymentSessionId);
27
+ } else {
28
+ await this.scope.service.providerOperation.reconcile(callback.paymentSessionId);
29
+ }
30
+ this.ctx.redirect(callback.continuationPath);
31
+ }
32
+ }
@@ -23,14 +23,21 @@ export class ControllerPaymentSession extends BeanBase {
23
23
  this.app.throw(404, 'payment session not found');
24
24
  }
25
25
  const started = await this.scope.service.paymentSession.start(session.id);
26
- return {
27
- id: started.id,
28
- state: started.state,
29
- providerName: started.providerName,
30
- nextAction: started.nextAction,
31
- amountMinor: started.amountMinor,
32
- currency: started.currency,
33
- };
26
+ return this._toView(started);
27
+ }
28
+
29
+ @Web.post(':id/reconcile')
30
+ @Api.body(DtoPaymentSessionView)
31
+ async reconcile(
32
+ @Arg.user() user: IUser,
33
+ @Arg.param('id', v.tableIdentity()) id: TableIdentity,
34
+ ): Promise<DtoPaymentSessionView> {
35
+ const session = await this.scope.model.paymentSession.getById(id);
36
+ if (!session || String(session.userId) !== String(user.id)) {
37
+ this.app.throw(404, 'payment session not found');
38
+ }
39
+ const reconciled = await this.scope.service.providerOperation.reconcile(session.id);
40
+ return this._toView(reconciled);
34
41
  }
35
42
 
36
43
  @Web.get(':id')
@@ -43,6 +50,17 @@ export class ControllerPaymentSession extends BeanBase {
43
50
  if (!session || String(session.userId) !== String(user.id)) {
44
51
  this.app.throw(404, 'payment session not found');
45
52
  }
53
+ return this._toView(session);
54
+ }
55
+
56
+ private _toView(session: {
57
+ id: TableIdentity;
58
+ state: DtoPaymentSessionView['state'];
59
+ providerName: string;
60
+ nextAction?: DtoPaymentSessionView['nextAction'];
61
+ amountMinor: number;
62
+ currency: string;
63
+ }): DtoPaymentSessionView {
46
64
  return {
47
65
  id: session.id,
48
66
  state: session.state,