From 5d00b0cba1e287256a23bd32c0ac4eb4b971a480 Mon Sep 17 00:00:00 2001 From: Wouter Boereboom <62436079+wboereboom@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:27:16 +0200 Subject: [PATCH] add requestoptions to all checkout requests in order to use idempotency key. update paymentlinks resource creation to prevent code bleed in unit tests (#943) --- src/__tests__/checkout.spec.ts | 99 +++++++++++++++---- src/services/checkout.ts | 52 +++++----- .../resource/checkout/paymentLinksId.ts | 29 +----- 3 files changed, 112 insertions(+), 68 deletions(-) diff --git a/src/__tests__/checkout.spec.ts b/src/__tests__/checkout.spec.ts index f330ef5..c1695cd 100644 --- a/src/__tests__/checkout.spec.ts +++ b/src/__tests__/checkout.spec.ts @@ -1,22 +1,3 @@ -/* - * ###### - * ###### - * ############ ####( ###### #####. ###### ############ ############ - * ############# #####( ###### #####. ###### ############# ############# - * ###### #####( ###### #####. ###### ##### ###### ##### ###### - * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### - * ###### ###### #####( ###### #####. ###### ##### ##### ###### - * ############# ############# ############# ############# ##### ###### - * ############ ############ ############# ############ ##### ###### - * ###### - * ############# - * ############ - * Adyen NodeJS API Library - * Copyright (c) 2021 Adyen B.V. - * This file is open source and available under the MIT license. - * See the LICENSE file for more info. - */ - import nock from "nock"; import {createClient} from "../__mocks__/base"; import {paymentMethodsSuccess} from "../__mocks__/checkout/paymentMethodsSuccess"; @@ -174,6 +155,86 @@ afterEach(() => { }); describe("Checkout", (): void => { + test("should add idempotency key to request headers", async (): Promise => { + const paymentsRequest: PaymentRequest = createPaymentsCheckoutRequest(); + scope.post("/payments") + .reply(200, paymentsSuccess) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.payments(paymentsRequest, {idempotencyKey: "testKey"}); + + const paymentMethodsRequest: PaymentMethodsRequest = {merchantAccount}; + scope.post("/paymentMethods") + .reply(200, paymentMethodsSuccess) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.paymentMethods(paymentMethodsRequest, {idempotencyKey: "testKey"}); + + const expiresAt = "2019-12-17T10:05:29Z"; + const paymentLinkSuccess: PaymentLinkResponse = getPaymentLinkSuccess(expiresAt); + scope.post("/paymentLinks") + .reply(200, paymentLinkSuccess) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.paymentLinks(createPaymentLinkRequest(), {idempotencyKey: "testKey"}); + + scope.patch("/paymentLinks/321") + .reply(200, { ...paymentLinkSuccess, status: "expired" }) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.updatePaymentLinks("321", "expired", {idempotencyKey: "testKey"}); + + scope.get("/paymentLinks/123") + .reply(200, paymentLinkSuccess) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.getPaymentLinks("123", {idempotencyKey: "testKey"}); + + scope.post("/payments/details") + .reply(200, paymentDetailsSuccess) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.paymentsDetails(createPaymentsDetailsRequest(), {idempotencyKey: "testKey"}); + + scope.post("/paymentSession") + .reply(200, paymentSessionSuccess) + .matchHeader("Idempotency-Key", "testKey"); + const paymentSessionRequest: PaymentSetupRequest = createPaymentSessionRequest(); + await checkout.paymentSession(paymentSessionRequest, {idempotencyKey: "testKey"}); + + scope.post("/payments/result") + .reply(200, paymentsResultSuccess) + .matchHeader("Idempotency-Key", "testKey"); + const paymentResultRequest: PaymentVerificationRequest = { + payload: "This is a test payload", + }; + await checkout.paymentResult(paymentResultRequest, {idempotencyKey: "testKey"}); + + const orderRequest: CheckoutCreateOrderRequest = { + amount: createAmountObject("USD", 1000), + merchantAccount, + reference + }; + scope.post("/orders") + .reply(200, {}) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.orders(orderRequest, {idempotencyKey: "testKey"}); + + scope.post("/orders/cancel") + .reply(200, {}) + .matchHeader("Idempotency-Key", "testKey"); + await checkout.ordersCancel({ + order: { + orderData: "mock_data", + pspReference: "mock_pspref" + }, + merchantAccount + }, {idempotencyKey: "testKey"}); + + scope.post("/sessions") + .reply(200, sessionsSuccess) + .matchHeader("Idempotency-Key", "testKey"); + + const sessionsRequest: CreateCheckoutSessionRequest = createSessionRequest(); + await checkout.sessions(sessionsRequest, {idempotencyKey: "testKey"}); + + }); + + test.each([false, true])("should make a payment. isMock: %p", async (isMock): Promise => { !isMock && nock.restore(); scope.post("/payments") diff --git a/src/services/checkout.ts b/src/services/checkout.ts index 665d993..81a39e3 100644 --- a/src/services/checkout.ts +++ b/src/services/checkout.ts @@ -72,7 +72,6 @@ class Checkout extends ApiKeyAuthenticatedService { private readonly _paymentSession: PaymentSession; private readonly _paymentsResult: PaymentsResult; private readonly _paymentLinks: PaymentLinks; - private readonly _paymentLinksId: PaymentLinksId; private readonly _originKeys: OriginKeys; private readonly _paymentMethodsBalance: PaymentMethodsBalance; private readonly _orders: Orders; @@ -89,7 +88,6 @@ class Checkout extends ApiKeyAuthenticatedService { this._paymentSession = new PaymentSession(this); this._paymentsResult = new PaymentsResult(this); this._paymentLinks = new PaymentLinks(this); - this._paymentLinksId = new PaymentLinksId(this); this._originKeys = new OriginKeys(this); this._paymentMethodsBalance = new PaymentMethodsBalance(this); this._orders = new Orders(this); @@ -101,18 +99,20 @@ class Checkout extends ApiKeyAuthenticatedService { // Payments - public async sessions(checkoutSessionRequest: CreateCheckoutSessionRequest): Promise { + public async sessions(checkoutSessionRequest: CreateCheckoutSessionRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._sessions, checkoutSessionRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "CreateCheckoutSessionResponse"); } - public async paymentMethods(paymentMethodsRequest: PaymentMethodsRequest): Promise { + public async paymentMethods(paymentMethodsRequest: PaymentMethodsRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._paymentMethods, paymentMethodsRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "PaymentMethodsResponse"); } @@ -130,53 +130,56 @@ class Checkout extends ApiKeyAuthenticatedService { const response = await getJsonResponse( this._paymentsDetails, paymentsDetailsRequest, - requestOptions + requestOptions, ); return ObjectSerializer.deserialize(response, "PaymentResponse"); } - public async donations(donationRequest: PaymentDonationRequest): Promise { + public async donations(donationRequest: PaymentDonationRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._donations, donationRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "DonationResponse"); } - public async cardDetails(cardDetailsRequest: CardDetailsRequest): Promise { + public async cardDetails(cardDetailsRequest: CardDetailsRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._cardDetails, cardDetailsRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "CardDetailsResponse"); } // Payment Links - public async paymentLinks(paymentLinkRequest: CreatePaymentLinkRequest): Promise { + public async paymentLinks(paymentLinkRequest: CreatePaymentLinkRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._paymentLinks, - paymentLinkRequest + paymentLinkRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "PaymentLinkResponse"); } - public async getPaymentLinks(linkId: string): Promise { - this._paymentLinksId.id = linkId; + public async getPaymentLinks(linkId: string, requestOptions?: IRequest.Options): Promise { + const paymentLinksId = new PaymentLinksId(this, linkId); const response = await getJsonResponse, PaymentLinkResponse>( - this._paymentLinksId, + paymentLinksId, {}, - { method: "GET" } + {...{ method: "GET" }, ...requestOptions}, ); return ObjectSerializer.deserialize(response, "PaymentLinkResponse"); } - public async updatePaymentLinks(linkId: string, status: "expired"): Promise { - this._paymentLinksId.id = linkId; + public async updatePaymentLinks(linkId: string, status: "expired", requestOptions?: IRequest.Options): Promise { + const paymentLinksId = new PaymentLinksId(this, linkId); const response = await getJsonResponse, PaymentLinkResponse>( - this._paymentLinksId, + paymentLinksId, { status }, - { method: "PATCH" } + {...{ method: "PATCH" }, ...requestOptions}, ); return ObjectSerializer.deserialize(response, "PaymentLinkResponse"); } @@ -268,26 +271,29 @@ class Checkout extends ApiKeyAuthenticatedService { // Orders - public async paymentMethodsBalance(paymentMethodsBalanceRequest: CheckoutBalanceCheckRequest): Promise { + public async paymentMethodsBalance(paymentMethodsBalanceRequest: CheckoutBalanceCheckRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._paymentMethodsBalance, paymentMethodsBalanceRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "CheckoutBalanceCheckResponse"); } - public async orders(ordersRequest: CheckoutCreateOrderRequest): Promise { + public async orders(ordersRequest: CheckoutCreateOrderRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._orders, ordersRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "CheckoutCreateOrderResponse"); } - public async ordersCancel(ordersCancelRequest: CheckoutCancelOrderRequest): Promise { + public async ordersCancel(ordersCancelRequest: CheckoutCancelOrderRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._ordersCancel, ordersCancelRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "CheckoutCancelOrderResponse"); } @@ -306,20 +312,22 @@ class Checkout extends ApiKeyAuthenticatedService { return ObjectSerializer.deserialize(response, "PaymentSetupResponse"); } - public async paymentResult(paymentResultRequest: PaymentVerificationRequest): Promise { + public async paymentResult(paymentResultRequest: PaymentVerificationRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._paymentsResult, paymentResultRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "PaymentVerificationResponse"); } //Utility - public async originKeys(originKeysRequest: CheckoutUtilityRequest): Promise { + public async originKeys(originKeysRequest: CheckoutUtilityRequest, requestOptions?: IRequest.Options): Promise { const response = await getJsonResponse( this._originKeys, originKeysRequest, + requestOptions, ); return ObjectSerializer.deserialize(response, "CheckoutUtilityResponse"); } diff --git a/src/services/resource/checkout/paymentLinksId.ts b/src/services/resource/checkout/paymentLinksId.ts index eb644de..56b7a96 100644 --- a/src/services/resource/checkout/paymentLinksId.ts +++ b/src/services/resource/checkout/paymentLinksId.ts @@ -1,40 +1,15 @@ -/* - * ###### - * ###### - * ############ ####( ###### #####. ###### ############ ############ - * ############# #####( ###### #####. ###### ############# ############# - * ###### #####( ###### #####. ###### ##### ###### ##### ###### - * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### - * ###### ###### #####( ###### #####. ###### ##### ##### ###### - * ############# ############# ############# ############# ##### ###### - * ############ ############ ############# ############ ##### ###### - * ###### - * ############# - * ############ - * Adyen NodeJS API Library - * Copyright (c) 2020 Adyen B.V. - * This file is open source and available under the MIT license. - * See the LICENSE file for more info. - */ - import Client from "../../../client"; import Service from "../../../service"; import Resource from "../../resource"; class PaymentLinksId extends Resource { - static _id: string; - public constructor(service: Service) { + public constructor(service: Service, paymentLinksId: string) { super( service, - `${service.client.config.checkoutEndpoint}/${Client.CHECKOUT_API_VERSION}/paymentLinks`, + `${service.client.config.checkoutEndpoint}/${Client.CHECKOUT_API_VERSION}/paymentLinks/${paymentLinksId}`, ); } - - public set id(id: string) { - PaymentLinksId._id = id; - this.endpoint = `${this.endpoint}/${PaymentLinksId._id}`; - } } export default PaymentLinksId;