@commercetools/connect-payments-sdk 0.0.1 → 0.0.4

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 (63) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/api/context/types/request-context.type.d.ts +2 -2
  3. package/dist/api/handlers/config.handler.d.ts +1 -1
  4. package/dist/api/handlers/status.handler.d.ts +12 -6
  5. package/dist/api/handlers/status.handler.js +4 -7
  6. package/dist/api/handlers/types/handler.type.d.ts +2 -2
  7. package/dist/api/hooks/jwt-auth.hook.d.ts +16 -0
  8. package/dist/api/hooks/jwt-auth.hook.js +22 -0
  9. package/dist/api/hooks/oauth2-auth.hook.d.ts +16 -0
  10. package/dist/api/hooks/oauth2-auth.hook.js +22 -0
  11. package/dist/api/hooks/session-auth.hook.d.ts +14 -13
  12. package/dist/api/hooks/session-auth.hook.js +18 -27
  13. package/dist/api/hooks/types/hook.type.d.ts +10 -0
  14. package/dist/api/index.d.ts +3 -0
  15. package/dist/api/index.js +3 -0
  16. package/dist/commercetools/index.d.ts +2 -0
  17. package/dist/commercetools/services/ct-authorization.service.d.ts +18 -0
  18. package/dist/commercetools/services/ct-authorization.service.js +42 -0
  19. package/dist/commercetools/services/ct-payment.service.d.ts +6 -1
  20. package/dist/commercetools/services/ct-payment.service.js +72 -0
  21. package/dist/commercetools/services/ct-session.service.d.ts +16 -0
  22. package/dist/commercetools/services/ct-session.service.js +45 -0
  23. package/dist/commercetools/types/api.type.d.ts +11 -0
  24. package/dist/commercetools/types/authorization.type.d.ts +9 -0
  25. package/dist/commercetools/types/payment.type.d.ts +10 -0
  26. package/dist/commercetools/types/session.type.d.ts +28 -0
  27. package/dist/commercetools/types/session.type.js +2 -0
  28. package/dist/errorx/errorx.d.ts +4 -1
  29. package/dist/errorx/errorx.js +15 -4
  30. package/dist/index.d.ts +8 -8
  31. package/dist/index.js +38 -12
  32. package/dist/security/authn/authns.d.ts +49 -0
  33. package/dist/security/authn/authns.js +123 -0
  34. package/dist/security/authn/bearer-utils.d.ts +1 -0
  35. package/dist/security/authn/bearer-utils.js +19 -0
  36. package/dist/security/authn/jwt-authn-manager.d.ts +12 -0
  37. package/dist/security/authn/jwt-authn-manager.js +33 -0
  38. package/dist/security/authn/oauth2-authn-manager.d.ts +17 -0
  39. package/dist/security/authn/oauth2-authn-manager.js +65 -0
  40. package/dist/security/authn/session-authn-manager.d.ts +10 -0
  41. package/dist/security/authn/session-authn-manager.js +25 -0
  42. package/dist/security/authn/types/authn.type.d.ts +27 -0
  43. package/dist/security/authn/types/authn.type.js +2 -0
  44. package/dist/security/index.d.ts +6 -2
  45. package/dist/security/index.js +6 -2
  46. package/dist/security/services/jwt.service.d.ts +10 -0
  47. package/dist/security/services/jwt.service.js +40 -0
  48. package/dist/security/services/oauth2.service.d.ts +7 -14
  49. package/dist/security/services/oauth2.service.js +22 -35
  50. package/dist/security/services/types/jwt.type.d.ts +5 -0
  51. package/dist/security/services/types/jwt.type.js +2 -0
  52. package/dist/security/services/types/oauth2.type.d.ts +14 -0
  53. package/dist/security/services/types/oauth2.type.js +2 -0
  54. package/package.json +5 -3
  55. package/.github/workflows/ci.yml +0 -34
  56. package/.github/workflows/release.yml +0 -46
  57. package/.husky/pre-commit +0 -4
  58. package/dist/security/auth/session.auth.d.ts +0 -20
  59. package/dist/security/auth/session.auth.js +0 -54
  60. package/dist/security/types/oauth2.type.d.ts +0 -13
  61. package/dist/security/types/session.type.d.ts +0 -10
  62. /package/dist/{security/types/oauth2.type.js → api/hooks/types/hook.type.js} +0 -0
  63. /package/dist/{security/types/session.type.js → commercetools/types/authorization.type.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @commercetools/connect-payments-sdk
2
2
 
3
+ ## 0.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - ab6200f: Support jwt, oauth2 authentication and support for authority based authorization
8
+
9
+ ## 0.0.3
10
+
11
+ ### Patch Changes
12
+
13
+ - f3f238f: Authentication and authorization layer via session API
14
+
3
15
  ## 0.0.1
4
16
 
5
17
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import { PaymentSessionData } from '../../../security';
1
+ import { Authentication } from '../../../security';
2
2
  /**
3
3
  * Context provider interface
4
4
  */
@@ -12,5 +12,5 @@ export interface ContextProvider<T> {
12
12
  export type RequestContextData = {
13
13
  correlationId: string;
14
14
  requestId: string;
15
- sessionData?: PaymentSessionData;
15
+ authentication?: Authentication;
16
16
  };
@@ -1,4 +1,4 @@
1
1
  import { HandlerResponse } from './types/handler.type';
2
2
  export declare const configHandler: (options: {
3
3
  configuration: () => Promise<object> | object;
4
- }) => () => Promise<HandlerResponse>;
4
+ }) => () => Promise<HandlerResponse<object>>;
@@ -1,5 +1,12 @@
1
- import { Oauth2Service } from '../../security/types/oauth2.type';
1
+ import { CommercetoolsAuthorizationService } from '../../commercetools';
2
2
  import { HandlerResponse } from './types/handler.type';
