@commercetools/connect-payments-sdk 0.0.7 → 0.1.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @commercetools/connect-payments-sdk
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f782132: Added validations for the different payment modification operations
8
+ Improved logic for calculating the cart payment amount
9
+
3
10
  ## 0.0.7
4
11
 
5
12
  ### Patch Changes
@@ -9,6 +9,8 @@ export declare class DefaultCartService implements CartService {
9
9
  private ctAPI;
10
10
  constructor(opts: CartServiceOptions);
11
11
  getCart(opts: GetCart): Promise<Cart>;
12
- getPaymentAmount(opts: GetPaymentAmount): PaymentAmount;
12
+ getPaymentAmount(opts: GetPaymentAmount): Promise<PaymentAmount>;
13
13
  addPayment(opts: AddPayment): Promise<Cart>;
14
+ private calculateTotalPaidAmount;
15
+ private calculatePaymentAmount;
14
16
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DefaultCartService = void 0;
4
4
  const ct_api_error_1 = require("../errors/ct-api.error");
5
+ const __1 = require("../..");
5
6
  /**
6
7
  * Default implementation of the CartService interface.
7
8
  */
@@ -13,17 +14,36 @@ class DefaultCartService {
13
14
  async getCart(opts) {
14
15
  return await this.ctAPI.cart.getCartById(opts.id);
15
16
  }
16
- getPaymentAmount(opts) {
17
+ async getPaymentAmount(opts) {
18
+ const paidAmount = await this.calculateTotalPaidAmount(opts.cart);
19
+ let cartAmount;
17
20
  if (opts.cart.taxedPrice) {
18
- return {
21
+ cartAmount = {
19
22
  currencyCode: opts.cart.taxedPrice.totalGross.currencyCode,
20
23
  centAmount: opts.cart.taxedPrice.totalGross.centAmount,
21
24
  };
22
25
  }
23
- return {
24
- currencyCode: opts.cart.totalPrice.currencyCode,
25
- centAmount: opts.cart.totalPrice.centAmount,
26
- };
26
+ else {
27
+ cartAmount = {
28
+ currencyCode: opts.cart.totalPrice.currencyCode,
29
+ centAmount: opts.cart.totalPrice.centAmount,
30
+ };
31
+ }
32
+ if (paidAmount >= cartAmount.centAmount) {
33
+ throw new __1.ErrorInvalidOperation('The cart has already been paid in full', {
34
+ fields: {
35
+ resource: opts.cart.id,
36
+ paidAmount,
37
+ cartAmount,
38
+ },
39
+ });
40
+ }
41
+ else {
42
+ return {
43
+ currencyCode: cartAmount.currencyCode,
44
+ centAmount: cartAmount.centAmount - paidAmount,
45
+ };
46
+ }
27
47
  }
28
48
  async addPayment(opts) {
29
49
  const maxRetries = 6;
@@ -50,5 +70,18 @@ class DefaultCartService {
50
70
  }
51
71
  throw err;
52
72
  }
73
+ async calculateTotalPaidAmount(cart) {
74
+ if (!cart.paymentInfo || cart.paymentInfo.payments.length === 0) {
75
+ return 0;
76
+ }
77
+ const payments = await Promise.all(cart.paymentInfo.payments.map((p) => this.ctAPI.payment.getPaymentById(p.id)));
78
+ return payments.reduce((total, payment) => total + this.calculatePaymentAmount(payment), 0);
79
+ }
80
+ calculatePaymentAmount(payment) {
81
+ return payment.transactions
82
+ .filter((transaction) => (transaction.state === 'Success' || transaction.state === 'Pending') &&
83
+ (transaction.type === 'Authorization' || transaction.type === 'Charge'))
84
+ .reduce((total, transaction) => total + transaction.amount.centAmount, 0);
85
+ }
53
86
  }
54
87
  exports.DefaultCartService = DefaultCartService;
@@ -1,5 +1,5 @@
1
1
  import { Payment, PaymentDraft } from '@commercetools/platform-sdk';
2
- import { GetPayment, PaymentModificationValidation, PaymentModificationValidationResult, PaymentService, PaymentServiceOptions, UpdatePayment } from '../types/payment.type';
2
+ import { GetPayment, PaymentCancelAuthorizationValidation, PaymentChargeValidation, PaymentModificationValidationResult, PaymentRefundValidation, PaymentService, PaymentServiceOptions, UpdatePayment } from '../types/payment.type';
3
3
  /**
4
4
  * This is the default implementation of the PaymentService interface.
5
5
  */
@@ -9,7 +9,9 @@ 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
+ validatePaymentCancelAuthorization(opts: PaymentCancelAuthorizationValidation): PaymentModificationValidationResult;
13
+ validatePaymentCharge(opts: PaymentChargeValidation): PaymentModificationValidationResult;
14
+ validatePaymentRefund(opts: PaymentRefundValidation): PaymentModificationValidationResult;
13
15
  private consolidateUpdateActions;
14
16
  private populateSetInterfaceIdAction;
15
17
  private populateChangeTransactionInteractionId;
@@ -18,8 +20,5 @@ export declare class DefaultPaymentService implements PaymentService {
18
20
  private populateSetPaymentMethod;
19
21
  private findMatchingTransactions;
20
22
  private consolidateTransactionChanges;
21
- private validateCancelAuthorization;
22
- private validateCapturePayment;
23
- private validateRefundPayment;
24
23
  private calculateTotalAmount;
25
24
  }
@@ -44,17 +44,58 @@ 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}`);
47
+ validatePaymentCancelAuthorization(opts) {
48
+ const totalAuthorized = this.calculateTotalAmount(opts.payment, 'Authorization', opts.payment.amountPlanned.currencyCode);
49
+ if (totalAuthorized === 0) {
50
+ return { isValid: false, reason: `No authorization transaction found for resource ${opts.payment.id}.` };
51
+ }
52
+ const totalCaptured = this.calculateTotalAmount(opts.payment, 'Charge', opts.payment.amountPlanned.currencyCode);
53
+ if (totalCaptured > 0) {
54
+ return { isValid: false, reason: `Resource ${opts.payment.id} has already been charged.` };
57
55
  }
56
+ return { isValid: true };
57
+ }
58
+ validatePaymentCharge(opts) {
59
+ if (opts.payment.amountPlanned.currencyCode !== opts.amount.currencyCode) {
60
+ return {
61
+ isValid: false,
62
+ reason: `Invalid currency ${opts.amount.currencyCode} for resource ${opts.payment.id}, expected ${opts.payment.amountPlanned.currencyCode}`,
63
+ };
64
+ }
65
+ const totalAuthorized = this.calculateTotalAmount(opts.payment, 'Authorization', opts.amount.currencyCode);
66
+ if (totalAuthorized === 0) {
67
+ return { isValid: false, reason: `No authorization transaction found for resource ${opts.payment.id}.` };
68
+ }
69
+ const totalCaptured = this.calculateTotalAmount(opts.payment, 'Charge', opts.amount.currencyCode);
70
+ const allowedAmount = totalAuthorized - totalCaptured;
71
+ if (opts.amount.centAmount > allowedAmount) {
72
+ return {
73
+ isValid: false,
74
+ reason: `The amount to capture ${opts.amount.centAmount} exceeds the allowed amount [${allowedAmount}]`,
75
+ };
76
+ }
77
+ return { isValid: true };
78
+ }
79
+ validatePaymentRefund(opts) {
80
+ if (opts.payment.amountPlanned.currencyCode !== opts.amount.currencyCode) {
81
+ return {
82
+ isValid: false,
83
+ reason: `Invalid currency ${opts.amount.currencyCode} for resource ${opts.payment.id}, expected ${opts.payment.amountPlanned.currencyCode}`,
84
+ };
85
+ }
86
+ const totalCaptured = this.calculateTotalAmount(opts.payment, 'Charge', opts.amount.currencyCode);
87
+ if (totalCaptured === 0) {
88
+ return { isValid: false, reason: `No charge transaction found for resource ${opts.payment.id}.` };
89
+ }
90
+ const totalRefunded = this.calculateTotalAmount(opts.payment, 'Refund', opts.amount.currencyCode);
91
+ const allowedAmount = totalCaptured - totalRefunded;
92
+ if (opts.amount.centAmount > allowedAmount) {
93
+ return {
94
+ isValid: false,
95
+ reason: `The amount to refund ${opts.amount.centAmount} exceeds the allowed amount [${allowedAmount}]`,
96
+ };
97
+ }
98
+ return { isValid: true };
58
99
  }
59
100
  consolidateUpdateActions(payment, updateInfo) {
60
101
  const actions = [];
@@ -137,59 +178,6 @@ class DefaultPaymentService {
137
178
  }
138
179
  return actions;
139
180
  }
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
181
  calculateTotalAmount(payment, type, currencyCode) {
194
182
  return payment.transactions
195
183
  .filter((transaction) => transaction.type === type &&
@@ -13,4 +13,5 @@ export declare class DefaultSessionService implements SessionService {
13
13
  verifySession(sessionId: string): Promise<Session>;
14
14
  getCartFromSession(session: Session): string;
15
15
  getAllowedPaymentMethodsFromSession(session: Session): string[];
16
+ getPaymentInterfaceFromSession(session: Session): string | undefined;
16
17
  }
@@ -41,5 +41,8 @@ class DefaultSessionService {
41
41
  getAllowedPaymentMethodsFromSession(session) {
42
42
  return session.metadata?.allowedPaymentMethods || [];
43
43
  }
44
+ getPaymentInterfaceFromSession(session) {
45
+ return session.metadata?.paymentInterface;
46
+ }
44
47
  }
45
48
  exports.DefaultSessionService = DefaultSessionService;
@@ -16,6 +16,6 @@ export type CartServiceOptions = {
16
16
  */
17
17
  export interface CartService {
18
18
  getCart(opts: GetCart): Promise<Cart>;
19
- getPaymentAmount(opts: GetPaymentAmount): PaymentAmount;
19
+ getPaymentAmount(opts: GetPaymentAmount): Promise<PaymentAmount>;
20
20
  addPayment(opts: AddPayment): Promise<Cart>;
21
21
  }
@@ -28,10 +28,16 @@ export type UpdatePayment = {
28
28
  transaction?: TransactionData;
29
29
  paymentMethod?: string;
30
30
  };
31
- export type PaymentModificationValidation = {
31
+ export type PaymentCancelAuthorizationValidation = {
32
+ payment: Payment;
33
+ };
34
+ export type PaymentChargeValidation = {
35
+ payment: Payment;
36
+ amount: Money;
37
+ };
38
+ export type PaymentRefundValidation = {
32
39
  payment: Payment;
33
40
  amount: Money;
34
- type: TransactionType;
35
41
  };
36
42
  export type PaymentModificationValidationResult = {
37
43
  isValid: boolean;
@@ -44,5 +50,7 @@ export interface PaymentService {
44
50
  getPayment(opts: GetPayment): Promise<Payment>;
45
51
  createPayment(draft: PaymentDraft): Promise<Payment>;
46
52
  updatePayment(opts: UpdatePayment): Promise<Payment>;
47
- validatePaymentModification(opts: PaymentModificationValidation): PaymentModificationValidationResult;
53
+ validatePaymentCancelAuthorization(opts: PaymentCancelAuthorizationValidation): PaymentModificationValidationResult;
54
+ validatePaymentCharge(opts: PaymentChargeValidation): PaymentModificationValidationResult;
55
+ validatePaymentRefund(opts: PaymentRefundValidation): PaymentModificationValidationResult;
48
56
  }
@@ -25,4 +25,5 @@ export interface SessionService {
25
25
  verifySession(sessionId: string): Promise<Session>;
26
26
  getCartFromSession(session: Session): string;
27
27
  getAllowedPaymentMethodsFromSession(session: Session): string[];
28
+ getPaymentInterfaceFromSession(session: Session): string | undefined;
28
29
  }
@@ -15,6 +15,7 @@ class SessionAuthenticationManager {
15
15
  return new authns_1.SessionAuthentication(principal.authHeader, {
16
16
  cartId: this.sessionService.getCartFromSession(session),
17
17
  allowedPaymentMethods: this.sessionService.getAllowedPaymentMethodsFromSession(session),
18
+ paymentInterface: this.sessionService.getPaymentInterfaceFromSession(session),
18
19
  });
19
20
  }
20
21
  catch (e) {
@@ -15,6 +15,7 @@ export type HeaderPrincipal = {
15
15
  export type SessionPrincipal = {
16
16
  cartId: string;
17
17
  allowedPaymentMethods: string[];
18
+ paymentInterface?: string;
18
19
  };
19
20
  export type Oauth2Principal = {
20
21
  clientId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercetools/connect-payments-sdk",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
4
4
  "description": "Payment SDK for commercetools payment connectors",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
- "@commercetools/platform-sdk": "7.2.0-alpha.4",
18
+ "@commercetools/platform-sdk": "7.3.0",
19
19
  "@commercetools/sdk-client-v2": "2.3.0",
20
20
  "jsonwebtoken": "9.0.2",
21
21
  "jwks-rsa": "3.1.0"