3
+ type HealthCheckStatus = {
4
+ status: 'OK' | 'Partially Available' | 'Unavailable';
5
+ timestamp: string;
6
+ checks: HealthCheckResult[];
7
+ version: string;
8
+ metadata?: object;
9
+ };
3
10
  export type HealthCheckResult = {
4
11
  name: string;
5
12
  status: 'UP' | 'DOWN';
@@ -10,16 +17,15 @@ export declare const statusHandler: (options: {
10
17
  timeout: number;
11
18
  checks: HealthCheck[];
12
19
  metadataFn?: () => Promise<object> | object;
13
- }) => () => Promise<HandlerResponse>;
20
+ }) => () => Promise<HandlerResponse<HealthCheckStatus>>;
14
21
  /**
15
22
  * Check if CoCo permissions are available
16
23
  * @param opts
17
24
  * @returns
18
25
  */
19
- export declare const healthCheckCoCoPermissions: (opts: {
20
- oauth2Service: Oauth2Service;
21
- clientId: string;
22
- clientSecret: string;
26
+ export declare const healthCheckCommercetoolsPermissions: (opts: {
27
+ ctAuthorizationService: CommercetoolsAuthorizationService;
23
28
  projectKey: string;
24
29
  requiredPermissions: string[];
25
30
  }) => () => Promise<HealthCheckResult>;
31
+ export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.healthCheckCoCoPermissions = exports.statusHandler = void 0;
3
+ exports.healthCheckCommercetoolsPermissions = exports.statusHandler = void 0;
4
4
  const statusHandler = (options) => async () => {
5
5
  const status = {
6
6
  timestamp: new Date().toISOString(),
@@ -45,11 +45,8 @@ exports.statusHandler = statusHandler;
45
45
  * @param opts
46
46
  * @returns
47
47
  */
48
- const healthCheckCoCoPermissions = (opts) => async () => {
49
- const token = await opts.oauth2Service.getAccessToken({
50
- clientId: opts.clientId,
51
- clientSecret: opts.clientSecret,
52
- });
48
+ const healthCheckCommercetoolsPermissions = (opts) => async () => {
49
+ const token = await opts.ctAuthorizationService.getAccessToken();
53
50
  const foundAll = opts.requiredPermissions.every((currentScope) => token.scope.split(' ').some((scopeInToken) => scopeInToken === `${currentScope}:${opts.projectKey}`));
54
51
  if (foundAll) {
55
52
  return {
@@ -70,4 +67,4 @@ const healthCheckCoCoPermissions = (opts) => async () => {
70
67
  },
71
68
  };
72
69
  };
73
- exports.healthCheckCoCoPermissions = healthCheckCoCoPermissions;
70
+ exports.healthCheckCommercetoolsPermissions = healthCheckCommercetoolsPermissions;
@@ -1,5 +1,5 @@
1
- export type HandlerResponse = {
1
+ export type HandlerResponse<T> = {
2
2
  status: number;
3
- body?: object;
3
+ body: T;
4
4
  headers?: object;
5
5
  };
@@ -0,0 +1,16 @@
1
+ /// <reference types="node" />
2
+ import { IncomingHttpHeaders } from 'node:http';
3
+ import { JWTAuthenticationManager } from '../../security/authn/jwt-authn-manager';
4
+ import { ContextProvider, RequestContextData } from '../context/types/request-context.type';
5
+ import { AuthenticationHook } from './types/hook.type';
6
+ export declare class JWTAuthenticationHook implements AuthenticationHook {
7
+ private authenticationManager;
8
+ private contextProvider;
9
+ constructor(opts: {
10
+ authenticationManager: JWTAuthenticationManager;
11
+ contextProvider: ContextProvider<RequestContextData>;
12
+ });
13
+ authenticate(): (request: {
14
+ headers: IncomingHttpHeaders;
15
+ }) => Promise<void>;
16
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JWTAuthenticationHook = void 0;
4
+ const security_1 = require("../../security");
5
+ class JWTAuthenticationHook {
6
+ authenticationManager;
7
+ contextProvider;
8
+ constructor(opts) {
9
+ this.authenticationManager = opts.authenticationManager;
10
+ this.contextProvider = opts.contextProvider;
11
+ }
12
+ authenticate() {
13
+ return async (request) => {
14
+ const authorizationHeader = new security_1.HeaderBasedAuthentication(request.headers['authorization']);
15
+ const authn = await this.authenticationManager.authenticate(authorizationHeader);
16
+ this.contextProvider.updateContextData({
17
+ authentication: authn,
18
+ });
19
+ };
20
+ }
21
+ }
22
+ exports.JWTAuthenticationHook = JWTAuthenticationHook;
@@ -0,0 +1,16 @@
1
+ /// <reference types="node" />
2
+ import { IncomingHttpHeaders } from 'node:http';
3
+ import { Oauth2AuthenticationManager } from '../../security/authn/oauth2-authn-manager';
4
+ import { ContextProvider, RequestContextData } from '../context/types/request-context.type';
5
+ import { AuthenticationHook } from './types/hook.type';
6
+ export declare class Oauth2AuthenticationHook implements AuthenticationHook {
7
+ private authenticationManager;
8
+ private contextProvider;
9
+ constructor(opts: {
10
+ authenticationManager: Oauth2AuthenticationManager;
11
+ contextProvider: ContextProvider<RequestContextData>;
12
+ });
13
+ authenticate(): (request: {
14
+ headers: IncomingHttpHeaders;
15
+ }) => Promise<void>;
16
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Oauth2AuthenticationHook = void 0;
4
+ const security_1 = require("../../security");
5
+ class Oauth2AuthenticationHook {
6
+ authenticationManager;
7
+ contextProvider;
8
+ constructor(opts) {
9
+ this.authenticationManager = opts.authenticationManager;
10
+ this.contextProvider = opts.contextProvider;
11
+ }
12
+ authenticate() {
13
+ return async (request) => {
14
+ const authorizationHeader = new security_1.HeaderBasedAuthentication(request.headers['authorization']);
15
+ const authn = await this.authenticationManager.authenticate(authorizationHeader);
16
+ this.contextProvider.updateContextData({
17
+ authentication: authn,
18
+ });
19
+ };
20
+ }
21
+ }
22
+ exports.Oauth2AuthenticationHook = Oauth2AuthenticationHook;
@@ -1,15 +1,16 @@
1
+ /// <reference types="node" />
1
2
  import { IncomingHttpHeaders } from 'node:http';
2
- import { SessionAuthenticator } from '../../security/types/session.type';
3
3
  import { ContextProvider, RequestContextData } from '../context/types/request-context.type';
4
- export type SessionAuthHookOptions = {
5
- sessionAuthenticator: SessionAuthenticator;
6
- contextProvider: ContextProvider<RequestContextData>;
7
- };
8
- /**
9
- * Fastify hook to authenticate and getting session information
10
- * @param opts
11
- * @returns
12
- */
13
- export declare const sessionAuthHook: (opts: SessionAuthHookOptions) => (req: {
14
- headers: IncomingHttpHeaders;
15
- }) => Promise<void>;
4
+ import { SessionAuthenticationManager } from '../../security';
5
+ import { AuthenticationHook } from './types/hook.type';
6
+ export declare class SessionAuthenticationHook implements AuthenticationHook {
7
+ private authenticationManager;
8
+ private contextProvider;
9
+ constructor(opts: {
10
+ authenticationManager: SessionAuthenticationManager;
11
+ contextProvider: ContextProvider<RequestContextData>;
12
+ });
13
+ authenticate(): (request: {
14
+ headers: IncomingHttpHeaders;
15
+ }) => Promise<void>;
16
+ }
@@ -1,31 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sessionAuthHook = void 0;
4
- const errorx_1 = require("../../errorx/errorx");
5
- /**
6
- * Fastify hook to authenticate and getting session information
7
- * @param opts
8
- * @returns
9
- */
10
- const sessionAuthHook = (opts) => async (req) => {
11
- const sessionId = req.headers['x-session-id'];
12
- if (sessionId) {
13
- const sessionData = await opts.sessionAuthenticator.introspectSession({
14
- sessionId,
15
- });
16
- opts.contextProvider.updateContextData({
17
- sessionData,
18
- });
19
- //updateSessionContext(sessionData);
3
+ exports.SessionAuthenticationHook = void 0;
4
+ const security_1 = require("../../security");
5
+ class SessionAuthenticationHook {
6
+ authenticationManager;
7
+ contextProvider;
8
+ constructor(opts) {
9
+ this.authenticationManager = opts.authenticationManager;
10
+ this.contextProvider = opts.contextProvider;
20
11
  }
21
- else {
22
- //TODO: throw a forbidden error
23
- throw new errorx_1.ErrorAuthErrorResponse({
24
- privateMessage: 'Session is not valid',
25
- fields: {
26
- id: sessionId,
27
- },
28
- });
12
+ authenticate() {
13
+ return async (request) => {
14
+ const sessionIdAuthn = new security_1.HeaderBasedAuthentication(request.headers['x-session-id']);
15
+ const authn = await this.authenticationManager.authenticate(sessionIdAuthn);
16
+ this.contextProvider.updateContextData({
17
+ authentication: authn,
18
+ });
19
+ };
29
20
  }
30
- };
31
- exports.sessionAuthHook = sessionAuthHook;
21
+ }
22
+ exports.SessionAuthenticationHook = SessionAuthenticationHook;
@@ -0,0 +1,10 @@
1
+ /// <reference types="node" />
2
+ import { IncomingHttpHeaders } from 'node:http';
3
+ export interface AuthenticationHook {
4
+ authenticate(): (request: {
5
+ headers: IncomingHttpHeaders;
6
+ }) => Promise<void>;
7
+ }
8
+ export interface AuthorizationHook {
9
+ authorize(...authorities: string[]): () => Promise<void>;
10
+ }
@@ -2,4 +2,7 @@ export * from './context/request-context.provider';
2
2
  export * from './context/types/request-context.type';
3
3
  export * from './handlers/config.handler';
4
4
  export * from './handlers/status.handler';
5
+ export * from './hooks/jwt-auth.hook';
6
+ export * from './hooks/oauth2-auth.hook';
5
7
  export * from './hooks/session-auth.hook';
8
+ export * from './hooks/types/hook.type';
package/dist/api/index.js CHANGED
@@ -18,4 +18,7 @@ __exportStar(require("./context/request-context.provider"), exports);
18
18
  __exportStar(require("./context/types/request-context.type"), exports);
19
19
  __exportStar(require("./handlers/config.handler"), exports);
20
20
  __exportStar(require("./handlers/status.handler"), exports);
21
+ __exportStar(require("./hooks/jwt-auth.hook"), exports);
22
+ __exportStar(require("./hooks/oauth2-auth.hook"), exports);
21
23
  __exportStar(require("./hooks/session-auth.hook"), exports);
24
+ __exportStar(require("./hooks/types/hook.type"), exports);
@@ -1,2 +1,4 @@
1
1
  export { CartService as CommercetoolsCartService } from './types/cart.type';
2
2
  export { PaymentService as CommercetoolsPaymentService, TransactionData, UpdatePayment } from './types/payment.type';
3
+ export { SessionService as CommercetoolsSessionService, Session } from './types/session.type';
4
+ export { AuthorizationService as CommercetoolsAuthorizationService } from './types/authorization.type';
@@ -0,0 +1,18 @@
1
+ import { Fetch } from '../../fetch/types/fetch.type';
2
+ import { Logger } from '../../logger';
3
+ import { AuthorizationService, CommercetoolsToken } from '../types/authorization.type';
4
+ export declare class DefaultAuthorizationService implements AuthorizationService {
5
+ private authUrl;
6
+ private clientId;
7
+ private clientSecret;
8
+ private fetch;
9
+ private logger?;
10
+ constructor(opts: {
11
+ authUrl: string;
12
+ clientId: string;
13
+ clientSecret: string;
14
+ fetch: Fetch;
15
+ logger?: Logger;
16
+ });
17
+ getAccessToken(): Promise<CommercetoolsToken>;
18
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultAuthorizationService = void 0;
4
+ const errorx_1 = require("../../errorx/errorx");
5
+ class DefaultAuthorizationService {
6
+ authUrl;
7
+ clientId;
8
+ clientSecret;
9
+ fetch;
10
+ logger;
11
+ constructor(opts) {
12
+ this.authUrl = opts.authUrl;
13
+ this.clientId = opts.clientId;
14
+ this.clientSecret = opts.clientSecret;
15
+ this.fetch = opts.fetch;
16
+ this.logger = opts.logger;
17
+ }
18
+ async getAccessToken() {
19
+ const encodedCredentials = btoa(`${this.clientId}:${this.clientSecret}`);
20
+ const urlencoded = new URLSearchParams();
21
+ urlencoded.append('grant_type', 'client_credentials');
22
+ const response = await this.fetch(`${this.authUrl}/oauth/token`, {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/x-www-form-urlencoded',
26
+ Authorization: `Basic ${encodedCredentials}`,
27
+ },
28
+ body: urlencoded,
29
+ });
30
+ if (!response.ok) {
31
+ throw new errorx_1.ErrorGeneral(undefined, {
32
+ privateMessage: 'Failed to get auth token',
33
+ privateFields: {
34
+ responseStatus: response.status,
35
+ responseText: await response.text(),
36
+ },
37
+ });
38
+ }
39
+ return (await response.json());
40
+ }
41
+ }
42
+ exports.DefaultAuthorizationService = DefaultAuthorizationService;
@@ -1,5 +1,5 @@
1
1
  import { Payment, PaymentDraft } from '@commercetools/platform-sdk';
2
- import { GetPayment, PaymentService, PaymentServiceOptions, UpdatePayment } from '../types/payment.type';
2
+ import { GetPayment, PaymentModificationValidation, PaymentModificationValidationResult, PaymentService, PaymentServiceOptions, UpdatePayment } from '../types/payment.type';
3
3
  /**
4
4
  * This is the default implementation of the PaymentService interface.
5
5
  */
@@ -9,6 +9,7 @@ export declare class DefaultPaymentService implements PaymentService {
9
9
  getPayment(opts: GetPayment): Promise<Payment>;
10
10
  createPayment(draft: PaymentDraft): Promise<Payment>;
11
11
  updatePayment(opts: UpdatePayment): Promise<Payment>;
12
+ validatePaymentModification(opts: PaymentModificationValidation): PaymentModificationValidationResult;
12
13
  private consolidateUpdateActions;
13
14
  private populateSetInterfaceIdAction;
14
15
  private populateChangeTransactionInteractionId;
@@ -17,4 +18,8 @@ export declare class DefaultPaymentService implements PaymentService {
17
18
  private populateSetPaymentMethod;
18
19
  private findMatchingTransactions;
19
20
  private consolidateTransactionChanges;
21
+ private validateCancelAuthorization;
22
+ private validateCapturePayment;
23
+ private validateRefundPayment;
24
+ private calculateTotalAmount;
20
25
  }
@@ -44,6 +44,18 @@ class DefaultPaymentService {
44
44
  }
45
45
  throw err;
46
46
  }
47
+ validatePaymentModification(opts) {
48
+ switch (opts.type) {
49
+ case 'cancelAuthorization':
50
+ return this.validateCancelAuthorization(opts.payment);
51
+ case 'capturePayment':
52
+ return this.validateCapturePayment(opts.payment, opts.amount);
53
+ case 'refundPayment':
54
+ return this.validateRefundPayment(opts.payment, opts.amount);
55
+ default:
56
+ throw new Error(`Invalid payment modification type: ${opts.type}`);
57
+ }
58
+ }
47
59
  consolidateUpdateActions(payment, updateInfo) {
48
60
  const actions = [];
49
61
  if (!payment.interfaceId && updateInfo.pspReference) {
@@ -125,5 +137,65 @@ class DefaultPaymentService {
125
137
  }
126
138
  return actions;
127
139
  }
140
+ validateCancelAuthorization(payment) {
141
+ const totalAuthorized = this.calculateTotalAmount(payment, 'Authorization', payment.amountPlanned.currencyCode);
142
+ if (totalAuthorized === 0) {
143
+ return { isValid: false, reason: `No authorization transaction found for resource ${payment.id}.` };
144
+ }
145
+ const totalCaptured = this.calculateTotalAmount(payment, 'Charge', payment.amountPlanned.currencyCode);
146
+ if (totalCaptured > 0) {
147
+ return { isValid: false, reason: `Resource ${payment.id} has already been charged.` };
148
+ }
149
+ return { isValid: true };
150
+ }
151
+ validateCapturePayment(payment, amount) {
152
+ if (payment.amountPlanned.currencyCode !== amount.currencyCode) {
153
+ return {
154
+ isValid: false,
155
+ reason: `Invalid currency ${amount.currencyCode} for resource ${payment.id}, expected ${payment.amountPlanned.currencyCode}`,
156
+ };
157
+ }
158
+ const totalAuthorized = this.calculateTotalAmount(payment, 'Authorization', amount.currencyCode);
159
+ if (totalAuthorized === 0) {
160
+ return { isValid: false, reason: `No authorization transaction found for resource ${payment.id}.` };
161
+ }
162
+ const totalCaptured = this.calculateTotalAmount(payment, 'Charge', amount.currencyCode);
163
+ const allowedAmount = totalAuthorized - totalCaptured;
164
+ if (amount.centAmount > allowedAmount) {
165
+ return {
166
+ isValid: false,
167
+ reason: `The amount to capture ${amount.centAmount} exceeds the allowed amount [${allowedAmount}]`,
168
+ };
169
+ }
170
+ return { isValid: true };
171
+ }
172
+ validateRefundPayment(payment, amount) {
173
+ if (payment.amountPlanned.currencyCode !== amount.currencyCode) {
174
+ return {
175
+ isValid: false,
176
+ reason: `Invalid currency ${amount.currencyCode} for resource ${payment.id}, expected ${payment.amountPlanned.currencyCode}`,
177
+ };
178
+ }
179
+ const totalCaptured = this.calculateTotalAmount(payment, 'Charge', amount.currencyCode);
180
+ if (totalCaptured === 0) {
181
+ return { isValid: false, reason: `No charge transaction found for resource ${payment.id}.` };
182
+ }
183
+ const totalRefunded = this.calculateTotalAmount(payment, 'Refund', amount.currencyCode);
184
+ const allowedAmount = totalCaptured - totalRefunded;
185
+ if (amount.centAmount > allowedAmount) {
186
+ return {
187
+ isValid: false,
188
+ reason: `The amount to refund ${amount.centAmount} exceeds the allowed amount [${allowedAmount}]`,
189
+ };
190
+ }
191
+ return { isValid: true };
192
+ }
193
+ calculateTotalAmount(payment, type, currencyCode) {
194
+ return payment.transactions
195
+ .filter((transaction) => transaction.type === type &&
196
+ transaction.state === 'Success' &&
197
+ transaction.amount.currencyCode === currencyCode)
198
+ .reduce((total, transaction) => total + transaction.amount.centAmount, 0);
199
+ }
128
200
  }
129
201
  exports.DefaultPaymentService = DefaultPaymentService;
@@ -0,0 +1,16 @@
1
+ import { AuthorizationService, CommercetoolsToken } from '../types/authorization.type';
2
+ import { Session, SessionService } from '../types/session.type';
3
+ export declare class DefaultSessionService implements SessionService {
4
+ private authorizationService;
5
+ private sessionUrl;
6
+ private projectKey;
7
+ protected token: CommercetoolsToken;
8
+ constructor(opts: {
9
+ authorizationService: AuthorizationService;
10
+ sessionUrl: string;
11
+ projectKey: string;
12
+ });
13
+ verifySession(sessionId: string): Promise<Session>;
14
+ getCartFromSession(session: Session): string;
15
+ getAllowedPaymentMethodsFromSession(session: Session): string[];
16
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultSessionService = void 0;
4
+ const errorx_1 = require("../../errorx/errorx");
5
+ class DefaultSessionService {
6
+ authorizationService;
7
+ sessionUrl;
8
+ projectKey;
9
+ token;
10
+ constructor(opts) {
11
+ this.authorizationService = opts.authorizationService;
12
+ this.sessionUrl = opts.sessionUrl;
13
+ this.projectKey = opts.projectKey;
14
+ }
15
+ async verifySession(sessionId) {
16
+ if (!this.token) {
17
+ this.token = await this.authorizationService.getAccessToken();
18
+ }
19
+ const res = await fetch(`${this.sessionUrl}/${this.projectKey}/sessions/${sessionId}`, {
20
+ method: 'GET',
21
+ headers: {
22
+ 'Content-Type': 'application/json',
23
+ Authorization: `Bearer ${this.token.access_token}`,
24
+ },
25
+ });
26
+ if (!res.ok) {
27
+ throw new errorx_1.ErrorGeneral('Could not get session');
28
+ }
29
+ const session = (await res.json());
30
+ if (session.state !== 'ACTIVE') {
31
+ throw new errorx_1.ErrorAuthErrorResponse();
32
+ }
33
+ return session;
34
+ }
35
+ getCartFromSession(session) {
36
+ if (!session.activeCart?.cartRef?.id) {
37
+ throw new errorx_1.ErrorAuthErrorResponse();
38
+ }
39
+ return session.activeCart.cartRef.id;
40
+ }
41
+ getAllowedPaymentMethodsFromSession(session) {
42
+ return session.metadata?.allowedPaymentMethods || [];
43
+ }
44
+ }
45
+ exports.DefaultSessionService = DefaultSessionService;
@@ -1,6 +1,7 @@
1
1
  import { Cart, Payment, PaymentDraft, PaymentPagedQueryResponse, PaymentUpdateAction } from '@commercetools/platform-sdk';
2
2
  import { ByProjectKeyRequestBuilder } from '@commercetools/platform-sdk/dist/declarations/src/generated/client/by-project-key-request-builder';
3
3
  export type CommercetoolsClient = ByProjectKeyRequestBuilder;
4
+ export type CommercetoolsSessionClient = ByProjectKeyRequestBuilder;
4
5
  export interface APIOpts {
5
6
  client: CommercetoolsClient;
6
7
  }
@@ -16,6 +17,16 @@ export type UpdatePayment = {
16
17
  resource: UpdateResource;
17
18
  actions: PaymentUpdateAction[];
18
19
  };
20
+ export type OauthToken = {
21
+ accessToken: string;
22
+ expiresIn: number;
23
+ tokenType: string;
24
+ scope: string;
25
+ refreshToken?: string;
26
+ };
27
+ export interface AuthorizationAPI {
28
+ getToken(): Promise<OauthToken>;
29
+ }
19
30
  export interface CartAPI {
20
31
  getCartById(id: string): Promise<Cart>;
21
32
  addPayment(opts: AddPayment): Promise<Cart>;
@@ -0,0 +1,9 @@
1
+ export type CommercetoolsToken = {
2
+ access_token: string;
3
+ token_type: string;
4
+ scope: string;
5
+ expires_in: number;
6
+ };
7
+ export interface AuthorizationService {
8
+ getAccessToken(): Promise<CommercetoolsToken>;
9
+ }
@@ -28,6 +28,15 @@ export type UpdatePayment = {
28
28
  transaction?: TransactionData;
29
29
  paymentMethod?: string;
30
30
  };
31
+ export type PaymentModificationValidation = {
32
+ payment: Payment;
33
+ amount: Money;
34
+ type: TransactionType;
35
+ };
36
+ export type PaymentModificationValidationResult = {
37
+ isValid: boolean;
38
+ reason?: string;
39
+ };
31
40
  /**
32
41
  * Payment service interface exposes methods to interact with the commercetools platform API.
33
42
  */
@@ -35,4 +44,5 @@ export interface PaymentService {
35
44
  getPayment(opts: GetPayment): Promise<Payment>;
36
45
  createPayment(draft: PaymentDraft): Promise<Payment>;
37
46
  updatePayment(opts: UpdatePayment): Promise<Payment>;
47
+ validatePaymentModification(opts: PaymentModificationValidation): PaymentModificationValidationResult;
38
48
  }
@@ -0,0 +1,28 @@
1
+ export type Session = {
2
+ id: string;
3
+ version: number;
4
+ createdAt: string;
5
+ lastModifiedAt: string;
6
+ state: 'ACTIVE' | 'EXPIRED';
7
+ activeCart?: {
8
+ cartRef: {
9
+ id: string;
10
+ };
11
+ };
12
+ customer?: {
13
+ customerRef: {
14
+ externalId?: string;
15
+ id?: string;
16
+ key?: string;
17
+ };
18
+ anonymousId?: string;
19
+ };
20
+ metadata?: {
21
+ [key: string]: unknown;
22
+ };
23
+ };
24
+ export interface SessionService {
25
+ verifySession(sessionId: string): Promise<Session>;
26
+ getCartFromSession(session: Session): string;
27
+ getAllowedPaymentMethodsFromSession(session: Session): string[];
28
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -44,7 +44,7 @@ export declare class MultiErrorx extends Error {
44
44
  * }
45
45
  */
46
46
  export declare class ErrorAuthErrorResponse extends Errorx {
47
- constructor(additionalOpts?: ErrorxAdditionalOpts);
47
+ constructor(message?: string, additionalOpts?: ErrorxAdditionalOpts, code?: string);
48
48
  }
49
49
  /**
50
50
  * General (https://docs.commercetools.com/api/errors#general)
@@ -148,3 +148,6 @@ export declare class ErrorResourceNotFound extends Errorx {
148
148
  export declare class ErrorConcurrentModification extends Errorx {
149
149
  constructor(resourceId: string, expectedVersion: number, currentVersion: number, additionalOpts?: ErrorxAdditionalOpts);
150
150
  }
151
+ export declare class ErrorMissingAuthenticationInfo extends Errorx {
152
+ constructor(additionalOpts?: ErrorxAdditionalOpts);
153
+ }